feat: serve TLS on HTTP/duplex and make TLS policy configurable - #2418
feat: serve TLS on HTTP/duplex and make TLS policy configurable#2418anithapriyanatarajan wants to merge 1 commit into
Conversation
|
qq: if we never terminated tls on HTTP, why not just set the default to 1.3 across the board? |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2418 +/- ##
===========================================
- Coverage 57.93% 46.54% -11.39%
===========================================
Files 50 73 +23
Lines 3119 4898 +1779
===========================================
+ Hits 1807 2280 +473
- Misses 1154 2367 +1213
- Partials 158 251 +93 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Thank you. This now unifies on TLS 1.3 across all serving paths - gRPC was already 1.3, so nothing changes there. it only defaults/raises the new HTTP/duplex path, with no regression. And since it's configurable, any operator whose path includes a sidecar, load balancer, or proxy that requires TLS 1.2 can opt down with --tls-min-version 1.2. Updating the Changes to reflect this. |
Adds a shared TLS policy layer so the HTTP/REST listener, the gRPC listener, and the duplex listener can all terminate TLS with a consistent, configurable policy. New flags: --http-tls-certificate / --http-tls-key enable TLS on the HTTP endpoint --tls-min-version minimum TLS version (1.2, 1.3) --tls-cipher-suites allowed TLS 1.2 cipher suites When --tls-min-version is unset, every serving path defaults to a TLS 1.3 floor. gRPC already required TLS 1.3, so this only raises the new HTTP/duplex paths and introduces no regression; operators fronted by a peer that cannot negotiate TLS 1.3 can opt down with --tls-min-version 1.2. The min-version and cipher-suite parsers reject unsupported versions and insecure/unknown suites, and validateTLSFlags fails fast at startup on mismatched certificate/key pairs. Certificates are hot-reloaded via the existing fsnotify watcher across all serving paths. Signed-off-by: Anitha Natarajan <anataraj@redhat.com>
ffb294b to
c9a92c0
Compare
| cmd.Flags().String("http-tls-certificate", "", "the certificate file to use for secure connections on the HTTP endpoint (two-listener mode) and the shared listener (duplex mode)") | ||
| cmd.Flags().String("http-tls-key", "", "the private key file to use for secure connections (without passphrase) on the HTTP endpoint (two-listener mode) and the shared listener (duplex mode)") | ||
| cmd.Flags().String("tls-min-version", "", "minimum TLS version for all serving paths (1.2 or 1.3); when unset defaults to 1.3. Lower to 1.2 for peers that cannot negotiate TLS 1.3.") | ||
| cmd.Flags().StringSlice("tls-cipher-suites", nil, "allowed TLS 1.2 cipher suite names (Go stdlib spelling) applied to all serving paths; empty keeps the Go default. Has no effect on TLS 1.3.") |
There was a problem hiding this comment.
Rather than go std lib, can we say crypto/tls?
There was a problem hiding this comment.
Can these functions go under an internal package like internal/tls? If they’re not exported, I’d prefer to not grow the cmd package.
| return nil, err | ||
| } | ||
|
|
||
| // Defaults to a TLS 1.3 floor unless an operator lowers it explicitly |
There was a problem hiding this comment.
Comment isn’t needed if values are set elsewhere
| cmd.Flags().String("grpc-tls-key", "", "the private key file to use for secure connections (without passphrase) - only applies to grpc-port") | ||
| cmd.Flags().String("http-tls-certificate", "", "the certificate file to use for secure connections on the HTTP endpoint (two-listener mode) and the shared listener (duplex mode)") | ||
| cmd.Flags().String("http-tls-key", "", "the private key file to use for secure connections (without passphrase) on the HTTP endpoint (two-listener mode) and the shared listener (duplex mode)") | ||
| cmd.Flags().String("tls-min-version", "", "minimum TLS version for all serving paths (1.2 or 1.3); when unset defaults to 1.3. Lower to 1.2 for peers that cannot negotiate TLS 1.3.") |
There was a problem hiding this comment.
I do wonder if there’s a need for 1.2. 1:3 is almost 10 years old. Like I mentioned, I don’t really want to be overly opinionated, but I also don’t want to support more than we have to.
There was a problem hiding this comment.
I agree to your point regarding 1.3. The main reason I prefer 1.2 as non default option is TLS 1.2 is not yet deprecated. RFC 8996 only deprecated 1.0/1.1. 1.2 is frozen (RFC 9851), not sunset. Most Managed Kubernetes platforms and many load balancers still run a 1.2 minimum, so dropping it would block valid deployments pinned to 1.2.
we could open a future issue to deprecate 1.2. Hope this is acceptable?
| viper.SetEnvPrefix(serveCmdEnvPrefix) | ||
| viper.AutomaticEnv() | ||
|
|
||
| // Fail fast on obviously invalid TLS configuration before any listeners start. |
There was a problem hiding this comment.
Comment doesn’t add additional detail, can be removed
There was a problem hiding this comment.
There’s a number of comments that fall under this as well, can you prune comments that aren’t adding additional context?
Adds a shared TLS policy layer so the HTTP/REST listener, the gRPC listener, and the duplex listener can all terminate TLS with a consistent, configurable policy.
New flags:
The min-version and cipher-suite parsers reject unsupported versions and insecure/unknown suites, and
validateTLSFlagsfails fast at startup on mismatched certificate/key pairs. Certificates are hot-reloaded via the existing fsnotify watcher across all serving paths.Summary
Closes #2415.
Today Fulcio can only terminate TLS on the gRPC listener (
--grpc-tls-certificate/--grpc-tls-key); the HTTP/REST endpoint is always served in plaintext, and the minimum TLS version and cipher suites are not configurable on any path. This makes it hard to run Fulcio behind operators/platforms that need TLS terminated at the component and want to enforce an organization-wide TLS policy.This PR introduces a small shared TLS policy layer (
cmd/app/tls.go) that is applied uniformly to all three serving paths — the two-listener HTTP and gRPC servers and the duplex (single shared listener) server:--http-tls-certificate/--http-tls-key. In duplex mode the same certificate secures the shared listener; ALPN advertisesh2thenhttp/1.1so gRPC-over-HTTP/2 still negotiates correctly, and the loopback gateway dial is upgraded to TLS.--tls-min-version(1.2or1.3). When unset, each path keeps its historical default — TLS 1.3 for gRPC, TLS 1.2 for HTTP/duplex — so behavior is unchanged for existing deployments.--tls-cipher-suites, restricted to the secure suites reported by the Go stdlib (insecure/unknown names are rejected). This has no effect on TLS 1.3, whose suite set is fixed by Go.validateTLSFlags) at startup: a certificate without its matching key (or vice versa) on either path, an unsupported version, or an unknown cipher suite name is rejected before serving.All flags are opt-in and default to the current behavior, so this is backward compatible: without the new flags, Fulcio serves exactly as it does today.
How to test
go test ./cmd/app/...- includes unit tests for version/cipher parsing and flag validation, plus an end-to-endTestDuplexTLSthat starts a TLS-enabled duplex server, confirmsGET /healthzsucceeds over HTTPS, and confirms a client offering below the minimum version is rejected.fulcio servewith--http-tls-certificate/--http-tls-key(optionally--tls-min-version 1.3) and confirm the REST endpoint is served over HTTPS and that a sub-minimum client handshake is refused.Release Note
Documentation
Updated
docs/setup.mdwith a new "Serving over TLS" section covering the--grpc-tls-*,--http-tls-*,--tls-min-version, and--tls-cipher-suitesflags and an example. No change to https://docs.sigstore.dev is required.