diff --git a/cmd/derper/derper.go b/cmd/derper/derper.go index b78603b71..3af280610 100644 --- a/cmd/derper/derper.go +++ b/cmd/derper/derper.go @@ -46,6 +46,7 @@ var ( meshPSKFile = flag.String("mesh-psk-file", defaultMeshPSKFile(), "if non-empty, path to file containing the mesh pre-shared key file. It should contain some hex string; whitespace is trimmed.") meshWith = flag.String("mesh-with", "", "optional comma-separated list of hostnames to mesh with; the server's own hostname can be in the list") bootstrapDNS = flag.String("bootstrap-dns-names", "", "optional comma-separated list of hostnames to make available at /bootstrap-dns") + verifyClients = flag.Bool("verify-clients", false, "verify clients to this DERP server through a local tailscaled instance.") ) type config struct { @@ -122,6 +123,7 @@ func main() { letsEncrypt := tsweb.IsProd443(*addr) s := derp.NewServer(key.Private(cfg.PrivateKey), log.Printf) + s.SetVerifyClient(*verifyClients) if *meshPSKFile != "" { b, err := ioutil.ReadFile(*meshPSKFile) diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index c1c4b62ac..1ba02d46e 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -15,7 +15,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep inet.af/netaddr from tailscale.com/cmd/tailscale/cli+ rsc.io/goversion/version from tailscale.com/version tailscale.com/atomicfile from tailscale.com/ipn - tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli + tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+ tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+ tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale tailscale.com/derp from tailscale.com/derp/derphttp diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index b8ff99d2a..8df69157c 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -76,6 +76,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de W 💣 inet.af/wf from tailscale.com/wf rsc.io/goversion/version from tailscale.com/version tailscale.com/atomicfile from tailscale.com/ipn+ + tailscale.com/client/tailscale from tailscale.com/derp tailscale.com/client/tailscale/apitype from tailscale.com/ipn/ipnlocal+ tailscale.com/control/controlclient from tailscale.com/ipn/ipnlocal+ tailscale.com/derp from tailscale.com/derp/derphttp+ @@ -117,7 +118,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+ tailscale.com/paths from tailscale.com/cmd/tailscaled+ tailscale.com/portlist from tailscale.com/ipn/ipnlocal - tailscale.com/safesocket from tailscale.com/ipn/ipnserver + tailscale.com/safesocket from tailscale.com/ipn/ipnserver+ tailscale.com/smallzstd from tailscale.com/ipn/ipnserver+ tailscale.com/syncs from tailscale.com/net/interfaces+ tailscale.com/tailcfg from tailscale.com/control/controlclient+ diff --git a/derp/derp_server.go b/derp/derp_server.go index 4ac578724..8ac4e59af 100644 --- a/derp/derp_server.go +++ b/derp/derp_server.go @@ -37,6 +37,7 @@ import ( "golang.org/x/crypto/nacl/box" "golang.org/x/sync/errgroup" "inet.af/netaddr" + "tailscale.com/client/tailscale" "tailscale.com/disco" "tailscale.com/metrics" "tailscale.com/types/key" @@ -127,6 +128,10 @@ type Server struct { removePktForwardOther expvar.Int avgQueueDuration *uint64 // In milliseconds; accessed atomically + // verifyClients only accepts client connections to the DERP server if the clientKey is a + // known peer in the network, as specified by a running tailscaled's client's local api. + verifyClients bool + mu sync.Mutex closed bool netConns map[Conn]chan struct{} // chan is closed when conn closes @@ -214,6 +219,13 @@ func (s *Server) SetMeshKey(v string) { s.meshKey = v } +// SetVerifyClients sets whether this DERP server verifies clients through tailscaled. +// +// It must be called before serving begins. +func (s *Server) SetVerifyClient(v bool) { + s.verifyClients = v +} + // HasMeshKey reports whether the server is configured with a mesh key. func (s *Server) HasMeshKey() bool { return s.meshKey != "" } @@ -770,8 +782,17 @@ func (c *sclient) requestMeshUpdate() { } func (s *Server) verifyClient(clientKey key.Public, info *clientInfo) error { - // TODO(crawshaw): implement policy constraints on who can use the DERP server - // TODO(bradfitz): ... and at what rate. + if !s.verifyClients { + return nil + } + status, err := tailscale.Status(context.TODO()) + if err != nil { + return fmt.Errorf("failed to query local tailscaled status: %w", err) + } + if _, exists := status.Peer[clientKey]; !exists { + return fmt.Errorf("client %v not in set of peers", clientKey) + } + // TODO(bradfitz): add policy for configurable bandwidth rate per client? return nil }