Skip to main content
Development preview — this is not a live application; it is still being built.
ProxmoxVEx
↑ ↓ to navigate Enter to open

Mutual TLS for the Admin Path

This guide documents how to enforce mutual TLS mTLS for the ProxmoxVEx admin path. The preferred implementation is performed at the reverse proxy, because Flask

This guide documents how to enforce mutual TLS (mTLS) for the ProxmoxVEx admin path. The preferred implementation is performed at the reverse proxy, because Flask does not natively handle client-certificate verification.

Scope

The “admin path” includes all routes under:

  • /admin/
  • /api/admin/
  • /settings/ (full settings modal backend)
  • Any endpoint that requires the admin role

Certificate authority

  1. Create or reuse an internal CA for admin client certificates.
  2. Issue client certificates to each admin user.
  3. Distribute the CA certificate to the reverse proxy.

Option A: NGINX reverse proxy

server {
    listen 443 ssl;
    server_name proxmoxvex.example.com;

    # Server certificate and key
    ssl_certificate     /etc/nginx/ssl/proxmoxvex.crt;
    ssl_certificate_key /etc/nginx/ssl/proxmoxvex.key;

    # Client certificate verification
    ssl_client_certificate /etc/nginx/ssl/admin-ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    # Admin routes require a valid client certificate and an admin role header
    location ~ ^/(admin|api/admin|settings)/ {
        if ($ssl_client_verify != SUCCESS) {
            return 403;
        }

        proxy_pass http://proxmoxvex_backend;
        proxy_set_header X-Admin-Client-DN $ssl_client_s_dn;
        proxy_set_header X-Admin-Client-Serial $ssl_client_serial;
        proxy_set_header X-Admin-mTLS-Verified true;
    }

    # Non-admin routes do not require client certificates
    location / {
        proxy_pass http://proxmoxvex_backend;
    }
}

Option B: Traefik reverse proxy

http:
  routers:
    proxmoxvex-admin:
      rule: "Host(`proxmoxvex.example.com`) && PathPrefix(`/admin/`, `/api/admin/`, `/settings/`)"
      service: proxmoxvex
      tls:
        certResolver: default
        options: admin-mtls
      middlewares:
        - admin-mtls

  middlewares:
    admin-mtls:
      passTLSClientCert:
        pem: true

And the TLS option:

tls:
  options:
    admin-mtls:
      clientAuth:
        caFiles:
          - /etc/traefik/certs/admin-ca.crt
        clientAuthType: RequireAndVerifyClientCert

Application-side enforcement

If the reverse proxy sets X-Admin-mTLS-Verified: true and X-Admin-Client-DN, ProxmoxVEx/app.py can optionally require these headers for admin routes:

from flask import request, abort

if request.path.startswith("/admin/") or request.path.startswith("/api/admin/"):
    if request.headers.get("X-Admin-mTLS-Verified") != "true":
        abort(403)

Operational notes

  • Keep client certificate validity short (e.g., 90 days) for high-risk admin access.
  • Revoke compromised certificates by removing the serial from the CRL or proxy allow-list.
  • Log client DN and serial in the audit log for every admin request.
  • Do not expose the admin path on a public internet address without mTLS or a bastion.

Was this page helpful?

ProxmoxVEx Source-Available License
Checking status…

© 2025-2026 ProxmoxVEx contributors