mirror of
https://github.com/juanfont/headscale.git
synced 2024-12-20 19:09:07 +01:00
move derp_server to derp server module
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
8c4c4c8633
commit
80ea87c032
@ -25,6 +25,7 @@ import (
|
|||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
"github.com/juanfont/headscale/hscontrol/db"
|
"github.com/juanfont/headscale/hscontrol/db"
|
||||||
"github.com/juanfont/headscale/hscontrol/derp"
|
"github.com/juanfont/headscale/hscontrol/derp"
|
||||||
|
derpServer "github.com/juanfont/headscale/hscontrol/derp/server"
|
||||||
"github.com/juanfont/headscale/hscontrol/policy"
|
"github.com/juanfont/headscale/hscontrol/policy"
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
@ -79,7 +80,7 @@ type Headscale struct {
|
|||||||
noisePrivateKey *key.MachinePrivate
|
noisePrivateKey *key.MachinePrivate
|
||||||
|
|
||||||
DERPMap *tailcfg.DERPMap
|
DERPMap *tailcfg.DERPMap
|
||||||
DERPServer *DERPServer
|
DERPServer *derpServer.DERPServer
|
||||||
|
|
||||||
ACLPolicy *policy.ACLPolicy
|
ACLPolicy *policy.ACLPolicy
|
||||||
|
|
||||||
@ -202,7 +203,8 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cfg.DERP.ServerEnabled {
|
if cfg.DERP.ServerEnabled {
|
||||||
embeddedDERPServer, err := app.NewDERPServer()
|
// TODO(kradalby): replace this key with a dedicated DERP key.
|
||||||
|
embeddedDERPServer, err := derpServer.NewDERPServer(cfg.ServerURL, key.NodePrivate(*privateKey), &cfg.DERP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -237,7 +239,7 @@ func (h *Headscale) expireExpiredMachines(milliSeconds int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// scheduledDERPMapUpdateWorker refreshes the DERPMap stored on the global object
|
// scheduledDERPMapUpdateWorker refreshes the DERPMap stored on the global object
|
||||||
// at a set interval
|
// at a set interval.
|
||||||
func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
|
func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
|
||||||
log.Info().
|
log.Info().
|
||||||
Dur("frequency", h.cfg.DERP.UpdateFrequency).
|
Dur("frequency", h.cfg.DERP.UpdateFrequency).
|
||||||
@ -253,7 +255,8 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
|
|||||||
log.Info().Msg("Fetching DERPMap updates")
|
log.Info().Msg("Fetching DERPMap updates")
|
||||||
h.DERPMap = derp.GetDERPMap(h.cfg.DERP)
|
h.DERPMap = derp.GetDERPMap(h.cfg.DERP)
|
||||||
if h.cfg.DERP.ServerEnabled {
|
if h.cfg.DERP.ServerEnabled {
|
||||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
region, _ := h.DERPServer.GenerateRegion()
|
||||||
|
h.DERPMap.Regions[region.RegionID] = ®ion
|
||||||
}
|
}
|
||||||
|
|
||||||
h.setLastStateChangeToNow()
|
h.setLastStateChangeToNow()
|
||||||
@ -456,9 +459,9 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
|
|||||||
Methods(http.MethodGet)
|
Methods(http.MethodGet)
|
||||||
|
|
||||||
if h.cfg.DERP.ServerEnabled {
|
if h.cfg.DERP.ServerEnabled {
|
||||||
router.HandleFunc("/derp", h.DERPHandler)
|
router.HandleFunc("/derp", h.DERPServer.DERPHandler)
|
||||||
router.HandleFunc("/derp/probe", h.DERPProbeHandler)
|
router.HandleFunc("/derp/probe", derpServer.DERPProbeHandler)
|
||||||
router.HandleFunc("/bootstrap-dns", h.DERPBootstrapDNSHandler)
|
router.HandleFunc("/bootstrap-dns", derpServer.DERPBootstrapDNSHandler(h.DERPMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
apiRouter := router.PathPrefix("/api").Subrouter()
|
apiRouter := router.PathPrefix("/api").Subrouter()
|
||||||
@ -483,8 +486,14 @@ func (h *Headscale) Serve() error {
|
|||||||
return errSTUNAddressNotSet
|
return errSTUNAddressNotSet
|
||||||
}
|
}
|
||||||
|
|
||||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
region, err := h.DERPServer.GenerateRegion()
|
||||||
go h.ServeSTUN()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.DERPMap.Regions[region.RegionID] = ®ion
|
||||||
|
|
||||||
|
go h.DERPServer.ServeSTUN()
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.cfg.DERP.AutoUpdate {
|
if h.cfg.DERP.AutoUpdate {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package hscontrol
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"tailscale.com/derp"
|
"tailscale.com/derp"
|
||||||
"tailscale.com/net/stun"
|
"tailscale.com/net/stun"
|
||||||
@ -26,23 +27,30 @@ import (
|
|||||||
const fastStartHeader = "Derp-Fast-Start"
|
const fastStartHeader = "Derp-Fast-Start"
|
||||||
|
|
||||||
type DERPServer struct {
|
type DERPServer struct {
|
||||||
|
serverURL string
|
||||||
|
key key.NodePrivate
|
||||||
|
cfg *types.DERPConfig
|
||||||
tailscaleDERP *derp.Server
|
tailscaleDERP *derp.Server
|
||||||
region tailcfg.DERPRegion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) NewDERPServer() (*DERPServer, error) {
|
func NewDERPServer(
|
||||||
|
serverURL string,
|
||||||
|
derpKey key.NodePrivate,
|
||||||
|
cfg *types.DERPConfig,
|
||||||
|
) (*DERPServer, error) {
|
||||||
log.Trace().Caller().Msg("Creating new embedded DERP server")
|
log.Trace().Caller().Msg("Creating new embedded DERP server")
|
||||||
server := derp.NewServer(key.NodePrivate(*h.privateKey2019), log.Info().Msgf)
|
server := derp.NewServer(derpKey, log.Debug().Msgf)
|
||||||
region, err := h.generateRegionLocalDERP()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DERPServer{server, region}, nil
|
return &DERPServer{
|
||||||
|
serverURL: serverURL,
|
||||||
|
key: derpKey,
|
||||||
|
cfg: cfg,
|
||||||
|
tailscaleDERP: server,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) {
|
func (d *DERPServer) GenerateRegion() (tailcfg.DERPRegion, error) {
|
||||||
serverURL, err := url.Parse(h.cfg.ServerURL)
|
serverURL, err := url.Parse(d.serverURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tailcfg.DERPRegion{}, err
|
return tailcfg.DERPRegion{}, err
|
||||||
}
|
}
|
||||||
@ -65,21 +73,21 @@ func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localDERPregion := tailcfg.DERPRegion{
|
localDERPregion := tailcfg.DERPRegion{
|
||||||
RegionID: h.cfg.DERP.ServerRegionID,
|
RegionID: d.cfg.ServerRegionID,
|
||||||
RegionCode: h.cfg.DERP.ServerRegionCode,
|
RegionCode: d.cfg.ServerRegionCode,
|
||||||
RegionName: h.cfg.DERP.ServerRegionName,
|
RegionName: d.cfg.ServerRegionName,
|
||||||
Avoid: false,
|
Avoid: false,
|
||||||
Nodes: []*tailcfg.DERPNode{
|
Nodes: []*tailcfg.DERPNode{
|
||||||
{
|
{
|
||||||
Name: fmt.Sprintf("%d", h.cfg.DERP.ServerRegionID),
|
Name: fmt.Sprintf("%d", d.cfg.ServerRegionID),
|
||||||
RegionID: h.cfg.DERP.ServerRegionID,
|
RegionID: d.cfg.ServerRegionID,
|
||||||
HostName: host,
|
HostName: host,
|
||||||
DERPPort: port,
|
DERPPort: port,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, portSTUNStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
|
_, portSTUNStr, err := net.SplitHostPort(d.cfg.STUNAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tailcfg.DERPRegion{}, err
|
return tailcfg.DERPRegion{}, err
|
||||||
}
|
}
|
||||||
@ -94,7 +102,7 @@ func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) {
|
|||||||
return localDERPregion, nil
|
return localDERPregion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headscale) DERPHandler(
|
func (d *DERPServer) DERPHandler(
|
||||||
writer http.ResponseWriter,
|
writer http.ResponseWriter,
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
) {
|
) {
|
||||||
@ -156,7 +164,7 @@ func (h *Headscale) DERPHandler(
|
|||||||
log.Trace().Caller().Msgf("Hijacked connection from %v", req.RemoteAddr)
|
log.Trace().Caller().Msgf("Hijacked connection from %v", req.RemoteAddr)
|
||||||
|
|
||||||
if !fastStart {
|
if !fastStart {
|
||||||
pubKey := h.privateKey2019.Public()
|
pubKey := d.key.Public()
|
||||||
pubKeyStr, _ := pubKey.MarshalText() //nolint
|
pubKeyStr, _ := pubKey.MarshalText() //nolint
|
||||||
fmt.Fprintf(conn, "HTTP/1.1 101 Switching Protocols\r\n"+
|
fmt.Fprintf(conn, "HTTP/1.1 101 Switching Protocols\r\n"+
|
||||||
"Upgrade: DERP\r\n"+
|
"Upgrade: DERP\r\n"+
|
||||||
@ -167,12 +175,12 @@ func (h *Headscale) DERPHandler(
|
|||||||
string(pubKeyStr))
|
string(pubKeyStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
h.DERPServer.tailscaleDERP.Accept(req.Context(), netConn, conn, netConn.RemoteAddr().String())
|
d.tailscaleDERP.Accept(req.Context(), netConn, conn, netConn.RemoteAddr().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// DERPProbeHandler is the endpoint that js/wasm clients hit to measure
|
// DERPProbeHandler is the endpoint that js/wasm clients hit to measure
|
||||||
// DERP latency, since they can't do UDP STUN queries.
|
// DERP latency, since they can't do UDP STUN queries.
|
||||||
func (h *Headscale) DERPProbeHandler(
|
func DERPProbeHandler(
|
||||||
writer http.ResponseWriter,
|
writer http.ResponseWriter,
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
) {
|
) {
|
||||||
@ -199,43 +207,47 @@ func (h *Headscale) DERPProbeHandler(
|
|||||||
// The initial implementation is here https://github.com/tailscale/tailscale/pull/1406
|
// 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.
|
// They have a cache, but not clear if that is really necessary at Headscale, uh, scale.
|
||||||
// An example implementation is found here https://derp.tailscale.com/bootstrap-dns
|
// An example implementation is found here https://derp.tailscale.com/bootstrap-dns
|
||||||
func (h *Headscale) DERPBootstrapDNSHandler(
|
func DERPBootstrapDNSHandler(
|
||||||
writer http.ResponseWriter,
|
derpMap *tailcfg.DERPMap,
|
||||||
req *http.Request,
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
) {
|
return func(
|
||||||
dnsEntries := make(map[string][]net.IP)
|
writer http.ResponseWriter,
|
||||||
|
req *http.Request,
|
||||||
|
) {
|
||||||
|
dnsEntries := make(map[string][]net.IP)
|
||||||
|
|
||||||
resolvCtx, cancel := context.WithTimeout(req.Context(), time.Minute)
|
resolvCtx, cancel := context.WithTimeout(req.Context(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
var resolver net.Resolver
|
var resolver net.Resolver
|
||||||
for _, region := range h.DERPMap.Regions {
|
for _, region := range derpMap.Regions {
|
||||||
for _, node := range region.Nodes { // we don't care if we override some nodes
|
for _, node := range region.Nodes { // we don't care if we override some nodes
|
||||||
addrs, err := resolver.LookupIP(resolvCtx, "ip", node.HostName)
|
addrs, err := resolver.LookupIP(resolvCtx, "ip", node.HostName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Caller().
|
Caller().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msgf("bootstrap DNS lookup failed %q", node.HostName)
|
Msgf("bootstrap DNS lookup failed %q", node.HostName)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
|
dnsEntries[node.HostName] = addrs
|
||||||
}
|
}
|
||||||
dnsEntries[node.HostName] = addrs
|
|
||||||
}
|
}
|
||||||
}
|
writer.Header().Set("Content-Type", "application/json")
|
||||||
writer.Header().Set("Content-Type", "application/json")
|
writer.WriteHeader(http.StatusOK)
|
||||||
writer.WriteHeader(http.StatusOK)
|
err := json.NewEncoder(writer).Encode(dnsEntries)
|
||||||
err := json.NewEncoder(writer).Encode(dnsEntries)
|
if err != nil {
|
||||||
if err != nil {
|
log.Error().
|
||||||
log.Error().
|
Caller().
|
||||||
Caller().
|
Err(err).
|
||||||
Err(err).
|
Msg("Failed to write response")
|
||||||
Msg("Failed to write response")
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeSTUN starts a STUN server on the configured addr.
|
// ServeSTUN starts a STUN server on the configured addr.
|
||||||
func (h *Headscale) ServeSTUN() {
|
func (d *DERPServer) ServeSTUN() {
|
||||||
packetConn, err := net.ListenPacket("udp", h.cfg.DERP.STUNAddr)
|
packetConn, err := net.ListenPacket("udp", d.cfg.STUNAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf("failed to open STUN listener: %v", err)
|
log.Fatal().Msgf("failed to open STUN listener: %v", err)
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user