traefik/pkg/server/router/tcp/router.go

383 lines
11 KiB
Go
Raw Normal View History

package tcp
import (
2022-03-17 17:02:08 +00:00
"bufio"
"bytes"
"crypto/tls"
"errors"
2022-03-17 17:02:08 +00:00
"io"
"net"
"net/http"
2022-03-17 17:02:08 +00:00
"time"
"github.com/traefik/traefik/v2/pkg/log"
2022-03-17 17:02:08 +00:00
tcpmuxer "github.com/traefik/traefik/v2/pkg/muxer/tcp"
"github.com/traefik/traefik/v2/pkg/tcp"
)
2022-03-17 17:02:08 +00:00
const defaultBufSize = 4096
// Router is a TCP router.
type Router struct {
// Contains TCP routes.
muxerTCP tcpmuxer.Muxer
// Contains TCP TLS routes.
muxerTCPTLS tcpmuxer.Muxer
// Contains HTTPS routes.
muxerHTTPS tcpmuxer.Muxer
// Forwarder handlers.
// Handles all HTTP requests.
httpForwarder tcp.Handler
// Handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
httpsForwarder tcp.Handler
// Neither is used directly, but they are held here, and recreated on config
// reload, so that they can be passed to the Switcher at the end of the config
// reload phase.
httpHandler http.Handler
httpsHandler http.Handler
// TLS configs.
httpsTLSConfig *tls.Config // default TLS config
hostHTTPTLSConfig map[string]*tls.Config // TLS configs keyed by SNI
2021-06-11 13:30:05 +00:00
}
2022-03-17 17:02:08 +00:00
// NewRouter returns a new TCP router.
func NewRouter() (*Router, error) {
muxTCP, err := tcpmuxer.NewMuxer()
if err != nil {
return nil, err
}
2022-03-17 17:02:08 +00:00
muxTCPTLS, err := tcpmuxer.NewMuxer()
if err != nil {
return nil, err
}
2022-03-17 17:02:08 +00:00
muxHTTPS, err := tcpmuxer.NewMuxer()
if err != nil {
return nil, err
}
2022-03-17 17:02:08 +00:00
return &Router{
muxerTCP: *muxTCP,
muxerTCPTLS: *muxTCPTLS,
muxerHTTPS: *muxHTTPS,
}, nil
}
2022-03-17 17:02:08 +00:00
// GetTLSGetClientInfo is called after a ClientHello is received from a client.
func (r *Router) GetTLSGetClientInfo() func(info *tls.ClientHelloInfo) (*tls.Config, error) {
return func(info *tls.ClientHelloInfo) (*tls.Config, error) {
if tlsConfig, ok := r.hostHTTPTLSConfig[info.ServerName]; ok {
return tlsConfig, nil
}
2022-03-17 17:02:08 +00:00
return r.httpsTLSConfig, nil
}
}
2022-03-17 17:02:08 +00:00
// ServeTCP forwards the connection to the right TCP/HTTP handler.
func (r *Router) ServeTCP(conn tcp.WriteCloser) {
// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS
// routers on the entryPoint, and if there is at least one non-TLS TCP router.
// In the case of a non-TLS TCP client (that does not "send" first), we would
// block forever on clientHelloServerName, which is why we want to detect and
// handle that case first and foremost.
if r.muxerTCP.HasRoutes() && !r.muxerTCPTLS.HasRoutes() && !r.muxerHTTPS.HasRoutes() {
connData, err := tcpmuxer.NewConnData("", conn)
if err != nil {
2022-03-17 17:02:08 +00:00
log.WithoutContext().Errorf("Error while reading TCP connection data: %v", err)
conn.Close()
return
}
handler, _ := r.muxerTCP.Match(connData)
2022-03-17 17:02:08 +00:00
// If there is a handler matching the connection metadata,
// we let it handle the connection.
if handler != nil {
handler.ServeTCP(conn)
return
}
2022-03-17 17:02:08 +00:00
// Otherwise, we keep going because:
// 1) we could be in the case where we have HTTP routers.
// 2) if it is an HTTPS request, even though we do not have any TLS routers,
// we still need to reply with a 404.
}
2022-03-17 17:02:08 +00:00
// FIXME -- Check if ProxyProtocol changes the first bytes of the request
br := bufio.NewReader(conn)
serverName, tls, peeked, err := clientHelloServerName(br)
if err != nil {
conn.Close()
return
}
2022-03-17 17:02:08 +00:00
// Remove read/write deadline and delegate this to underlying tcp server (for now only handled by HTTP Server)
err = conn.SetReadDeadline(time.Time{})
if err != nil {
log.WithoutContext().Errorf("Error while setting read deadline: %v", err)
}
2022-03-17 17:02:08 +00:00
err = conn.SetWriteDeadline(time.Time{})
if err != nil {
2022-03-17 17:02:08 +00:00
log.WithoutContext().Errorf("Error while setting write deadline: %v", err)
}
2022-03-17 17:02:08 +00:00
connData, err := tcpmuxer.NewConnData(serverName, conn)
if err != nil {
log.WithoutContext().Errorf("Error while reading TCP connection data: %v", err)
conn.Close()
return
}
2022-03-17 17:02:08 +00:00
if !tls {
handler, _ := r.muxerTCP.Match(connData)
2022-03-17 17:02:08 +00:00
switch {
case handler != nil:
handler.ServeTCP(r.GetConn(conn, peeked))
case r.httpForwarder != nil:
r.httpForwarder.ServeTCP(r.GetConn(conn, peeked))
default:
conn.Close()
}
2022-03-17 17:02:08 +00:00
return
}
// For real, the handler eventually used for HTTPS is (almost) always the same:
// it is the httpsForwarder that is used for all HTTPS connections that match
// (which is also incidentally the same used in the last block below for 404s).
// The added value from doing Match is to find and use the specific TLS config
// (wrapped inside the returned handler) requested for the given HostSNI.
handlerHTTPS, catchAllHTTPS := r.muxerHTTPS.Match(connData)
if handlerHTTPS != nil && !catchAllHTTPS {
// In order not to depart from the behavior in 2.6, we only allow an HTTPS router
// to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router (so
// basically any router that has a specific HostSNI based rule).
handlerHTTPS.ServeTCP(r.GetConn(conn, peeked))
2022-03-17 17:02:08 +00:00
return
}
// Contains also TCP TLS passthrough routes.
handlerTCPTLS, catchAllTCPTLS := r.muxerTCPTLS.Match(connData)
if handlerTCPTLS != nil && !catchAllTCPTLS {
handlerTCPTLS.ServeTCP(r.GetConn(conn, peeked))
return
}
// Fallback on HTTPS catchAll.
// We end up here for e.g. an HTTPS router that only has a PathPrefix rule,
// which under the scenes is counted as an HostSNI(*) rule.
if handlerHTTPS != nil {
handlerHTTPS.ServeTCP(r.GetConn(conn, peeked))
return
}
// Fallback on TCP TLS catchAll.
if handlerTCPTLS != nil {
handlerTCPTLS.ServeTCP(r.GetConn(conn, peeked))
2022-03-17 17:02:08 +00:00
return
}
2022-03-17 17:02:08 +00:00
// needed to handle 404s for HTTPS, as well as all non-Host (e.g. PathPrefix) matches.
if r.httpsForwarder != nil {
r.httpsForwarder.ServeTCP(r.GetConn(conn, peeked))
return
}
2022-03-17 17:02:08 +00:00
conn.Close()
}
2022-03-17 17:02:08 +00:00
// AddRoute defines a handler for the given rule.
func (r *Router) AddRoute(rule string, priority int, target tcp.Handler) error {
return r.muxerTCP.AddRoute(rule, priority, target)
}
// AddRouteTLS defines a handler for a given rule and sets the matching tlsConfig.
func (r *Router) AddRouteTLS(rule string, priority int, target tcp.Handler, config *tls.Config) error {
// TLS PassThrough
if config == nil {
return r.muxerTCPTLS.AddRoute(rule, priority, target)
}
return r.muxerTCPTLS.AddRoute(rule, priority, &tcp.TLSHandler{
Next: target,
Config: config,
})
}
// AddHTTPTLSConfig defines a handler for a given sniHost and sets the matching tlsConfig.
func (r *Router) AddHTTPTLSConfig(sniHost string, config *tls.Config) {
if r.hostHTTPTLSConfig == nil {
r.hostHTTPTLSConfig = map[string]*tls.Config{}
}
2022-03-17 17:02:08 +00:00
r.hostHTTPTLSConfig[sniHost] = config
}
2022-03-17 17:02:08 +00:00
// GetConn creates a connection proxy with a peeked string.
func (r *Router) GetConn(conn tcp.WriteCloser, peeked string) tcp.WriteCloser {
// FIXME should it really be on Router ?
conn = &Conn{
Peeked: []byte(peeked),
WriteCloser: conn,
}
2022-03-17 17:02:08 +00:00
return conn
}
2022-03-17 17:02:08 +00:00
// GetHTTPHandler gets the attached http handler.
func (r *Router) GetHTTPHandler() http.Handler {
return r.httpHandler
}
2022-03-17 17:02:08 +00:00
// GetHTTPSHandler gets the attached https handler.
func (r *Router) GetHTTPSHandler() http.Handler {
return r.httpsHandler
}
2022-03-17 17:02:08 +00:00
// SetHTTPForwarder sets the tcp handler that will forward the connections to an http handler.
func (r *Router) SetHTTPForwarder(handler tcp.Handler) {
r.httpForwarder = handler
}
2022-03-17 17:02:08 +00:00
// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler.
func (r *Router) SetHTTPSForwarder(handler tcp.Handler) {
for sniHost, tlsConf := range r.hostHTTPTLSConfig {
// muxerHTTPS only contains single HostSNI rules (and no other kind of rules),
// so there's no need for specifying a priority for them.
err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, &tcp.TLSHandler{
Next: handler,
Config: tlsConf,
})
if err != nil {
log.WithoutContext().Errorf("Error while adding route for host: %v", err)
}
}
2022-03-17 17:02:08 +00:00
r.httpsForwarder = &tcp.TLSHandler{
Next: handler,
Config: r.httpsTLSConfig,
}
}
2022-03-17 17:02:08 +00:00
// SetHTTPHandler attaches http handlers on the router.
func (r *Router) SetHTTPHandler(handler http.Handler) {
r.httpHandler = handler
}
2022-03-17 17:02:08 +00:00
// SetHTTPSHandler attaches https handlers on the router.
func (r *Router) SetHTTPSHandler(handler http.Handler, config *tls.Config) {
r.httpsHandler = handler
r.httpsTLSConfig = config
}
2022-03-17 17:02:08 +00:00
// Conn is a connection proxy that handles Peeked bytes.
type Conn struct {
// Peeked are the bytes that have been read from Conn for the
// purposes of route matching, but have not yet been consumed
// by Read calls. It set to nil by Read when fully consumed.
Peeked []byte
// Conn is the underlying connection.
// It can be type asserted against *net.TCPConn or other types
// as needed. It should not be read from directly unless
// Peeked is nil.
tcp.WriteCloser
}
2022-03-17 17:02:08 +00:00
// Read reads bytes from the connection (using the buffer prior to actually reading).
func (c *Conn) Read(p []byte) (n int, err error) {
if len(c.Peeked) > 0 {
n = copy(p, c.Peeked)
c.Peeked = c.Peeked[n:]
if len(c.Peeked) == 0 {
c.Peeked = nil
}
2022-03-17 17:02:08 +00:00
return n, nil
}
return c.WriteCloser.Read(p)
}
2022-03-17 17:02:08 +00:00
// clientHelloServerName returns the SNI server name inside the TLS ClientHello,
// without consuming any bytes from br.
// On any error, the empty string is returned.
func clientHelloServerName(br *bufio.Reader) (string, bool, string, error) {
hdr, err := br.Peek(1)
if err != nil {
var opErr *net.OpError
if !errors.Is(err, io.EOF) && (!errors.As(err, &opErr) || opErr.Timeout()) {
log.WithoutContext().Errorf("Error while Peeking first byte: %s", err)
}
2022-03-17 17:02:08 +00:00
return "", false, "", err
}
2022-03-17 17:02:08 +00:00
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
// start with a uint16 length where the MSB is set and the first record
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
// an SSLv2 client.
const recordTypeSSLv2 = 0x80
const recordTypeHandshake = 0x16
if hdr[0] != recordTypeHandshake {
if hdr[0] == recordTypeSSLv2 {
// we consider SSLv2 as TLS and it will be refused by real TLS handshake.
return "", true, getPeeked(br), nil
}
return "", false, getPeeked(br), nil // Not TLS.
}
2022-03-17 17:02:08 +00:00
const recordHeaderLen = 5
hdr, err = br.Peek(recordHeaderLen)
if err != nil {
log.Errorf("Error while Peeking hello: %s", err)
return "", false, getPeeked(br), nil
2021-06-11 13:30:05 +00:00
}
2022-03-17 17:02:08 +00:00
recLen := int(hdr[3])<<8 | int(hdr[4]) // ignoring version in hdr[1:3]
if recordHeaderLen+recLen > defaultBufSize {
br = bufio.NewReaderSize(br, recordHeaderLen+recLen)
2021-06-11 13:30:05 +00:00
}
2022-03-17 17:02:08 +00:00
helloBytes, err := br.Peek(recordHeaderLen + recLen)
2021-06-11 13:30:05 +00:00
if err != nil {
2022-03-17 17:02:08 +00:00
log.Errorf("Error while Hello: %s", err)
return "", true, getPeeked(br), nil
2021-06-11 13:30:05 +00:00
}
2022-03-17 17:02:08 +00:00
sni := ""
server := tls.Server(sniSniffConn{r: bytes.NewReader(helloBytes)}, &tls.Config{
GetConfigForClient: func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
sni = hello.ServerName
return nil, nil
},
})
_ = server.Handshake()
return sni, true, getPeeked(br), nil
}
2021-06-11 13:30:05 +00:00
2022-03-17 17:02:08 +00:00
func getPeeked(br *bufio.Reader) string {
peeked, err := br.Peek(br.Buffered())
if err != nil {
log.Errorf("Could not get anything: %s", err)
return ""
}
return string(peeked)
2021-06-11 13:30:05 +00:00
}
2022-03-17 17:02:08 +00:00
// sniSniffConn is a net.Conn that reads from r, fails on Writes,
// and crashes otherwise.
type sniSniffConn struct {
r io.Reader
net.Conn // nil; crash on any unexpected use
}
// Read reads from the underlying reader.
func (c sniSniffConn) Read(p []byte) (int, error) { return c.r.Read(p) }
// Write crashes all the time.
func (sniSniffConn) Write(p []byte) (int, error) { return 0, io.EOF }