This commit is contained in:
Charlotte Brandhorst-Satzkorn 2022-08-04 15:44:18 -07:00
parent 8e821d7aa8
commit d910c44959
3 changed files with 364 additions and 82 deletions

View File

@ -0,0 +1,138 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"testing"
"tailscale.com/derp/derphttp"
"tailscale.com/types/key"
)
// THIS FILE WILL NOT BE INCLUDED, ITS JUST FOR LOCAL TESTING.
// PLEASE IGNORE :)
func BenchmarkDerpSend(b *testing.B) {
derper := &Derper{flagCfg: DerperFlagConfig{
dev: true,
},
}
go func() {
if err := derper.Run(); err != nil {
b.Errorf("unable to run derper")
}
}()
for {
// block until the server is ready
resp, err := http.Get("http://localhost:3340/derp/probe")
if err != nil {
fmt.Println(err)
continue
}
if resp.StatusCode == http.StatusOK {
fmt.Println(resp.Status)
break
}
}
clientPriv := key.NewNode()
c, err := derphttp.NewClient(clientPriv, "http://localhost:3340/derp", log.Printf)
if err != nil {
b.Fatal(err)
return
}
defer c.Close()
err = c.Connect(context.Background())
if err != nil {
b.Fatal("could not connect: ", err)
return
}
n := key.NewNode()
nc, err := derphttp.NewClient(n, "http://localhost:3340/derp", log.Printf)
if err != nil {
b.Fatal(err)
return
}
defer nc.Close()
err = nc.Connect(context.Background())
if err != nil {
b.Fatalf("could not connect")
return
}
// b.Run("i dont know", func(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Println(i)
if err := c.Send(n.Public(), []byte{1, 2, 3, 4, 5, 6, 7, 8}); err != nil {
b.Fatalf("cannot send packet: %v", err)
return
}
_, err := nc.Recv()
if err != nil {
b.Fatalf("error receiving: %v", err)
return
}
}
b.Cleanup(func() {
err := derper.Stop()
if err != nil {
b.Fatalf("oops: %s", err)
}
})
}
// func dothestuff() {
// // n := key.NewNode()
// // nc, err := derphttp.NewClient(clientPriv, "http://localhost:3340/derp", log.Printf)
// // if err != nil {
// // log.Fatal(err)
// // }
// // defer nc.Close()
// // err = nc.Connect(context.Background())
// // if err != nil {
// // fmt.Println(err)
// // panic("connect")
// // }
// fmt.Println(c.ServerPublicKey())
// for i := 0; i < 2; i++ {
// fmt.Println("hi?")
// if err := c.SendPing([8]byte{1, 2, 3, 4, 5, 6, 7, 8}); err != nil {
// fmt.Println(err)
// }
// time.Sleep(time.Millisecond * 100)
// if err := c.Send(n.Public(), []byte{1, 2, 3, 4, 5, 6, 7, 8}); err != nil {
// fmt.Println(err)
// }
// time.Sleep(time.Millisecond * 100)
// if err := nc.Send(clientPriv.Public(), []byte{1, 2, 3, 4, 5, 6, 7, 8}); err != nil {
// fmt.Println(err)
// }
// c.NotePreferred(true)
// time.Sleep(time.Millisecond * 100)
// }
// }

View File

@ -35,6 +35,18 @@
"tailscale.com/types/key" "tailscale.com/types/key"
) )
//*
/* in part it ends up being a preference thing vs. the reviewers,
/* but yeah one of the more ongoing flexible ways is to pull it out into a struct that you can configure before you run it,
/* so a really simple transition is to effectively take the flags and put them in a struct rather than globals,
/* and then take the parts of main that do more configuration/defaults setup
/* and put that in something like func (s *Struct) Configure()
/* and then finally put the part of main that actually runs it in to a func (s *Struct) Run()
/* that way a test / benchmark would mostly set the fields on the struct
/* and/or call configure, and then call run
/* the other thing we needed that we didn't have from main was a way to stop it after starting it
**/
var ( var (
dev = flag.Bool("dev", false, "run in localhost development mode") dev = flag.Bool("dev", false, "run in localhost development mode")
addr = flag.String("a", ":443", "server HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces.") addr = flag.String("a", ":443", "server HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces.")
@ -71,93 +83,69 @@
stunIPv6 = stunAddrFamily.Get("ipv6") stunIPv6 = stunAddrFamily.Get("ipv6")
) )
func init() { type Derper struct {
stats.Set("counter_requests", stunDisposition) flagCfg DerperFlagConfig
stats.Set("counter_addrfamily", stunAddrFamily) derpSrv *derp.Server
expvar.Publish("stun", stats) httpSrv *http.Server
expvar.Publish("derper_tls_request_version", tlsRequestVersion) port80srv *http.Server
expvar.Publish("gauge_derper_tls_active_version", tlsActiveVersion)
} }
type config struct { type DerperFlagConfig struct {
PrivateKey key.NodePrivate dev bool
addr string
httpPort int
stunPort int
configPath string
certMode string
certDir string
hostname string
runSTUN bool
meshPSKFile string
meshWith string
bootstrapDNS string
verifyClients bool
acceptConnLimit float64
acceptConnBurst int
} }
func loadConfig() config { func (d *Derper) Configure() error {
if *dev {
return config{PrivateKey: key.NewNode()} return nil
}
if *configPath == "" {
if os.Getuid() == 0 {
*configPath = "/var/lib/derper/derper.key"
} else {
log.Fatalf("derper: -c <config path> not specified")
}
log.Printf("no config path specified; using %s", *configPath)
}
b, err := ioutil.ReadFile(*configPath)
switch {
case errors.Is(err, os.ErrNotExist):
return writeNewConfig()
case err != nil:
log.Fatal(err)
panic("unreachable")
default:
var cfg config
if err := json.Unmarshal(b, &cfg); err != nil {
log.Fatalf("derper: config: %v", err)
}
return cfg
}
} }
func writeNewConfig() config { func (d *Derper) Run() error {
k := key.NewNode() if d.flagCfg.dev {
if err := os.MkdirAll(filepath.Dir(*configPath), 0777); err != nil { d.flagCfg.addr = ":3340" // above the keys DERP
log.Fatal(err)
}
cfg := config{
PrivateKey: k,
}
b, err := json.MarshalIndent(cfg, "", "\t")
if err != nil {
log.Fatal(err)
}
if err := atomicfile.WriteFile(*configPath, b, 0600); err != nil {
log.Fatal(err)
}
return cfg
}
func main() {
flag.Parse()
if *dev {
*addr = ":3340" // above the keys DERP
log.Printf("Running in dev mode.") log.Printf("Running in dev mode.")
tsweb.DevMode = true tsweb.DevMode = true
} }
listenHost, _, err := net.SplitHostPort(*addr) listenHost, _, err := net.SplitHostPort(d.flagCfg.addr)
if err != nil { if err != nil {
log.Fatalf("invalid server address: %v", err) log.Fatalf("invalid server address: %v", err)
} }
cfg := loadConfig() cfg := d.loadConfig()
serveTLS := tsweb.IsProd443(*addr) || *certMode == "manual" serveTLS := tsweb.IsProd443(d.flagCfg.addr) || d.flagCfg.certMode == "manual"
s := derp.NewServer(cfg.PrivateKey, log.Printf) s := derp.NewServer(cfg.PrivateKey, log.Printf)
s.SetVerifyClient(*verifyClients) s.SetVerifyClient(d.flagCfg.verifyClients)
if *meshPSKFile != "" { d.derpSrv = s
b, err := ioutil.ReadFile(*meshPSKFile)
if d.flagCfg.meshPSKFile != "" {
b, err := ioutil.ReadFile(d.flagCfg.meshPSKFile)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
key := strings.TrimSpace(string(b)) key := strings.TrimSpace(string(b))
if matched, _ := regexp.MatchString(`(?i)^[0-9a-f]{64,}$`, key); !matched { if matched, _ := regexp.MatchString(`(?i)^[0-9a-f]{64,}$`, key); !matched {
log.Fatalf("key in %s must contain 64+ hex digits", *meshPSKFile) log.Fatalf("key in %s must contain 64+ hex digits", d.flagCfg.meshPSKFile)
} }
s.SetMeshKey(key) s.SetMeshKey(key)
log.Printf("DERP mesh key configured") log.Printf("DERP mesh key configured")
@ -191,7 +179,7 @@ func main() {
} }
})) }))
debug := tsweb.Debugger(mux) debug := tsweb.Debugger(mux)
debug.KV("TLS hostname", *hostname) debug.KV("TLS hostname", d.flagCfg.hostname)
debug.KV("Mesh key", s.HasMeshKey()) debug.KV("Mesh key", s.HasMeshKey())
debug.Handle("check", "Consistency check", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { debug.Handle("check", "Consistency check", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := s.ConsistencyCheck() err := s.ConsistencyCheck()
@ -203,12 +191,12 @@ func main() {
})) }))
debug.Handle("traffic", "Traffic check", http.HandlerFunc(s.ServeDebugTraffic)) debug.Handle("traffic", "Traffic check", http.HandlerFunc(s.ServeDebugTraffic))
if *runSTUN { if d.flagCfg.runSTUN {
go serveSTUN(listenHost, *stunPort) go serveSTUN(listenHost, d.flagCfg.stunPort)
} }
httpsrv := &http.Server{ httpsrv := &http.Server{
Addr: *addr, Addr: d.flagCfg.addr,
Handler: mux, Handler: mux,
// Set read/write timeout. For derper, this basically // Set read/write timeout. For derper, this basically
@ -222,10 +210,12 @@ func main() {
WriteTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second,
} }
d.httpSrv = httpsrv
if serveTLS { if serveTLS {
log.Printf("derper: serving on %s with TLS", *addr) log.Printf("derper: serving on %s with TLS", d.flagCfg.addr)
var certManager certProvider var certManager certProvider
certManager, err = certProviderByCertMode(*certMode, *certDir, *hostname) certManager, err = certProviderByCertMode(d.flagCfg.certMode, d.flagCfg.certDir, d.flagCfg.hostname)
if err != nil { if err != nil {
log.Fatalf("derper: can not start cert provider: %v", err) log.Fatalf("derper: can not start cert provider: %v", err)
} }
@ -273,18 +263,19 @@ func main() {
w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; form-action 'none'; base-uri 'self'; block-all-mixed-content; plugin-types 'none'") w.Header().Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; form-action 'none'; base-uri 'self'; block-all-mixed-content; plugin-types 'none'")
mux.ServeHTTP(w, r) mux.ServeHTTP(w, r)
}) })
if *httpPort > -1 { if d.flagCfg.httpPort > -1 {
port80srv := &http.Server{
Addr: net.JoinHostPort(listenHost, fmt.Sprintf("%d", d.flagCfg.httpPort)),
Handler: certManager.HTTPHandler(tsweb.Port80Handler{Main: mux}),
ReadTimeout: 30 * time.Second,
// Crank up WriteTimeout a bit more than usually
// necessary just so we can do long CPU profiles
// and not hit net/http/pprof's "profile
// duration exceeds server's WriteTimeout".
WriteTimeout: 5 * time.Minute,
}
d.port80srv = port80srv
go func() { go func() {
port80srv := &http.Server{
Addr: net.JoinHostPort(listenHost, fmt.Sprintf("%d", *httpPort)),
Handler: certManager.HTTPHandler(tsweb.Port80Handler{Main: mux}),
ReadTimeout: 30 * time.Second,
// Crank up WriteTimeout a bit more than usually
// necessary just so we can do long CPU profiles
// and not hit net/http/pprof's "profile
// duration exceeds server's WriteTimeout".
WriteTimeout: 5 * time.Minute,
}
err := port80srv.ListenAndServe() err := port80srv.ListenAndServe()
if err != nil { if err != nil {
if err != http.ErrServerClosed { if err != http.ErrServerClosed {
@ -295,12 +286,134 @@ func main() {
} }
err = rateLimitedListenAndServeTLS(httpsrv) err = rateLimitedListenAndServeTLS(httpsrv)
} else { } else {
log.Printf("derper: serving on %s", *addr) log.Printf("derper: serving on %s", d.flagCfg.addr)
err = httpsrv.ListenAndServe() err = httpsrv.ListenAndServe()
} }
if err != nil && err != http.ErrServerClosed { if err != nil && err != http.ErrServerClosed {
log.Fatalf("derper: %v", err) log.Fatalf("derper: %v", err)
} }
return nil
}
func (d *Derper) Stop() error {
var httpSrvErr, port80srvErr error
if d.derpSrv != nil {
err := d.derpSrv.Close()
if err != nil {
return fmt.Errorf("damn")
}
}
if d.httpSrv != nil {
httpSrvErr = d.httpSrv.Close()
}
if d.port80srv != nil {
port80srvErr = d.port80srv.Close()
}
switch {
case httpSrvErr != nil && port80srvErr != nil:
log.Println("failed to close port80srv:", port80srvErr)
return fmt.Errorf("failed to close httpsrv: %w", httpSrvErr)
case httpSrvErr != nil:
return fmt.Errorf("failed to close httpsrv: %w", httpSrvErr)
case port80srvErr != nil:
return fmt.Errorf("failed to close port80srv: %w", port80srvErr)
}
return nil
}
func init() {
stats.Set("counter_requests", stunDisposition)
stats.Set("counter_addrfamily", stunAddrFamily)
expvar.Publish("stun", stats)
expvar.Publish("derper_tls_request_version", tlsRequestVersion)
expvar.Publish("gauge_derper_tls_active_version", tlsActiveVersion)
}
type config struct {
PrivateKey key.NodePrivate
}
func (d *Derper) loadConfig() config {
if d.flagCfg.dev {
return config{PrivateKey: key.NewNode()}
}
if d.flagCfg.configPath == "" {
if os.Getuid() == 0 {
d.flagCfg.configPath = "/var/lib/derper/derper.key"
} else {
log.Fatalf("derper: -c <config path> not specified")
}
log.Printf("no config path specified; using %s", d.flagCfg.configPath)
}
b, err := ioutil.ReadFile(d.flagCfg.configPath)
switch {
case errors.Is(err, os.ErrNotExist):
return writeNewConfig()
case err != nil:
log.Fatal(err)
panic("unreachable")
default:
var cfg config
if err := json.Unmarshal(b, &cfg); err != nil {
log.Fatalf("derper: config: %v", err)
}
return cfg
}
}
func writeNewConfig() config {
k := key.NewNode()
if err := os.MkdirAll(filepath.Dir(*configPath), 0777); err != nil {
log.Fatal(err)
}
cfg := config{
PrivateKey: k,
}
b, err := json.MarshalIndent(cfg, "", "\t")
if err != nil {
log.Fatal(err)
}
if err := atomicfile.WriteFile(*configPath, b, 0600); err != nil {
log.Fatal(err)
}
return cfg
}
func main() {
flag.Parse()
derper := &Derper{flagCfg: DerperFlagConfig{
dev: *dev,
addr: *addr,
httpPort: *httpPort,
stunPort: *stunPort,
configPath: *configPath,
certMode: *certMode,
certDir: *certDir,
hostname: *hostname,
runSTUN: *runSTUN,
meshPSKFile: *meshPSKFile,
meshWith: *meshWith,
bootstrapDNS: *bootstrapDNS,
verifyClients: *verifyClients,
acceptConnLimit: *acceptConnLimit,
acceptConnBurst: *acceptConnBurst,
},
}
if err := derper.Run(); err != nil {
log.Fatal("unable to run derper")
}
} }
// probeHandler is the endpoint that js/wasm clients hit to measure // probeHandler is the endpoint that js/wasm clients hit to measure

31
old.txt Normal file
View File

@ -0,0 +1,31 @@
2022/08/04 15:43:37 Running in dev mode.
2022/08/04 15:43:37 derper: serving on :3340
2022/08/04 15:43:37 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:37 running STUN server on [::]:3478
2022/08/04 15:43:37 derphttp.Client.Connect: connecting to http://localhost:3340/derp
goos: linux
goarch: amd64
pkg: tailscale.com/cmd/derper
2022/08/04 15:43:37 derp client [::1]:55814/6e6f64656b65793a39313565343537666538633835303532333432386231396631383238323234643963373936356262393231326266316632326534646466663331623337363734: read EOF
cpu: AMD Ryzen 9 5950X 16-Core Processor
BenchmarkDerpSend-32 2022/08/04 15:43:37 derp client [::1]:55814/6e6f64656b65793a39313565343537666538633835303532333432386231396631383238323234643963373936356262393231326266316632326534646466663331623337363734: removing connection
2022/08/04 15:43:37 derp client [::1]:55804/6e6f64656b65793a37666439653266633166653463356232643663336463666632393836626265393537306136383433393064313037303739613662383333346235333137383035: removing connection
2022/08/04 15:43:37 derp: [::1]:55804: client 6e6f64656b65793a37666439653266633166653463356232643663336463666632393836626265393537306136383433393064313037303739613662383333346235333137383035: readFrameHeader: read tcp [::1]:3340->[::1]:55804: read: connection reset by peer
2022/08/04 15:43:37 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:37 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:37 derp client [::1]:55824/6e6f64656b65793a32373562346333346436346633646338656132393363376237646638663364633263633066336339346264356631303236613661643239333163646538373532: read EOF
2022/08/04 15:43:37 derp client [::1]:55824/6e6f64656b65793a32373562346333346436346633646338656132393363376237646638663364633263633066336339346264356631303236613661643239333163646538373532: removing connection
2022/08/04 15:43:37 derp client [::1]:55820/6e6f64656b65793a63373934623165623130633532393536643936626437666563663165303138613937333132336534323535656462653133303566663839366161343962623361: removing connection
2022/08/04 15:43:37 derp: [::1]:55820: client 6e6f64656b65793a63373934623165623130633532393536643936626437666563663165303138613937333132336534323535656462653133303566663839366161343962623361: readFrameHeader: read tcp [::1]:3340->[::1]:55820: read: connection reset by peer
2022/08/04 15:43:37 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:37 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:38 derp client [::1]:55836/6e6f64656b65793a36363733333036363562396531316436333161343531333339383635386634393733333765383236346136396132666364616434633563313833636334353236: read EOF
2022/08/04 15:43:38 derp client [::1]:55836/6e6f64656b65793a36363733333036363562396531316436333161343531333339383635386634393733333765383236346136396132666364616434633563313833636334353236: removing connection
2022/08/04 15:43:38 derp client [::1]:55834/6e6f64656b65793a33383939363766613636623134356431393134376432356633313763653337336635656235643736666666663839623432363232343266363233366531623239: removing connection
2022/08/04 15:43:38 derp: [::1]:55834: client 6e6f64656b65793a33383939363766613636623134356431393134376432356633313763653337336635656235643736666666663839623432363232343266363233366531623239: readFrameHeader: read tcp [::1]:3340->[::1]:55834: read: connection reset by peer
2022/08/04 15:43:38 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:38 derphttp.Client.Connect: connecting to http://localhost:3340/derp
2022/08/04 15:43:39 derp client [::1]:55864/6e6f64656b65793a61653265313834373232356462613663656132633336383462663138363965366563626334613361363935393130633935363131626463316339653637663630: read EOF
24733 66103 ns/op
PASS
ok tailscale.com/cmd/derper 2.134s