|
|
|
|
@ -63,7 +63,7 @@ type Direct struct { |
|
|
|
|
logf logger.Logf |
|
|
|
|
linkMon *monitor.Mon // or nil
|
|
|
|
|
discoPubKey tailcfg.DiscoKey |
|
|
|
|
machinePrivKey wgkey.Private |
|
|
|
|
getMachinePrivKey func() (wgkey.Private, error) |
|
|
|
|
debugFlags []string |
|
|
|
|
keepSharerAndUserSplit bool |
|
|
|
|
|
|
|
|
|
@ -81,19 +81,19 @@ type Direct struct { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type Options struct { |
|
|
|
|
Persist persist.Persist // initial persistent data
|
|
|
|
|
MachinePrivateKey wgkey.Private // the machine key to use
|
|
|
|
|
ServerURL string // URL of the tailcontrol server
|
|
|
|
|
AuthKey string // optional node auth key for auto registration
|
|
|
|
|
TimeNow func() time.Time // time.Now implementation used by Client
|
|
|
|
|
Hostinfo *tailcfg.Hostinfo // non-nil passes ownership, nil means to use default using os.Hostname, etc
|
|
|
|
|
DiscoPublicKey tailcfg.DiscoKey |
|
|
|
|
NewDecompressor func() (Decompressor, error) |
|
|
|
|
KeepAlive bool |
|
|
|
|
Logf logger.Logf |
|
|
|
|
HTTPTestClient *http.Client // optional HTTP client to use (for tests only)
|
|
|
|
|
DebugFlags []string // debug settings to send to control
|
|
|
|
|
LinkMonitor *monitor.Mon // optional link monitor
|
|
|
|
|
Persist persist.Persist // initial persistent data
|
|
|
|
|
GetMachinePrivateKey func() (wgkey.Private, error) // returns the machine key to use
|
|
|
|
|
ServerURL string // URL of the tailcontrol server
|
|
|
|
|
AuthKey string // optional node auth key for auto registration
|
|
|
|
|
TimeNow func() time.Time // time.Now implementation used by Client
|
|
|
|
|
Hostinfo *tailcfg.Hostinfo // non-nil passes ownership, nil means to use default using os.Hostname, etc
|
|
|
|
|
DiscoPublicKey tailcfg.DiscoKey |
|
|
|
|
NewDecompressor func() (Decompressor, error) |
|
|
|
|
KeepAlive bool |
|
|
|
|
Logf logger.Logf |
|
|
|
|
HTTPTestClient *http.Client // optional HTTP client to use (for tests only)
|
|
|
|
|
DebugFlags []string // debug settings to send to control
|
|
|
|
|
LinkMonitor *monitor.Mon // optional link monitor
|
|
|
|
|
|
|
|
|
|
// KeepSharerAndUserSplit controls whether the client
|
|
|
|
|
// understands Node.Sharer. If false, the Sharer is mapped to the User.
|
|
|
|
|
@ -110,8 +110,8 @@ func NewDirect(opts Options) (*Direct, error) { |
|
|
|
|
if opts.ServerURL == "" { |
|
|
|
|
return nil, errors.New("controlclient.New: no server URL specified") |
|
|
|
|
} |
|
|
|
|
if opts.MachinePrivateKey.IsZero() { |
|
|
|
|
return nil, errors.New("controlclient.New: no MachinePrivateKey specified") |
|
|
|
|
if opts.GetMachinePrivateKey == nil { |
|
|
|
|
return nil, errors.New("controlclient.New: no GetMachinePrivateKey specified") |
|
|
|
|
} |
|
|
|
|
opts.ServerURL = strings.TrimRight(opts.ServerURL, "/") |
|
|
|
|
serverURL, err := url.Parse(opts.ServerURL) |
|
|
|
|
@ -147,7 +147,7 @@ func NewDirect(opts Options) (*Direct, error) { |
|
|
|
|
|
|
|
|
|
c := &Direct{ |
|
|
|
|
httpc: httpc, |
|
|
|
|
machinePrivKey: opts.MachinePrivateKey, |
|
|
|
|
getMachinePrivKey: opts.GetMachinePrivateKey, |
|
|
|
|
serverURL: opts.ServerURL, |
|
|
|
|
timeNow: opts.TimeNow, |
|
|
|
|
logf: opts.Logf, |
|
|
|
|
@ -301,8 +301,12 @@ func (c *Direct) doLogin(ctx context.Context, t *tailcfg.Oauth2Token, flags Logi |
|
|
|
|
expired := c.expiry != nil && !c.expiry.IsZero() && c.expiry.Before(c.timeNow()) |
|
|
|
|
c.mu.Unlock() |
|
|
|
|
|
|
|
|
|
if c.machinePrivKey.IsZero() { |
|
|
|
|
return false, "", errors.New("controlclient.Direct requires a machine private key") |
|
|
|
|
machinePrivKey, err := c.getMachinePrivKey() |
|
|
|
|
if err != nil { |
|
|
|
|
return false, "", fmt.Errorf("getMachinePrivKey: %w", err) |
|
|
|
|
} |
|
|
|
|
if machinePrivKey.IsZero() { |
|
|
|
|
return false, "", errors.New("getMachinePrivKey returned zero key") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if expired { |
|
|
|
|
@ -370,7 +374,7 @@ func (c *Direct) doLogin(ctx context.Context, t *tailcfg.Oauth2Token, flags Logi |
|
|
|
|
request.Auth.Provider = persist.Provider |
|
|
|
|
request.Auth.LoginName = persist.LoginName |
|
|
|
|
request.Auth.AuthKey = authKey |
|
|
|
|
err = signRegisterRequest(&request, c.serverURL, c.serverKey, c.machinePrivKey.Public()) |
|
|
|
|
err = signRegisterRequest(&request, c.serverURL, c.serverKey, machinePrivKey.Public()) |
|
|
|
|
if err != nil { |
|
|
|
|
// If signing failed, clear all related fields
|
|
|
|
|
request.SignatureType = tailcfg.SignatureNone |
|
|
|
|
@ -384,13 +388,13 @@ func (c *Direct) doLogin(ctx context.Context, t *tailcfg.Oauth2Token, flags Logi |
|
|
|
|
c.logf("RegisterReq sign error: %v", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
bodyData, err := encode(request, &serverKey, &c.machinePrivKey) |
|
|
|
|
bodyData, err := encode(request, &serverKey, &machinePrivKey) |
|
|
|
|
if err != nil { |
|
|
|
|
return regen, url, err |
|
|
|
|
} |
|
|
|
|
body := bytes.NewReader(bodyData) |
|
|
|
|
|
|
|
|
|
u := fmt.Sprintf("%s/machine/%s", c.serverURL, c.machinePrivKey.Public().HexString()) |
|
|
|
|
u := fmt.Sprintf("%s/machine/%s", c.serverURL, machinePrivKey.Public().HexString()) |
|
|
|
|
req, err := http.NewRequest("POST", u, body) |
|
|
|
|
if err != nil { |
|
|
|
|
return regen, url, err |
|
|
|
|
@ -408,8 +412,8 @@ func (c *Direct) doLogin(ctx context.Context, t *tailcfg.Oauth2Token, flags Logi |
|
|
|
|
res.StatusCode, strings.TrimSpace(string(msg))) |
|
|
|
|
} |
|
|
|
|
resp := tailcfg.RegisterResponse{} |
|
|
|
|
if err := decode(res, &resp, &serverKey, &c.machinePrivKey); err != nil { |
|
|
|
|
c.logf("error decoding RegisterResponse with server key %s and machine key %s: %v", serverKey, c.machinePrivKey.Public(), err) |
|
|
|
|
if err := decode(res, &resp, &serverKey, &machinePrivKey); err != nil { |
|
|
|
|
c.logf("error decoding RegisterResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err) |
|
|
|
|
return regen, url, fmt.Errorf("register request: %v", err) |
|
|
|
|
} |
|
|
|
|
// Log without PII:
|
|
|
|
|
@ -536,6 +540,14 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm |
|
|
|
|
everEndpoints := c.everEndpoints |
|
|
|
|
c.mu.Unlock() |
|
|
|
|
|
|
|
|
|
machinePrivKey, err := c.getMachinePrivKey() |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("getMachinePrivKey: %w", err) |
|
|
|
|
} |
|
|
|
|
if machinePrivKey.IsZero() { |
|
|
|
|
return errors.New("getMachinePrivKey returned zero key") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if persist.PrivateNodeKey.IsZero() { |
|
|
|
|
return errors.New("privateNodeKey is zero") |
|
|
|
|
} |
|
|
|
|
@ -593,7 +605,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm |
|
|
|
|
request.ReadOnly = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bodyData, err := encode(request, &serverKey, &c.machinePrivKey) |
|
|
|
|
bodyData, err := encode(request, &serverKey, &machinePrivKey) |
|
|
|
|
if err != nil { |
|
|
|
|
vlogf("netmap: encode: %v", err) |
|
|
|
|
return err |
|
|
|
|
@ -602,7 +614,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm |
|
|
|
|
ctx, cancel := context.WithCancel(ctx) |
|
|
|
|
defer cancel() |
|
|
|
|
|
|
|
|
|
machinePubKey := tailcfg.MachineKey(c.machinePrivKey.Public()) |
|
|
|
|
machinePubKey := tailcfg.MachineKey(machinePrivKey.Public()) |
|
|
|
|
t0 := time.Now() |
|
|
|
|
u := fmt.Sprintf("%s/machine/%s/map", serverURL, machinePubKey.HexString()) |
|
|
|
|
|
|
|
|
|
@ -695,7 +707,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm |
|
|
|
|
vlogf("netmap: read body after %v", time.Since(t0).Round(time.Millisecond)) |
|
|
|
|
|
|
|
|
|
var resp tailcfg.MapResponse |
|
|
|
|
if err := c.decodeMsg(msg, &resp); err != nil { |
|
|
|
|
if err := c.decodeMsg(msg, &resp, &machinePrivKey); err != nil { |
|
|
|
|
vlogf("netmap: decode error: %v") |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
@ -878,12 +890,12 @@ var debugMap, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_MAP")) |
|
|
|
|
|
|
|
|
|
var jsonEscapedZero = []byte(`\u0000`) |
|
|
|
|
|
|
|
|
|
func (c *Direct) decodeMsg(msg []byte, v interface{}) error { |
|
|
|
|
func (c *Direct) decodeMsg(msg []byte, v interface{}, machinePrivKey *wgkey.Private) error { |
|
|
|
|
c.mu.Lock() |
|
|
|
|
serverKey := c.serverKey |
|
|
|
|
c.mu.Unlock() |
|
|
|
|
|
|
|
|
|
decrypted, err := decryptMsg(msg, &serverKey, &c.machinePrivKey) |
|
|
|
|
decrypted, err := decryptMsg(msg, &serverKey, machinePrivKey) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
@ -917,8 +929,8 @@ func (c *Direct) decodeMsg(msg []byte, v interface{}) error { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func decodeMsg(msg []byte, v interface{}, serverKey *wgkey.Key, mkey *wgkey.Private) error { |
|
|
|
|
decrypted, err := decryptMsg(msg, serverKey, mkey) |
|
|
|
|
func decodeMsg(msg []byte, v interface{}, serverKey *wgkey.Key, machinePrivKey *wgkey.Private) error { |
|
|
|
|
decrypted, err := decryptMsg(msg, serverKey, machinePrivKey) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|