It seems the reverse proxy always sends a SNI

1. Caddy version (caddy version):

2.5.1

2. How I run Caddy:

a. System environment:

NixOS

b. Command:

caddy run

c. Service/unit/compose file:

Paste full file contents here.
Make sure backticks stay on their own lines,
and the post looks nice in the preview pane.

d. My complete Caddyfile or JSON config:

https://:1111 {
    reverse_proxy {host} {
        transport http {
            tls
            tls_trusted_ca_certs /etc/ssl/certs/ca-bundle.crt
        }
    }
    tls {
        issuer internal
        on_demand
    }
}

3. The problem I’m having:

I’m trying to manuplate my traffic with caddy. But I found it doesn’t do what I want. According to the doc the connection to the upstream doesn’t send the SNI unless I set one but it seems it always sends one. I checked it with https://clienttest.ssllabs.com:8443/ssltest/viewMyClient.html and it says that the SNI is enabled.

4. Error messages and/or full log output:

5. What I already tried:

6. Links to relevant resources:

You’re right, the documentation is wrong/misleading.

I dug through the code, and I think we misunderstood how the Go stdlib actually uses ServerName (i.e. tls_server_name in Caddy’s config).

So this doesn’t configure SNI at all (SNI is always sent), what it does is it controls what server name that the TLS handshake logic uses for validating that the certificate served by the upstream matches the requested hostname. By default, this will be the configured upstream dial address’ host part. Configuring tls_server_name will override that default.

Essentially the only situation where it’s useful is when you configure the dial address with something like an IP address, but your upstream is an HTTPS server which is serving an actual domain name. So say you configure Caddy with an upstream https://172.20.0.1 (because the server is in your private network) but the upstream server wants to serve a certificate with foo.example.com in its SAN, then you would need to configure tls_server_name to foo.example.com for the handshake to actually succeed (otherwise it would try to look for 172.20.0.1 in the cert, but wouldn’t find it).

I’ll make sure we fix the docs.

1 Like

Could you fix the behavior? Nginx can config the SNI. This config can disable the SNI:

tls_server_name 127.0.0.1
tls_insecure_skip_verify

But it’s insecure. Thanks!

Why do you need to disable SNI? I’m not sure Go gives us a way to do that.

SNI is used for censorship and I’m trying to use caddy to bypass it as said in net/http: SNI in TLS Handshake should match Request.Host · Issue #22704 · golang/go · GitHub. This is not a standard use case. I don’t know if Go provides a secure method to do that. Some other packages just uses InsecureSkipVerify to disable SNI.

I don’t plan on spending time looking deeper into this, tbh. I don’t really see any value in having support for turning off SNI in Caddy.

@linsui Are you sure you are not looking for a forward proxy?

I don’t quite understand how the SNI docs are wrong. :face_with_monocle:

Understand. In Nginx’s case it doesn’t support SNI at the begining.

In fact I’m looking for a MitM proxy and caddy can do that.

The doc says that

  • tls_server_name sets the ServerName (SNI) to put in the ClientHello; only needed if the remote server requires it.

which implies that without tls_server_name the SNI is not sent.

Oh, I see what you mean. That makes more sense, thanks :slight_smile: