Skip to main content

Draft - Auto-enrolment api docs

Overview

This document provides comprehensive API documentation for PatchMon's Proxmox integration, focusing on how to leverage the auto-enrollment API endpoints for automated device management using tools like Ansible.

The PatchMon Proxmox integration provides a secure, token-based API for automatically enrolling LXC containers from Proxmox hosts into PatchMon for centralized patch management.

Table of Contents

API Architecture

Base URL Structure

https://your-patchmon-server.com/api/v1/

Note: The API version is configurable via the API_VERSION environment variable (defaults to v1).

Endpoint Categories

  1. Admin Endpoints (/auto-enrollment/tokens/*) - Token management
  2. Enrollment Endpoints (/auto-enrollment/*) - Host enrollment
  3. Host Endpoints (/hosts/*) - Host management and agent installation

Two-Tier Security Model

Tier 1: Auto-Enrollment Token

  • Purpose: Create new host entries
  • Scope: Limited to enrollment operations
  • Authentication: Token key + secret
  • Rate Limited: Yes (hosts per day)

Tier 2: Host API Credentials

  • Purpose: Agent communication and data reporting
  • Scope: Per-host unique credentials
  • Authentication: API ID + key
  • Rate Limited: No (per-host)

Authentication

Admin Endpoints Authentication

Admin endpoints require JWT authentication:

curl -H "Authorization: Bearer <jwt_token>" \
     -H "Content-Type: application/json" \
     https://your-patchmon-server.com/api/v1/auto-enrollment/tokens

Enrollment Endpoints Authentication

Enrollment endpoints use token-based authentication:

curl -H "X-Auto-Enrollment-Key: patchmon_ae_abc123..." \
     -H "X-Auto-Enrollment-Secret: def456ghi789..." \
     -H "Content-Type: application/json" \
     https://your-patchmon-server.com/api/v1/auto-enrollment/enroll

Host Endpoints Authentication

Host endpoints use API credentials:

curl -H "X-API-ID: patchmon_abc123" \
     -H "X-API-KEY: def456ghi789" \
     https://your-patchmon-server.com/api/v1/hosts/install

Admin Endpoints

Create Auto-Enrollment Token

Endpoint: POST /api/v1/auto-enrollment/tokens

Headers:

Authorization: Bearer <jwt_token>
Content-Type: application/json

Request Body:

{
  "token_name": "Proxmox Production",
  "max_hosts_per_day": 100,
  "default_host_group_id": "uuid-of-host-group",
  "allowed_ip_ranges": ["192.168.1.10", "10.0.0.0/24"],
  "expires_at": "2026-12-31T23:59:59Z",
  "metadata": {
    "integration_type": "proxmox-lxc",
    "environment": "production",
    "description": "Token for production Proxmox cluster"
  }
}

Response: 201 Created

{
  "message": "Auto-enrollment token created successfully",
  "token": {
    "id": "uuid",
    "token_name": "Proxmox Production",
    "token_key": "patchmon_ae_abc123...",
    "token_secret": "def456ghi789...",
    "max_hosts_per_day": 100,
    "default_host_group": {
      "id": "uuid",
      "name": "Proxmox LXC",
      "color": "#3B82F6"
    },
    "created_by": {
      "id": "uuid",
      "username": "admin",
      "first_name": "John",
      "last_name": "Doe"
    },
    "expires_at": "2026-12-31T23:59:59Z"
  },
  "warning": "⚠️ Save the token_secret now - it cannot be retrieved later!"
}

List Auto-Enrollment Tokens

Endpoint: GET /api/v1/auto-enrollment/tokens

Response: 200 OK

[
  {
    "id": "uuid",
    "token_name": "Proxmox Production",
    "token_key": "patchmon_ae_abc123...",
    "is_active": true,
    "allowed_ip_ranges": ["192.168.1.10"],
    "max_hosts_per_day": 100,
    "hosts_created_today": 15,
    "last_used_at": "2025-10-11T14:30:00Z",
    "expires_at": "2026-12-31T23:59:59Z",
    "created_at": "2025-10-01T10:00:00Z",
    "default_host_group_id": "uuid",
    "metadata": {"integration_type": "proxmox-lxc"},
    "host_groups": {
      "id": "uuid",
      "name": "Proxmox LXC",
      "color": "#3B82F6"
    },
    "users": {
      "id": "uuid",
      "username": "admin",
      "first_name": "John",
      "last_name": "Doe"
    }
  }
]

Get Token Details

Endpoint: GET /api/v1/auto-enrollment/tokens/{tokenId}

Response: 200 OK (same structure as single token in list)

Update Token

Endpoint: PATCH /api/v1/auto-enrollment/tokens/{tokenId}

Request Body:

{
  "is_active": false,
  "max_hosts_per_day": 200,
  "allowed_ip_ranges": ["192.168.1.0/24"],
  "expires_at": "2027-01-01T00:00:00Z"
}

Delete Token

Endpoint: DELETE /api/v1/auto-enrollment/tokens/{tokenId}

Response: 200 OK

{
  "message": "Auto-enrollment token deleted successfully",
  "deleted_token": {
    "id": "uuid",
    "token_name": "Proxmox Production"
  }
}

Enrollment Endpoints

Download Proxmox Enrollment Script

Endpoint: GET /api/v1/auto-enrollment/proxmox-lxc

Query Parameters:

  • token_key (required): Auto-enrollment token key
  • token_secret (required): Auto-enrollment token secret
  • force (optional): true to enable force install mode

Example:

curl "https://patchmon.example.com/api/v1/auto-enrollment/proxmox-lxc?token_key=KEY&token_secret=SECRET&force=true"

Response: 200 OK (bash script with credentials injected)

Enroll Single Host

Endpoint: POST /api/v1/auto-enrollment/enroll

Headers:

X-Auto-Enrollment-Key: patchmon_ae_abc123...
X-Auto-Enrollment-Secret: def456ghi789...
Content-Type: application/json

Request Body:

{
  "friendly_name": "webserver",
  "machine_id": "proxmox-lxc-100-abc123",
  "metadata": {
    "vmid": "100",
    "proxmox_node": "proxmox01",
    "ip_address": "10.0.0.10",
    "os_info": "Ubuntu 22.04 LTS",
    "container_type": "lxc"
  }
}

Response: 201 Created

{
  "message": "Host enrolled successfully",
  "host": {
    "id": "uuid",
    "friendly_name": "webserver",
    "api_id": "patchmon_abc123",
    "api_key": "def456ghi789",
    "host_group": {
      "id": "uuid",
      "name": "Proxmox LXC",
      "color": "#3B82F6"
    },
    "status": "pending"
  }
}

Error Responses:

400 Bad Request - Validation errors:

{
  "errors": [
    {
      "msg": "Friendly name is required",
      "param": "friendly_name",
      "location": "body"
    }
  ]
}

401 Unauthorized - Invalid credentials:

{
  "error": "Auto-enrollment credentials required"
}

403 Forbidden - IP not authorized:

{
  "error": "IP address not authorized for this token"
}

409 Conflict - Host already exists:

{
  "error": "Host already exists",
  "host_id": "uuid",
  "api_id": "patchmon_abc123",
  "machine_id": "proxmox-lxc-100-abc123",
  "friendly_name": "webserver",
  "message": "This machine is already enrolled in PatchMon (matched by machine ID)"
}

429 Too Many Requests - Rate limit exceeded:

{
  "error": "Rate limit exceeded",
  "message": "Maximum 100 hosts per day allowed for this token"
}

Bulk Enroll Hosts

Endpoint: POST /api/v1/auto-enrollment/enroll/bulk

Headers:

X-Auto-Enrollment-Key: patchmon_ae_abc123...
X-Auto-Enrollment-Secret: def456ghi789...
Content-Type: application/json

Request Body:

{
  "hosts": [
    {
      "friendly_name": "webserver",
      "machine_id": "proxmox-lxc-100-abc123",
      "metadata": {
        "vmid": "100",
        "proxmox_node": "proxmox01"
      }
    },
    {
      "friendly_name": "database",
      "machine_id": "proxmox-lxc-101-def456",
      "metadata": {
        "vmid": "101",
        "proxmox_node": "proxmox01"
      }
    }
  ]
}

Limits:

  • Minimum: 1 host
  • Maximum: 50 hosts per request

Response: 201 Created

{
  "message": "Bulk enrollment completed: 2 succeeded, 0 failed, 0 skipped",
  "results": {
    "success": [
      {
        "id": "uuid",
        "friendly_name": "webserver",
        "api_id": "patchmon_abc123",
        "api_key": "def456"
      },
      {
        "id": "uuid",
        "friendly_name": "database",
        "api_id": "patchmon_ghi789",
        "api_key": "jkl012"
      }
    ],
    "failed": [],
    "skipped": []
  }
}

Host Management Endpoints

Download Agent Installation Script

Endpoint: GET /api/v1/hosts/install

Headers:

X-API-ID: patchmon_abc123
X-API-KEY: def456ghi789

Query Parameters:

  • force (optional): true to enable force install mode

Response: 200 OK (bash script with credentials injected)

Download Agent Script

Endpoint: GET /api/v1/hosts/agent/download

Headers:

X-API-ID: patchmon_abc123
X-API-KEY: def456ghi789

Response: 200 OK (agent script with credentials injected)

Host Data Update

Endpoint: POST /api/v1/hosts/update

Headers:

X-API-ID: patchmon_abc123
X-API-KEY: def456ghi789
Content-Type: application/json

Request Body:

{
  "packages": [
    {
      "name": "nginx",
      "currentVersion": "1.18.0",
      "availableVersion": "1.20.0",
      "needsUpdate": true,
      "isSecurityUpdate": false
    }
  ],
  "agentVersion": "1.2.3",
  "cpuModel": "Intel Xeon E5-2680 v4",
  "cpuCores": 8,
  "ramInstalled": 16.0,
  "swapSize": 2.0,
  "diskDetails": [
    {
      "device": "/dev/sda1",
      "mountPoint": "/",
      "size": "50GB",
      "used": "25GB",
      "available": "25GB"
    }
  ],
  "gatewayIp": "192.168.1.1",
  "dnsServers": ["8.8.8.8", "8.8.4.4"],
  "networkInterfaces": [
    {
      "name": "eth0",
      "ip": "192.168.1.10",
      "mac": "00:11:22:33:44:55"
    }
  ],
  "kernelVersion": "5.4.0-74-generic",
  "selinuxStatus": "disabled"
}

Ansible Integration Examples

Basic Ansible Playbook for Proxmox Enrollment

---
- name: Enroll Proxmox LXC containers in PatchMon
  hosts: proxmox_hosts
  become: yes
  vars:
    patchmon_url: "https://patchmon.example.com"
    token_key: "{{ vault_patchmon_token_key }}"
    token_secret: "{{ vault_patchmon_token_secret }}"
    host_prefix: "prod-"
    
  tasks:
    - name: Install dependencies
      apt:
        name:
          - curl
          - jq
        state: present

    - name: Download enrollment script
      get_url:
        url: "{{ patchmon_url }}/api/v1/auto-enrollment/proxmox-lxc?token_key={{ token_key }}&token_secret={{ token_secret }}"
        dest: /root/proxmox_auto_enroll.sh
        mode: '0700'

    - name: Run enrollment
      command: /root/proxmox_auto_enroll.sh
      environment:
        HOST_PREFIX: "{{ host_prefix }}"
        DEBUG: "true"
      register: enrollment_output

    - name: Show enrollment results
      debug:
        var: enrollment_output.stdout_lines

Advanced Ansible Playbook with Token Management

---
- name: Manage PatchMon Proxmox Integration
  hosts: localhost
  vars:
    patchmon_url: "https://patchmon.example.com"
    admin_token: "{{ vault_patchmon_admin_token }}"
    
  tasks:
    - name: Create Proxmox enrollment token
      uri:
        url: "{{ patchmon_url }}/api/v1/auto-enrollment/tokens"
        method: POST
        headers:
          Authorization: "Bearer {{ admin_token }}"
          Content-Type: "application/json"
        body_format: json
        body:
          token_name: "{{ inventory_hostname }}-proxmox"
          max_hosts_per_day: 200
          default_host_group_id: "{{ proxmox_host_group_id }}"
          allowed_ip_ranges: ["{{ proxmox_host_ip }}"]
          expires_at: "2026-12-31T23:59:59Z"
          metadata:
            integration_type: "proxmox-lxc"
            environment: "{{ environment }}"
            created_by_ansible: true
        status_code: 201
      register: token_response

    - name: Store token credentials
      set_fact:
        enrollment_token_key: "{{ token_response.json.token.token_key }}"
        enrollment_token_secret: "{{ token_response.json.token.token_secret }}"

    - name: Deploy enrollment script to Proxmox hosts
      include_tasks: deploy_enrollment.yml
      vars:
        enrollment_token_key: "{{ enrollment_token_key }}"
        enrollment_token_secret: "{{ enrollment_token_secret }}"

Ansible Task for Bulk Enrollment

---
- name: Bulk enroll Proxmox containers
  hosts: proxmox_hosts
  become: yes
  vars:
    patchmon_url: "https://patchmon.example.com"
    token_key: "{{ vault_patchmon_token_key }}"
    token_secret: "{{ vault_patchmon_token_secret }}"
    
  tasks:
    - name: Get LXC container list
      shell: |
        pct list | tail -n +2 | while read -r line; do
          vmid=$(echo "$line" | awk '{print $1}')
          name=$(echo "$line" | awk '{print $3}')
          status=$(echo "$line" | awk '{print $2}')
          
          if [ "$status" = "running" ]; then
            machine_id=$(pct exec "$vmid" -- bash -c "cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null || echo 'proxmox-lxc-$vmid-'$(cat /proc/sys/kernel/random/uuid)" 2>/dev/null || echo "proxmox-lxc-$vmid-unknown")
            echo "{\"friendly_name\":\"$name\",\"machine_id\":\"$machine_id\",\"metadata\":{\"vmid\":\"$vmid\",\"proxmox_node\":\"$(hostname)\"}}"
          fi
        done | jq -s '.'
      register: containers_json

    - name: Bulk enroll containers
      uri:
        url: "{{ patchmon_url }}/api/v1/auto-enrollment/enroll/bulk"
        method: POST
        headers:
          X-Auto-Enrollment-Key: "{{ token_key }}"
          X-Auto-Enrollment-Secret: "{{ token_secret }}"
          Content-Type: "application/json"
        body_format: json
        body:
          hosts: "{{ containers_json.stdout | from_json }}"
        status_code: 201
      register: enrollment_result

    - name: Display enrollment results
      debug:
        msg: "{{ enrollment_result.json.message }}"

Ansible Role for PatchMon Integration

# roles/patchmon_proxmox/tasks/main.yml
---
- name: Install PatchMon dependencies
  package:
    name:
      - curl
      - jq
    state: present

- name: Create PatchMon directory
  file:
    path: /opt/patchmon
    state: directory
    mode: '0755'

- name: Download enrollment script
  get_url:
    url: "{{ patchmon_url }}/api/v1/auto-enrollment/proxmox-lxc?token_key={{ token_key }}&token_secret={{ token_secret }}&force={{ force_install | default('false') }}"
    dest: /opt/patchmon/proxmox_auto_enroll.sh
    mode: '0700'

- name: Run enrollment script
  command: /opt/patchmon/proxmox_auto_enroll.sh
  environment:
    PATCHMON_URL: "{{ patchmon_url }}"
    AUTO_ENROLLMENT_KEY: "{{ token_key }}"
    AUTO_ENROLLMENT_SECRET: "{{ token_secret }}"
    HOST_PREFIX: "{{ host_prefix | default('') }}"
    DRY_RUN: "{{ dry_run | default('false') }}"
    DEBUG: "{{ debug | default('false') }}"
    FORCE_INSTALL: "{{ force_install | default('false') }}"
  register: enrollment_output

- name: Display enrollment results
  debug:
    var: enrollment_output.stdout_lines
  when: enrollment_output.stdout_lines is defined

- name: Fail if enrollment had errors
  fail:
    msg: "Enrollment failed with errors"
  when: enrollment_output.rc != 0

Ansible Vault Integration

# group_vars/all/vault.yml (encrypted with ansible-vault)
---
vault_patchmon_admin_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
vault_patchmon_token_key: "patchmon_ae_abc123..."
vault_patchmon_token_secret: "def456ghi789..."

Ansible Playbook with Error Handling

---
- name: Robust Proxmox enrollment with error handling
  hosts: proxmox_hosts
  become: yes
  vars:
    patchmon_url: "https://patchmon.example.com"
    token_key: "{{ vault_patchmon_token_key }}"
    token_secret: "{{ vault_patchmon_token_secret }}"
    max_retries: 3
    retry_delay: 30
    
  tasks:
    - name: Test PatchMon connectivity
      uri:
        url: "{{ patchmon_url }}/api/v1/auto-enrollment/tokens"
        method: GET
        headers:
          Authorization: "Bearer {{ vault_patchmon_admin_token }}"
        status_code: 200
      retries: "{{ max_retries }}"
      delay: "{{ retry_delay }}"

    - name: Download enrollment script
      get_url:
        url: "{{ patchmon_url }}/api/v1/auto-enrollment/proxmox-lxc?token_key={{ token_key }}&token_secret={{ token_secret }}"
        dest: /root/proxmox_auto_enroll.sh
        mode: '0700'
      retries: "{{ max_retries }}"
      delay: "{{ retry_delay }}"

    - name: Run enrollment with retry logic
      shell: |
        for i in {1..{{ max_retries }}}; do
          echo "Attempt $i of {{ max_retries }}"
          if /root/proxmox_auto_enroll.sh; then
            echo "Enrollment successful"
            exit 0
          else
            echo "Enrollment failed, retrying in {{ retry_delay }} seconds..."
            sleep {{ retry_delay }}
          fi
        done
        echo "All enrollment attempts failed"
        exit 1
      register: enrollment_result

    - name: Handle enrollment failure
      fail:
        msg: "Proxmox enrollment failed after {{ max_retries }} attempts"
      when: enrollment_result.rc != 0

    - name: Parse enrollment results
      set_fact:
        enrolled_count: "{{ enrollment_result.stdout | regex_search('Successfully Enrolled:\\s+(\\d+)', '\\1') | default('0') }}"
        failed_count: "{{ enrollment_result.stdout | regex_search('Failed:\\s+(\\d+)', '\\1') | default('0') }}"

    - name: Report enrollment statistics
      debug:
        msg: |
          Enrollment completed:
          - Successfully enrolled: {{ enrolled_count }} containers
          - Failed: {{ failed_count }} containers

Error Handling

Common HTTP Status Codes

Code Meaning Description
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request data
401 Unauthorized Invalid authentication
403 Forbidden IP not authorized
404 Not Found Resource not found
409 Conflict Host already exists
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server error

Error Response Format

{
  "error": "Error message",
  "message": "Detailed description",
  "code": "ERROR_CODE",
  "details": {
    "field": "additional context"
  }
}

Validation Errors

{
  "errors": [
    {
      "msg": "Token name is required",
      "param": "token_name",
      "location": "body"
    }
  ]
}

Rate Limiting

Token Rate Limits

  • Default: 100 hosts per day per token
  • Maximum: 1000 hosts per day per token
  • Reset: Daily at midnight UTC
  • Scope: Per-token (not per-host)

Rate Limit Headers

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 85
X-RateLimit-Reset: 1640995200

Rate Limit Exceeded Response

{
  "error": "Rate limit exceeded",
  "message": "Maximum 100 hosts per day allowed for this token",
  "retry_after": 3600
}

Security Considerations

Token Security

  1. Secret Storage: Token secrets are hashed using bcrypt
  2. One-Time Display: Secrets are only shown during token creation
  3. Rotation: Regular token rotation recommended (90 days)
  4. Scope: Tokens only create hosts, cannot access existing data

IP Restrictions

{
  "allowed_ip_ranges": ["192.168.1.10", "10.0.0.0/24"]
}

Network Security

  • Use HTTPS in production
  • Implement proper firewall rules
  • Consider VPN for remote Proxmox hosts
  • Monitor for unusual enrollment patterns

Audit Logging

All enrollment activities are logged with:

  • Token name and ID
  • Enrolled host details
  • Timestamp and source IP
  • Success/failure status

API Reference

Complete Endpoint List

Admin Endpoints (JWT Authentication)

  • POST /api/v1/auto-enrollment/tokens - Create token
  • GET /api/v1/auto-enrollment/tokens - List tokens
  • GET /api/v1/auto-enrollment/tokens/{tokenId} - Get token details
  • PATCH /api/v1/auto-enrollment/tokens/{tokenId} - Update token
  • DELETE /api/v1/auto-enrollment/tokens/{tokenId} - Delete token

Enrollment Endpoints (Token Authentication)

  • GET /api/v1/auto-enrollment/proxmox-lxc - Download script
  • POST /api/v1/auto-enrollment/enroll - Enroll single host
  • POST /api/v1/auto-enrollment/enroll/bulk - Bulk enroll hosts

Host Endpoints (API Credentials)

  • GET /api/v1/hosts/install - Download installation script
  • GET /api/v1/hosts/agent/download - Download agent script
  • POST /api/v1/hosts/update - Update host data

Request/Response Examples

Create Token Request

curl -X POST \
  -H "Authorization: Bearer <jwt_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "token_name": "Production Proxmox",
    "max_hosts_per_day": 100,
    "default_host_group_id": "uuid",
    "allowed_ip_ranges": ["192.168.1.10"],
    "expires_at": "2026-12-31T23:59:59Z"
  }' \
  https://patchmon.example.com/api/v1/auto-enrollment/tokens

Enroll Host Request

curl -X POST \
  -H "X-Auto-Enrollment-Key: patchmon_ae_abc123..." \
  -H "X-Auto-Enrollment-Secret: def456ghi789..." \
  -H "Content-Type: application/json" \
  -d '{
    "friendly_name": "webserver",
    "machine_id": "proxmox-lxc-100-abc123",
    "metadata": {
      "vmid": "100",
      "proxmox_node": "proxmox01"
    }
  }' \
  https://patchmon.example.com/api/v1/auto-enrollment/enroll

Download Installation Script

curl -H "X-API-ID: patchmon_abc123" \
     -H "X-API-KEY: def456ghi789" \
     https://patchmon.example.com/api/v1/hosts/install

Integration Patterns

Pattern 1: Direct Script Download

# Download and execute in one command
curl -s "https://patchmon.example.com/api/v1/auto-enrollment/proxmox-lxc?token_key=KEY&token_secret=SECRET" | bash

Pattern 2: API-First Approach

# 1. Create token via API
# 2. Use token to enroll hosts via API
# 3. Download agent scripts for each host
# 4. Install agents using host-specific credentials

Pattern 3: Hybrid Approach

# 1. Create token via API
# 2. Download enrollment script with token embedded
# 3. Run script on Proxmox hosts
# 4. Script handles both enrollment and agent installation

This API documentation provides everything needed to integrate PatchMon's Proxmox functionality with automation tools like Ansible. The endpoints are designed to be secure, scalable, and easy to integrate into existing infrastructure automation workflows.