Skip to content

Feature Guide mTLS RBAC

fuomag9 edited this page Sep 26, 2026 · 5 revisions

Feature Guide: mTLS Role-Based Access Control

Control which client certificates can access which paths on mTLS-protected proxy hosts.

Table of Contents

  1. Overview
  2. Concepts
  3. Setup Walkthrough
  4. Path-Based Access Rules
  5. Scoped mTLS Paths
  6. Trust Models
  7. Revocation Behaviour
  8. REST API
  9. Troubleshooting

Overview

Basic mTLS lets any client certificate signed by a trusted CA access the proxy host. mTLS RBAC adds fine-grained control:

  • Roles — Named groups of client certificates (e.g. "ops", "developers", "monitoring").
  • Cert-to-role assignment — Assign issued client certificates to one or more roles.
  • Path-based access rules — Restrict URL paths to specific roles or individual certificates.

This lets you host multiple services behind one domain and control who reaches what.


Concepts

Roles

A role is a named label attached to one or more client certificates. Roles are managed on the Certificates → Roles tab.

Access rules

An access rule ties a path pattern to allowed roles and/or individual certificates. Rules are configured per proxy host in the mTLS RBAC section.

Each rule has:

  • Path pattern — Caddy path matcher (e.g. /admin/*, /api/v1/*, /*)
  • Allowed roles — Roles whose certificates may access this path
  • Allowed certificates — Individual certificates (in addition to roles)
  • Deny all — Explicitly block all access to this path

Rules are evaluated by priority (highest first). Paths without a matching rule fall through to a default allow (any valid mTLS cert).


Setup Walkthrough

1. Issue client certificates

On the Certificates → CA / mTLS tab:

  1. Generate a CA if you haven't already.
  2. Issue client certificates for your users/services.

With Feature Guide Instance Sync, issue certificates on the master. Since v1.13.1, CA private keys are not synced. Slaves enforce mTLS with the synced CA and client certificates, but cannot issue new ones.

2. Create roles

On the same page, switch to the Roles tab:

  1. Click Create New Role.
  2. Name it (e.g. "ops") and click Create Role.
  3. Assign certificates to the role.

3. Configure the proxy host

Edit the proxy host and enable mTLS:

  1. Select trusted certificates or roles (the new trust model).
  2. In the mTLS RBAC section, add path-based access rules.
  3. Save.

4. Test

Use curl with a client certificate:

curl --cert client.pem --key client-key.pem https://app.example.com/admin/

A certificate in the "ops" role accessing /admin/* (if allowed) returns 200. A certificate not in the allowed set returns 403.


Path-Based Access Rules

Rules are enforced at the HTTP layer (after the TLS handshake) using Caddy CEL expressions that match the client certificate's fingerprint.

Rule priority

Rules are ordered by priority (descending). The first matching rule wins. If no rule matches a path, any valid mTLS certificate is allowed through (backward-compatible default).

Deny-all rules

A deny-all rule blocks every client certificate for the matched path, regardless of roles. Use this for paths that should never be accessed via the proxy (e.g. internal health endpoints).

Example configuration

Priority Path Allowed Effect
100 /admin/* Role: ops Only "ops" certs reach admin
90 /api/* Role: ops, Role: developers Both roles reach API
80 /internal/* Deny all No cert can access /internal
— /* (default) Any valid cert passes

Scoped mTLS Paths

mTLS enforcement can be scoped to specific paths using protected paths or excluded paths. This is configured in the proxy host editor, in the mTLS section.

Protected paths

mTLS is required only on the listed path prefixes. All other paths are accessible without a client certificate. Use this when you want to restrict only certain parts of a site (e.g. an admin area) to cert holders while leaving the rest public.

Excluded paths

mTLS is required everywhere except the listed path prefixes. The excluded paths are accessible without a client certificate. Use this when most of the site should be protected but specific paths (health checks, webhooks, public APIs) must remain open.

Summary

Mode Use case
Protected paths Restrict certain admin paths to cert holders while keeping the rest public
Excluded paths Protect most of the site but expose specific public paths (health checks, webhooks)
No scoping (default) All paths require a valid client certificate

Scoped paths are evaluated at the Caddy routing layer, before RBAC rules. A request to an unscoped path bypasses mTLS entirely.


Trust Models

CPM supports two trust models for mTLS on a proxy host:

Cert-based trust (new model)

Select individual certificates or roles. CPM derives the CAs automatically and pins to the selected leaf certificates. This is more restrictive — only the explicitly selected certificates pass the TLS handshake.

Since v1.13.1, hosts get separate TLS client-authentication policies when their trusted CAs or their pinned certificates differ. Two hosts that pin different certificates from the same CA therefore each accept only their own certificates in the handshake. In v1.12.0 and earlier, hosts with the same CA set shared one policy, which trusted the certificates pinned by all of them.

CA-based trust (legacy model)

Select entire CAs. Any non-revoked certificate signed by those CAs passes the TLS handshake. RBAC rules then control path-level access.

The cert-based model is recommended for new setups.


Revocation Behaviour

When a client certificate is revoked:

  1. The certificate is removed from the active leaf cert list.
  2. Caddy config is regenerated and applied immediately.
  3. The revoked certificate can no longer complete the TLS handshake.

Fail-closed: If all certificates for a CA are revoked, all connections to that domain are rejected. CPM does not silently downgrade to no-mTLS.


REST API

mTLS RBAC is fully manageable via the REST API (admin only):

  • GET /api/v1/mtls-roles — List all roles
  • POST /api/v1/mtls-roles — Create a role
  • GET /api/v1/mtls-roles/:id — Get a role with the IDs of its certificates
  • PUT /api/v1/mtls-roles/:id — Update a role
  • DELETE /api/v1/mtls-roles/:id — Delete a role
  • POST /api/v1/mtls-roles/:id/certificates — Add a certificate to a role
  • DELETE /api/v1/mtls-roles/:id/certificates/:certId — Remove a certificate from a role
  • GET /api/v1/proxy-hosts/:id/mtls-access-rules — List access rules for a host
  • POST /api/v1/proxy-hosts/:id/mtls-access-rules — Add an access rule
  • GET|PUT|DELETE /api/v1/proxy-hosts/:id/mtls-access-rules/:ruleId — Read, update or delete a rule

See /api-docs for full schemas.


Troubleshooting

403 on a path that should be allowed

  1. Check the certificate's fingerprint matches an allowed role or direct cert in the rule.
  2. Verify rule priority — a higher-priority deny rule may be matching first.
  3. Confirm the certificate is assigned to the correct role on the Roles tab.

TLS handshake failure after revoking all certs

This is expected (fail-closed). Re-issue a certificate or change the proxy host's trust configuration.

Certificate not appearing in role assignment

Only non-revoked certificates from managed CAs appear. Check the certificate's status on the CA / mTLS tab.

Cannot issue a client certificate

If issuing fails with "The CA private key cannot be decrypted with the current SESSION_SECRET. Restore the previous secret via SESSION_SECRET_PREVIOUS or create a new CA.", or if the CA offers no option to issue (a CA imported without its key, or any CA on an Instance Sync slave), see Certificate Management.


Related Documentation


Need help? Open an issue with your mTLS configuration and Caddy logs.

Clone this wiki locally