mholt/caddy-l4

Dynamic proxy based on SNI

vk496 opened this issue · 4 comments

Hello.

Related to #240, there are any way to route traffic based on the SNI? For example:

Instead of static configuration like this:


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

			@app2 tls sni app2.internal
			route @app2 {
				tls
				proxy 192.168.1.156:3333
			}

			@app3 tls sni app3.internal
			route @app3 {
				tls
				proxy 192.168.1.157:3333
			}

			@insecure http
			@secure tls

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

Have the app routed directly based on the SNI. Maybe similar to this:


	layer4 {
		:18000 {
                        #regex or simple wildcard
			@app tls sni app[0-9]{1,2}.internal
			route @app {
				tls
				proxy ${sni}:3333 # or maybe object access ${app.tls.sni}
			}

			@insecure http
			@secure tls

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

The idea behind is that there will be multiple instances of the app, and the client could route based on the SNI value. Im not sure how ports treatement could be possible (or regex/parser of the sni), but at least using the sni directly could reduce a lot the config redundancy.

So basically the SNI matcher needs to support regex, I guess? Or a new sni_regexp matcher?

So basically the SNI matcher needs to support regex, I guess? Or a new sni_regexp matcher?

That's one of the things. And the other, reference those attributes, so it can be "reused". Even better, if we're able to use basic functions like split or array acces, we could "construct" proxy destination from sni string. Something like:

# sni example: app02_3333_.internal
@app tls sni app[0-9]{1,2}_[0-9]{1,4}_.internal
	route @app {
		tls
		proxy ${app.tls.sni.split("_")[0]}:${app.tls.sni.split("_")[1]}
	}

Im not fluent in Go, but the idea is simple: regex on SNI for matching and then accessing what was matched (and if possible, manipulate/parse it)

Hi! My 2 cents:

  • proxy ${sni}:3333 # or maybe object access ${app.tls.sni} is already implemented by tls matcher, use {l4.tls.server_name}.
  • sni (sub)matcher is a part of Caddy, not caddy-l4. Whether it is an expanded syntax for sni matcher or a new sni_regexp matcher, it would be more logical to put it to modules/caddytls/matchers.go of the mainline.

Even better, if we're able to use basic functions like split or array acces, we could "construct" proxy destination from sni string.

You may also implement your own sni_advanced matcher with any syntax you like that perfectly satisfies your needs and build Caddy with it.

I'm using something like this in my Caddyfile to perform SRV lookups to identify backends for HTTP:

*.srv.example.com {
	map {host} {consul_service} {
		~(.*)\.srv\.example\.com$ "${1}.service.consul"
	}
	reverse_proxy {
		dynamic srv {consul_service} {
			resolvers "10.10.10.10"
		}
	}
}

Being able to do the same for L4 would be amazing.