Skip to content

Add PEM certificate support for TLS - #1937

Merged
kevin-montrose merged 3 commits into
microsoft:mainfrom
hexonal:add-pem-tls-cert-support
Jul 17, 2026
Merged

kevin-montrose merged 3 commits into
microsoft:mainfrom
hexonal:add-pem-tls-cert-support

Conversation

@hexonal

Copy link
Copy Markdown
Contributor

The TLS cert loading path only accepted .pfx files: the extension allowlist in CertFileValidationAttribute was hardcoded to .pfx, so PEM certs (the standard format on Unix/Linux) were rejected outright. Fixes #1433.

Changes

  • CertificateUtils.GetMachineCertificateByFile now sniffs the file's leading bytes for the -----BEGIN marker 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's CreatePemUserCertificateCallback): X509Certificate2.CreateFromPemFile, then re-exporting through PKCS#12 so the certificate carries a persisted key set instead of an ephemeral one, which SslStream doesn't handle consistently on all platforms.
  • When the cert file turns out to be PEM-encoded, cert-password / CertPassword is 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, .cer so these pass config validation before we ever get to loading the file.
  • Updated the security docs and the testcerts README, both of which said a pfx was required.

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, mirrors RespTlsTests): boots a real GarnetServer with 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.
  • New self-signed PEM cert/key pair checked in under test/testcerts (testcert.pem / testcert.key.pem), generated locally with openssl 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-fixture LightEpoch teardown race triggered only when a TLS test runs immediately after that failure) — both reproduce identically on unmodified upstream main.
  • dotnet format Garnet.slnx --verify-no-changes passes on the changed files.

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.
Copilot AI review requested due to automatic review settings July 16, 2026 02:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.GetMachineCertificateByFile to 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-password being 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.

Comment thread libs/server/TLS/CertificateUtils.cs
Comment thread test/standalone/Garnet.test/CertificateUtilsTests.cs
Comment thread test/standalone/Garnet.test/CertificateUtilsTests.cs Outdated
Comment thread test/testcerts/README.md Outdated
@hexonal

Copy link
Copy Markdown
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 kevin-montrose self-assigned this Jul 17, 2026
@kevin-montrose
kevin-montrose merged commit 96833a3 into microsoft:main Jul 17, 2026
315 of 317 checks passed
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
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PEM format cert for TLS

3 participants