Add ALPN/NextProtos helpers for TLS config - #21
Conversation
Add SetNextProtos and WithHTTP2 composable functions that follow the existing func(*tls.Config) pattern used by controller-runtime's TLSOpts. Currently every operator using NewTLSConfigFromProfile must manually append a function to set tls.Config.NextProtos for ALPN negotiation. This is copy-pasted across 49+ repos in the openshift org, typically as a CVE-2023-44487 mitigation (disabling HTTP/2). These helpers eliminate that boilerplate: // Before: disableHTTP2 := func(c *tls.Config) { c.NextProtos = []string{"http/1.1"} } tlsOpts := []func(*tls.Config){tlsConfig, disableHTTP2} // After: tlsOpts := []func(*tls.Config){tlsConfig, openshifttls.WithHTTP2(false)} New exports: - HTTP2NextProtos: []string{"h2", "http/1.1"} - HTTP1NextProtos: []string{"http/1.1"} - SetNextProtos(protos ...string): generic ALPN setter - WithHTTP2(enabled bool): convenience for HTTP/2 enable/disable Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Reword HTTP1NextProtos comment to clarify this is defense-in-depth against CVE-2023-44487/CVE-2023-39325, not a standalone fix. The primary fixes are in Go 1.21.3+ and golang.org/x/net v0.17.0+. 2. Fix SetNextProtos to preserve nil semantics when called with zero arguments. Previously make([]string, 0) produced a non-nil empty slice, which has different TLS behavior than nil (nil = no ALPN negotiation, empty = ALPN extension with no protocols). 3. Update test assertion to use BeNil() instead of BeEmpty() to verify the nil vs empty distinction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@sdodson @joelanford would appreciate a review when you get a chance. This adds ALPN/NextProtos to the TLS config returned by NewTLSConfigFromProfile. Currently every operator using this library has to append NextProtos manually. This fixes it at the library level. |
Address review feedback from joelanford: WithHTTP2 is too opinionated
for a shared library. It silently replaces the entire NextProtos list,
which doesn't compose well when controller-runtime or other callers
already have protos set.
Changes:
- Remove WithHTTP2 function entirely
- Add empty-string filtering to SetNextProtos so callers can use
conditional inclusion patterns without wrapper functions
- Update godoc examples to show the well-known protocol list vars
- Replace WithHTTP2 tests with empty-string filtering and
composability tests
Callers are now explicit about what they're setting:
SetNextProtos(openshifttls.HTTP1NextProtos...)
SetNextProtos(openshifttls.HTTP2NextProtos...)
SetNextProtos("h2", "http/1.1")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@ugiordan: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
|
/approve |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: joelanford, ugiordan The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
@joelanford can I get a lgtm? |
|
@joelanford could you please help with this one? |
Summary
Adds composable ALPN protocol negotiation helpers to
pkg/tls, eliminatingcopy-paste boilerplate found in 49+ repos across the openshift org.
Currently every operator using
NewTLSConfigFromProfilemust manually appenda
func(*tls.Config)to settls.Config.NextProtos. This change provides:HTTP2NextProtos/HTTP1NextProtos: well-known ALPN protocol listsSetNextProtos(protos ...string): explicit ALPN setter with defensive copy and empty-string filteringBefore (every operator today):
After:
Other usage patterns:
Concrete use-cases
Repos already using
NewTLSConfigFromProfilealongside manual NextProtos:cmd/operator/operator.go,cmd/vgmanager/vgmanager.go)main.go)cmd/main.go)cmd/main.go)cmd/cluster-node-tuning-operator/main.go)GitHub code search shows 49 total repos in the openshift org with this pattern.
Design decisions
NewTLSConfigFromProfile: NextProtos is HTTP protocolnegotiation, not TLS cipher/version selection. Different consumers want
different values. Composable functions preserve the existing API.
they're setting rather than going through a boolean abstraction.
SetNextProtos: prevents callers from mutating thecaptured slice.
requiring wrapper functions.
SetNextProtos()with zero non-empty args returns nilto preserve
tls.Configdefault semantics.func(*tls.Config)return type, exportedpackage-level vars with
nolint:gochecknoglobals, Ginkgo/Gomega tests.Verification
make testpasses (all tests green with race detector)make lintpasses (0 issues)