Working /bootstrap-dns DERP helper
This commit is contained in:
parent
54c3e00a1f
commit
70910c4595
1 changed files with 22 additions and 51 deletions
|
@ -2,14 +2,12 @@ package headscale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -26,11 +24,6 @@ import (
|
||||||
// following its HTTP request.
|
// following its HTTP request.
|
||||||
const fastStartHeader = "Derp-Fast-Start"
|
const fastStartHeader = "Derp-Fast-Start"
|
||||||
|
|
||||||
var (
|
|
||||||
dnsCache atomic.Value // of []byte
|
|
||||||
bootstrapDNS = "derp.tailscale.com"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DERPServer struct {
|
type DERPServer struct {
|
||||||
tailscaleDERP *derp.Server
|
tailscaleDERP *derp.Server
|
||||||
region tailcfg.DERPRegion
|
region tailcfg.DERPRegion
|
||||||
|
@ -137,14 +130,29 @@ func (h *Headscale) DERPProbeHandler(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DERPBootstrapDNSHandler implements the /bootsrap-dns endpoint
|
||||||
|
// Described in https://github.com/tailscale/tailscale/issues/1405,
|
||||||
|
// this endpoint provides a way to help a client when it fails to start up
|
||||||
|
// because its DNS are broken.
|
||||||
|
// The initial implementation is here https://github.com/tailscale/tailscale/pull/1406
|
||||||
|
// They have a cache, but not clear if that is really necessary at Headscale, uh, scale.
|
||||||
func (h *Headscale) DERPBootstrapDNSHandler(ctx *gin.Context) {
|
func (h *Headscale) DERPBootstrapDNSHandler(ctx *gin.Context) {
|
||||||
ctx.Header("Content-Type", "application/json")
|
dnsEntries := make(map[string][]net.IP)
|
||||||
j, _ := dnsCache.Load().([]byte)
|
|
||||||
// Bootstrap DNS requests occur cross-regions,
|
resolvCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
// and are randomized per request,
|
defer cancel()
|
||||||
// so keeping a connection open is pointlessly expensive.
|
var r net.Resolver
|
||||||
ctx.Header("Connection", "close")
|
for _, region := range h.DERPMap.Regions {
|
||||||
ctx.Writer.Write(j)
|
for _, node := range region.Nodes { // we don't care if we override some nodes
|
||||||
|
addrs, err := r.LookupIP(resolvCtx, "ip", node.HostName)
|
||||||
|
if err != nil {
|
||||||
|
log.Trace().Caller().Err(err).Msgf("bootstrap DNS lookup failed %q", node.HostName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dnsEntries[node.HostName] = addrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, dnsEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeSTUN starts a STUN server on udp/3478
|
// ServeSTUN starts a STUN server on udp/3478
|
||||||
|
@ -188,40 +196,3 @@ func serverSTUNListener(ctx context.Context, pc *net.UDPConn) {
|
||||||
pc.WriteTo(res, ua)
|
pc.WriteTo(res, ua)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shamelessly taken from
|
|
||||||
// https://github.com/tailscale/tailscale/blob/main/cmd/derper/bootstrap_dns.go
|
|
||||||
func refreshBootstrapDNSLoop() {
|
|
||||||
if bootstrapDNS == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
refreshBootstrapDNS()
|
|
||||||
time.Sleep(10 * time.Minute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshBootstrapDNS() {
|
|
||||||
if bootstrapDNS == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dnsEntries := make(map[string][]net.IP)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
names := strings.Split(bootstrapDNS, ",")
|
|
||||||
var r net.Resolver
|
|
||||||
for _, name := range names {
|
|
||||||
addrs, err := r.LookupIP(ctx, "ip", name)
|
|
||||||
if err != nil {
|
|
||||||
log.Trace().Caller().Err(err).Msgf("bootstrap DNS lookup %q", name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dnsEntries[name] = addrs
|
|
||||||
}
|
|
||||||
j, err := json.MarshalIndent(dnsEntries, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
// leave the old values in place
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dnsCache.Store(j)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue