mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-22 08:51:41 +00:00
cmd/derper,derp: add DERP mesh port isolation
Add support to only allow derp mesh frames a specific port. Fixes #26312 Signed-off-by: Mike O'Driscoll <mikeo@tailscale.com>
This commit is contained in:
parent
886ab4fad4
commit
34e459ac1e
@ -58,6 +58,7 @@ var (
|
||||
dev = flag.Bool("dev", false, "run in localhost development mode (overrides -a)")
|
||||
versionFlag = flag.Bool("version", false, "print version and exit")
|
||||
addr = flag.String("a", ":443", "server HTTP/HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces. Serves HTTPS if the port is 443 and/or -certmode is manual, otherwise HTTP.")
|
||||
meshAddr = flag.String("mesh-addr", "", "optional, Specify a separate http/https listen address for mesh connections, if unset, mesh will use address specified in 'a'")
|
||||
httpPort = flag.Int("http-port", 80, "The port on which to serve HTTP. Set to -1 to disable. The listener is bound to the same IP (if any) as specified in the -a flag.")
|
||||
stunPort = flag.Int("stun-port", 3478, "The UDP port on which to serve STUN. The listener is bound to the same IP (if any) as specified in the -a flag.")
|
||||
configPath = flag.String("c", "", "config file path")
|
||||
@ -203,6 +204,13 @@ func main() {
|
||||
s.SetVerifyClientURL(*verifyClientURL)
|
||||
s.SetVerifyClientURLFailOpen(*verifyFailOpen)
|
||||
s.SetTCPWriteTimeout(*tcpWriteTimeout)
|
||||
if *meshAddr != "" {
|
||||
_, port, err := net.SplitHostPort(*meshAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse mesh port addr %s: %v", *meshAddr, err)
|
||||
}
|
||||
s.SetMeshPort(port)
|
||||
}
|
||||
|
||||
var meshKey string
|
||||
if *dev {
|
||||
@ -345,9 +353,23 @@ func main() {
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
}
|
||||
var meshSrv *http.Server
|
||||
if *meshAddr != "" {
|
||||
// Same as httpsrv above, different port.
|
||||
meshSrv = &http.Server{
|
||||
Addr: *meshAddr,
|
||||
Handler: mux,
|
||||
ErrorLog: quietLogger,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
httpsrv.Shutdown(ctx)
|
||||
if meshSrv != nil {
|
||||
meshSrv.Shutdown(ctx)
|
||||
}
|
||||
}()
|
||||
|
||||
if serveTLS {
|
||||
@ -359,7 +381,7 @@ func main() {
|
||||
}
|
||||
httpsrv.TLSConfig = certManager.TLSConfig()
|
||||
getCert := httpsrv.TLSConfig.GetCertificate
|
||||
httpsrv.TLSConfig.GetCertificate = func(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
certFunc := func(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
cert, err := getCert(hi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -367,9 +389,10 @@ func main() {
|
||||
cert.Certificate = append(cert.Certificate, s.MetaCert())
|
||||
return cert, nil
|
||||
}
|
||||
httpsrv.TLSConfig.GetCertificate = certFunc
|
||||
// Disable TLS 1.0 and 1.1, which are obsolete and have security issues.
|
||||
httpsrv.TLSConfig.MinVersion = tls.VersionTLS12
|
||||
httpsrv.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.TLS != nil {
|
||||
label := "unknown"
|
||||
switch r.TLS.Version {
|
||||
@ -389,6 +412,17 @@ func main() {
|
||||
|
||||
mux.ServeHTTP(w, r)
|
||||
})
|
||||
httpsrv.Handler = handlerFunc
|
||||
|
||||
if meshSrv != nil {
|
||||
go func() {
|
||||
meshSrv.TLSConfig = certManager.TLSConfig()
|
||||
meshSrv.TLSConfig.GetCertificate = certFunc
|
||||
meshSrv.TLSConfig.MinVersion = tls.VersionTLS12
|
||||
meshSrv.Handler = handlerFunc
|
||||
err = rateLimitedListenAndServeTLS(meshSrv, &lc)
|
||||
}()
|
||||
}
|
||||
if *httpPort > -1 {
|
||||
go func() {
|
||||
port80mux := http.NewServeMux()
|
||||
@ -420,6 +454,17 @@ func main() {
|
||||
}
|
||||
err = rateLimitedListenAndServeTLS(httpsrv, &lc)
|
||||
} else {
|
||||
if meshSrv != nil {
|
||||
go func() {
|
||||
log.Printf("derper: starting mesh on %s", *meshAddr)
|
||||
var lnm net.Listener
|
||||
lnm, err = lc.Listen(context.Background(), "tcp", *meshAddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = meshSrv.Serve(lnm)
|
||||
}()
|
||||
}
|
||||
log.Printf("derper: serving on %s", *addr)
|
||||
var ln net.Listener
|
||||
ln, err = lc.Listen(context.Background(), "tcp", httpsrv.Addr)
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand/v2"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
@ -133,6 +134,7 @@ type Server struct {
|
||||
logf logger.Logf
|
||||
memSys0 uint64 // runtime.MemStats.Sys at start (or early-ish)
|
||||
meshKey string
|
||||
meshPort string
|
||||
limitedLogf logger.Logf
|
||||
metaCert []byte // the encoded x509 cert to send after LetsEncrypt cert+intermediate
|
||||
dupPolicy dupPolicy
|
||||
@ -503,6 +505,10 @@ func (s *Server) SetTCPWriteTimeout(d time.Duration) {
|
||||
s.tcpWriteTimeout = d
|
||||
}
|
||||
|
||||
func (s *Server) SetMeshPort(port string) {
|
||||
s.meshPort = port
|
||||
}
|
||||
|
||||
// HasMeshKey reports whether the server is configured with a mesh key.
|
||||
func (s *Server) HasMeshKey() bool { return s.meshKey != "" }
|
||||
|
||||
@ -900,6 +906,8 @@ func (s *Server) accept(ctx context.Context, nc Conn, brw *bufio.ReadWriter, rem
|
||||
return fmt.Errorf("receive client key: %v", err)
|
||||
}
|
||||
|
||||
_, localPort, _ := net.SplitHostPort(nc.LocalAddr().String())
|
||||
|
||||
remoteIPPort, _ := netip.ParseAddrPort(remoteAddr)
|
||||
if err := s.verifyClient(ctx, clientKey, clientInfo, remoteIPPort.Addr()); err != nil {
|
||||
return fmt.Errorf("client %v rejected: %v", clientKey, err)
|
||||
@ -926,7 +934,7 @@ func (s *Server) accept(ctx context.Context, nc Conn, brw *bufio.ReadWriter, rem
|
||||
discoSendQueue: make(chan pkt, s.perClientSendQueueDepth),
|
||||
sendPongCh: make(chan [8]byte, 1),
|
||||
peerGone: make(chan peerGoneMsg),
|
||||
canMesh: s.isMeshPeer(clientInfo),
|
||||
canMesh: s.hasMeshPSK(clientInfo) && s.usingMeshPort(localPort),
|
||||
isNotIdealConn: IdealNodeContextKey.Value(ctx) != "",
|
||||
peerGoneLim: rate.NewLimiter(rate.Every(time.Second), 3),
|
||||
}
|
||||
@ -1331,16 +1339,26 @@ func (c *sclient) requestMeshUpdate() {
|
||||
}
|
||||
}
|
||||
|
||||
// isMeshPeer reports whether the client is a trusted mesh peer
|
||||
// node in the DERP region.
|
||||
func (s *Server) isMeshPeer(info *clientInfo) bool {
|
||||
// hasMeshPSK reports whether the client provides
|
||||
// a matching pre-shared key and can be trusted.
|
||||
func (s *Server) hasMeshPSK(info *clientInfo) bool {
|
||||
return info != nil && info.MeshKey != "" && info.MeshKey == s.meshKey
|
||||
}
|
||||
|
||||
// usingMeshPort reports if the given port is
|
||||
// authorized for mesh connections.
|
||||
func (s *Server) usingMeshPort(port string) bool {
|
||||
if s.meshPort == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return s.meshPort == port
|
||||
}
|
||||
|
||||
// verifyClient checks whether the client is allowed to connect to the derper,
|
||||
// depending on how & whether the server's been configured to verify.
|
||||
func (s *Server) verifyClient(ctx context.Context, clientKey key.NodePublic, info *clientInfo, clientIP netip.Addr) error {
|
||||
if s.isMeshPeer(info) {
|
||||
if s.hasMeshPSK(info) {
|
||||
// Trusted mesh peer. No need to verify further. In fact, verifying
|
||||
// further wouldn't work: it's not part of the tailnet so tailscaled and
|
||||
// likely the admission control URL wouldn't know about it.
|
||||
|
Loading…
x
Reference in New Issue
Block a user