Automates version bumps across all version source files: - Cargo.toml (PRIMARY - workspace.package.version) - debian/changelog (prepend new entry) - debian/control (update Version field) - scripts/build-package.sh (update VERSION variable) - frontend/package.json (update version field) - Stale references check after bump Usage: ./scripts/bump-version.sh <new_version> <old_version>
15 KiB
Linux Patch API — Comprehensive Research Summary
Generated: 2026-04-28 Sources: Extracted .deb package (v1.0.0-1), pm-agent-client crate, linux_patch_manager project docs
1. What the Project Is and Does
Linux Patch API is a secure, mTLS-authenticated REST API service that runs on each managed Linux host. It is the agent-side counterpart to the Linux Patch Manager (the management plane). The agent exposes endpoints for:
- Health monitoring — liveness checks and uptime reporting
- System information — hostname, OS, kernel, architecture, pending reboot status
- Package listing — installed and upgradable packages with CVE associations
- Patch discovery — available patches with severity, CVE data, and reboot requirements
- Patch application — async job-based patch deployment with optional reboot
- Job status tracking — polling async job progress and output
- Job rollback — reverting a previously applied patch job
The agent is designed for fleet management at scale (up to 2,500 hosts per Manager instance) with:
- Mutual TLS (mTLS) authentication — TLS 1.3 only
- IP whitelist enforcement
- Asynchronous job processing for long-running operations
- Comprehensive audit logging
- Systemd integration with security hardening
Architecture Position
+-----------------------------+
| Linux Patch Manager | <- Management plane (separate project)
| (Rust/Axum + React/TS) |
| PostgreSQL + WebSocket |
+--------------+--------------+
|
| mTLS / REST + WSS (TLS 1.3, port 12443)
+-------+-------+
v v v
+------+ +------+ +------+
| Host | | Host | | Host | <- Linux Patch API agents (this project)
| A | | B | | C | (up to 2,500)
+------+ +------+ +------+
2. How to Build and Install
From Pre-built .deb Package (Recommended)
Download the release package:
wget https://gitea-lxc.moon-dragon.us/echo/linux_patch_api/releases/download/v0.0.2/linux-patch-api_1.0.0-1_amd64.deb
sudo apt install ./linux-patch-api_1.0.0-1_amd64.deb
Package dependencies (from DEBIAN/control):
systemdlibsystemd0libc6 (>= 2.39)libgcc-s1 (>= 4.2)
Installed files:
/usr/bin/linux-patch-api— the binary/etc/linux_patch_api/config.yaml— configuration file/etc/linux_patch_api/whitelist.yaml— IP whitelist/lib/systemd/system/linux-patch-api.service— systemd unit
Post-install actions (automatic via postinst script):
- Copies example configs if they don't exist
- Sets ownership to
linux-patch-api:linux-patch-api - Sets file permissions (640 on config/whitelist)
- Reloads systemd daemon
- Enables the service (does NOT auto-start — admin must configure first)
Installation Steps Summary
# 1. Install the package
sudo apt install ./linux-patch-api_1.0.0-1_amd64.deb
# 2. Configure /etc/linux_patch_api/config.yaml with your settings
sudo nano /etc/linux_patch_api/config.yaml
# 3. Place TLS certificates in /etc/linux_patch_api/certs/
# Required: ca.pem, server.pem, server.key
sudo mkdir -p /etc/linux_patch_api/certs
sudo cp ca.pem server.pem server.key /etc/linux_patch_api/certs/
# 4. Configure IP whitelist in /etc/linux_patch_api/whitelist.yaml
sudo nano /etc/linux_patch_api/whitelist.yaml
# 5. Start the service
sudo systemctl start linux-patch-api
sudo systemctl status linux-patch-api
3. Configuration Requirements
config.yaml — Full Reference
# Server Configuration
server:
port: 12443 # HTTPS/mTLS port (default: 12443)
bind: "0.0.0.0" # Bind address
timeout_seconds: 30 # Request timeout
# TLS/mTLS Configuration
tls:
enabled: true # TLS is mandatory
port: 12443 # TLS port
ca_cert: "/etc/linux_patch_api/certs/ca.pem" # Internal CA certificate
server_cert: "/etc/linux_patch_api/certs/server.pem" # Server certificate
server_key: "/etc/linux_patch_api/certs/server.key" # Server private key
min_tls_version: "1.3" # TLS 1.3 enforced
# Job Configuration
jobs:
max_concurrent: 5 # Maximum concurrent async jobs
timeout_minutes: 30 # Job timeout
storage_path: "/var/lib/linux_patch_api/jobs" # Job state directory
# Logging Configuration
logging:
level: "info" # trace, debug, info, warn, error
journal_enabled: true # systemd journal logging
syslog_enabled: false # syslog (optional)
# syslog_server: "udp://localhost:514"
file_path: "/var/log/linux_patch_api/audit.log" # Audit log file
retention_days: 30 # Log retention
# IP Whitelist Configuration
whitelist:
path: "/etc/linux_patch_api/whitelist.yaml"
# Entries: individual IPs, CIDR subnets, or hostnames
# Package Manager Backend
package_manager:
backend: "auto" # auto, apt, dnf, yum, apk, pacman
whitelist.yaml — IP Whitelist
# Block all by default - only listed IPs/CIDRs/hostnames can access the API
entries:
- "192.168.1.0/24" # Management network
- "10.0.0.50" # Specific admin workstation
# - "admin-server.internal" # Hostname (resolved at startup)
Required Directories and Permissions
| Path | Purpose | Owner | Mode |
|---|---|---|---|
/etc/linux_patch_api/ |
Configuration | linux-patch-api:linux-patch-api | 750 |
/etc/linux_patch_api/config.yaml |
Main config | linux-patch-api:linux-patch-api | 640 |
/etc/linux_patch_api/whitelist.yaml |
IP whitelist | linux-patch-api:linux-patch-api | 640 |
/etc/linux_patch_api/certs/ |
TLS certificates | — | 700 |
/var/lib/linux_patch_api/jobs/ |
Job state | linux-patch-api:linux-patch-api | 750 |
/var/log/linux_patch_api/ |
Audit logs | linux-patch-api:linux-patch-api | 750 |
Required TLS Certificates
The agent requires three PEM files in /etc/linux_patch_api/certs/:
- ca.pem — Internal CA certificate (same CA used by Patch Manager for mTLS)
- server.pem — Server certificate issued by the internal CA
- server.key — Server private key (must be 0600 permissions)
These are distributed manually by server administrators (not automated by the Manager).
4. How It Connects to the Patch Manager Server
Connection Model
- Protocol: HTTPS with mutual TLS (mTLS)
- TLS Version: 1.3 only (enforced, no fallback)
- Port: 12443
- Base Path:
/api/v1/ - Direction: Manager initiates connections to agents (agents are passive servers)
- Authentication: mTLS with client certificates issued by the Patch Manager's internal CA
mTLS Configuration (Manager Side)
The Patch Manager's config.example.toml specifies:
[security]
# mTLS client certificate for agent communication
agent_client_cert_path = "/etc/patch-manager/certs/client.crt"
agent_client_key_path = "/etc/patch-manager/certs/client.key"
# Internal CA certificate and private key
ca_cert_path = "/etc/patch-manager/ca/ca.crt"
ca_key_path = "/etc/patch-manager/ca/ca.key"
The pm-agent-client crate constructs the mTLS client with:
rustlsTLS backend (no OpenSSL dependency)- Built-in system root CAs disabled — only the internal CA is trusted
- TLS 1.3 minimum version enforced
- Client identity (cert + key) presented during handshake
- 30-second request timeout
API Endpoints
All endpoints are prefixed with /api/v1/. The Manager communicates with agents via the AgentClient struct.
| Method | Endpoint | Description | Request | Response Type |
|---|---|---|---|---|
| GET | /api/v1/health |
Agent liveness check | — | HealthData |
| GET | /api/v1/system/info |
Host system information | — | SystemInfoData |
| GET | /api/v1/packages?status=upgradable |
List upgradable packages | Query param | PackagesData |
| GET | /api/v1/patches |
List available patches | — | PatchesData |
| POST | /api/v1/patches/apply |
Trigger patch application | ApplyPatchesRequest |
ApplyPatchesResponse |
| GET | /api/v1/jobs/{id} |
Poll async job status | Path param | AgentJobStatus |
| POST | /api/v1/jobs/{id}/rollback |
Rollback a patch job | Path param | RollbackResponse |
Response Envelope
All agent responses use a standard envelope:
{
"success": true,
"request_id": "uuid-v4",
"timestamp": "2026-04-28T12:00:00Z",
"data": { ... },
"error": null
}
On error:
{
"success": false,
"request_id": "uuid-v4",
"timestamp": "2026-04-28T12:00:00Z",
"data": null,
"error": {
"code": "INTERNAL_ERROR",
"message": "Description",
"details": null,
"retryable": false
}
}
Response Type Details
HealthData
{
"status": "ok", // "ok" or "degraded"
"uptime_seconds": 86400,
"version": "1.0.0"
}
SystemInfoData
{
"hostname": "web-server-01",
"os": "Ubuntu",
"os_version": "24.04",
"kernel": "6.8.0-45-generic",
"architecture": "x86_64",
"last_update_check": "2026-04-28T10:00:00Z",
"last_update_apply": "2026-04-27T02:00:00Z",
"pending_reboot": false
}
PackagesData
{
"packages": [
{
"name": "openssl",
"version": "3.0.13-0ubuntu3",
"status": "upgradable",
"upgradable": true,
"latest_version": "3.0.13-0ubuntu3.1",
"description": "Secure Sockets Layer toolkit",
"cve_ids": ["CVE-2024-1234"]
}
],
"total": 42
}
PatchesData
{
"patches": [
{
"name": "openssl",
"current_version": "3.0.13-0ubuntu3",
"available_version": "3.0.13-0ubuntu3.1",
"severity": "critical",
"description": "Security update for OpenSSL",
"cve_ids": ["CVE-2024-1234"],
"requires_reboot": false
}
],
"total": 15,
"security_updates": 3,
"requires_reboot": true
}
ApplyPatchesRequest
{
"packages": ["openssl", "nginx"], // Empty = apply all
"allow_reboot": true
}
ApplyPatchesResponse
{
"job_id": "abc-123-def",
"status": "running" // "running" or "queued"
}
AgentJobStatus
{
"job_id": "abc-123-def",
"status": "running", // "running", "succeeded", "failed", "cancelled"
"progress_percent": 75,
"output": "Applying openssl...",
"error": null,
"started_at": "2026-04-28T12:00:00Z",
"completed_at": null
}
RollbackResponse
{
"job_id": "abc-123-def",
"status": "running"
}
Polling and Communication Patterns
The Patch Manager communicates with agents on two schedules:
- Health polling — every 5 minutes (configurable via
health_poll_interval_secs) - Patch data polling — every 30 minutes (configurable via
patch_poll_interval_secs) - On-demand refresh — triggered by operator from the UI
- Patch deployment — triggered by operator, either queued for maintenance window or immediate
- Job status — polled via
GET /api/v1/jobs/{id}or streamed via WebSocket
The Manager uses exponential backoff retry (3 retries, max 30 minutes) for failed agent communications.
5. Setup Scripts and Systemd Units
Agent Systemd Unit (linux-patch-api.service)
[Unit]
Description=Linux Patch API - Secure Remote Package Management
Documentation=man:linux-patch-api(8)
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/linux-patch-api --config /etc/linux_patch_api/config.yaml
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
# Process management
RuntimeDirectory=linux-patch-api
RuntimeDirectoryMode=0755
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/linux_patch_api /var/log/linux_patch_api
PrivateTmp=true
PrivateDevices=true
ProtectHostname=true
ProtectClock=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=false
RestrictRealtime=true
RestrictSUIDSGID=true
RemoveIPC=true
# System call filtering
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
# Environment
Environment="RUST_BACKTRACE=1"
Environment="RUST_LOG=info"
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=linux-patch-api
SyslogFacility=daemon
SyslogLevel=info
# Resource limits
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=multi-user.target
Key systemd hardening features:
Type=notify— service notifies systemd when readyProtectSystem=strict— read-only filesystem except explicit write pathsNoNewPrivileges=true— prevents privilege escalationSystemCallFilter=@system-service— whitelist-only syscall filteringPrivateTmp,PrivateDevices,ProtectKernel*— kernel resource isolation
Manager Systemd Units
patch-manager.target — groups both services:
[Unit]
Description=Linux Patch Manager — Service Target
Wants=patch-manager-web.service patch-manager-worker.service
[Install]
WantedBy=multi-user.target
patch-manager-web.service — Axum web server:
- Binary:
/usr/local/bin/pm-web - Config:
/etc/patch-manager/config.toml - Runs as
patch-manageruser - Requires PostgreSQL
AmbientCapabilities=CAP_NET_BIND_SERVICEfor port 443- Restart: always, 5s delay
patch-manager-worker.service — Background worker:
- Binary:
/usr/local/bin/pm-worker - Config:
/etc/patch-manager/config.toml - Runs as
patch-manageruser - Requires PostgreSQL, wants pm-web
- Restart: always, 10s delay
- Longer stop timeout (120s) for job draining
Manager Setup Script (setup.sh)
The scripts/setup.sh automates initial host setup:
- Creates
patch-managerservice user/group - Creates directory structure (
/etc/patch-manager/{ca,certs,jwt,tls},/var/log/patch-manager,/opt/patch-manager,/usr/share/patch-manager/frontend,/var/backups/patch-manager) - Installs PostgreSQL 16 if not present
- Creates database and user with random password
- Writes config.toml with database URL
- Generates Ed25519 JWT signing/verification keys
- Generates self-signed TLS certificate (ECDSA P-256, valid 365 days, SAN for hostname + localhost)
- Installs systemd units
- Installs binaries and frontend assets
- Runs database migrations
Appendix: Package Manager Backend Support
The agent's package_manager.backend config supports:
| Value | Distribution | Package Manager |
|---|---|---|
apt |
Debian, Ubuntu | APT |
dnf |
RHEL 8+, Fedora | DNF |
yum |
CentOS 7, older RHEL | YUM |
apk |
Alpine | APK |
pacman |
Arch Linux | Pacman |
auto |
Auto-detected | Detected at runtime |
Appendix: Certificate Distribution Model
- Patch Manager runs an internal CA on the same host
- The CA issues:
- Server certificates for the web UI
- Client certificates for mTLS communication with agents
- The CA certificate itself is distributed to agents
- Agent certificates are manually distributed by server administrators
- The Patch Manager has no direct permissions on managed clients
- Certificate renewal is handled by the Patch Manager; physical distribution is manual
Appendix: Error Handling
- Agent unreachable: Manager marks host as unhealthy, retries with exponential backoff (3 retries, max 30 min between)
- Patch job failure: Auto-retry once if within maintenance window, then surface to operator
- Batch partial failure: Auto-retry failed hosts once, report remaining failures
- Agent error responses: Structured
AgentErrorBodywithcode,message,details,retryableflag