summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/tlsrouter/acme.go101
-rw-r--r--cmd/tlsrouter/config.go9
-rw-r--r--cmd/tlsrouter/e2e_test.go10
-rw-r--r--sni.go98
-rw-r--r--tcpproxy.go4
-rw-r--r--tcpproxy_test.go62
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)
diff --git a/sni.go b/sni.go
index eb826d4..c2d37e0 100644
--- a/sni.go
+++ b/sni.go
@@ -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)
- }
- }
-}