mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-23 01:11:40 +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)")
|
dev = flag.Bool("dev", false, "run in localhost development mode (overrides -a)")
|
||||||
versionFlag = flag.Bool("version", false, "print version and exit")
|
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.")
|
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.")
|
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.")
|
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")
|
configPath = flag.String("c", "", "config file path")
|
||||||
@ -203,6 +204,13 @@ func main() {
|
|||||||
s.SetVerifyClientURL(*verifyClientURL)
|
s.SetVerifyClientURL(*verifyClientURL)
|
||||||
s.SetVerifyClientURLFailOpen(*verifyFailOpen)
|
s.SetVerifyClientURLFailOpen(*verifyFailOpen)
|
||||||
s.SetTCPWriteTimeout(*tcpWriteTimeout)
|
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
|
var meshKey string
|
||||||
if *dev {
|
if *dev {
|
||||||
@ -345,9 +353,23 @@ func main() {
|
|||||||
ReadTimeout: 30 * time.Second,
|
ReadTimeout: 30 * time.Second,
|
||||||
WriteTimeout: 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() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
httpsrv.Shutdown(ctx)
|
httpsrv.Shutdown(ctx)
|
||||||
|
if meshSrv != nil {
|
||||||
|
meshSrv.Shutdown(ctx)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if serveTLS {
|
if serveTLS {
|
||||||
@ -359,7 +381,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
httpsrv.TLSConfig = certManager.TLSConfig()
|
httpsrv.TLSConfig = certManager.TLSConfig()
|
||||||
getCert := httpsrv.TLSConfig.GetCertificate
|
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)
|
cert, err := getCert(hi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -367,9 +389,10 @@ func main() {
|
|||||||
cert.Certificate = append(cert.Certificate, s.MetaCert())
|
cert.Certificate = append(cert.Certificate, s.MetaCert())
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
httpsrv.TLSConfig.GetCertificate = certFunc
|
||||||
// Disable TLS 1.0 and 1.1, which are obsolete and have security issues.
|
// Disable TLS 1.0 and 1.1, which are obsolete and have security issues.
|
||||||
httpsrv.TLSConfig.MinVersion = tls.VersionTLS12
|
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 {
|
if r.TLS != nil {
|
||||||
label := "unknown"
|
label := "unknown"
|
||||||
switch r.TLS.Version {
|
switch r.TLS.Version {
|
||||||
@ -389,6 +412,17 @@ func main() {
|
|||||||
|
|
||||||
mux.ServeHTTP(w, r)
|
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 {
|
if *httpPort > -1 {
|
||||||
go func() {
|
go func() {
|
||||||
port80mux := http.NewServeMux()
|
port80mux := http.NewServeMux()
|
||||||
@ -420,6 +454,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
err = rateLimitedListenAndServeTLS(httpsrv, &lc)
|
err = rateLimitedListenAndServeTLS(httpsrv, &lc)
|
||||||
} else {
|
} 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)
|
log.Printf("derper: serving on %s", *addr)
|
||||||
var ln net.Listener
|
var ln net.Listener
|
||||||
ln, err = lc.Listen(context.Background(), "tcp", httpsrv.Addr)
|
ln, err = lc.Listen(context.Background(), "tcp", httpsrv.Addr)
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@ -133,6 +134,7 @@ type Server struct {
|
|||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
memSys0 uint64 // runtime.MemStats.Sys at start (or early-ish)
|
memSys0 uint64 // runtime.MemStats.Sys at start (or early-ish)
|
||||||
meshKey string
|
meshKey string
|
||||||
|
meshPort string
|
||||||
limitedLogf logger.Logf
|
limitedLogf logger.Logf
|
||||||
metaCert []byte // the encoded x509 cert to send after LetsEncrypt cert+intermediate
|
metaCert []byte // the encoded x509 cert to send after LetsEncrypt cert+intermediate
|
||||||
dupPolicy dupPolicy
|
dupPolicy dupPolicy
|
||||||
@ -503,6 +505,10 @@ func (s *Server) SetTCPWriteTimeout(d time.Duration) {
|
|||||||
s.tcpWriteTimeout = d
|
s.tcpWriteTimeout = d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetMeshPort(port string) {
|
||||||
|
s.meshPort = port
|
||||||
|
}
|
||||||
|
|
||||||
// HasMeshKey reports whether the server is configured with a mesh key.
|
// HasMeshKey reports whether the server is configured with a mesh key.
|
||||||
func (s *Server) HasMeshKey() bool { return s.meshKey != "" }
|
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)
|
return fmt.Errorf("receive client key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, localPort, _ := net.SplitHostPort(nc.LocalAddr().String())
|
||||||
|
|
||||||
remoteIPPort, _ := netip.ParseAddrPort(remoteAddr)
|
remoteIPPort, _ := netip.ParseAddrPort(remoteAddr)
|
||||||
if err := s.verifyClient(ctx, clientKey, clientInfo, remoteIPPort.Addr()); err != nil {
|
if err := s.verifyClient(ctx, clientKey, clientInfo, remoteIPPort.Addr()); err != nil {
|
||||||
return fmt.Errorf("client %v rejected: %v", clientKey, err)
|
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),
|
discoSendQueue: make(chan pkt, s.perClientSendQueueDepth),
|
||||||
sendPongCh: make(chan [8]byte, 1),
|
sendPongCh: make(chan [8]byte, 1),
|
||||||
peerGone: make(chan peerGoneMsg),
|
peerGone: make(chan peerGoneMsg),
|
||||||
canMesh: s.isMeshPeer(clientInfo),
|
canMesh: s.hasMeshPSK(clientInfo) && s.usingMeshPort(localPort),
|
||||||
isNotIdealConn: IdealNodeContextKey.Value(ctx) != "",
|
isNotIdealConn: IdealNodeContextKey.Value(ctx) != "",
|
||||||
peerGoneLim: rate.NewLimiter(rate.Every(time.Second), 3),
|
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
|
// hasMeshPSK reports whether the client provides
|
||||||
// node in the DERP region.
|
// a matching pre-shared key and can be trusted.
|
||||||
func (s *Server) isMeshPeer(info *clientInfo) bool {
|
func (s *Server) hasMeshPSK(info *clientInfo) bool {
|
||||||
return info != nil && info.MeshKey != "" && info.MeshKey == s.meshKey
|
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,
|
// verifyClient checks whether the client is allowed to connect to the derper,
|
||||||
// depending on how & whether the server's been configured to verify.
|
// 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 {
|
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
|
// 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
|
// further wouldn't work: it's not part of the tailnet so tailscaled and
|
||||||
// likely the admission control URL wouldn't know about it.
|
// likely the admission control URL wouldn't know about it.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user