Skip to content

feat: serve TLS on HTTP/duplex and make TLS policy configurable - #2418

Open
anithapriyanatarajan wants to merge 1 commit into
sigstore:mainfrom
anithapriyanatarajan:allow-tls-policy-config
Open

feat: serve TLS on HTTP/duplex and make TLS policy configurable#2418
anithapriyanatarajan wants to merge 1 commit into
sigstore:mainfrom
anithapriyanatarajan:allow-tls-policy-config

Conversation

@anithapriyanatarajan

@anithapriyanatarajan anithapriyanatarajan commented Aug 5, 2026

Copy link
Copy Markdown

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 and the shared listener
--tls-min-version                        minimum TLS version for all serving paths (1.2 or 1.3)
--tls-cipher-suites                      allowed TLS 1.2 cipher suite names applied to all serving paths

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.

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:

  • TLS on the HTTP endpoint via --http-tls-certificate / --http-tls-key. In duplex mode the same certificate secures the shared listener; ALPN advertises h2 then http/1.1 so gRPC-over-HTTP/2 still negotiates correctly, and the loopback gateway dial is upgraded to TLS.
  • Configurable minimum TLS version via --tls-min-version (1.2 or 1.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.
  • Configurable TLS 1.2 cipher suites via --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.
  • Fail-fast validation (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.
  • Certificates continue to be hot-reloaded through the existing fsnotify-based watcher on every serving path.

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

  • Run go test ./cmd/app/... - includes unit tests for version/cipher parsing and flag validation, plus an end-to-end TestDuplexTLS that starts a TLS-enabled duplex server, confirms GET /healthz succeeds over HTTPS, and confirms a client offering below the minimum version is rejected.
  • Manually: start fulcio serve with --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

Added TLS support for the HTTP endpoint and a configurable TLS policy across all serving paths. New `serve` flags: `--http-tls-certificate` and `--http-tls-key` enable TLS on the HTTP/REST endpoint (and the shared listener in duplex mode), `--tls-min-version` sets the minimum TLS version (1.2 or 1.3), and `--tls-cipher-suites` restricts the allowed TLS 1.2 cipher suites. All flags are opt-in. Existing deployments are unaffected.

Documentation

Updated docs/setup.md with a new "Serving over TLS" section covering the --grpc-tls-*, --http-tls-*, --tls-min-version, and --tls-cipher-suites flags and an example. No change to https://docs.sigstore.dev is required.

@anithapriyanatarajan
anithapriyanatarajan requested a review from a team as a code owner August 5, 2026 11:32
@bobcallaway

Copy link
Copy Markdown
Member

qq: if we never terminated tls on HTTP, why not just set the default to 1.3 across the board?

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.07080% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.54%. Comparing base (cf238ac) to head (c9a92c0).
⚠️ Report is 638 commits behind head on main.

Files with missing lines Patch % Lines
cmd/app/http.go 69.56% 3 Missing and 4 partials ⚠️
cmd/app/serve.go 79.31% 4 Missing and 2 partials ⚠️
cmd/app/grpc.go 75.00% 2 Missing and 1 partial ⚠️
cmd/app/tls.go 95.91% 1 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@anithapriyanatarajan

Copy link
Copy Markdown
Author

qq: if we never terminated tls on HTTP, why not just set the default to 1.3 across the board?

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>
Comment thread cmd/app/serve.go
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.")

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.

Rather than go std lib, can we say crypto/tls?

Comment thread cmd/app/tls.go

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.

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.

Comment thread cmd/app/grpc.go
return nil, err
}

// Defaults to a TLS 1.3 floor unless an operator lowers it explicitly

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.

Comment isn’t needed if values are set elsewhere

Comment thread cmd/app/serve.go
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.")

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.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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?

Comment thread cmd/app/serve.go
viper.SetEnvPrefix(serveCmdEnvPrefix)
viper.AutomaticEnv()

// Fail fast on obviously invalid TLS configuration before any listeners start.

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.

Comment doesn’t add additional detail, can be removed

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.

There’s a number of comments that fall under this as well, can you prune comments that aren’t adding additional context?

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 TLS on the HTTP endpoint and make TLS policy configurable

3 participants