🛡️ Reverse proxy

Important

Usually the ca service will be deployed behind a reverse proxy, which requires special attention.

To make the CA work behind a reverse proxy, follow these rules:

  1. Remove all direct port mappings (i.e. 8443)

  2. Point your reverse proxy to port 8443 instead (the only exposed port)

  3. Pass through TLS connections to the CA, don’t intercept them on the reverse proxy

  4. Disable strict SNI checking

  5. Put the CA into the same network as the proxy

Below there are some examples for reverse proxies.

Traefik

CA deployment

To deploy the CA service behind Traefik proxy, the following labels must be added to the container:

services:
  ca:

    # … existing config (see Deployment chapter)

    # Passthrough connections from Traefik directly to CA.
    labels:
      - traefik.enable=true
      - traefik.tcp.routers.ca.rule=HostSNI(`ca.example.net`)
      - traefik.tcp.routers.ca.tls.passthrough=true

    # Add CA to proxy network.
    networks:
      - proxy

# Make proxy network available for Compose service.
networks:
  proxy:
    external: true

Hint

If you’ve strict SNI checking enabled, create a new dynamic config such as this:

tls:
  options:
    sniStrictDisable:
      sniStrict: false

Then use the option as another label:

labels:
    # … existing labels
    - traefik.tcp.routers.ca.tls.options=sniStrictDisable@file

ACME

To issue certificates via the CA’s ACME provisioner, add a certificate resolver to the static config:

certificatesResolvers:
  ca:
    acme:
      caServer: https://{FQDN}/acme/acme/directory
      storage: /acme/ca.json
      tlsChallenge: {}

Then make Traefik trust the root certificate via environment variable:

environment:
  LEGO_CA_CERTIFICATES: /path/to/root_ca.crt

Use the resolver on a router as usual:

labels:
  - traefik.http.routers.{router}.tls.certresolver=ca

mTLS

To use the CA for mTLS in Traefik proxy, use the following TLS configuration:

tls:
  options:

    mTLS:
      clientAuth:
        caFiles:
          - /path/to/ca.pem
        clientAuthType: RequireAndVerifyClientCert

Hint

If you’ve default options, and you want to extend them to the mTLS options, you can use YAML anchors like this:

tls:
  options:

    default: &defaultOptions
      # your default options here

    mTLS:
      <<: *defaultOptions
      clientAuth:
        caFiles:
          - /path/to/ca.pem
        clientAuthType: RequireAndVerifyClientCert

Caddy

CA deployment

Caddy doesn’t support TLS passthrough out of the box, so you’ll need a custom build that includes the caddy-l4 (Layer 4) plugin:

xcaddy build --with github.com/mholt/caddy-l4

Then pass through the CA’s TLS connections via listener wrapper in the global options of your Caddyfile:

{
    servers {
        listener_wrappers {
            layer4 {
                @ca tls sni ca.example.net
                route @ca {
                    proxy ca:8443
                }
            }
            tls
        }
    }
}

Note

Connections not matching the @ca route fall through to Caddy’s regular TLS handling, so your other sites keep working.

ACME

To issue certificates via the CA’s ACME provisioner, configure the ca & ca_root subdirectives in your Caddyfile:

example.com {
    tls {
        ca https://{FQDN}/acme/acme/directory
        ca_root /path/to/root_ca.crt
    }

    reverse_proxy backend:8080
}

Hint

To use the CA for all sites, use the acme_ca & acme_ca_root global options instead.

mTLS

To use the CA for mTLS in Caddy, configure the client_auth directive in your Caddyfile:

example.com {
    tls {
        client_auth {
            mode require_and_verify
            trusted_ca_cert_file /path/to/ca.pem
        }
    }

    reverse_proxy backend:8080
}

Hint

If multiple sites should require mTLS, define a snippet and import it:

(mtls) {
    tls {
        client_auth {
            mode require_and_verify
            trusted_ca_cert_file /path/to/ca.pem
        }
    }
}

example.com {
    import mtls
    reverse_proxy backend:8080
}