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.
- Fail closed: without a valid CRL, or after
NextUpdate, authenticated requests are denied with HTTP503. - 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 (
1mby default), never per HTTP request. - Efficient HTTP polling: uses
ETagandLast-Modifiedconditional 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:
403for revoked or ineligible client certificates;503when no valid CRL is available. - Diagnostics:
INFO,WARN, andERRORevent text, plus optionallogChecks: trueto log every successful poll.
- 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: RequireAndVerifyClientCertwith the appropriate client CA(s).
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.0Restart Traefik after changing the static plugin configuration. A release must exist with the matching Git tag.
tls:
options:
protected-mtls:
minVersion: VersionTLS12
clientAuth:
caFiles:
- /etc/traefik/pki/client-ca.pem
clientAuthType: RequireAndVerifyClientCerthttp:
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: falsehttpCAFile 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.
http:
routers:
protected-app:
rule: "Host(`app.example.net`)"
entryPoints:
- websecure
service: app
middlewares:
- secure-crl
tls:
options: protected-mtlsThe 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.
| 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. |
| 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.
- Validate an existing persistent cache on startup; attempt the remote HTTPS download.
- Every
checkInterval, perform a conditional HTTP GET if HTTP validators exist and the cached CRL is still valid; otherwise perform an ordinary GET. - Verify all accepted CRLs before updating in-memory state and the cache file. Compare signed CRL numbers and reject rollbacks.
- If HTTPS is temporarily unavailable, use the previous verified CRL only while it remains valid according to
NextUpdate. - 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.
go test ./...
go test -race ./...
go vet ./...Integration smoke test (using test client certificates, not administrator credentials):
- Request with a valid client certificate: upstream response received.
- Revoke the test certificate at your CA and publish a new complete CRL.
- After the next poll:
403 secure-crl-checker: client certificate revoked. - Temporarily make the CA unavailable: an unexpired cached CRL continues to work.
- Point to an unreachable CA with an empty cache on a separate test router:
503(fail-closed). - 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.
experimental:
localPlugins:
secureCrlChecker:
moduleName: github.com/imswel/secure-crl-checkerPlace 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.
- 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.ymlmanifest 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.
MIT — see LICENSE.