Skip to content

Commit

Permalink
ipn/ipnlocal: refresh node key without blocking if cap enabled (tails…
Browse files Browse the repository at this point in the history
…cale#10529)

Updates tailscale/corp#16016

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
Co-authored-by: Maisem Ali <maisem@tailscale.com>
  • Loading branch information
zofrex and maisem committed Jan 4, 2024
1 parent 3a9450b commit 10c595d
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 7 deletions.
8 changes: 8 additions & 0 deletions control/controlknobs/controlknobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ type Knobs struct {
// LinuxForceNfTables is whether the node should use nftables for Linux
// netfiltering, unless overridden by the user.
LinuxForceNfTables atomic.Bool

// SeamlessKeyRenewal is whether to enable the alpha functionality of
// renewing node keys without breaking connections.
// http://go/seamless-key-renewal
SeamlessKeyRenewal atomic.Bool
}

// UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
Expand All @@ -89,6 +94,7 @@ func (k *Knobs) UpdateFromNodeAttributes(selfNodeAttrs []tailcfg.NodeCapability,
silentDisco = has(tailcfg.NodeAttrSilentDisco)
forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
)

if has(tailcfg.NodeAttrOneCGNATEnable) {
Expand All @@ -109,6 +115,7 @@ func (k *Knobs) UpdateFromNodeAttributes(selfNodeAttrs []tailcfg.NodeCapability,
k.SilentDisco.Store(silentDisco)
k.LinuxForceIPTables.Store(forceIPTables)
k.LinuxForceNfTables.Store(forceNfTables)
k.SeamlessKeyRenewal.Store(seamlessKeyRenewal)
}

// AsDebugJSON returns k as something that can be marshalled with json.Marshal
Expand All @@ -130,5 +137,6 @@ func (k *Knobs) AsDebugJSON() map[string]any {
"SilentDisco": k.SilentDisco.Load(),
"LinuxForceIPTables": k.LinuxForceIPTables.Load(),
"LinuxForceNfTables": k.LinuxForceNfTables.Load(),
"SeamlessKeyRenewal": k.SeamlessKeyRenewal.Load(),
}
}
27 changes: 21 additions & 6 deletions ipn/ipnlocal/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -1074,9 +1074,11 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
b.blockEngineUpdates(false)
}

if st.LoginFinished() && wasBlocked {
// Auth completed, unblock the engine
b.blockEngineUpdates(false)
if st.LoginFinished() && (wasBlocked || b.seamlessRenewalEnabled()) {
if wasBlocked {
// Auth completed, unblock the engine
b.blockEngineUpdates(false)
}
b.authReconfig()
b.send(ipn.Notify{LoginFinished: &empty.Message{}})
}
Expand Down Expand Up @@ -1108,7 +1110,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
b.authURL = st.URL
b.authURLSticky = st.URL
}
if wasBlocked && st.LoginFinished() {
if (wasBlocked || b.seamlessRenewalEnabled()) && st.LoginFinished() {
// Interactive login finished successfully (URL visited).
// After an interactive login, the user always wants
// WantRunning.
Expand Down Expand Up @@ -2456,8 +2458,10 @@ func (b *LocalBackend) popBrowserAuthNow() {

b.logf("popBrowserAuthNow: url=%v", url != "")

b.blockEngineUpdates(true)
b.stopEngineAndWait()
if !b.seamlessRenewalEnabled() {
b.blockEngineUpdates(true)
b.stopEngineAndWait()
}
b.tellClientToBrowseToURL(url)
if b.State() == ipn.Running {
b.enterState(ipn.Starting)
Expand Down Expand Up @@ -4176,6 +4180,9 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State) {
switch newState {
case ipn.NeedsLogin:
systemd.Status("Needs login: %s", authURL)
if b.seamlessRenewalEnabled() {
break
}
b.blockEngineUpdates(true)
fallthrough
case ipn.Stopped:
Expand Down Expand Up @@ -5801,6 +5808,14 @@ func (b *LocalBackend) AdvertiseRoute(ipp netip.Prefix) error {
return err
}

// seamlessRenewalEnabled reports whether seamless key renewals are enabled
// (i.e. we saw our self node with the SeamlessKeyRenewal attr in a netmap).
// This enables beta functionality of renewing node keys without breaking
// connections.
func (b *LocalBackend) seamlessRenewalEnabled() bool {
return b.ControlKnobs().SeamlessKeyRenewal.Load()
}

var (
disallowedAddrs = []netip.Addr{
netip.MustParseAddr("::1"),
Expand Down
7 changes: 6 additions & 1 deletion tailcfg/tailcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ type CapabilityVersion int
// - 81: 2023-11-17: MapResponse.PacketFilters (incremental packet filter updates)
// - 82: 2023-12-01: Client understands NodeAttrLinuxMustUseIPTables, NodeAttrLinuxMustUseNfTables, c2n /netfilter-kind
// - 83: 2023-12-18: Client understands DefaultAutoUpdate
const CurrentCapabilityVersion CapabilityVersion = 83
// - 84: 2024-01-04: Client understands SeamlessKeyRenewal
const CurrentCapabilityVersion CapabilityVersion = 84

type StableID string

Expand Down Expand Up @@ -2190,6 +2191,10 @@ const (
// netfilter management.
// This cannot be set simultaneously with NodeAttrLinuxMustUseIPTables.
NodeAttrLinuxMustUseNfTables NodeCapability = "linux-netfilter?v=nftables"

// NodeAttrSeamlessKeyRenewal makes clients enable beta functionality
// of renewing node keys without breaking connections.
NodeAttrSeamlessKeyRenewal NodeCapability = "seamless-key-renewal"
)

// SetDNSRequest is a request to add a DNS record.
Expand Down

0 comments on commit 10c595d

Please sign in to comment.