@marcel91200 / caddy Mirror · read-only / commits / 3c1def24
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

caddytls: Support wildcard matching in ServerName conn policy matcher

Matthew Holt committed Mar 20, 2020 at 15:51 UTC 3c1def243020a3897121d4c5badf07ed45d2397d
3 files changed +105 -2
modules/caddytls/connpolicy.go
+1
@@ -76,6 +76,7 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
76 // using ServerName to match policies is extremely common, especially in configs
77 // with lots and lots of different policies; we can fast-track those by indexing
78 // them by SNI, so we don't have to iterate potentially thousands of policies
79 + // (TODO: this map does not account for wildcards, see if this is a problem in practice?)
80 indexedBySNI := make(map[string]ConnectionPolicies)
81 if len(cp) > 30 {
82 for _, p := range cp {
modules/caddytls/matchers.go
+18 -2
@@ -16,6 +16,7 @@ package caddytls
16
17 import (
18 "crypto/tls"
19 + "strings"
20
21 "github.com/caddyserver/caddy/v2"
22 )
@@ -24,7 +25,9 @@ func init() {
25 caddy.RegisterModule(MatchServerName{})
26 }
27
27 -// MatchServerName matches based on SNI.
28 +// MatchServerName matches based on SNI. Names in
29 +// this list may use left-most-label wildcards,
30 +// similar to wildcard certificates.
31 type MatchServerName []string
32
33 // CaddyModule returns the Caddy module information.
@@ -38,10 +41,23 @@ func (MatchServerName) CaddyModule() caddy.ModuleInfo {
41 // Match matches hello based on SNI.
42 func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
43 for _, name := range m {
41 - // TODO: support wildcards (and regex?)
44 if hello.ServerName == name {
45 return true
46 }
47 +
48 + // check for wildcard match on this name, but only
49 + // bother if there is even a wildcard character
50 + if !strings.Contains(name, "*") {
51 + continue
52 + }
53 + labels := strings.Split(hello.ServerName, ".")
54 + for i := range labels {
55 + labels[i] = "*"
56 + candidate := strings.Join(labels, ".")
57 + if candidate == name {
58 + return true
59 + }
60 + }
61 }
62 return false
63 }
modules/caddytls/matchers_test.go new
+86
@@ -0,0 +1,86 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package caddytls
16 +
17 +import (
18 + "crypto/tls"
19 + "testing"
20 +)
21 +
22 +func TestServerNameMatcher(t *testing.T) {
23 + for i, tc := range []struct {
24 + names []string
25 + input string
26 + expect bool
27 + }{
28 + {
29 + names: []string{"example.com"},
30 + input: "example.com",
31 + expect: true,
32 + },
33 + {
34 + names: []string{"example.com"},
35 + input: "foo.com",
36 + expect: false,
37 + },
38 + {
39 + names: []string{"example.com"},
40 + input: "",
41 + expect: false,
42 + },
43 + {
44 + names: []string{},
45 + input: "",
46 + expect: false,
47 + },
48 + {
49 + names: []string{"foo", "example.com"},
50 + input: "example.com",
51 + expect: true,
52 + },
53 + {
54 + names: []string{"foo", "example.com"},
55 + input: "sub.example.com",
56 + expect: false,
57 + },
58 + {
59 + names: []string{"foo", "example.com"},
60 + input: "foo.com",
61 + expect: false,
62 + },
63 + {
64 + names: []string{"*.example.com"},
65 + input: "example.com",
66 + expect: false,
67 + },
68 + {
69 + names: []string{"*.example.com"},
70 + input: "sub.example.com",
71 + expect: true,
72 + },
73 + {
74 + names: []string{"*.example.com", "*.sub.example.com"},
75 + input: "sub2.sub.example.com",
76 + expect: true,
77 + },
78 + } {
79 + chi := &tls.ClientHelloInfo{ServerName: tc.input}
80 + actual := MatchServerName(tc.names).Match(chi)
81 + if actual != tc.expect {
82 + t.Errorf("Test %d: Expected %t but got %t (input=%s match=%v)",
83 + i, tc.expect, actual, tc.input, tc.names)
84 + }
85 + }
86 +}