Skip to content

Commit 11929c9

Browse files
authored
fix: HTTP server IPv6 matching (#2345)
1 parent e020767 commit 11929c9

File tree

3 files changed

+85
-14
lines changed

3 files changed

+85
-14
lines changed

challenge/http01/domain_matcher.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package http01
33
import (
44
"fmt"
55
"net/http"
6+
"net/netip"
67
"strings"
78
)
89

@@ -54,7 +55,7 @@ func (m *hostMatcher) name() string {
5455
}
5556

5657
func (m *hostMatcher) matches(r *http.Request, domain string) bool {
57-
return strings.HasPrefix(r.Host, domain)
58+
return matchDomain(r.Host, domain)
5859
}
5960

6061
// arbitraryMatcher checks whether the specified (*net/http.Request).Header value starts with a domain name.
@@ -65,7 +66,7 @@ func (m arbitraryMatcher) name() string {
6566
}
6667

6768
func (m arbitraryMatcher) matches(r *http.Request, domain string) bool {
68-
return strings.HasPrefix(r.Header.Get(m.name()), domain)
69+
return matchDomain(r.Header.Get(m.name()), domain)
6970
}
7071

7172
// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name.
@@ -87,7 +88,7 @@ func (m *forwardedMatcher) matches(r *http.Request, domain string) bool {
8788
}
8889

8990
host := fwds[0]["host"]
90-
return strings.HasPrefix(host, domain)
91+
return matchDomain(host, domain)
9192
}
9293

9394
// parsing requires some form of state machine.
@@ -133,9 +134,7 @@ func parseForwardedHeader(s string) (elements []map[string]string, err error) {
133134

134135
case r == ',': // end of forwarded-element
135136
if key != "" {
136-
if val == "" {
137-
val = s[pos:i]
138-
}
137+
val = s[pos:i]
139138
cur[key] = val
140139
}
141140
elements = append(elements, cur)
@@ -185,3 +184,12 @@ func skipWS(s string, i int) int {
185184
func isWS(r rune) bool {
186185
return strings.ContainsRune(" \t\v\r\n", r)
187186
}
187+
188+
func matchDomain(src, domain string) bool {
189+
addr, err := netip.ParseAddr(domain)
190+
if err == nil && addr.Is6() {
191+
domain = "[" + domain + "]"
192+
}
193+
194+
return strings.HasPrefix(src, domain)
195+
}

challenge/http01/domain_matcher_test.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package http01
22

33
import (
4+
"net/http"
5+
"net/http/httptest"
46
"testing"
57

68
"github.com/stretchr/testify/assert"
79
"github.com/stretchr/testify/require"
810
)
911

10-
func TestParseForwardedHeader(t *testing.T) {
12+
func Test_parseForwardedHeader(t *testing.T) {
1113
testCases := []struct {
1214
name string
1315
input string
@@ -83,3 +85,54 @@ func TestParseForwardedHeader(t *testing.T) {
8385
})
8486
}
8587
}
88+
89+
func Test_hostMatcher_matches(t *testing.T) {
90+
hm := &hostMatcher{}
91+
92+
testCases := []struct {
93+
desc string
94+
domain string
95+
req *http.Request
96+
expected assert.BoolAssertionFunc
97+
}{
98+
{
99+
desc: "exact domain",
100+
domain: "example.com",
101+
req: httptest.NewRequest(http.MethodGet, "http://example.com", nil),
102+
expected: assert.True,
103+
},
104+
{
105+
desc: "request with path",
106+
domain: "example.com",
107+
req: httptest.NewRequest(http.MethodGet, "http://example.com/foo/bar", nil),
108+
expected: assert.True,
109+
},
110+
{
111+
desc: "ipv4",
112+
domain: "127.0.0.1",
113+
req: httptest.NewRequest(http.MethodGet, "http://127.0.0.1", nil),
114+
expected: assert.True,
115+
},
116+
{
117+
desc: "ipv6",
118+
domain: "2001:db8::1",
119+
req: httptest.NewRequest(http.MethodGet, "http://[2001:db8::1]", nil),
120+
expected: assert.True,
121+
},
122+
{
123+
desc: "ipv6 with brackets",
124+
domain: "[2001:db8::1]",
125+
req: httptest.NewRequest(http.MethodGet, "http://[2001:db8::1]", nil),
126+
expected: assert.True,
127+
},
128+
}
129+
130+
for _, test := range testCases {
131+
t.Run(test.desc, func(t *testing.T) {
132+
t.Parallel()
133+
hm.matches(test.req, test.domain)
134+
135+
test.expected(t, hm.matches(test.req, test.domain))
136+
})
137+
}
138+
}

challenge/http01/http_challenge_server.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error {
5656
}
5757

5858
s.done = make(chan bool)
59+
5960
go s.serve(domain, token, keyAuth)
61+
6062
return nil
6163
}
6264

@@ -69,8 +71,11 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
6971
if s.listener == nil {
7072
return nil
7173
}
74+
7275
s.listener.Close()
76+
7377
<-s.done
78+
7479
return nil
7580
}
7681

@@ -107,19 +112,23 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) {
107112
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
108113
if r.Method == http.MethodGet && s.matcher.matches(r, domain) {
109114
w.Header().Set("Content-Type", "text/plain")
115+
110116
_, err := w.Write([]byte(keyAuth))
111117
if err != nil {
112118
http.Error(w, err.Error(), http.StatusInternalServerError)
113119
return
114120
}
121+
115122
log.Infof("[%s] Served key authentication", domain)
116-
} else {
117-
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure you are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
118-
_, err := w.Write([]byte("TEST"))
119-
if err != nil {
120-
http.Error(w, err.Error(), http.StatusInternalServerError)
121-
return
122-
}
123+
return
124+
}
125+
126+
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure you are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
127+
128+
_, err := w.Write([]byte("TEST"))
129+
if err != nil {
130+
http.Error(w, err.Error(), http.StatusInternalServerError)
131+
return
123132
}
124133
})
125134

@@ -133,5 +142,6 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) {
133142
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
134143
log.Println(err)
135144
}
145+
136146
s.done <- true
137147
}

0 commit comments

Comments
 (0)