@@ -21,18 +21,13 @@ import (
21
21
"crypto/tls"
22
22
"io"
23
23
"net"
24
- "strings"
25
24
)
26
25
27
26
// AddSNIRoute appends a route to the ipPort listener that routes to
28
27
// dest if the incoming TLS SNI server name is sni. If it doesn't
29
28
// match, rule processing continues for any additional routes on
30
29
// ipPort.
31
30
//
32
- // By default, the proxy will route all ACME tls-sni-01 challenges
33
- // received on ipPort to all SNI dests. You can disable ACME routing
34
- // with AddStopACMESearch.
35
- //
36
31
// The ipPort is any valid net.Listen TCP address.
37
32
func (p * Proxy ) AddSNIRoute (ipPort , sni string , dest Target ) {
38
33
p .AddSNIMatchRoute (ipPort , equals (sni ), dest )
@@ -43,20 +38,8 @@ func (p *Proxy) AddSNIRoute(ipPort, sni string, dest Target) {
43
38
// matcher. If it doesn't match, rule processing continues for any
44
39
// additional routes on ipPort.
45
40
//
46
- // By default, the proxy will route all ACME tls-sni-01 challenges
47
- // received on ipPort to all SNI dests. You can disable ACME routing
48
- // with AddStopACMESearch.
49
- //
50
41
// The ipPort is any valid net.Listen TCP address.
51
42
func (p * Proxy ) AddSNIMatchRoute (ipPort string , matcher Matcher , dest Target ) {
52
- cfg := p .configFor (ipPort )
53
- if ! cfg .stopACME {
54
- if len (cfg .acmeTargets ) == 0 {
55
- p .addRoute (ipPort , & acmeMatch {cfg })
56
- }
57
- cfg .acmeTargets = append (cfg .acmeTargets , dest )
58
- }
59
-
60
43
p .addRoute (ipPort , sniMatch {matcher : matcher , target : dest })
61
44
}
62
45
@@ -69,14 +52,6 @@ func (p *Proxy) AddSNIRouteFunc(ipPort string, fn SNITargetFunc) {
69
52
p .addRoute (ipPort , sniMatch {targetFunc : fn })
70
53
}
71
54
72
- // AddStopACMESearch prevents ACME probing of subsequent SNI routes.
73
- // Any ACME challenges on ipPort for SNI routes previously added
74
- // before this call will still be proxied to all possible SNI
75
- // backends.
76
- func (p * Proxy ) AddStopACMESearch (ipPort string ) {
77
- p .configFor (ipPort ).stopACME = true
78
- }
79
-
80
55
type sniMatch struct {
81
56
matcher Matcher
82
57
target Target
@@ -102,79 +77,6 @@ func (m sniMatch) match(br *bufio.Reader) (Target, string) {
102
77
return nil , ""
103
78
}
104
79
105
- // acmeMatch matches "*.acme.invalid" ACME tls-sni-01 challenges and
106
- // searches for a Target in cfg.acmeTargets that has the challenge
107
- // response.
108
- type acmeMatch struct {
109
- cfg * config
110
- }
111
-
112
- func (m * acmeMatch ) match (br * bufio.Reader ) (Target , string ) {
113
- sni := clientHelloServerName (br )
114
- if ! strings .HasSuffix (sni , ".acme.invalid" ) {
115
- return nil , ""
116
- }
117
-
118
- // TODO: cache. ACME issuers will hit multiple times in a short
119
- // burst for each issuance event. A short TTL cache + singleflight
120
- // should have an excellent hit rate.
121
- // TODO: maybe an acme-specific timeout as well?
122
- // TODO: plumb context upwards?
123
- ctx , cancel := context .WithCancel (context .Background ())
124
- defer cancel ()
125
-
126
- ch := make (chan Target , len (m .cfg .acmeTargets ))
127
- for _ , target := range m .cfg .acmeTargets {
128
- go tryACME (ctx , ch , target , sni )
129
- }
130
- for range m .cfg .acmeTargets {
131
- if target := <- ch ; target != nil {
132
- return target , sni
133
- }
134
- }
135
-
136
- // No target was happy with the provided challenge.
137
- return nil , ""
138
- }
139
-
140
- func tryACME (ctx context.Context , ch chan <- Target , dest Target , sni string ) {
141
- var ret Target
142
- defer func () { ch <- ret }()
143
-
144
- conn , targetConn := net .Pipe ()
145
- defer conn .Close ()
146
- go dest .HandleConn (targetConn )
147
-
148
- deadline , ok := ctx .Deadline ()
149
- if ok {
150
- conn .SetDeadline (deadline )
151
- }
152
-
153
- client := tls .Client (conn , & tls.Config {
154
- ServerName : sni ,
155
- InsecureSkipVerify : true ,
156
- })
157
- if err := client .Handshake (); err != nil {
158
- // TODO: log?
159
- return
160
- }
161
- certs := client .ConnectionState ().PeerCertificates
162
- if len (certs ) == 0 {
163
- // TODO: log?
164
- return
165
- }
166
- // acme says the first cert offered by the server must match the
167
- // challenge hostname.
168
- if err := certs [0 ].VerifyHostname (sni ); err != nil {
169
- // TODO: log?
170
- return
171
- }
172
-
173
- // Target presented what looks like a valid challenge
174
- // response, send it back to the matcher.
175
- ret = dest
176
- }
177
-
178
80
// clientHelloServerName returns the SNI server name inside the TLS ClientHello,
179
81
// without consuming any bytes from br.
180
82
// On any error, the empty string is returned.
0 commit comments