8000 GitHub - imswel/secure-crl-checker: Secure, fail-closed CRL validation for Traefik mTLS client certificates · GitHub
[go: up one dir, main page]

Skip to content

About

Secure, fail-closed CRL validation for Traefik mTLS client certificates

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Secure CRL Checker

Traefik Plugin Catalog · License: MIT

Secure CRL Checker is a fail-closed HTTP middleware for Traefik Proxy that checks already TLS-verified client certificates against a signed X.509 Certificate Revocation List (CRL) published by a remote PKI/CA (for example, OpenBao).

Use it alongside Traefik's native RequireAndVerifyClientCert option. This plugin does not authenticate a client during the TLS handshake; it checks revocation after Traefik has verified the client certificate and before forwarding the HTTP request.

Features

  • Fail closed: without a valid CRL, or after NextUpdate, authenticated requests are denied with HTTP 503.
  • Signed CRL validation: checks the issuer, signature, issuer's CRL-signing permission, signed CRL number, ThisUpdate/NextUpdate, and supported CRL extensions.
  • Periodic refresh: retrieves DER or PEM CRLs via HTTPS once per interval (1m by default), never per HTTP request.
  • Efficient HTTP polling: uses ETag and Last-Modified conditional requests when the CRL server supports them; otherwise compares signed CRL numbers.
  • Offline support: continues using a validated, unexpired cached CRL if the server becomes unreachable. Cache is persisted atomically to disk and revalidated on startup.
  • Anti-rollback: refuses lower CRL numbers and refuses changed content with an unchanged CRL number while the previous snapshot is loaded.
  • Explicit denial reasons: 403 for revoked or ineligible client certificates; 503 when no valid CRL is available.
  • Diagnostics: INFO, WARN, and ERROR event text, plus optional logChecks: true to log every successful poll.

Requirements

  • Traefik Proxy with Yaegi middleware plugin support (tested in Go; smoke-test under your Traefik version).
  • An HTTPS endpoint serving a complete, directly issued DER or PEM CRL. No delta, indirect, or scoped CRLs.
  • The public certificate of the direct issuer CA that signs both client certificates and the CRL. Do not provide any private CA key.
  • A writable directory inside the Traefik process/container to persist its CRL cache.
  • Traefik TLS options that require and verify client certificates. Configure clientAuthType: RequireAndVerifyClientCert with the appropriate client CA(s).

Quick start

1. Enable the published plugin (static Traefik YAML)

Add the following to your existing static config (experimental can contain other settings):

experimental:
  plugins:
    secureCrlChecker:
      moduleName: github.com/imswel/secure-crl-checker
      version: v0.2.0

Restart Traefik after changing the static plugin configuration. A release must exist with the matching Git tag.

2. Configure client TLS verification (dynamic YAML)

tls:
  options:
    protected-mtls:
      minVersion: VersionTLS12
      clientAuth:
        caFiles:
          - /etc/traefik/pki/client-ca.pem
        clientAuthType: RequireAndVerifyClientCert

3. Configure the revocation middleware (dynamic YAML)

http:
  middlewares:
    secure-crl:
      plugin:
        secureCrlChecker:
          crlURL: "https://pki.example.net/crl/issuer.crl"
          issuerCertFile: "/etc/traefik/pki/client-ca.pem"
          httpCAFile: "/etc/traefik/pki/root-ca.pem"
          cacheFile: "/var/lib/traefik/crl/issuer.crl"
          checkInterval: "1m"
          requestTimeout: "5s"
          logChecks: false

httpCAFile contains trusted certificates for the HTTPS endpoint (not necessarily the same issuer as your client certificates). Omit it to use the default system trust store. The plugin requires standard TLS server-name verification; do not disable certificate validation. The configured CRL URL must point directly to a PKI endpoint reachable from Traefik without passing through a route guarded by this same middleware.

4. Attach middleware and mTLS options to a router

http:
  routers:
    protected-app:
      rule: "Host(`app.example.net`)"
      entryPoints:
        - websecure
      service: app
      middlewares:
        - secure-crl
      tls:
        options: protected-mtls

The middleware is a dynamic HTTP object; TLS options are applied at the TLS handshake (before HTTP routing). If using Docker labels instead of a file router, the middleware reference is secure-crl@file.

Mount /etc/traefik/pki read-only and /var/lib/traefik/crl as a writable persistent volume in your Traefik container.

Configuration reference

Key Required Default Description
crlURL Yes — Absolute HTTPS URL to the complete CRL (DER or PEM).
issuerCertFile One of the two issuer settings — Path to the PEM or DER certificate of the direct issuing CA.
issuerCertPEM One of the two issuer settings — Inline public PEM issuer certificate. Useful for self-contained test fixtures. Do not set both issuer settings.
httpCAFile No System trust PEM trust anchors for HTTPS CRL download.
cacheFile Yes — Writable persistent path for the validated DER CRL cache. Parent directory must exist.
checkInterval No 1m Interval between background HTTP checks.
requestTimeout No 5s HTTP download timeout.
logChecks No false Log every successful poll, including unchanged CRLs.

Response codes

HTTP status Meaning
Pass-through Client certificate is TLS-verified and not listed in the current valid CRL.
403 No verified client certificate, wrong issuer, wrong client purpose, or certificate revoked.
503 CRL missing, expired, malformed, inaccessible with no usable cache, or issuer unavailable for verification.

The rejection body begins with secure-crl-checker: and includes the reason. For a client rejected during the TLS handshake, Traefik terminates the handshake before the HTTP middleware can send a response.

How refresh and offline fallback work

  1. Validate an existing persistent cache on startup; attempt the remote HTTPS download.
  2. Every checkInterval, perform a conditional HTTP GET if HTTP validators exist and the cached CRL is still valid; otherwise perform an ordinary GET.
  3. Verify all accepted CRLs before updating in-memory state and the cache file. Compare signed CRL numbers and reject rollbacks.
  4. If HTTPS is temporarily unavailable, use the previous verified CRL only while it remains valid according to NextUpdate.
  5. If there is no usable CRL, block requests even if the TLS client certificate itself is valid.

No network request is made in the HTTP request path. Actual revocation propagation latency also dep 7B23 ends on when the PKI publishes an updated CRL, not just polling frequency.

Testing

go test ./...
go test -race ./...
go vet ./...

Integration smoke test (using test client certificates, not administrator credentials):

  1. Request with a valid client certificate: upstream response received.
  2. Revoke the test certificate at your CA and publish a new complete CRL.
  3. After the next poll: 403 secure-crl-checker: client certificate revoked.
  4. Temporarily make the CA unavailable: an unexpired cached CRL continues to work.
  5. Point to an unreachable CA with an empty cache on a separate test router: 503 (fail-closed).
  6. Let a dedicated test CRL expire or use a controlled fixture: 503 secure-crl-checker: CRL expired.

Temporarily set logChecks: true for successful polling diagnostics; remember that Traefik's Go-plugin logging bridge may label stdout and stderr differently from the inner level=... message.

Local development without publishing

experimental:
  localPlugins:
    secureCrlChecker:
      moduleName: github.com/imswel/secure-crl-checker

Place the repository at ./plugins-local/src/github.com/imswel/secure-crl-checker relative to the working directory of the Traefik process, or mount it at /plugins-local/src/github.com/imswel/secure-crl-checker in the official Traefik container. Do not configure experimental.localPlugins.secureCrlChecker and experimental.plugins.secureCrlChecker together; switch modes and restart.

Security and scope

  • This is not an OCSP client or a CRL-generation service. It only checks a remote complete CRL.
  • Only one direct client issuer CA is supported per middleware; for multiple issuers, configure separate policies or extend the implementation and test it carefully.
  • During a CA outage, revocations made after the last successfully fetched CRL cannot be detected until a new list arrives. This is a deliberate availability trade-off.
  • A different TLS-terminating proxy in front of Traefik prevents this middleware from seeing the original client certificate.
  • Plain HTTP or CRL URLs with embedded credentials are rejected; redirect responses are not followed. Requests enforce normal TLS certificate validation.
  • Sensitive services should maintain an out-of-band administration path and should be smoke-tested before activation.
  • The .traefik.yml manifest includes a public demonstration CA certificate strictly for catalog startup validation. Its private key is not included and it should never be trusted by your Traefik instance.

License

MIT — see LICENSE.

About

Secure, fail-closed CRL validation for Traefik mTLS client certificates

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0