Repository navigation
Add PEM certificate support for TLS - #1937
Merged
Merged
Conversation
Garnet's TLS cert loading only accepted .pfx files, hardcoded via the CertFileValidation extension list. PEM is the standard format on Unix/Linux, so requiring a pfx conversion was an unnecessary hurdle (microsoft#1433). CertificateUtils.GetMachineCertificateByFile now sniffs the file's leading bytes for the "-----BEGIN" marker instead of trusting the extension, and loads PEM certs via X509Certificate2.CreateFromPemFile, re-importing through PKCS#12 to avoid the ephemeral-key-set issue with SslStream. cert-password is repurposed as the path to a separate PEM private key file when the cert is PEM-encoded, or left empty when the key is already embedded in the certificate file. The CertFileValidation extension allowlist now also accepts .pem/.crt/.cer so PEM certs pass config validation. Adds CertificateUtilsTests and RespTlsPemTests using a freshly generated self-signed PEM cert/key pair under test/testcerts, alongside config-validation coverage for the new extensions.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds TLS server certificate support for PEM-encoded certificates (common on Unix/Linux) by detecting certificate format from file contents rather than relying on a .pfx-only extension allowlist, and updates configuration/docs/tests accordingly.
Changes:
- Updated
CertificateUtils.GetMachineCertificateByFileto auto-detect PEM vs PKCS#12/PFX by inspecting file contents, and to load PEM certificates (optionally with a separate key file). - Relaxed TLS cert filename validation to allow PEM-associated extensions (
.pem,.crt,.cer) and updated help text/defaults/docs to describe the new behavior (including--cert-passwordbeing used as a key-file path for PEM). - Added new tests and test fixtures to validate PEM loading and to boot a real server over TLS using PEM certs.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| website/docs/getting-started/security.md | Documents PEM cert support and the updated meaning of --cert-password for PEM. |
| test/testcerts/testcert.pem | Adds a static PEM certificate fixture for tests. |
| test/testcerts/testcert.key.pem | Adds the matching PEM private key fixture for tests. |
| test/testcerts/README.md | Updates test certificate README with PEM usage guidance (also contains a CLI flag issue noted in comments). |
| test/standalone/Garnet.test/TestUtils.cs | Allows tests to override TLS cert filename/password (key path) when creating a server. |
| test/standalone/Garnet.test/RespTlsPemTests.cs | New end-to-end TLS test using PEM certs (SE.Redis + GarnetClient). |
| test/standalone/Garnet.test/GarnetServerConfigTests.cs | Adds validator tests ensuring .pem/.crt/.cer pass config validation. |
| test/standalone/Garnet.test/Garnet.test.csproj | Copies new PEM test fixtures to the test output directory. |
| test/standalone/Garnet.test/CertificateUtilsTests.cs | New unit tests for PEM/PFX loading and format detection. |
| libs/server/TLS/CertificateUtils.cs | Implements PEM sniffing and PEM certificate loading (including re-export via PKCS#12). |
| libs/host/defaults.conf | Updates default config comments to reflect PEM support. |
| libs/host/Configuration/OptionsValidators.cs | Expands cert filename extension allowlist to include PEM-associated extensions. |
| libs/host/Configuration/Options.cs | Updates CLI help text to reflect PEM support and PEM key-file behavior. |
Contributor
Author
|
@microsoft-github-policy-service agree |
- IsPemFile now tolerates a leading UTF-8 BOM and blank lines/whitespace before the -----BEGIN marker, and loops until the read buffer is full instead of assuming a single Stream.Read call fills it - CertificateUtilsTests now inherits TestBase and calls TestUtils.OnTearDown() like the other test fixtures in this file, so epoch leak checks run - the combined-PEM test now guarantees a newline between the concatenated cert and key so it can't produce an invalid PEM stream - fixed --password -> --cert-password in test/testcerts/README.md, which was already wrong before this PR but is right next to the lines this PR touches Added a regression test for the BOM/leading-whitespace case.
kevin-montrose
approved these changes
Jul 17, 2026
Badrish Chandramouli (badrishc)
added a commit
that referenced
this pull request
Aug 18, 2026
The PEM certificate support added in #1937 introduced test/testcerts/testcert.key.pem, a PEM-encoded private key for the self-signed certificate used by TLS unit tests. CredScan flags its private key, breaking the compliance build. Add it to the CredScan exclusion list alongside the other test certificate/key files. Co-authored-by: Copilot <[email protected]> Copilot-Session: ebda3282-4ac2-49f9-820a-d658443b15ae
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
The TLS cert loading path only accepted
.pfxfiles: the extension allowlist inCertFileValidationAttributewas hardcoded to.pfx, so PEM certs (the standard format on Unix/Linux) were rejected outright. Fixes #1433.Changes
CertificateUtils.GetMachineCertificateByFilenow sniffs the file's leading bytes for the-----BEGINmarker to detect PEM vs PKCS#12, instead of trusting the extension. This follows the approach Marc Gravell (@mgravell) described in the issue (and uses the same building blocks as SE.Redis'sCreatePemUserCertificateCallback):X509Certificate2.CreateFromPemFile, then re-exporting through PKCS#12 so the certificate carries a persisted key set instead of an ephemeral one, whichSslStreamdoesn't handle consistently on all platforms.cert-password/CertPasswordis repurposed as the path to a separate private key PEM file, left empty when the private key is already embedded in the same file as the certificate.CertFileValidationAttribute's accepted-extensions list now also includes.pem,.crt,.cerso these pass config validation before we ever get to loading the file.Testing
CertificateUtilsTests(new): PEM with a separate key file, PEM with an embedded key, format auto-detection regardless of extension, and a PFX regression check.RespTlsPemTests(new, mirrorsRespTlsTests): boots a realGarnetServerwith a PEM cert and does a SET/GET over TLS via both the StackExchange.Redis client and GarnetClient.GarnetServerConfigTests.CertFileName_AcceptsPemExtensions/_RejectsUnsupportedExtension(new): exercises the validator directly.test/testcerts(testcert.pem/testcert.key.pem), generated locally withopenssl req -x509 -newkey rsa:2048 -nodes -keyout testcert.key.pem -out testcert.pem -days 3650 -subj "/CN=GarnetTest"(long-lived since it's a static test fixture, not a throwaway).dotnet test test/standalone/Garnet.test/Garnet.test.csproj -f net10.0 --filter "FullyQualifiedName~RespTlsTests|FullyQualifiedName~RespTlsPemTests|FullyQualifiedName~CertificateUtilsTests|FullyQualifiedName~GarnetServerConfigTests"is green except two pre-existing, unrelated flaky failures (MultiTcpSocketTest's IPv6 socket issue; a cross-fixtureLightEpochteardown race triggered only when a TLS test runs immediately after that failure) — both reproduce identically on unmodified upstreammain.dotnet format Garnet.slnx --verify-no-changespasses on the changed files.