Add support for ALPN

Closes: https://todo.sr.ht/~emersion/tlstunnel/11
This commit is contained in:
Simon Ser 2020-10-19 10:53:36 +02:00
parent 64285842fe
commit e8f71081cb
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 74 additions and 11 deletions

View File

@ -51,6 +51,11 @@ func parseFrontend(srv *Server, d *scfg.Directive) error {
}
}
protocolDirective := d.Children.Get("protocol")
if protocolDirective != nil {
frontend.Protocols = protocolDirective.Params
}
for _, addr := range d.Params {
host, port, err := net.SplitHostPort(addr)
if err != nil {

View File

@ -133,35 +133,68 @@ func (ln *Listener) handle(conn net.Conn) error {
defer conn.Close()
// TODO: setup timeouts
tlsConn := tls.Server(conn, ln.Server.ACMEConfig.TLSConfig())
tlsConfig := ln.Server.ACMEConfig.TLSConfig()
getConfigForClient := tlsConfig.GetConfigForClient
tlsConfig.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
// Call previous GetConfigForClient function, if any
var tlsConfig *tls.Config
if getConfigForClient != nil {
var err error
tlsConfig, err = getConfigForClient(hello)
if err != nil {
return nil, err
}
} else {
tlsConfig = ln.Server.ACMEConfig.TLSConfig()
}
fe, err := ln.matchFrontend(hello.ServerName)
if err != nil {
return nil, err
}
tlsConfig.NextProtos = fe.Protocols
return tlsConfig, nil
}
tlsConn := tls.Server(conn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
return err
}
tlsState := tlsConn.ConnectionState()
fe, err := ln.matchFrontend(tlsState.ServerName)
if err != nil {
return err
}
fe, ok := ln.Frontends[tlsState.ServerName]
return fe.handle(tlsConn, &tlsState)
}
func (ln *Listener) matchFrontend(serverName string) (*Frontend, error) {
fe, ok := ln.Frontends[serverName]
if !ok {
// match wildcard certificates, allowing only a single, non-partial wildcard, in the left-most label
i := strings.IndexByte(tlsState.ServerName, '.')
// don't allow wildcards with only a TLD (eg *.com)
if i >= 0 && strings.IndexByte(tlsState.ServerName[i+1:], '.') >= 0 {
fe, ok = ln.Frontends["*"+tlsState.ServerName[i:]]
// Match wildcard certificates, allowing only a single, non-partial
// wildcard, in the left-most label
i := strings.IndexByte(serverName, '.')
// Don't allow wildcards with only a TLD (e.g. *.com)
if i >= 0 && strings.IndexByte(serverName[i+1:], '.') >= 0 {
fe, ok = ln.Frontends["*"+serverName[i:]]
}
}
if !ok {
fe, ok = ln.Frontends[""]
}
if !ok {
return fmt.Errorf("can't find frontend for server name %q", tlsState.ServerName)
return nil, fmt.Errorf("can't find frontend for server name %q", serverName)
}
return fe.handle(tlsConn, &tlsState)
return fe, nil
}
type Frontend struct {
Server *Server
Backend Backend
Server *Server
Backend Backend
Protocols []string
}
func (fe *Frontend) handle(downstream net.Conn, tlsState *tls.ConnectionState) error {
@ -184,6 +217,9 @@ func (fe *Frontend) handle(downstream net.Conn, tlsState *tls.ConnectionState) e
if tlsState.ServerName != "" {
tlvs = append(tlvs, authorityTLV(tlsState.ServerName))
}
if tlsState.NegotiatedProtocol != "" {
tlvs = append(tlvs, alpnTLV(tlsState.NegotiatedProtocol))
}
if tlv, err := sslTLV(tlsState); err != nil {
return fmt.Errorf("failed to set PROXY protocol header SSL TLV: %v", err)
} else {
@ -228,6 +264,13 @@ func authorityTLV(name string) proxyproto.TLV {
}
}
func alpnTLV(proto string) proxyproto.TLV {
return proxyproto.TLV{
Type: proxyproto.PP2_TYPE_ALPN,
Value: []byte(proto),
}
}
func sslTLV(state *tls.ConnectionState) (proxyproto.TLV, error) {
pp2ssl := tlvparse.PP2SSL{
Client: tlvparse.PP2_BITFIELD_CLIENT_SSL, // all of our connections are TLS

View File

@ -66,6 +66,21 @@ The following directives are supported:
This disables automatic TLS.
*protocol* <name>...
List of supported application-layer protocols.
The first protocol which is also supported by the client is negociated.
The protocols will be advertised via the TLS ALPN extension. See the
IANA registry for a list of protocol names:
https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
For instance, for an HTTP server supporting HTTP/1 and HTTP/2:
```
protocol h2 http/1.1 http/1.0
```
*tls* { ... }
Customise global TLS configuration.