Skip to content

feat: TLS (HTTPS) for tunnel public ports with cert hot-reload (1.28.0)#74

Merged
gmpassos merged 3 commits into
masterfrom
feat/tunnel-tls-public-ports
Jun 22, 2026
Merged

feat: TLS (HTTPS) for tunnel public ports with cert hot-reload (1.28.0)#74
gmpassos merged 3 commits into
masterfrom
feat/tunnel-tls-public-ports

Conversation

@gmpassos

Copy link
Copy Markdown
Contributor

Summary

Adds opt-in TLS termination on tunnel public ports so external clients can speak HTTPS to a tunnel (e.g. https://sites.menuici.com:20010). TLS terminates at the Hub's public port; the Hub→exposer→target hops stay plaintext, unchanged. Tunnels remain plain TCP by default.

How it works

  • Open securely: omnyshell tunnel open <node> <port> --secure (also the interactive :tunnel --secure, or ClientRuntime.openTunnel(secure: true)). Each accepted connection is upgraded via SecureSocket.secureServer, so the existing bind / port-range allocation and the byte-bridge are untouched.
  • Configure the cert (Hub host): omnyshell hub start --tunnel-tls-dir <dir> — a directory holding fullchain.pem + privkey.pem (LetsEncrypt layout) that should match --tunnel-public-host.
  • Hot reload: the new TunnelTlsSource re-checks the files every 12h and rebuilds the TLS context automatically when they change, so renewals are picked up without a Hub restart. A renewal caught mid-write (invalid/partial files) is skipped, keeping the previous certificate in service.
  • Fail closed: requesting --secure against a Hub with no certificate configured is rejected with secure_unavailable.
  • --tunnel-tls-dir is carried through service install/reconfigure.

Key changes

  • Protocol: secure flag on TunnelOpenRequest / TunnelOpened / TunnelInfo (defaults false → backward compatible).
  • HubBroker: validates secure requests and upgrades accepted connections; tunnelSecurityContext is now mutable for reloads.
  • OmnyShellHub / HubConfig: tunnelTlsDirectory + tunnelTlsReloadInterval manage a TunnelTlsSource across start/stop.
  • CLI: --secure (tunnel open) and --tunnel-tls-dir (hub start); CLI prints https:// and tunnel list shows the scheme.

Testing

  • dart format / dart analyze: clean.
  • dart test: 448 passed.
  • New: secure round-trip through TLS to a plaintext target + plaintext-handshake-fails assertion; secure_unavailable negative test; TunnelTlsSource unit tests (load, no-op, renewal reload, invalid-mid-write keeps old context).

Note

Certificate files must be readable on the Hub host (TLS terminates there). The cert's SAN is not validated against --tunnel-public-host — the admin supplies a cert for that host.

🤖 Generated with Claude Code

gmpassos and others added 3 commits June 22, 2026 17:24
Add opt-in TLS termination on tunnel public ports. A tunnel opened with
`secure: true` (CLI `--secure`) has the Hub terminate TLS on the public
port; the Hub->exposer->target hops stay plaintext. Each accepted
connection is upgraded via `SecureSocket.secureServer`, leaving the
existing bind/port-range allocation and bridge untouched.

The Hub certificate is configured with `hub start --tunnel-tls-dir <dir>`
(a directory holding fullchain.pem + privkey.pem, LetsEncrypt layout)
matching `--tunnel-public-host`. A new `TunnelTlsSource` re-checks the
files every 12h and reloads the TLS context automatically when they
change, so renewals are picked up without a restart; a renewal caught
mid-write is skipped, keeping the previous cert in service. Requesting
`--secure` when no cert is configured fails with `secure_unavailable`.

Tunnels remain plain TCP by default; `--tunnel-tls-dir` is also carried
through `service install`/`reconfigure`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The unit test sourced a second certificate from certs/server.{crt,key},
but /certs/ is gitignored (only test/support/certs/localhost.* is
committed), so the test failed in a clean checkout with "missing test
certificate". Rework it to use only the committed localhost pair: the
"renewal" case rewrites fullchain.pem with a leading comment so the bytes
differ while staying a valid, parseable PEM.

Also document --secure in the interactive :tunnel command usage/help.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump to the latest major releases (checkout v7.0.0, codecov-action
v7.0.0). dart-lang/setup-dart stays at v1 (latest release is v1.7.2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gmpassos gmpassos merged commit 464fc4c into master Jun 22, 2026
5 checks passed
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.

1 participant