Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to properly deploy ECH in transparent proxy mode #110

Open
cuonglm opened this issue Jun 3, 2022 · 9 comments
Open

How to properly deploy ECH in transparent proxy mode #110

cuonglm opened this issue Jun 3, 2022 · 9 comments

Comments

@cuonglm
Copy link
Contributor

cuonglm commented Jun 3, 2022

I have a TLS transparent proxy which works like this:

Client ==|TLS request|==> Proxy ==|HTTP Connect|==> Target 

I use https://github.com/inconshreveable/go-vhost to sniff SNI and construct the HTTP Connect request to the target site.

Now I want to add ECH support to my transparent proxy. But I stuck after getting the inner SNI:

tlsConn, _ := vhost.TLS(conn)
sconn := tls.Server(tlsConn, &tls.Config{
    ECHEnabled: true,
    ServerECHProvider: echProvider,
    Certificates: []tls.Certificate{cert},
})
sconn.Handshake()
sconn.ConnectionState().Servername // This is the inner SNI

IIUC, the proxy must tell the client to construct the new inner ClientHello, how can I archive that with the current crypto/tls API?

cc @cjpatton

@cjpatton
Copy link
Contributor

cjpatton commented Jun 9, 2022

Hi @cuonglm, I'm not sure I'm familiar with your deployment scenario. Can you provide a bit more detail? For context, I'm not familiar with the go-vhost.

To configure a client to use ECH, you need to set crypto/tls.Config.ECHEnabled to true and you need to set crypto/tls.Config.ClientECHConfigs to a set of ECH configs advertised by the server.

@cuonglm
Copy link
Contributor Author

cuonglm commented Jun 9, 2022

@cjpatton Sorry for my bad description, let me re-phrase the deployment.

I have a TLS proxy written in Go, what it does:

  • For each TLS request, sniff the SNI from net.Conn (this is where go-vhost helps me).
  • After getting the SNI, for example cloudflare.com, my proxy will open a HTTP CONNECT request to cloudflare.com.
  • Then the proxy will forward the TLS request to cloudflare.com, and let the TLS handshake happens normally.
  • Client can now access cloudflare.com via my proxy.

Now, I want to add ECH support to my proxy, what should I do on my proxy?

Currently, I am able to get the inner SNI:

tlsConn, _ := vhost.TLS(conn)  // After this, the outer SNI is extracted, for example, ech.my-domain.com
sconn := tls.Server(tlsConn, &tls.Config{
    ECHEnabled: true,
    ServerECHProvider: echProvider,
    Certificates: []tls.Certificate{cert},
})
sconn.Handshake()
sconn.ConnectionState().Servername  // This is the inner SNI, for example, cloudflare.com

Now, I'm stuck here. What should I do next? AFAIU, after the call to sconn.Handshake(), I'm able to get the inner hello, so how can I forward it to the target site (cloudflare.com in this case)?

@cjpatton
Copy link
Contributor

Setting aside ECH for a second: What do you mean by "TLS request"? Do you mean the ClientHello? Is this the sequence of events (passive proxy):

  1. Client sends ClientHello to proxy
  2. Proxy forwards ClientHello (without changing it) to server
  3. Server sends ServerHello to Proxy
  4. Proxy forwards ServerHello to Client

Or do you mean this (active proxy):

  1. Client sends ClientHello to proxy
  2. Proxy terminates TLS, sending its own ServerHello in response to the client. Meanwhile, it establishes a TLS connection to the server with which it can forward HTTP requests.

@cuonglm
Copy link
Contributor Author

cuonglm commented Jun 10, 2022

@cjpatton yes, it's a passive proxy (transparent proxy).

@cjpatton
Copy link
Contributor

cjpatton commented Jun 10, 2022

If ECH is used in the handshake, then a passive proxy won't have access to the inner SNI. This is because the inner ClientHello is encrypted the server's HPKE public key. Only the outer SNI is sent in the clear.

If you need to passively inspect the inner SNI, then you'll have to arrange for the client to use an HPKE public key for which the proxy knows the corresponding secret key. However, this won't be possible in a typical deployment.

@cuonglm
Copy link
Contributor Author

cuonglm commented Jun 10, 2022

If ECH is used in the handshake, then a passive proxy won't have access to the inner SNI. This is because the inner ClientHello is encrypted the server's HPKE public key. Only the outer SNI is sent in the clear.

If you need to passively inspect the inner SNI, then you'll have to arrange for the client to use an HPKE public key for which the proxy knows the corresponding secret key. However, this won't be possible in a typical deployment.

Yes, you can see I was able to decrypt the inner SNI. I mean after that, what must I do to forward the inner hello to target?

@cjpatton
Copy link
Contributor

cjpatton commented Jun 10, 2022

Oh, perhaps I misunderstood. You mean that you're forwarding the inner ClientHello to the server, not the outer ClientHello?

You might be asking about "Split Mode". The transparent proxy is known as the "client-facing server" in ECH-lingo; and the target server is known as the "backend server". The client-facing server has the HPKE secret key. Split Mode is designed so that the backend server does not need the secret key.

Split Mode is not currently supported by our implementation. We might be willing to consider a patch to add Split Mode, however the design would need to be carefully thought out. When we first looked at this, we concluded that Split Mode would complicate the state machine significantly.

There may be other implementations of ECH that have support for Split Mode. You should consider reaching out to the mailing list ([email protected]).

@cuonglm
Copy link
Contributor Author

cuonglm commented Jun 10, 2022

@cjpatton Yes, split-mode is what I'm trying to do. Do you have plan and accept contribution outside Cloudflare? I'm happy to join and implement split-mode.

@cjpatton
Copy link
Contributor

I'd be happy to review a PR! A good place to start might be to propose the changes to the API.

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

No branches or pull requests

2 participants