Skip to content

feat: hot-reload SSH manual CA private key#376

Open
clement0010 wants to merge 6 commits into
masterfrom
feat/ct/hot-reload-ssh-manual-ca
Open

feat: hot-reload SSH manual CA private key#376
clement0010 wants to merge 6 commits into
masterfrom
feat/ct/hot-reload-ssh-manual-ca

Conversation

@clement0010

@clement0010 clement0010 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Related Tickets

Closes #375

Changes

  • The manual SSH CA private key file is now watched for changes, so rotating the CA key on disk takes effect without restarting the gateway.
  • On rotation, the gateway's SSH host certificate is immediately re-signed with the new CA key, so clients receive updated trust material right away; user certificates are issued per connection and pick up the new key on the next connection.
  • A bad rotation (unreadable or invalid key file) keeps the last good key and logs an error; startup still fails fast on an invalid key, as before.
  • Each rotation logs the new CA public key so it can be distributed to upstream and client trust stores.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.86047% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.32%. Comparing base (105f62b) to head (8de5ec6).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
internal/sshhandler/key_reloader.go 93.22% 3 Missing and 1 partial ⚠️
internal/sshhandler/cert.go 84.61% 1 Missing and 1 partial ⚠️
internal/sshhandler/ca.go 90.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #376      +/-   ##
==========================================
+ Coverage   86.06%   86.32%   +0.25%     
==========================================
  Files          40       41       +1     
  Lines        2828     2896      +68     
==========================================
+ Hits         2434     2500      +66     
- Misses        269      271       +2     
  Partials      125      125              
Flag Coverage Δ
integration 54.67% <41.86%> (-0.23%) ⬇️
unit 79.04% <89.53%> (+0.50%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/sshhandler/config.go 82.94% <100.00%> (+1.99%) ⬆️
internal/sshhandler/ca.go 79.85% <90.00%> (+0.44%) ⬆️
internal/sshhandler/cert.go 83.33% <84.61%> (+0.72%) ⬆️
internal/sshhandler/key_reloader.go 93.22% <93.22%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@clement0010 clement0010 force-pushed the feat/ct/hot-reload-ssh-manual-ca branch 3 times, most recently from e29f3ad to 68df914 Compare July 6, 2026 11:04
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SACLEGn4b97yMUTEws2MyU
@clement0010 clement0010 force-pushed the feat/ct/hot-reload-ssh-manual-ca branch from 68df914 to 14c2065 Compare July 6, 2026 11:14
@clement0010 clement0010 force-pushed the feat/ct/hot-reload-ssh-manual-ca branch from 7e752bf to b15889d Compare July 6, 2026 12:14
@clement0010 clement0010 requested a review from Copilot July 6, 2026 12:34
@clement0010 clement0010 marked this pull request as ready for review July 6, 2026 12:35

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 hot-reload support for the manual SSH CA private key used by the SSH proxy, enabling on-disk CA key rotation without restarting the gateway and triggering immediate re-signing of the gateway’s SSH host certificate.

Changes:

  • Introduces a keyReloader that watches the manual CA private key file and reloads the in-memory CA signer on changes.
  • Updates the embedded CA implementation to fetch the signer via a getter so it can be swapped at runtime.
  • Extends autoRenewingCertSigner to accept an optional renewal signal channel to re-sign immediately on CA key rotation, with accompanying tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/sshhandler/key_reloader.go Adds filesystem watcher + reload signaling for manual CA private key rotation.
internal/sshhandler/key_reloader_test.go Adds tests for key reload behavior across writes, atomic replace, Kubernetes secret updates, and error recovery.
internal/sshhandler/config.go Wires CA key reload signal into host cert auto-renew signer.
internal/sshhandler/cert.go Adds renewCh support to re-sign certificates immediately on CA key rotation.
internal/sshhandler/cert_test.go Adds coverage for re-signing behavior on CA key rotation.
internal/sshhandler/ca.go Switches embedded CA to signer getter and adds background key reload start for manual CA.
internal/sshhandler/ca_test.go Updates embedded CA tests and adds manual CA reload test.
CLAUDE.md Documents manual CA hot-reload behavior in the SSH handler overview.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/sshhandler/key_reloader.go
Comment thread internal/sshhandler/cert.go
@clement0010 clement0010 requested a review from minhtule July 6, 2026 13:03
// update sequence for a projected secret volume:
//
// mount/ca -> ..data/ca, ..data -> ..timestamp1, update swaps ..data to ..timestamp2 and removes ..timestamp1
func TestKeyReloaderReloadWhenKubernetesSecretUpdated(t *testing.T) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Based on the atomic write algorithm described here

return nil
}

func (kr *keyReloader) watch(ctx context.Context) error {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This logic is similar to the watch function in cert_reloader.go. I'm keeping them separated for now, if there's a third reloader, we could create a common reloader package and reuse them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support hot-reloading the manual SSH CA private key

2 participants