Support ALPN for TCP + TLS routers

This commit is contained in:
Dmitry Sharshakov 2022-07-07 17:58:09 +03:00 committed by GitHub
parent aff334ffb4
commit 4dc379c601
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 228 additions and 34 deletions

View file

@ -1,10 +1,12 @@
package tcp
import (
"fmt"
"net"
"testing"
"time"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v2/pkg/tcp"
@ -58,6 +60,7 @@ func Test_addTCPRoute(t *testing.T) {
rule string
serverName string
remoteAddr string
protos []string
routeErr bool
matchErr bool
}{
@ -436,6 +439,66 @@ func Test_addTCPRoute(t *testing.T) {
serverName: "bar",
remoteAddr: "10.0.0.1:80",
},
{
desc: "Invalid ALPN rule matching ACME-TLS/1",
rule: fmt.Sprintf("ALPN(`%s`)", tlsalpn01.ACMETLS1Protocol),
protos: []string{"foo"},
routeErr: true,
},
{
desc: "Valid ALPN rule matching single protocol",
rule: "ALPN(`foo`)",
protos: []string{"foo"},
},
{
desc: "Valid ALPN rule matching ACME-TLS/1 protocol",
rule: "ALPN(`foo`)",
protos: []string{tlsalpn01.ACMETLS1Protocol},
matchErr: true,
},
{
desc: "Valid ALPN rule not matching single protocol",
rule: "ALPN(`foo`)",
protos: []string{"bar"},
matchErr: true,
},
{
desc: "Valid alternative case ALPN rule matching single protocol without another being supported",
rule: "ALPN(`foo`) && !alpn(`h2`)",
protos: []string{"foo", "bar"},
},
{
desc: "Valid alternative case ALPN rule not matching single protocol because of another being supported",
rule: "ALPN(`foo`) && !alpn(`h2`)",
protos: []string{"foo", "h2", "bar"},
matchErr: true,
},
{
desc: "Valid complex alternative case ALPN and HostSNI rule",
rule: "ALPN(`foo`) && (!alpn(`h2`) || hostsni(`foo`))",
protos: []string{"foo", "bar"},
serverName: "foo",
},
{
desc: "Valid complex alternative case ALPN and HostSNI rule not matching by SNI",
rule: "ALPN(`foo`) && (!alpn(`h2`) || hostsni(`foo`))",
protos: []string{"foo", "bar", "h2"},
serverName: "bar",
matchErr: true,
},
{
desc: "Valid complex alternative case ALPN and HostSNI rule matching by ALPN",
rule: "ALPN(`foo`) && (!alpn(`h2`) || hostsni(`foo`))",
protos: []string{"foo", "bar"},
serverName: "bar",
},
{
desc: "Valid complex alternative case ALPN and HostSNI rule not matching by protos",
rule: "ALPN(`foo`) && (!alpn(`h2`) || hostsni(`foo`))",
protos: []string{"h2", "bar"},
serverName: "bar",
matchErr: true,
},
}
for _, test := range testCases {
@ -471,7 +534,7 @@ func Test_addTCPRoute(t *testing.T) {
remoteAddr: fakeAddr{addr: addr},
}
connData, err := NewConnData(test.serverName, conn)
connData, err := NewConnData(test.serverName, conn, test.protos)
require.NoError(t, err)
matchingHandler, _ := router.Match(connData)
@ -918,6 +981,75 @@ func Test_ClientIP(t *testing.T) {
}
}
func Test_ALPN(t *testing.T) {
testCases := []struct {
desc string
ruleALPNProtos []string
connProto string
buildErr bool
matchErr bool
}{
{
desc: "Empty",
buildErr: true,
},
{
desc: "ACME TLS proto",
ruleALPNProtos: []string{tlsalpn01.ACMETLS1Protocol},
buildErr: true,
},
{
desc: "Not matching empty proto",
ruleALPNProtos: []string{"h2"},
matchErr: true,
},
{
desc: "Not matching ALPN",
ruleALPNProtos: []string{"h2"},
connProto: "mqtt",
matchErr: true,
},
{
desc: "Matching ALPN",
ruleALPNProtos: []string{"h2"},
connProto: "h2",
},
{
desc: "Not matching multiple ALPNs",
ruleALPNProtos: []string{"h2", "mqtt"},
connProto: "h2c",
matchErr: true,
},
{
desc: "Matching multiple ALPNs",
ruleALPNProtos: []string{"h2", "h2c", "mqtt"},
connProto: "h2c",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
matchersTree := &matchersTree{}
err := alpn(matchersTree, test.ruleALPNProtos...)
if test.buildErr {
require.Error(t, err)
return
}
require.NoError(t, err)
meta := ConnData{
alpnProtos: []string{test.connProto},
}
assert.Equal(t, test.matchErr, !matchersTree.match(meta))
})
}
}
func Test_Priority(t *testing.T) {
testCases := []struct {
desc string