mirror of
https://github.com/caddyserver/caddy.git
synced 2026-06-05 13:35:19 -04:00
caddytls: match IDN SNI in connection policies (#7742)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 1m41s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m29s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m12s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m17s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m12s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m19s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m43s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m40s
Lint / govulncheck (push) Successful in 2m41s
Lint / dependency-review (push) Failing after 1m29s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m12s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 25m52s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 1m41s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m29s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m12s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m17s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m12s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m19s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m43s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m40s
Lint / govulncheck (push) Successful in 2m41s
Lint / dependency-review (push) Failing after 1m29s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m12s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 25m52s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
This commit is contained in:
@@ -107,7 +107,8 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
|
||||
if sni, ok := m.(MatchServerName); ok {
|
||||
for _, sniName := range sni {
|
||||
// index for fast lookups during handshakes
|
||||
indexedBySNI[sniName] = append(indexedBySNI[sniName], p)
|
||||
indexName := asciiServerNameForMatch(sniName)
|
||||
indexedBySNI[indexName] = append(indexedBySNI[indexName], p)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +119,7 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
|
||||
// filter policies by SNI first, if possible, to speed things up
|
||||
// when there may be lots of policies
|
||||
possiblePolicies := cp
|
||||
if indexedPolicies, ok := indexedBySNI[hello.ServerName]; ok {
|
||||
if indexedPolicies, ok := indexedBySNI[asciiServerNameForMatch(hello.ServerName)]; ok {
|
||||
possiblePolicies = indexedPolicies
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -24,6 +26,40 @@ import (
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
)
|
||||
|
||||
func TestConnectionPolicyIDNSNIMatcherFastPath(t *testing.T) {
|
||||
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||
defer cancel()
|
||||
|
||||
targetTLSConfig := &tls.Config{ClientAuth: tls.RequireAnyClientCert}
|
||||
policies := ConnectionPolicies{
|
||||
{
|
||||
matchers: []ConnectionMatcher{MatchServerName{"つ.Localhost"}},
|
||||
TLSConfig: targetTLSConfig,
|
||||
},
|
||||
}
|
||||
|
||||
const sniFastPathThreshold = 30
|
||||
for i := len(policies); i < sniFastPathThreshold; i++ {
|
||||
policies = append(policies, &ConnectionPolicy{
|
||||
matchers: []ConnectionMatcher{MatchServerName{fmt.Sprintf("example-%d.localhost", i)}},
|
||||
TLSConfig: &tls.Config{},
|
||||
})
|
||||
}
|
||||
policies = append(policies, &ConnectionPolicy{
|
||||
matchers: []ConnectionMatcher{MatchServerName{"xn--k9j.localhost"}},
|
||||
TLSConfig: &tls.Config{ClientAuth: tls.NoClientCert},
|
||||
})
|
||||
|
||||
tlsConfig := policies.TLSConfig(ctx)
|
||||
got, err := tlsConfig.GetConfigForClient(&tls.ClientHelloInfo{ServerName: "XN--K9J.LOCALHOST"})
|
||||
if err != nil {
|
||||
t.Fatalf("GetConfigForClient() error = %v", err)
|
||||
}
|
||||
if got != targetTLSConfig {
|
||||
t.Fatalf("expected Unicode IDN policy to match before later punycode policy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientAuthenticationUnmarshalCaddyfileWithDirectiveName(t *testing.T) {
|
||||
const test_der_1 = `MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==`
|
||||
const test_cert_file_1 = "../../caddytest/caddy.ca.cer"
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/caddyserver/certmagic"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/idna"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
@@ -69,15 +70,45 @@ func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
|
||||
repl = caddy.NewReplacer()
|
||||
}
|
||||
|
||||
serverName := asciiServerNameForMatch(hello.ServerName)
|
||||
for _, name := range m {
|
||||
rs := repl.ReplaceAll(name, "")
|
||||
if certmagic.MatchWildcard(hello.ServerName, rs) {
|
||||
rs := asciiServerNameForMatch(repl.ReplaceAll(name, ""))
|
||||
if certmagic.MatchWildcard(serverName, rs) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func asciiServerNameForMatch(name string) string {
|
||||
if name == "" {
|
||||
return name
|
||||
}
|
||||
|
||||
// SNI is ASCII on the wire, but config can use Unicode IDNs.
|
||||
ascii, err := idna.ToASCII(name)
|
||||
if err == nil {
|
||||
return strings.ToLower(ascii)
|
||||
}
|
||||
|
||||
if !strings.Contains(name, "*") {
|
||||
return strings.ToLower(name)
|
||||
}
|
||||
|
||||
labels := strings.Split(name, ".")
|
||||
for i, label := range labels {
|
||||
if label == "" || label == "*" {
|
||||
continue
|
||||
}
|
||||
ascii, err := idna.ToASCII(label)
|
||||
if err != nil {
|
||||
return strings.ToLower(name)
|
||||
}
|
||||
labels[i] = strings.ToLower(ascii)
|
||||
}
|
||||
return strings.Join(labels, ".")
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile sets up the MatchServerName from Caddyfile tokens. Syntax:
|
||||
//
|
||||
// sni <domains...>
|
||||
|
||||
@@ -79,6 +79,26 @@ func TestServerNameMatcher(t *testing.T) {
|
||||
input: "sub2.sub.example.com",
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
names: []string{"つ.localhost"},
|
||||
input: "xn--k9j.localhost",
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
names: []string{"つ.Localhost"},
|
||||
input: "XN--K9J.LOCALHOST",
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
names: []string{"*.つ.localhost"},
|
||||
input: "sub.xn--k9j.localhost",
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
names: []string{"*.つ.Localhost"},
|
||||
input: "Sub.XN--K9J.LOCALHOST",
|
||||
expect: true,
|
||||
},
|
||||
} {
|
||||
chi := &tls.ClientHelloInfo{ServerName: tc.input}
|
||||
actual := MatchServerName(tc.names).Match(chi)
|
||||
|
||||
Reference in New Issue
Block a user