Skip to content

TLS termination with SNI #240

Description

@vk496

Hello and thank you for this amazing project!

I'm trying to create a configuration where I use only one port for http, https, and custom TCP protocol of external apps (encapsulated with TLS and forwarded based on SNI).

{
	debug
	local_certs

	servers {
		listener_wrappers {
			http_redirect
			tls
		}
	}

	layer4 {
		:18000 {
			@app1 tls sni app1.internal
			route @app1 {
				tls
				proxy 192.168.1.159:3333
			}

			@secure tls
			route @secure {
				proxy localhost:443
			}

			@insecure http
			route @insecure {
				proxy localhost:443
			}
		}
	}
}

localhost:443 {
	tls internal {
		on_demand
	}

	# reverse_proxy google.com:80
	respond "Hello from the website!"
}

However, I'm not able to forward the traffic based on SNI. Reducing my config to:

{
	layer4 {
		:18000 {
			@app1 tls sni app1.internal
			route @app1 {
				proxy 192.168.1.159:3333
			}
		}
	}
}

If I open a ncat -l 3333 and run echo hello | openssl s_client -connect 127.0.0.1:18000 -servername app1.internal, I get garbage on the ncat terminal (which is expected). However, if I use:

{
	layer4 {
		:18000 {
			@app1 tls sni app1.internal
			route @app1 {
				tls
				proxy 192.168.1.159:3333
			}
		}
	}
}

And run the same command, I get:

$ echo hello | openssl s_client -connect 127.0.0.1:18000 -servername app1.internal
Connecting to 127.0.0.1
CONNECTED(00000003)
4027BB94637F0000:error:0A000438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:ssl/record/rec_layer_s3.c:865:SSL alert number 80
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 321 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

and the debug messages are:

caddy-1  | {"level":"debug","ts":1724427725.8702772,"logger":"layer4","msg":"matching","remote":"172.19.0.1:59716","error":"consumed all prefetched bytes","matcher":"layer4.matchers.tls","matched":false}
caddy-1  | {"level":"debug","ts":1724427725.8702905,"logger":"layer4","msg":"matching","remote":"172.19.0.1:59716","error":"consumed all prefetched bytes","matcher":"layer4.matchers.tls","matched":false}
caddy-1  | {"level":"debug","ts":1724427725.8702946,"logger":"layer4","msg":"matching","remote":"172.19.0.1:59716","error":"consumed all prefetched bytes","matcher":"layer4.matchers.http","matched":false}
caddy-1  | {"level":"debug","ts":1724427725.8703141,"logger":"layer4","msg":"prefetched","remote":"172.19.0.1:59716","bytes":321}
caddy-1  | {"level":"debug","ts":1724427725.870344,"logger":"layer4.matchers.tls","msg":"matched","remote":"172.19.0.1:59716","server_name":"app1.internal"}
caddy-1  | {"level":"debug","ts":1724427725.8703463,"logger":"layer4","msg":"matching","remote":"172.19.0.1:59716","matcher":"layer4.matchers.tls","matched":true}
caddy-1  | {"level":"debug","ts":1724427725.8705084,"logger":"events","msg":"event","name":"tls_get_certificate","id":"3d7c68ae-a4fc-407e-ab17-9bc40ead6fce","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47,255],"ServerName":"app1.internal","SupportedCurves":[29,23,30,25,24,256,257,258,259,260],"SupportedPoints":"AAEC","SignatureSchemes":[1027,1283,1539,2055,2056,2074,2075,2076,2057,2058,2059,2052,2053,2054,1025,1281,1537,771,769,770,1026,1282,1538],"SupportedProtos":null,"SupportedVersions":[772,771],"RemoteAddr":{"IP":"172.19.0.1","Port":59716,"Zone":""},"LocalAddr":{"IP":"172.19.0.2","Port":18000,"Zone":""}}}}
caddy-1  | {"level":"debug","ts":1724427725.8705153,"logger":"tls.handshake","msg":"no matching certificates and no custom selection logic","identifier":"app1.internal"}
caddy-1  | {"level":"debug","ts":1724427725.8705628,"logger":"tls.handshake","msg":"no matching certificates and no custom selection logic","identifier":"*.internal"}
caddy-1  | {"level":"debug","ts":1724427725.870565,"logger":"tls.handshake","msg":"no matching certificates and no custom selection logic","identifier":"*.*"}
caddy-1  | {"level":"debug","ts":1724427725.8705704,"logger":"tls.handshake","msg":"no certificate matching TLS ClientHello","remote_ip":"172.19.0.1","remote_port":"59716","server_name":"app1.internal","remote":"172.19.0.1:59716","identifier":"app1.internal","cipher_suites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47,255],"cert_cache_fill":0,"load_or_obtain_if_necessary":true,"on_demand":false}
caddy-1  | {"level":"error","ts":1724427725.8706036,"logger":"layer4","msg":"handling connection","remote":"172.19.0.1:59716","error":"no certificate available for 'app1.internal'"}
caddy-1  | {"level":"debug","ts":1724427725.8706071,"logger":"layer4","msg":"connection stats","remote":"172.19.0.1:59716","read":321,"written":7,"duration":0.000339871}

Is this a bug? is intentional? There is any way to handle this correctly?

BR,
Valentin

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions