diff options
author | Brad Fitzpatrick <[email protected]> | 2022-10-16 18:56:27 -0700 |
---|---|---|
committer | Brad Fitzpatrick <[email protected]> | 2022-10-16 18:56:27 -0700 |
commit | 91f861402626c6ba93eaa57ee257109c4f07bd00 (patch) | |
tree | bd4f4f46ccbf9ced54d6facb3e549b8bb9d28452 | |
parent | 74ca1dc5d55168d202044c415dcf2e08d80c3fdc (diff) |
remove old ACME tls-sni-01 stuff that LetsEncrypt removed March 2019
No point keeping it around. We can look at the git history to do
something similar later if we end up doing TLS-ALPN-01 in a similar
way.
-rw-r--r-- | cmd/tlsrouter/acme.go | 101 | ||||
-rw-r--r-- | cmd/tlsrouter/config.go | 9 | ||||
-rw-r--r-- | cmd/tlsrouter/e2e_test.go | 10 | ||||
-rw-r--r-- | sni.go | 98 | ||||
-rw-r--r-- | tcpproxy.go | 4 | ||||
-rw-r--r-- | tcpproxy_test.go | 62 |
6 files changed, 7 insertions, 277 deletions
diff --git a/cmd/tlsrouter/acme.go b/cmd/tlsrouter/acme.go deleted file mode 100644 index ab8d59a..0000000 --- a/cmd/tlsrouter/acme.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "crypto/tls" - "net" - "time" -) - -type acmeCacheEntry struct { - backend string - expires time.Time -} - -// ACME locates backends that are attempting ACME SNI-based validation. -type ACME struct { - backends []string - // *.acme.invalid domain to cache entry - cache map[string]acmeCacheEntry -} - -// Match returns the backend for hostname, if one is found. -func (s *ACME) Match(hostname string) string { - c := s.cache[hostname] - if time.Now().Before(c.expires) { - return c.backend - } - - // Cache entry is either expired or invalid, need to figure out - // which backend is the right one. - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - ch := make(chan string, len(s.backends)) - for _, backend := range s.backends { - go tryAcme(ctx, ch, backend, hostname) - } - for range s.backends { - backend := <-ch - if backend != "" { - s.cache[hostname] = acmeCacheEntry{backend, time.Now().Add(5 * time.Second)} - return backend - } - } - - // No usable backends found :( - s.cache[hostname] = acmeCacheEntry{"", time.Now().Add(5 * time.Second)} - return "" -} - -func tryAcme(ctx context.Context, ch chan string, backend, hostname string) { - var res string - var err error - defer func() { ch <- res }() - - dialer := net.Dialer{Timeout: 10 * time.Second} - conn, err := dialer.DialContext(ctx, "tcp", backend) - if err != nil { - return - } - defer conn.Close() - - deadline, ok := ctx.Deadline() - if ok { - conn.SetDeadline(deadline) - } - client := tls.Client(conn, &tls.Config{ - ServerName: hostname, - InsecureSkipVerify: true, - }) - if err != nil { - return - } - if err = client.Handshake(); err != nil { - return - } - - certs := client.ConnectionState().PeerCertificates - if len(certs) == 0 { - return - } - if err = certs[0].VerifyHostname(hostname); err != nil { - return - } - - res = backend -} diff --git a/cmd/tlsrouter/config.go b/cmd/tlsrouter/config.go index 1c8151f..692b04b 100644 --- a/cmd/tlsrouter/config.go +++ b/cmd/tlsrouter/config.go @@ -37,7 +37,6 @@ type Route struct { type Config struct { mu sync.Mutex routes []Route - acme *ACME } func dnsRegex(s string) (*regexp.Regexp, error) { @@ -64,10 +63,6 @@ func (c *Config) Match(hostname string) (string, bool) { c.mu.Lock() defer c.mu.Unlock() - if strings.HasSuffix(hostname, ".acme.invalid") { - return c.acme.Match(hostname), false - } - for _, r := range c.routes { if r.match.MatchString(hostname) { return r.backend, r.proxyInfo @@ -123,10 +118,6 @@ func (c *Config) Read(r io.Reader) error { c.mu.Lock() defer c.mu.Unlock() c.routes = routes - c.acme = &ACME{ - backends: backends, - cache: make(map[string]acmeCacheEntry), - } return nil } diff --git a/cmd/tlsrouter/e2e_test.go b/cmd/tlsrouter/e2e_test.go index 92551e2..6e54021 100644 --- a/cmd/tlsrouter/e2e_test.go +++ b/cmd/tlsrouter/e2e_test.go @@ -34,12 +34,6 @@ func TestRouting(t *testing.T) { } defer s2.Close() - s3, err := serveTLS(t, "server3", false, "blarghblargh.acme.invalid") - if err != nil { - t.Fatalf("server TLS server3: %s", err) - } - defer s3.Close() - s4, err := serveTLS(t, "server4", true, "proxy.design") if err != nil { t.Fatalf("server TLS server4: %s", err) @@ -58,9 +52,8 @@ func TestRouting(t *testing.T) { if err := p.Config.ReadString(fmt.Sprintf(` test.com %s foo.net %s -borkbork.tf %s proxy.design %s PROXY -`, s1.Addr(), s2.Addr(), s3.Addr(), s4.Addr())); err != nil { +`, s1.Addr(), s2.Addr(), s4.Addr())); err != nil { t.Fatalf("configure proxy: %s", err) } @@ -73,7 +66,6 @@ proxy.design %s PROXY {"test.com", "server1", s1.Pool, true, false}, {"foo.net", "server2", s2.Pool, true, false}, {"bar.org", "", s1.Pool, false, false}, - {"blarghblargh.acme.invalid", "server3", s3.Pool, true, false}, {"proxy.design", "server4", s4.Pool, true, true}, } { res, transparent, err := getTLS(l.Addr().String(), test.N, test.P) @@ -21,7 +21,6 @@ import ( "crypto/tls" "io" "net" - "strings" ) // AddSNIRoute appends a route to the ipPort listener that routes to @@ -29,10 +28,6 @@ import ( // match, rule processing continues for any additional routes on // ipPort. // -// By default, the proxy will route all ACME tls-sni-01 challenges -// received on ipPort to all SNI dests. You can disable ACME routing -// with AddStopACMESearch. -// // The ipPort is any valid net.Listen TCP address. func (p *Proxy) AddSNIRoute(ipPort, sni string, dest Target) { p.AddSNIMatchRoute(ipPort, equals(sni), dest) @@ -43,20 +38,8 @@ func (p *Proxy) AddSNIRoute(ipPort, sni string, dest Target) { // matcher. If it doesn't match, rule processing continues for any // additional routes on ipPort. // -// By default, the proxy will route all ACME tls-sni-01 challenges -// received on ipPort to all SNI dests. You can disable ACME routing -// with AddStopACMESearch. -// // The ipPort is any valid net.Listen TCP address. func (p *Proxy) AddSNIMatchRoute(ipPort string, matcher Matcher, dest Target) { - cfg := p.configFor(ipPort) - if !cfg.stopACME { - if len(cfg.acmeTargets) == 0 { - p.addRoute(ipPort, &acmeMatch{cfg}) - } - cfg.acmeTargets = append(cfg.acmeTargets, dest) - } - p.addRoute(ipPort, sniMatch{matcher: matcher, target: dest}) } @@ -69,14 +52,6 @@ func (p *Proxy) AddSNIRouteFunc(ipPort string, fn SNITargetFunc) { p.addRoute(ipPort, sniMatch{targetFunc: fn}) } -// AddStopACMESearch prevents ACME probing of subsequent SNI routes. -// Any ACME challenges on ipPort for SNI routes previously added -// before this call will still be proxied to all possible SNI -// backends. -func (p *Proxy) AddStopACMESearch(ipPort string) { - p.configFor(ipPort).stopACME = true -} - type sniMatch struct { matcher Matcher target Target @@ -102,79 +77,6 @@ func (m sniMatch) match(br *bufio.Reader) (Target, string) { return nil, "" } -// acmeMatch matches "*.acme.invalid" ACME tls-sni-01 challenges and -// searches for a Target in cfg.acmeTargets that has the challenge -// response. -type acmeMatch struct { - cfg *config -} - -func (m *acmeMatch) match(br *bufio.Reader) (Target, string) { - sni := clientHelloServerName(br) - if !strings.HasSuffix(sni, ".acme.invalid") { - return nil, "" - } - - // TODO: cache. ACME issuers will hit multiple times in a short - // burst for each issuance event. A short TTL cache + singleflight - // should have an excellent hit rate. - // TODO: maybe an acme-specific timeout as well? - // TODO: plumb context upwards? - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ch := make(chan Target, len(m.cfg.acmeTargets)) - for _, target := range m.cfg.acmeTargets { - go tryACME(ctx, ch, target, sni) - } - for range m.cfg.acmeTargets { - if target := <-ch; target != nil { - return target, sni - } - } - - // No target was happy with the provided challenge. - return nil, "" -} - -func tryACME(ctx context.Context, ch chan<- Target, dest Target, sni string) { - var ret Target - defer func() { ch <- ret }() - - conn, targetConn := net.Pipe() - defer conn.Close() - go dest.HandleConn(targetConn) - - deadline, ok := ctx.Deadline() - if ok { - conn.SetDeadline(deadline) - } - - client := tls.Client(conn, &tls.Config{ - ServerName: sni, - InsecureSkipVerify: true, - }) - if err := client.Handshake(); err != nil { - // TODO: log? - return - } - certs := client.ConnectionState().PeerCertificates - if len(certs) == 0 { - // TODO: log? - return - } - // acme says the first cert offered by the server must match the - // challenge hostname. - if err := certs[0].VerifyHostname(sni); err != nil { - // TODO: log? - return - } - - // Target presented what looks like a valid challenge - // response, send it back to the matcher. - ret = dest -} - // clientHelloServerName returns the SNI server name inside the TLS ClientHello, // without consuming any bytes from br. // On any error, the empty string is returned. diff --git a/tcpproxy.go b/tcpproxy.go index 6d32137..5d178c6 100644 --- a/tcpproxy.go +++ b/tcpproxy.go @@ -93,9 +93,7 @@ func equals(want string) Matcher { // config contains the proxying state for one listener. type config struct { - routes []route - acmeTargets []Target // accumulates targets that should be probed for acme. - stopACME bool // if true, AddSNIRoute doesn't add targets to acmeTargets. + routes []route } // A route matches a connection to a target. diff --git a/tcpproxy_test.go b/tcpproxy_test.go index b6135b2..38feb06 100644 --- a/tcpproxy_test.go +++ b/tcpproxy_test.go @@ -28,10 +28,8 @@ import ( "fmt" "io" "io/ioutil" - "log" "math/big" "net" - "os" "strings" "testing" "time" @@ -377,9 +375,9 @@ type tlsServer struct { } func (t *tlsServer) Start() { - cert, acmeCert := cert(t.Test, t.Domain), cert(t.Test, t.Domain+".acme.invalid") + cert := cert(t.Test, t.Domain) cfg := &tls.Config{ - Certificates: []tls.Certificate{cert, acmeCert}, + Certificates: []tls.Certificate{cert}, } cfg.BuildNameToCertificate() @@ -442,9 +440,9 @@ func cert(t *testing.T, domain string) tls.Certificate { } // newTLSServer starts a TLS server that serves a self-signed cert for -// domain, and a corresonding acme.invalid dummy domain. +// domain. func newTLSServer(t *testing.T, domain string) net.Listener { - cert, acmeCert := cert(t, domain), cert(t, domain+".acme.invalid") + cert := cert(t, domain) l := newLocalListener(t) go func() { @@ -455,7 +453,7 @@ func newTLSServer(t *testing.T, domain string) net.Listener { } cfg := &tls.Config{ - Certificates: []tls.Certificate{cert, acmeCert}, + Certificates: []tls.Certificate{cert}, } cfg.BuildNameToCertificate() conn := tls.Server(rawConn, cfg) @@ -485,53 +483,3 @@ func readTLS(dest, domain string) (string, error) { } return string(bs), nil } - -func TestProxyACME(t *testing.T) { - log.SetOutput(ioutil.Discard) - defer log.SetOutput(os.Stderr) - - front := newLocalListener(t) - defer front.Close() - - backFoo := newTLSServer(t, "foo.com") - defer backFoo.Close() - backBar := newTLSServer(t, "bar.com") - defer backBar.Close() - backQuux := newTLSServer(t, "quux.com") - defer backQuux.Close() - - p := testProxy(t, front) - p.AddSNIRoute(testFrontAddr, "foo.com", To(backFoo.Addr().String())) - p.AddSNIRoute(testFrontAddr, "bar.com", To(backBar.Addr().String())) - p.AddStopACMESearch(testFrontAddr) - p.AddSNIRoute(testFrontAddr, "quux.com", To(backQuux.Addr().String())) - if err := p.Start(); err != nil { - t.Fatal(err) - } - - tests := []struct { - domain, want string - succeeds bool - }{ - {"foo.com", "foo.com", true}, - {"bar.com", "bar.com", true}, - {"quux.com", "quux.com", true}, - {"xyzzy.com", "", false}, - {"foo.com.acme.invalid", "foo.com", true}, - {"bar.com.acme.invalid", "bar.com", true}, - {"quux.com.acme.invalid", "", false}, - } - for _, test := range tests { - got, err := readTLS(front.Addr().String(), test.domain) - if test.succeeds { - if err != nil { - t.Fatalf("readTLS %q got error %q, want nil", test.domain, err) - } - if got != test.want { - t.Fatalf("readTLS %q got %q, want %q", test.domain, got, test.want) - } - } else if err == nil { - t.Fatalf("readTLS %q unexpectedly succeeded", test.domain) - } - } -} |