mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
control/controlclient,ipn/ipnlocal,tailcfg: rotate node-key signature on register
CAPVER 47 Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
parent
26af329fde
commit
227777154a
@ -43,11 +43,13 @@
|
|||||||
"tailscale.com/net/tshttpproxy"
|
"tailscale.com/net/tshttpproxy"
|
||||||
"tailscale.com/syncs"
|
"tailscale.com/syncs"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/tka"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/netmap"
|
"tailscale.com/types/netmap"
|
||||||
"tailscale.com/types/opt"
|
"tailscale.com/types/opt"
|
||||||
"tailscale.com/types/persist"
|
"tailscale.com/types/persist"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/multierr"
|
"tailscale.com/util/multierr"
|
||||||
"tailscale.com/util/singleflight"
|
"tailscale.com/util/singleflight"
|
||||||
@ -68,7 +70,7 @@ type Direct struct {
|
|||||||
linkMon *monitor.Mon // or nil
|
linkMon *monitor.Mon // or nil
|
||||||
discoPubKey key.DiscoPublic
|
discoPubKey key.DiscoPublic
|
||||||
getMachinePrivKey func() (key.MachinePrivate, error)
|
getMachinePrivKey func() (key.MachinePrivate, error)
|
||||||
getNLPublicKey func() (key.NLPublic, error) // or nil
|
getNLPrivateKey func() (key.NLPrivate, error) // or nil
|
||||||
debugFlags []string
|
debugFlags []string
|
||||||
keepSharerAndUserSplit bool
|
keepSharerAndUserSplit bool
|
||||||
skipIPForwardingCheck bool
|
skipIPForwardingCheck bool
|
||||||
@ -115,9 +117,9 @@ type Options struct {
|
|||||||
Dialer *tsdial.Dialer // non-nil
|
Dialer *tsdial.Dialer // non-nil
|
||||||
C2NHandler http.Handler // or nil
|
C2NHandler http.Handler // or nil
|
||||||
|
|
||||||
// GetNLPublicKey specifies an optional function to use
|
// GetNLPrivateKey specifies an optional function to use
|
||||||
// Network Lock. If nil, it's not used.
|
// Network Lock. If nil, it's not used.
|
||||||
GetNLPublicKey func() (key.NLPublic, error)
|
GetNLPrivateKey func() (key.NLPrivate, error)
|
||||||
|
|
||||||
// Status is called when there's a change in status.
|
// Status is called when there's a change in status.
|
||||||
Status func(Status)
|
Status func(Status)
|
||||||
@ -229,7 +231,7 @@ func NewDirect(opts Options) (*Direct, error) {
|
|||||||
c := &Direct{
|
c := &Direct{
|
||||||
httpc: httpc,
|
httpc: httpc,
|
||||||
getMachinePrivKey: opts.GetMachinePrivateKey,
|
getMachinePrivKey: opts.GetMachinePrivateKey,
|
||||||
getNLPublicKey: opts.GetNLPublicKey,
|
getNLPrivateKey: opts.GetNLPrivateKey,
|
||||||
serverURL: opts.ServerURL,
|
serverURL: opts.ServerURL,
|
||||||
timeNow: opts.TimeNow,
|
timeNow: opts.TimeNow,
|
||||||
logf: opts.Logf,
|
logf: opts.Logf,
|
||||||
@ -324,7 +326,7 @@ func (c *Direct) GetPersist() persist.Persist {
|
|||||||
func (c *Direct) TryLogout(ctx context.Context) error {
|
func (c *Direct) TryLogout(ctx context.Context) error {
|
||||||
c.logf("[v1] direct.TryLogout()")
|
c.logf("[v1] direct.TryLogout()")
|
||||||
|
|
||||||
mustRegen, newURL, err := c.doLogin(ctx, loginOpt{Logout: true})
|
mustRegen, newURL, _, err := c.doLogin(ctx, loginOpt{Logout: true})
|
||||||
c.logf("[v1] TryLogout control response: mustRegen=%v, newURL=%v, err=%v", mustRegen, newURL, err)
|
c.logf("[v1] TryLogout control response: mustRegen=%v, newURL=%v, err=%v", mustRegen, newURL, err)
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
@ -348,13 +350,14 @@ func (c *Direct) WaitLoginURL(ctx context.Context, url string) (newURL string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Direct) doLoginOrRegen(ctx context.Context, opt loginOpt) (newURL string, err error) {
|
func (c *Direct) doLoginOrRegen(ctx context.Context, opt loginOpt) (newURL string, err error) {
|
||||||
mustRegen, url, err := c.doLogin(ctx, opt)
|
mustRegen, url, oldNodeKeySignature, err := c.doLogin(ctx, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return url, err
|
return url, err
|
||||||
}
|
}
|
||||||
if mustRegen {
|
if mustRegen {
|
||||||
opt.Regen = true
|
opt.Regen = true
|
||||||
_, url, err = c.doLogin(ctx, opt)
|
opt.OldNodeKeySignature = oldNodeKeySignature
|
||||||
|
_, url, _, err = c.doLogin(ctx, opt)
|
||||||
}
|
}
|
||||||
return url, err
|
return url, err
|
||||||
}
|
}
|
||||||
@ -380,6 +383,10 @@ type loginOpt struct {
|
|||||||
// It is ignored if Logout is set since Logout works by setting a
|
// It is ignored if Logout is set since Logout works by setting a
|
||||||
// expiry time in the far past.
|
// expiry time in the far past.
|
||||||
Expiry *time.Time
|
Expiry *time.Time
|
||||||
|
|
||||||
|
// OldNodeKeySignature indicates the former NodeKeySignature
|
||||||
|
// that must be resigned for the new node-key.
|
||||||
|
OldNodeKeySignature tkatype.MarshaledSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpClient provides a common interface for the noiseClient and
|
// httpClient provides a common interface for the noiseClient and
|
||||||
@ -396,7 +403,7 @@ func (c *Direct) hostInfoLocked() *tailcfg.Hostinfo {
|
|||||||
return hi
|
return hi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, newURL string, err error) {
|
func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, newURL string, nks tkatype.MarshaledSignature, err error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
persist := c.persist
|
persist := c.persist
|
||||||
tryingNewKey := c.tryingNewKey
|
tryingNewKey := c.tryingNewKey
|
||||||
@ -410,10 +417,10 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
|
|
||||||
machinePrivKey, err := c.getMachinePrivKey()
|
machinePrivKey, err := c.getMachinePrivKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", fmt.Errorf("getMachinePrivKey: %w", err)
|
return false, "", nil, fmt.Errorf("getMachinePrivKey: %w", err)
|
||||||
}
|
}
|
||||||
if machinePrivKey.IsZero() {
|
if machinePrivKey.IsZero() {
|
||||||
return false, "", errors.New("getMachinePrivKey returned zero key")
|
return false, "", nil, errors.New("getMachinePrivKey returned zero key")
|
||||||
}
|
}
|
||||||
|
|
||||||
regen := opt.Regen
|
regen := opt.Regen
|
||||||
@ -435,7 +442,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
if serverKey.IsZero() {
|
if serverKey.IsZero() {
|
||||||
keys, err := loadServerPubKeys(ctx, c.httpc, c.serverURL)
|
keys, err := loadServerPubKeys(ctx, c.httpc, c.serverURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, opt.URL, err
|
return regen, opt.URL, nil, err
|
||||||
}
|
}
|
||||||
c.logf("control server key from %s: ts2021=%s, legacy=%v", c.serverURL, keys.PublicKey.ShortString(), keys.LegacyPublicKey.ShortString())
|
c.logf("control server key from %s: ts2021=%s, legacy=%v", c.serverURL, keys.PublicKey.ShortString(), keys.LegacyPublicKey.ShortString())
|
||||||
|
|
||||||
@ -472,43 +479,53 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
oldNodeKey = persist.OldPrivateNodeKey.Public()
|
oldNodeKey = persist.OldPrivateNodeKey.Public()
|
||||||
}
|
}
|
||||||
|
|
||||||
var nlPub key.NLPublic
|
|
||||||
if c.getNLPublicKey != nil {
|
|
||||||
nlPub, err = c.getNLPublicKey()
|
|
||||||
if err != nil {
|
|
||||||
return false, "", fmt.Errorf("get nl key: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if tryingNewKey.IsZero() {
|
if tryingNewKey.IsZero() {
|
||||||
if opt.Logout {
|
if opt.Logout {
|
||||||
return false, "", errors.New("no nodekey to log out")
|
return false, "", nil, errors.New("no nodekey to log out")
|
||||||
}
|
}
|
||||||
log.Fatalf("tryingNewKey is empty, give up")
|
log.Fatalf("tryingNewKey is empty, give up")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nlPub key.NLPublic
|
||||||
|
var nodeKeySignature tkatype.MarshaledSignature
|
||||||
|
if c.getNLPrivateKey != nil {
|
||||||
|
priv, err := c.getNLPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return false, "", nil, fmt.Errorf("get nl key: %v", err)
|
||||||
|
}
|
||||||
|
nlPub = priv.Public()
|
||||||
|
|
||||||
|
if !oldNodeKey.IsZero() && opt.OldNodeKeySignature != nil {
|
||||||
|
if nodeKeySignature, err = resignNKS(priv, tryingNewKey.Public(), opt.OldNodeKeySignature); err != nil {
|
||||||
|
c.logf("Failed re-signing node-key signature: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if backendLogID == "" {
|
if backendLogID == "" {
|
||||||
err = errors.New("hostinfo: BackendLogID missing")
|
err = errors.New("hostinfo: BackendLogID missing")
|
||||||
return regen, opt.URL, err
|
return regen, opt.URL, nil, err
|
||||||
}
|
}
|
||||||
now := time.Now().Round(time.Second)
|
now := time.Now().Round(time.Second)
|
||||||
request := tailcfg.RegisterRequest{
|
request := tailcfg.RegisterRequest{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
OldNodeKey: oldNodeKey,
|
OldNodeKey: oldNodeKey,
|
||||||
NodeKey: tryingNewKey.Public(),
|
NodeKey: tryingNewKey.Public(),
|
||||||
NLKey: nlPub,
|
NLKey: nlPub,
|
||||||
Hostinfo: hi,
|
Hostinfo: hi,
|
||||||
Followup: opt.URL,
|
Followup: opt.URL,
|
||||||
Timestamp: &now,
|
Timestamp: &now,
|
||||||
Ephemeral: (opt.Flags & LoginEphemeral) != 0,
|
Ephemeral: (opt.Flags & LoginEphemeral) != 0,
|
||||||
|
NodeKeySignature: nodeKeySignature,
|
||||||
}
|
}
|
||||||
if opt.Logout {
|
if opt.Logout {
|
||||||
request.Expiry = time.Unix(123, 0) // far in the past
|
request.Expiry = time.Unix(123, 0) // far in the past
|
||||||
} else if opt.Expiry != nil {
|
} else if opt.Expiry != nil {
|
||||||
request.Expiry = *opt.Expiry
|
request.Expiry = *opt.Expiry
|
||||||
}
|
}
|
||||||
c.logf("RegisterReq: onode=%v node=%v fup=%v",
|
c.logf("RegisterReq: onode=%v node=%v fup=%v nks=%v",
|
||||||
request.OldNodeKey.ShortString(),
|
request.OldNodeKey.ShortString(),
|
||||||
request.NodeKey.ShortString(), opt.URL != "")
|
request.NodeKey.ShortString(), opt.URL != "", len(nodeKeySignature) > 0)
|
||||||
request.Auth.Oauth2Token = opt.Token
|
request.Auth.Oauth2Token = opt.Token
|
||||||
request.Auth.Provider = persist.Provider
|
request.Auth.Provider = persist.Provider
|
||||||
request.Auth.LoginName = persist.LoginName
|
request.Auth.LoginName = persist.LoginName
|
||||||
@ -542,33 +559,33 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
request.Version = tailcfg.CurrentCapabilityVersion
|
request.Version = tailcfg.CurrentCapabilityVersion
|
||||||
httpc, err = c.getNoiseClient()
|
httpc, err = c.getNoiseClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, opt.URL, fmt.Errorf("getNoiseClient: %w", err)
|
return regen, opt.URL, nil, fmt.Errorf("getNoiseClient: %w", err)
|
||||||
}
|
}
|
||||||
url = fmt.Sprintf("%s/machine/register", c.serverURL)
|
url = fmt.Sprintf("%s/machine/register", c.serverURL)
|
||||||
url = strings.Replace(url, "http:", "https:", 1)
|
url = strings.Replace(url, "http:", "https:", 1)
|
||||||
}
|
}
|
||||||
bodyData, err := encode(request, serverKey, serverNoiseKey, machinePrivKey)
|
bodyData, err := encode(request, serverKey, serverNoiseKey, machinePrivKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, opt.URL, err
|
return regen, opt.URL, nil, err
|
||||||
}
|
}
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(bodyData))
|
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(bodyData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, opt.URL, err
|
return regen, opt.URL, nil, err
|
||||||
}
|
}
|
||||||
res, err := httpc.Do(req)
|
res, err := httpc.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, opt.URL, fmt.Errorf("register request: %w", err)
|
return regen, opt.URL, nil, fmt.Errorf("register request: %w", err)
|
||||||
}
|
}
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
msg, _ := io.ReadAll(res.Body)
|
msg, _ := io.ReadAll(res.Body)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
return regen, opt.URL, fmt.Errorf("register request: http %d: %.200s",
|
return regen, opt.URL, nil, fmt.Errorf("register request: http %d: %.200s",
|
||||||
res.StatusCode, strings.TrimSpace(string(msg)))
|
res.StatusCode, strings.TrimSpace(string(msg)))
|
||||||
}
|
}
|
||||||
resp := tailcfg.RegisterResponse{}
|
resp := tailcfg.RegisterResponse{}
|
||||||
if err := decode(res, &resp, serverKey, serverNoiseKey, machinePrivKey); err != nil {
|
if err := decode(res, &resp, serverKey, serverNoiseKey, machinePrivKey); err != nil {
|
||||||
c.logf("error decoding RegisterResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
|
c.logf("error decoding RegisterResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
|
||||||
return regen, opt.URL, fmt.Errorf("register request: %v", err)
|
return regen, opt.URL, nil, fmt.Errorf("register request: %v", err)
|
||||||
}
|
}
|
||||||
if debugRegister() {
|
if debugRegister() {
|
||||||
j, _ := json.MarshalIndent(resp, "", "\t")
|
j, _ := json.MarshalIndent(resp, "", "\t")
|
||||||
@ -580,15 +597,19 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
resp.NodeKeyExpired, resp.MachineAuthorized, resp.AuthURL != "")
|
resp.NodeKeyExpired, resp.MachineAuthorized, resp.AuthURL != "")
|
||||||
|
|
||||||
if resp.Error != "" {
|
if resp.Error != "" {
|
||||||
return false, "", UserVisibleError(resp.Error)
|
return false, "", nil, UserVisibleError(resp.Error)
|
||||||
}
|
}
|
||||||
|
if len(resp.NodeKeySignature) > 0 {
|
||||||
|
return true, "", resp.NodeKeySignature, nil
|
||||||
|
}
|
||||||
|
|
||||||
if resp.NodeKeyExpired {
|
if resp.NodeKeyExpired {
|
||||||
if regen {
|
if regen {
|
||||||
return true, "", fmt.Errorf("weird: regen=true but server says NodeKeyExpired: %v", request.NodeKey)
|
return true, "", nil, fmt.Errorf("weird: regen=true but server says NodeKeyExpired: %v", request.NodeKey)
|
||||||
}
|
}
|
||||||
c.logf("server reports new node key %v has expired",
|
c.logf("server reports new node key %v has expired",
|
||||||
request.NodeKey.ShortString())
|
request.NodeKey.ShortString())
|
||||||
return true, "", nil
|
return true, "", nil, nil
|
||||||
}
|
}
|
||||||
if resp.Login.Provider != "" {
|
if resp.Login.Provider != "" {
|
||||||
persist.Provider = resp.Login.Provider
|
persist.Provider = resp.Login.Provider
|
||||||
@ -621,12 +642,51 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, "", err
|
return regen, "", nil, err
|
||||||
}
|
}
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return regen, "", ctx.Err()
|
return regen, "", nil, ctx.Err()
|
||||||
}
|
}
|
||||||
return false, resp.AuthURL, nil
|
return false, resp.AuthURL, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resignNKS re-signs a node-key signature for a new node-key.
|
||||||
|
//
|
||||||
|
// This only matters on network-locked tailnets, because node-key signatures are
|
||||||
|
// how other nodes know that a node-key is authentic. When the node-key is
|
||||||
|
// rotated then the existing signature becomes invalid, so this function is
|
||||||
|
// responsible for generating a new wrapping signature to certify the new node-key.
|
||||||
|
//
|
||||||
|
// The signature itself is a SigRotation signature, which embeds the old signature
|
||||||
|
// and certifies the new node-key as a replacement for the old by signing the new
|
||||||
|
// signature with RotationPubkey (which is the node's own network-lock key).
|
||||||
|
func resignNKS(priv key.NLPrivate, nodeKey key.NodePublic, oldNKS tkatype.MarshaledSignature) (tkatype.MarshaledSignature, error) {
|
||||||
|
var oldSig tka.NodeKeySignature
|
||||||
|
if err := oldSig.Unserialize(oldNKS); err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding NKS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nk, err := nodeKey.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("marshalling node-key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(nk, oldSig.Pubkey) {
|
||||||
|
// The old signature is valid for the node-key we are using, so just
|
||||||
|
// use it verbatim.
|
||||||
|
return oldNKS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newSig := tka.NodeKeySignature{
|
||||||
|
SigKind: tka.SigRotation,
|
||||||
|
Pubkey: nk,
|
||||||
|
Nested: &oldSig,
|
||||||
|
}
|
||||||
|
if newSig.Signature, err = priv.SignNKS(newSig.SigHash()); err != nil {
|
||||||
|
return nil, fmt.Errorf("signing NKS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSig.Serialize(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sameEndpoints(a, b []tailcfg.Endpoint) bool {
|
func sameEndpoints(a, b []tailcfg.Endpoint) bool {
|
||||||
|
@ -1164,7 +1164,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
// but it won't take effect until the next Start().
|
// but it won't take effect until the next Start().
|
||||||
cc, err := b.getNewControlClientFunc()(controlclient.Options{
|
cc, err := b.getNewControlClientFunc()(controlclient.Options{
|
||||||
GetMachinePrivateKey: b.createGetMachinePrivateKeyFunc(),
|
GetMachinePrivateKey: b.createGetMachinePrivateKeyFunc(),
|
||||||
GetNLPublicKey: b.createGetNLPublicKeyFunc(),
|
GetNLPrivateKey: b.createGetNLPrivateKeyFunc(),
|
||||||
Logf: logger.WithPrefix(b.logf, "control: "),
|
Logf: logger.WithPrefix(b.logf, "control: "),
|
||||||
Persist: *persistv,
|
Persist: *persistv,
|
||||||
ServerURL: b.serverURL,
|
ServerURL: b.serverURL,
|
||||||
@ -1634,18 +1634,18 @@ func (b *LocalBackend) createGetMachinePrivateKeyFunc() func() (key.MachinePriva
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) createGetNLPublicKeyFunc() func() (key.NLPublic, error) {
|
func (b *LocalBackend) createGetNLPrivateKeyFunc() func() (key.NLPrivate, error) {
|
||||||
var cache syncs.AtomicValue[key.NLPublic]
|
var cache syncs.AtomicValue[key.NLPrivate]
|
||||||
return func() (key.NLPublic, error) {
|
return func() (key.NLPrivate, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
if v, ok := cache.LoadOk(); ok {
|
if v, ok := cache.LoadOk(); ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pub := b.nlPrivKey.Public()
|
priv := b.nlPrivKey
|
||||||
cache.Store(pub)
|
cache.Store(priv)
|
||||||
return pub, nil
|
return priv, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,8 @@
|
|||||||
// - 44: 2022-09-22: MapResponse.ControlDialPlan
|
// - 44: 2022-09-22: MapResponse.ControlDialPlan
|
||||||
// - 45: 2022-09-26: c2n /debug/{goroutines,prefs,metrics}
|
// - 45: 2022-09-26: c2n /debug/{goroutines,prefs,metrics}
|
||||||
// - 46: 2022-10-04: c2n /debug/component-logging
|
// - 46: 2022-10-04: c2n /debug/component-logging
|
||||||
const CurrentCapabilityVersion CapabilityVersion = 46
|
// - 47: 2022-10-11: Register{Request,Response}.NodeKeySignature
|
||||||
|
const CurrentCapabilityVersion CapabilityVersion = 47
|
||||||
|
|
||||||
type StableID string
|
type StableID string
|
||||||
|
|
||||||
@ -827,6 +828,13 @@ type RegisterRequest struct {
|
|||||||
// when it stops being active.
|
// when it stops being active.
|
||||||
Ephemeral bool `json:",omitempty"`
|
Ephemeral bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// NodeKeySignature is the node's own node-key signature, re-signed
|
||||||
|
// for its new node key using its network-lock key.
|
||||||
|
//
|
||||||
|
// This field is set when the client retries registration after learning
|
||||||
|
// its NodeKeySignature (which is in need of rotation).
|
||||||
|
NodeKeySignature tkatype.MarshaledSignature
|
||||||
|
|
||||||
// The following fields are not used for SignatureNone and are required for
|
// The following fields are not used for SignatureNone and are required for
|
||||||
// SignatureV1:
|
// SignatureV1:
|
||||||
SignatureType SignatureType `json:",omitempty"`
|
SignatureType SignatureType `json:",omitempty"`
|
||||||
@ -854,6 +862,7 @@ func (req *RegisterRequest) Clone() *RegisterRequest {
|
|||||||
}
|
}
|
||||||
res.DeviceCert = append(res.DeviceCert[:0:0], res.DeviceCert...)
|
res.DeviceCert = append(res.DeviceCert[:0:0], res.DeviceCert...)
|
||||||
res.Signature = append(res.Signature[:0:0], res.Signature...)
|
res.Signature = append(res.Signature[:0:0], res.Signature...)
|
||||||
|
res.NodeKeySignature = append(res.NodeKeySignature[:0:0], res.NodeKeySignature...)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,6 +874,10 @@ type RegisterResponse struct {
|
|||||||
MachineAuthorized bool // TODO(crawshaw): move to using MachineStatus
|
MachineAuthorized bool // TODO(crawshaw): move to using MachineStatus
|
||||||
AuthURL string // if set, authorization pending
|
AuthURL string // if set, authorization pending
|
||||||
|
|
||||||
|
// If set, this is the current node-key signature that needs to be
|
||||||
|
// re-signed for the node's new node-key.
|
||||||
|
NodeKeySignature tkatype.MarshaledSignature
|
||||||
|
|
||||||
// Error indicates that authorization failed. If this is non-empty,
|
// Error indicates that authorization failed. If this is non-empty,
|
||||||
// other status fields should be ignored.
|
// other status fields should be ignored.
|
||||||
Error string
|
Error string
|
||||||
|
@ -254,6 +254,7 @@ func (src *RegisterResponse) Clone() *RegisterResponse {
|
|||||||
dst := new(RegisterResponse)
|
dst := new(RegisterResponse)
|
||||||
*dst = *src
|
*dst = *src
|
||||||
dst.User = *src.User.Clone()
|
dst.User = *src.User.Clone()
|
||||||
|
dst.NodeKeySignature = append(src.NodeKeySignature[:0:0], src.NodeKeySignature...)
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +265,7 @@ func (src *RegisterResponse) Clone() *RegisterResponse {
|
|||||||
NodeKeyExpired bool
|
NodeKeyExpired bool
|
||||||
MachineAuthorized bool
|
MachineAuthorized bool
|
||||||
AuthURL string
|
AuthURL string
|
||||||
|
NodeKeySignature tkatype.MarshaledSignature
|
||||||
Error string
|
Error string
|
||||||
}{})
|
}{})
|
||||||
|
|
||||||
|
@ -581,12 +581,13 @@ func (v *RegisterResponseView) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v RegisterResponseView) User() UserView { return v.ж.User.View() }
|
func (v RegisterResponseView) User() UserView { return v.ж.User.View() }
|
||||||
func (v RegisterResponseView) Login() Login { return v.ж.Login }
|
func (v RegisterResponseView) Login() Login { return v.ж.Login }
|
||||||
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
|
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
|
||||||
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
||||||
func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL }
|
func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL }
|
||||||
func (v RegisterResponseView) Error() string { return v.ж.Error }
|
func (v RegisterResponseView) NodeKeySignature() mem.RO { return mem.B(v.ж.NodeKeySignature) }
|
||||||
|
func (v RegisterResponseView) Error() string { return v.ж.Error }
|
||||||
|
|
||||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||||
var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct {
|
var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct {
|
||||||
@ -595,6 +596,7 @@ func (v RegisterResponseView) Error() string { return v.ж.Error }
|
|||||||
NodeKeyExpired bool
|
NodeKeyExpired bool
|
||||||
MachineAuthorized bool
|
MachineAuthorized bool
|
||||||
AuthURL string
|
AuthURL string
|
||||||
|
NodeKeySignature tkatype.MarshaledSignature
|
||||||
Error string
|
Error string
|
||||||
}{})
|
}{})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user