mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-26 11:11:01 +00:00
Merge f2a5a1aa1420fc58da87fa5f4af1ba807d87f67e into b3455fa99a5e8d07133d5140017ec7c49f032a07
This commit is contained in:
commit
353f77b3e0
@ -5,13 +5,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tailscale/setec/client/setec"
|
||||||
"tailscale.com/prober"
|
"tailscale.com/prober"
|
||||||
"tailscale.com/tsweb"
|
"tailscale.com/tsweb"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
@ -20,7 +25,15 @@ import (
|
|||||||
_ "tailscale.com/tsweb/promvarz"
|
_ "tailscale.com/tsweb/promvarz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const meshKeyEnvVar = "TAILSCALE_DERPER_MESH_KEY"
|
||||||
|
const setecMeshKeyName = "meshkey"
|
||||||
|
|
||||||
|
func defaultSetecCacheDir() string {
|
||||||
|
return filepath.Join(os.Getenv("HOME"), ".cache", "derper-secrets")
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
dev = flag.Bool("dev", false, "run in localhost development mode")
|
||||||
derpMapURL = flag.String("derp-map", "https://login.tailscale.com/derpmap/default", "URL to DERP map (https:// or file://) or 'local' to use the local tailscaled's DERP map")
|
derpMapURL = flag.String("derp-map", "https://login.tailscale.com/derpmap/default", "URL to DERP map (https:// or file://) or 'local' to use the local tailscaled's DERP map")
|
||||||
versionFlag = flag.Bool("version", false, "print version and exit")
|
versionFlag = flag.Bool("version", false, "print version and exit")
|
||||||
listen = flag.String("listen", ":8030", "HTTP listen address")
|
listen = flag.String("listen", ":8030", "HTTP listen address")
|
||||||
@ -36,6 +49,10 @@ var (
|
|||||||
qdPacketsPerSecond = flag.Int("qd-packets-per-second", 0, "if greater than 0, queuing delay will be measured continuously using 260 byte packets (approximate size of a CallMeMaybe packet) sent at this rate per second")
|
qdPacketsPerSecond = flag.Int("qd-packets-per-second", 0, "if greater than 0, queuing delay will be measured continuously using 260 byte packets (approximate size of a CallMeMaybe packet) sent at this rate per second")
|
||||||
qdPacketTimeout = flag.Duration("qd-packet-timeout", 5*time.Second, "queuing delay packets arriving after this period of time from being sent are treated like dropped packets and don't count toward queuing delay timings")
|
qdPacketTimeout = flag.Duration("qd-packet-timeout", 5*time.Second, "queuing delay packets arriving after this period of time from being sent are treated like dropped packets and don't count toward queuing delay timings")
|
||||||
regionCodeOrID = flag.String("region-code", "", "probe only this region (e.g. 'lax' or '17'); if left blank, all regions will be probed")
|
regionCodeOrID = flag.String("region-code", "", "probe only this region (e.g. 'lax' or '17'); if left blank, all regions will be probed")
|
||||||
|
meshPSKFile = flag.String("mesh-psk-file", "", "if non-empty, path to file containing the mesh pre-shared key file. It should contain some hex string; whitespace is trimmed.")
|
||||||
|
secretsURL = flag.String("secrets-url", "", "SETEC server URL for secrets retrieval of mesh key")
|
||||||
|
secretPrefix = flag.String("secrets-path-prefix", "prod/derp", "setec path prefix for \""+setecMeshKeyName+"\" secret for DERP mesh key")
|
||||||
|
secretsCacheDir = flag.String("secrets-cache-dir", defaultSetecCacheDir(), "directory to cache setec secrets in (required if --secrets-url is set)")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -51,6 +68,7 @@ func main() {
|
|||||||
prober.WithSTUNProbing(*stunInterval),
|
prober.WithSTUNProbing(*stunInterval),
|
||||||
prober.WithTLSProbing(*tlsInterval),
|
prober.WithTLSProbing(*tlsInterval),
|
||||||
prober.WithQueuingDelayProbing(*qdPacketsPerSecond, *qdPacketTimeout),
|
prober.WithQueuingDelayProbing(*qdPacketsPerSecond, *qdPacketTimeout),
|
||||||
|
prober.WithMeshKey(getMeshKey()),
|
||||||
}
|
}
|
||||||
if *bwInterval > 0 {
|
if *bwInterval > 0 {
|
||||||
opts = append(opts, prober.WithBandwidthProbing(*bwInterval, *bwSize, *bwTUNIPv4Address))
|
opts = append(opts, prober.WithBandwidthProbing(*bwInterval, *bwSize, *bwTUNIPv4Address))
|
||||||
@ -95,6 +113,49 @@ func main() {
|
|||||||
log.Fatal(http.ListenAndServe(*listen, mux))
|
log.Fatal(http.ListenAndServe(*listen, mux))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMeshKey() string {
|
||||||
|
var meshKey string
|
||||||
|
|
||||||
|
if *dev {
|
||||||
|
meshKey = os.Getenv(meshKeyEnvVar)
|
||||||
|
if meshKey == "" {
|
||||||
|
log.Printf("No mesh key specified for dev via %s\n", meshKeyEnvVar)
|
||||||
|
} else {
|
||||||
|
log.Printf("Set mesh key from %s\n", meshKeyEnvVar)
|
||||||
|
}
|
||||||
|
} else if *secretsURL != "" {
|
||||||
|
meshKeySecret := path.Join(*secretPrefix, setecMeshKeyName)
|
||||||
|
fc, err := setec.NewFileCache(*secretsCacheDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("NewFileCache: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("Setting up setec store from %q", *secretsURL)
|
||||||
|
st, err := setec.NewStore(context.Background(),
|
||||||
|
setec.StoreConfig{
|
||||||
|
Client: setec.Client{Server: *secretsURL},
|
||||||
|
Secrets: []string{
|
||||||
|
meshKeySecret,
|
||||||
|
},
|
||||||
|
Cache: fc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("NewStore: %v", err)
|
||||||
|
}
|
||||||
|
meshKey = st.Secret(meshKeySecret).GetString()
|
||||||
|
log.Println("Got mesh key from setec store")
|
||||||
|
st.Close()
|
||||||
|
} else if *meshPSKFile != "" {
|
||||||
|
b, err := setec.StaticFile(*meshPSKFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("StaticFile failed to get key: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("Got mesh key from static file")
|
||||||
|
meshKey = b.GetString()
|
||||||
|
}
|
||||||
|
|
||||||
|
return meshKey
|
||||||
|
}
|
||||||
|
|
||||||
type overallStatus struct {
|
type overallStatus struct {
|
||||||
good, bad []string
|
good, bad []string
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ import (
|
|||||||
type derpProber struct {
|
type derpProber struct {
|
||||||
p *Prober
|
p *Prober
|
||||||
derpMapURL string // or "local"
|
derpMapURL string // or "local"
|
||||||
|
meshKey string
|
||||||
udpInterval time.Duration
|
udpInterval time.Duration
|
||||||
meshInterval time.Duration
|
meshInterval time.Duration
|
||||||
tlsInterval time.Duration
|
tlsInterval time.Duration
|
||||||
@ -71,7 +72,7 @@ type derpProber struct {
|
|||||||
udpProbeFn func(string, int) ProbeClass
|
udpProbeFn func(string, int) ProbeClass
|
||||||
meshProbeFn func(string, string) ProbeClass
|
meshProbeFn func(string, string) ProbeClass
|
||||||
bwProbeFn func(string, string, int64) ProbeClass
|
bwProbeFn func(string, string, int64) ProbeClass
|
||||||
qdProbeFn func(string, string, int, time.Duration) ProbeClass
|
qdProbeFn func(string, string, int, time.Duration, string) ProbeClass
|
||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
lastDERPMap *tailcfg.DERPMap
|
lastDERPMap *tailcfg.DERPMap
|
||||||
@ -143,6 +144,12 @@ func WithRegionCodeOrID(regionCode string) DERPOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithMeshKey(meshKey string) DERPOpt {
|
||||||
|
return func(d *derpProber) {
|
||||||
|
d.meshKey = meshKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DERP creates a new derpProber.
|
// DERP creates a new derpProber.
|
||||||
//
|
//
|
||||||
// If derpMapURL is "local", the DERPMap is fetched via
|
// If derpMapURL is "local", the DERPMap is fetched via
|
||||||
@ -250,7 +257,7 @@ func (d *derpProber) probeMapFn(ctx context.Context) error {
|
|||||||
wantProbes[n] = true
|
wantProbes[n] = true
|
||||||
if d.probes[n] == nil {
|
if d.probes[n] == nil {
|
||||||
log.Printf("adding DERP queuing delay probe for %s->%s (%s)", server.Name, to.Name, region.RegionName)
|
log.Printf("adding DERP queuing delay probe for %s->%s (%s)", server.Name, to.Name, region.RegionName)
|
||||||
d.probes[n] = d.p.Run(n, -10*time.Second, labels, d.qdProbeFn(server.Name, to.Name, d.qdPacketsPerSecond, d.qdPacketTimeout))
|
d.probes[n] = d.p.Run(n, -10*time.Second, labels, d.qdProbeFn(server.Name, to.Name, d.qdPacketsPerSecond, d.qdPacketTimeout, d.meshKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,7 +291,7 @@ func (d *derpProber) probeMesh(from, to string) ProbeClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dm := d.lastDERPMap
|
dm := d.lastDERPMap
|
||||||
return derpProbeNodePair(ctx, dm, fromN, toN)
|
return derpProbeNodePair(ctx, dm, fromN, toN, d.meshKey)
|
||||||
},
|
},
|
||||||
Class: "derp_mesh",
|
Class: "derp_mesh",
|
||||||
Labels: Labels{"derp_path": derpPath},
|
Labels: Labels{"derp_path": derpPath},
|
||||||
@ -308,7 +315,7 @@ func (d *derpProber) probeBandwidth(from, to string, size int64) ProbeClass {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return derpProbeBandwidth(ctx, d.lastDERPMap, fromN, toN, size, &transferTimeSeconds, &totalBytesTransferred, d.bwTUNIPv4Prefix)
|
return derpProbeBandwidth(ctx, d.lastDERPMap, fromN, toN, size, &transferTimeSeconds, &totalBytesTransferred, d.bwTUNIPv4Prefix, d.meshKey)
|
||||||
},
|
},
|
||||||
Class: "derp_bw",
|
Class: "derp_bw",
|
||||||
Labels: Labels{
|
Labels: Labels{
|
||||||
@ -336,7 +343,7 @@ func (d *derpProber) probeBandwidth(from, to string, size int64) ProbeClass {
|
|||||||
// to the queuing delay measurement and are recorded as dropped. 'from' and 'to' are
|
// to the queuing delay measurement and are recorded as dropped. 'from' and 'to' are
|
||||||
// expected to be names (DERPNode.Name) of two DERP servers in the same region,
|
// expected to be names (DERPNode.Name) of two DERP servers in the same region,
|
||||||
// and may refer to the same server.
|
// and may refer to the same server.
|
||||||
func (d *derpProber) probeQueuingDelay(from, to string, packetsPerSecond int, packetTimeout time.Duration) ProbeClass {
|
func (d *derpProber) probeQueuingDelay(from, to string, packetsPerSecond int, packetTimeout time.Duration, meshKey string) ProbeClass {
|
||||||
derpPath := "mesh"
|
derpPath := "mesh"
|
||||||
if from == to {
|
if from == to {
|
||||||
derpPath = "single"
|
derpPath = "single"
|
||||||
@ -349,7 +356,7 @@ func (d *derpProber) probeQueuingDelay(from, to string, packetsPerSecond int, pa
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return derpProbeQueuingDelay(ctx, d.lastDERPMap, fromN, toN, packetsPerSecond, packetTimeout, &packetsDropped, qdh)
|
return derpProbeQueuingDelay(ctx, d.lastDERPMap, fromN, toN, packetsPerSecond, packetTimeout, &packetsDropped, qdh, meshKey)
|
||||||
},
|
},
|
||||||
Class: "derp_qd",
|
Class: "derp_qd",
|
||||||
Labels: Labels{"derp_path": derpPath},
|
Labels: Labels{"derp_path": derpPath},
|
||||||
@ -368,15 +375,15 @@ func (d *derpProber) probeQueuingDelay(from, to string, packetsPerSecond int, pa
|
|||||||
// derpProbeQueuingDelay continuously sends data between two local DERP clients
|
// derpProbeQueuingDelay continuously sends data between two local DERP clients
|
||||||
// connected to two DERP servers in order to measure queuing delays. From and to
|
// connected to two DERP servers in order to measure queuing delays. From and to
|
||||||
// can be the same server.
|
// can be the same server.
|
||||||
func derpProbeQueuingDelay(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.DERPNode, packetsPerSecond int, packetTimeout time.Duration, packetsDropped *expvar.Float, qdh *histogram) (err error) {
|
func derpProbeQueuingDelay(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.DERPNode, packetsPerSecond int, packetTimeout time.Duration, packetsDropped *expvar.Float, qdh *histogram, meshKey string) (err error) {
|
||||||
// This probe uses clients with isProber=false to avoid spamming the derper
|
// This probe uses clients with isProber=false to avoid spamming the derper
|
||||||
// logs with every packet sent by the queuing delay probe.
|
// logs with every packet sent by the queuing delay probe.
|
||||||
fromc, err := newConn(ctx, dm, from, false)
|
fromc, err := newConn(ctx, dm, from, false, meshKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fromc.Close()
|
defer fromc.Close()
|
||||||
toc, err := newConn(ctx, dm, to, false)
|
toc, err := newConn(ctx, dm, to, false, meshKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -662,15 +669,15 @@ func derpProbeUDP(ctx context.Context, ipStr string, port int) error {
|
|||||||
// DERP clients connected to two DERP servers.If tunIPv4Address is specified,
|
// DERP clients connected to two DERP servers.If tunIPv4Address is specified,
|
||||||
// probes will use a TCP connection over a TUN device at this address in order
|
// probes will use a TCP connection over a TUN device at this address in order
|
||||||
// to exercise TCP-in-TCP in similar fashion to TCP over Tailscale via DERP.
|
// to exercise TCP-in-TCP in similar fashion to TCP over Tailscale via DERP.
|
||||||
func derpProbeBandwidth(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.DERPNode, size int64, transferTimeSeconds, totalBytesTransferred *expvar.Float, tunIPv4Prefix *netip.Prefix) (err error) {
|
func derpProbeBandwidth(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.DERPNode, size int64, transferTimeSeconds, totalBytesTransferred *expvar.Float, tunIPv4Prefix *netip.Prefix, meshKey string) (err error) {
|
||||||
// This probe uses clients with isProber=false to avoid spamming the derper logs with every packet
|
// This probe uses clients with isProber=false to avoid spamming the derper logs with every packet
|
||||||
// sent by the bandwidth probe.
|
// sent by the bandwidth probe.
|
||||||
fromc, err := newConn(ctx, dm, from, false)
|
fromc, err := newConn(ctx, dm, from, false, meshKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fromc.Close()
|
defer fromc.Close()
|
||||||
toc, err := newConn(ctx, dm, to, false)
|
toc, err := newConn(ctx, dm, to, false, meshKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -700,13 +707,13 @@ func derpProbeBandwidth(ctx context.Context, dm *tailcfg.DERPMap, from, to *tail
|
|||||||
|
|
||||||
// derpProbeNodePair sends a small packet between two local DERP clients
|
// derpProbeNodePair sends a small packet between two local DERP clients
|
||||||
// connected to two DERP servers.
|
// connected to two DERP servers.
|
||||||
func derpProbeNodePair(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.DERPNode) (err error) {
|
func derpProbeNodePair(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.DERPNode, meshKey string) (err error) {
|
||||||
fromc, err := newConn(ctx, dm, from, true)
|
fromc, err := newConn(ctx, dm, from, true, meshKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fromc.Close()
|
defer fromc.Close()
|
||||||
toc, err := newConn(ctx, dm, to, true)
|
toc, err := newConn(ctx, dm, to, true, meshKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1104,7 +1111,7 @@ func derpProbeBandwidthTUN(ctx context.Context, transferTimeSeconds, totalBytesT
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConn(ctx context.Context, dm *tailcfg.DERPMap, n *tailcfg.DERPNode, isProber bool) (*derphttp.Client, error) {
|
func newConn(ctx context.Context, dm *tailcfg.DERPMap, n *tailcfg.DERPNode, isProber bool, meshKey string) (*derphttp.Client, error) {
|
||||||
// To avoid spamming the log with regular connection messages.
|
// To avoid spamming the log with regular connection messages.
|
||||||
l := logger.Filtered(log.Printf, func(s string) bool {
|
l := logger.Filtered(log.Printf, func(s string) bool {
|
||||||
return !strings.Contains(s, "derphttp.Client.Connect: connecting to")
|
return !strings.Contains(s, "derphttp.Client.Connect: connecting to")
|
||||||
@ -1120,6 +1127,7 @@ func newConn(ctx context.Context, dm *tailcfg.DERPMap, n *tailcfg.DERPNode, isPr
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
dc.IsProber = isProber
|
dc.IsProber = isProber
|
||||||
|
dc.MeshKey = meshKey
|
||||||
err := dc.Connect(ctx)
|
err := dc.Connect(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
x
Reference in New Issue
Block a user