egress: MITM tunneled TLS with per-SNI certificate minting - #871
Conversation
195d6c4 to
7976e5a
Compare
6080f7e to
7b74711
Compare
There was a problem hiding this comment.
- Remove the intermediate CA code
- Some of the options seem like we don't need to expose. If so, then the logic inside should be simplified.
- Move the CA generating logic into its own package (suggest
sdsmint/certauth) that exports a very narrow interface out to the server. Seem like the only things that need to be exported are a Get() and Forget() - I don't know if we need an optional minter.Forgetter() -- this comment applies to other places in the code that seem to have more options than we need.
- Go through the comments and scrub for the agent-based self-conversation. The comments are pretty verbose in some places and look more like agent thinking tokens than code comments.
I mostly looked at the server code. Once you get the simplifications in, I will take a more detailed look at the rest of the change.
Done
Done
Done
Done |
|
|
||
| cmd.Flags().StringVar(&cfg.UDSPath, "uds-path", "", "unix socket to listen on; required, and the only transport offered, because leaf private keys transit this channel") | ||
| cmd.Flags().StringVar(&cfg.CAPoolPath, "ca-pool-path", "", "path to a localca pool JSON holding the MITM CA, the format substrate mounts its other CAs in") | ||
| cmd.Flags().StringVar(&cfg.CAID, "ca-id", "", "which CA in the pool to sign with; empty takes the first") |
There was a problem hiding this comment.
Let's leave the behavior to just take the first --- localca.Pool should be tracking which localca.CA is active for signing. I will send a PR to do this (and add rotation commands for the CA secrets).
There was a problem hiding this comment.
Taahir Ahmed (@ahmedtd) , are you suggesting removing this flag and take the first CA from the pool by default?
There was a problem hiding this comment.
Does your PR need to block this PR?
There was a problem hiding this comment.
Can we default to first for now or it doesn't work without this flag.
There was a problem hiding this comment.
It works as is. Here is how the sdsmint container in the egress gateway Pod sets it
args:
- "sdsmint"
- "--uds-path=/var/run/sdsmint/sdsmint.sock"
- "--ca-pool-path=/run/ca-state/mitm-pool.json"
- "--ca-id=mitm"
There was a problem hiding this comment.
Yeah, we can just default to the first CA for now.
|
I rebased after #959 is merged. We should expect |
|
https://github.com/agent-substrate/substrate/actions/runs/31837847684/job/94888039437?pr=871 The test Bowei Du (@bowei) , let us merge #926 first. Then I will rebase and update this PR. |
Lior Lieberman (LiorLieberman)
left a comment
There was a problem hiding this comment.
Keith Mattix II (@keithmattix) has done a nice thing on #715 where he builds the listener in go (see xds.go in his PR) based on whether someone enabled connect (using a flag).
we should do something similar for MITM listener and other config changes.
Go's random sources cannot be exhausted --- they are a CSPRNG keyed with initial randomness read from /dev/urandom. /dev/urandom itself is a CSPRNG seeded with a few bytes of random derived from startup or the RDRAND instruction. Linux does a bunch of accounting of entropy sources, and tries to reseed /dev/random and /dev/urandom once many values are read from them. This is widely considered to be superstitious hokum. |
|
|
||
| cmd.Flags().StringVar(&cfg.UDSPath, "uds-path", "", "unix socket to listen on; required, and the only transport offered, because leaf private keys transit this channel") | ||
| cmd.Flags().StringVar(&cfg.CAPoolPath, "ca-pool-path", "", "path to a localca pool JSON holding the MITM CA, the format substrate mounts its other CAs in") | ||
| cmd.Flags().StringVar(&cfg.CAID, "ca-id", "", "which CA in the pool to sign with; empty takes the first") |
There was a problem hiding this comment.
Yeah, we can just default to the first CA for now.
|
LGTM with some nits |
Thanks for sharing. Filed an issue #1003. |
e492ea2 to
e69d2a7
Compare
| return nil, errors.New("empty host") | ||
| } | ||
|
|
||
| ca := s.active |
There was a problem hiding this comment.
Should we defensively validate the CA is not expired, to avoid signing certs with an invalid CA?
There was a problem hiding this comment.
Let us do it in a follwoup PR since we plan to move selectCA to internal/localca in a follow up PR (see line 77)
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if err := active.Validate(); err != nil { |
There was a problem hiding this comment.
Should we also validate that the CA is not expired/past NotBefore here (or in ca.Validate())?
There was a problem hiding this comment.
Let us do it in a follwoup PR since we plan to move selectCA to internal/localca in a follow up PR (see line 77)
Lior Lieberman (LiorLieberman)
left a comment
There was a problem hiding this comment.
A few more comments:
-
I think grpc and websocket break with this change. The issue is that we are trying to now parse in the inner connection, and we dont have any support for anything thats not h1 AFAICT. This breaks egress traffic that was not http in many ways
-
related to 1 - we need to add e2e tests for non http traffic..
| # Two chains, selected by what the tunnel actually carries. tls_inspector | ||
| # tags a ClientHello "tls" and anything else "raw_buffer", so a cleartext | ||
| # HTTP tunnel gets an HTTP chain instead of being fed to a TLS transport | ||
| # socket, which would fail the handshake and close the tunnel. |
There was a problem hiding this comment.
What about non http traffic? Actors sends non http all the time, I think with this config it would not work for them. common example includes git clone ssh:// but any other tcp traffic as well.
There was a problem hiding this comment.
I created #1017 to add e2e test coverage for non-HTTP traffic.
To unblock this PR, I will do a manual verification of running git clone git@github.com:agent-substrate/substrate.git from an actor.
I filed #1018 to add support for non-HTTP traffic when sdsmint is enabled. We can address it as a followup. |
What does |
|
h1 is HTTP/1.1. Mostly people call HTTP/2 as |
|
The experimental flag guards enablement, so we should not see any issues with this change. |
|
For some reason, the Google CLA is not working |
Envoy now terminates the TLS session inside an actor's CONNECT tunnel with a leaf minted on demand for the SNI the client asked for, instead of forwarding the tunneled bytes opaquely. That puts the plaintext request on a filter chain where policy can be applied later. A new atenet sdsmint subcommand serves those leaves over DELTA_GRPC SDS on a unix socket. Opt-in: --experimental-use-sdsmint selects atenet-egress-with-sdsmint.yaml and creates the egress-mitm-ca-pool secret. The default egress manifest and install path are unchanged. Also: localca gains CA.Validate, GenerateCA options and a crypto.Signer SigningKey; kubectl-ate admin make-ca-pool gains --key-type and --common-name; atunnel returns ErrGatewayHandshake and ConnectRejectedError instead of formatted strings. The sdsmint e2e suite is skipped for now.
|
Bowei Du (@bowei) , I force-pushed the branch. The Google CLA is green now. |
New changes:
picked by tls_inspector: tls terminates using Envoy 1.37's on_demand_secret selector + sni cert mapper (hence the
1.34 → 1.37 bump), raw_buffer proxies plaintext http:// and enforces the allowlist as an :authority match, since
there's no mint on that path to refuse.
--common-name; install-ate.sh creates the egress-mitm-ca-pool secret before the Deployment.
Not included:
egress-mitm-ca-poolFollowups:
cmd/atenet/internal/sdsmint/certauth/certauth.gofrom tointernal/localcaThis is to address #823