Repository navigation
feat: reject revoked mTLS client certificates from local CRL files - #4197
Open
pardeep-singh wants to merge 1 commit into
Open
pardeep-singh wants to merge 1 commit into
pardeep-singh wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Description
Fixes #3922.
On master,
openmed/service/mtls.pychecks the client chain against the CAbundle but takes no revocation input. A compromised client certificate keeps
working until it expires. #832 left out online CRL and OCSP checks, and #3580
leaves out CRL hosting, so nothing handled an offline, operator-supplied CRL.
This PR adds local CRL files to REST mTLS. OpenMed verifies each CRL's
signature against the configured CA bundle at startup. On every request it
rejects revoked certificates and checks CRL freshness before principal mapping
runs. Invalid or stale CRLs fail closed in production and are reported in the
devandtestprofiles. Errors and logs use only fixed codes.Type of Change
Changes Made
openmed/service/mtls.py: AddLocalCRLChecker, which loads PEM or DERCRL files, verifies each signature against a bundle CA with a matching
subject and
cRLSignusage, and checks every certificate in the verifiedchain by serial number. Add three settings:
OPENMED_SERVICE_MTLS_CRL_FILES,OPENMED_SERVICE_MTLS_CRL_GRACE_SECONDSand
OPENMED_SERVICE_MTLS_CRL_FAIL_CLOSED.ServiceMTLSruns the checkinside chain verification, before
_principal_for. Add theclient_certificate_revoked(401),crl_stale(503) andcrl_invalidcodes.
MTLSAuthenticationErrornow carries a status code (default 401).verify_client_certificate_chaintakes an optionalcrl_checker, so thegRPC listener can reuse the same check later.
tests/unit/service/test_mtls_crl.py: 40 new tests. They build asynthetic CA, client certificates and CRLs at test time.
docs/serving/mtls.md: Document the settings, the error codes, profilebehavior, what "reported" means, and how to produce and rotate a CRL with
OpenSSL (placeholder paths).
CHANGELOG.md: Add an Unreleased Security entry.Testing
New tests cover:
client_certificate_revokedand an unrevoked one still authenticates, both directly, through the REST
app, and through the trusted-proxy header;
crl_invalidfor a CRL signed by the wrong key, a CRL from a CA outside thebundle, a corrupted signature, a malformed file, a delta CRL and a missing
file;
crl_stalebeyond the grace period, acceptance within it, and a CRL thatgoes stale while the service runs (injected clock);
one;
devandtestlog one value-free warning and let the requestproceed; a revoked certificate is still rejected in report-only mode;
OPENMED_SERVICE_MTLS_CRL_FAIL_CLOSED=falseoutside
devandtest;hex, upper-case hex and colon-separated hex) and the CRL directory path never
appear in responses, headers, exception text or captured logs.
Before the fix, the new test file failed at collection with an
ImportErrorfor the new symbols (0 of 40 ran). After the fix, all 40 pass. With the
revocation call removed from the code path, 14 of the 40 fail, so the tests
exercise the check itself.
Full suite (
pytest tests/ -q): 29576 passed, 185 skipped, 2 failed,18 errors. All 20 failures and errors are in
tests/unit/security/test_vulnerability_scan_gate.py. That file fails the sameway (2 failed, 23 passed, 18 errors) on a clean export of the base commit, so
these failures already exist on master and are not caused by this change.
Documentation
Code Quality
make format,make lint, andmake format-checkmake format-swiftandmake lint-swiftDependencies
mtls.pyalready importscryptography. The CRL code uses the same package.Checklist
Notes
cryptographyverifies the chain and before principal mapping. It does notuse
ssl.VERIFY_CRL_CHECK_LEAF. OpenMed does not own the TLS context:certificates arrive through the ASGI TLS extension or a trusted proxy
header, and Uvicorn has no CRL flag. One check covers both paths.
OPENMED_PROFILE, which defaults toprod, the same wayauth.pyandruntime.pyread it. Onlydevandtestare report-only.prod,fast,low_resourceand custom profilesfail closed.
OPENMED_SERVICE_MTLS_CRL_FAIL_CLOSED=trueopts a developmentprofile into fail-closed. Setting it to
falseoutsidedevandtestis astartup error, so production has no fail-open switch.
crl_invalidwhen the app is built, like an invalid CA bundle does today.The error names only the file's 1-based position, never the path. Freshness
changes over time, so
crl_staleis checked per request and returns 503.The client cannot fix a stale CRL, and a 503 lets load balancers and alerts
notice it. A revoked certificate returns 401.
devandtest, each problem produces oneWARNINGon theopenmed.service.mtlslogger. The record holds only thecode (and the file position for an invalid file), plus an
openmed_codeattribute. Invalid files are skipped. Stale CRLs are still used. Requests
proceed. Revoked certificates are always rejected.
against CRLs signed by its issuer. A certificate whose issuer has no
configured CRL is not checked. Freshness is checked only for the CRLs that
apply to the chain, so a stale CRL for one CA does not block clients of
another CA.
nextUpdate, PEM files withmore than one CRL block, and files over 64 MiB are rejected as
crl_invalid. A delta CRL lists only recent changes, so treating it as afull list could accept certificates revoked in the base CRL. CRL load errors
are raised
from None, so tracebacks do not carry OS error text that namesthe path.
without restart are not included, as the issue boundary states. A future
lastUpdatetime is not rejected.mkdocsis not installed in my environment, so I did not run a strict docsbuild. The new in-page anchor uses the default slug for its heading.
Related Issues
Closes #3922
Related to #832, #3580