TUN-1522: If we can't get SRV from default resolver, get them from 1.1.1.1 DoT

This commit is contained in:
Nick Vollmar 2019-03-07 21:12:24 -05:00
parent b02718f86b
commit d22e214000
3 changed files with 66 additions and 5 deletions

View File

@ -1,8 +1,14 @@
package origin package origin
import ( import (
"context"
"crypto/tls"
"fmt" "fmt"
"net" "net"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
) )
const ( const (
@ -10,9 +16,25 @@ const (
srvService = "warp" srvService = "warp"
srvProto = "tcp" srvProto = "tcp"
srvName = "cloudflarewarp.com" srvName = "cloudflarewarp.com"
// Used to fallback to DoT when we can't use the default resolver to
// discover HA Warp servers (GitHub issue #75).
dotServerName = "cloudflare-dns.com"
dotServerAddr = "1.1.1.1:853"
dotTimeout = time.Duration(15 * time.Second)
) )
func ResolveEdgeIPs(addresses []string) ([]*net.TCPAddr, error) { var friendlyDNSErrorLines = []string{
`Please try the following things to diagnose this issue:`,
` 1. ensure that cloudflarewarp.com is returning "warp" service records.`,
` Run your system's equivalent of: dig srv _warp._tcp.cloudflarewarp.com`,
` 2. ensure that your DNS resolver is not returning compressed SRV records.`,
` See GitHub issue https://github.com/golang/go/issues/27546`,
` For example, you could use Cloudflare's 1.1.1.1 as your resolver:`,
` https://developers.cloudflare.com/1.1.1.1/setting-up-1.1.1.1/`,
}
func ResolveEdgeIPs(logger *log.Logger, addresses []string) ([]*net.TCPAddr, error) {
if len(addresses) > 0 { if len(addresses) > 0 {
var tcpAddrs []*net.TCPAddr var tcpAddrs []*net.TCPAddr
for _, address := range addresses { for _, address := range addresses {
@ -28,7 +50,30 @@ func ResolveEdgeIPs(addresses []string) ([]*net.TCPAddr, error) {
// HA service discovery lookup // HA service discovery lookup
_, addrs, err := net.LookupSRV(srvService, srvProto, srvName) _, addrs, err := net.LookupSRV(srvService, srvProto, srvName)
if err != nil { if err != nil {
return nil, err // Try to fall back to DoT from Cloudflare directly.
//
// Note: Instead of DoT, we could also have used DoH. Either of these:
// - directly via the JSON API (https://1.1.1.1/dns-query?ct=application/dns-json&name=_warp._tcp.cloudflarewarp.com&type=srv)
// - indirectly via `tunneldns.NewUpstreamHTTPS()`
// But both of these cases miss out on a key feature from the stdlib:
// "The returned records are sorted by priority and randomized by weight within a priority."
// (https://golang.org/pkg/net/#Resolver.LookupSRV)
// Does this matter? I don't know. It may someday. Let's use DoT so we don't need to worry about it.
// See also: Go feature request for stdlib-supported DoH: https://github.com/golang/go/issues/27552
r := fallbackResolver(dotServerName, dotServerAddr)
ctx, cancel := context.WithTimeout(context.Background(), dotTimeout)
defer cancel()
_, fallbackAddrs, fallbackErr := r.LookupSRV(ctx, srvService, srvProto, srvName)
if fallbackErr != nil || len(fallbackAddrs) == 0 {
// use the original DNS error `err` in messages, not `fallbackErr`
logger.Errorln("Error looking up Cloudflare edge IPs: the DNS query failed:", err)
for _, s := range friendlyDNSErrorLines {
logger.Errorln(s)
}
return nil, errors.Wrap(err, "Could not lookup srv records on _warp._tcp.cloudflarewarp.com")
}
// Accept the fallback results and keep going
addrs = fallbackAddrs
} }
var resolvedIPsPerCNAME [][]*net.TCPAddr var resolvedIPsPerCNAME [][]*net.TCPAddr
var lookupErr error var lookupErr error
@ -80,3 +125,19 @@ func FlattenServiceIPs(ipsByService [][]*net.TCPAddr) []*net.TCPAddr {
} }
return result return result
} }
// Inspiration: https://github.com/artyom/dot/blob/master/dot.go
func fallbackResolver(serverName, serverAddress string) *net.Resolver {
return &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, _ string, _ string) (net.Conn, error) {
var dialer net.Dialer
conn, err := dialer.DialContext(ctx, "tcp", serverAddress)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{ServerName: serverName}
return tls.Client(conn, tlsConfig), nil
},
}
}

View File

@ -124,7 +124,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, u
func (s *Supervisor) initialize(ctx context.Context, connectedSignal *signal.Signal, u uuid.UUID) error { func (s *Supervisor) initialize(ctx context.Context, connectedSignal *signal.Signal, u uuid.UUID) error {
logger := s.config.Logger logger := s.config.Logger
edgeIPs, err := ResolveEdgeIPs(s.config.EdgeAddrs) edgeIPs, err := ResolveEdgeIPs(logger, s.config.EdgeAddrs)
if err != nil { if err != nil {
logger.Infof("ResolveEdgeIPs err") logger.Infof("ResolveEdgeIPs err")
return err return err
@ -223,7 +223,7 @@ func (s *Supervisor) refreshEdgeIPs() {
} }
s.resolverC = make(chan resolveResult) s.resolverC = make(chan resolveResult)
go func() { go func() {
edgeIPs, err := ResolveEdgeIPs(s.config.EdgeAddrs) edgeIPs, err := ResolveEdgeIPs(s.config.Logger, s.config.EdgeAddrs)
s.resolverC <- resolveResult{edgeIPs: edgeIPs, err: err} s.resolverC <- resolveResult{edgeIPs: edgeIPs, err: err}
}() }()
} }

View File

@ -146,7 +146,7 @@ func StartTunnelDaemon(config *TunnelConfig, shutdownC <-chan struct{}, connecte
if config.HAConnections > 1 { if config.HAConnections > 1 {
return NewSupervisor(config).Run(ctx, connectedSignal, u) return NewSupervisor(config).Run(ctx, connectedSignal, u)
} else { } else {
addrs, err := ResolveEdgeIPs(config.EdgeAddrs) addrs, err := ResolveEdgeIPs(config.Logger, config.EdgeAddrs)
if err != nil { if err != nil {
return err return err
} }