ipn/ipnlocal: refresh node key without blocking if cap enabled (#10529)

Updates tailscale/corp#16016

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
Co-authored-by: Maisem Ali <maisem@tailscale.com>
andrew/nixos-vm-tests
James 'zofrex' Sanderson 4 months ago committed by GitHub
parent 3a9450bc06
commit 10c595d962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -64,6 +64,11 @@ type Knobs struct {
// LinuxForceNfTables is whether the node should use nftables for Linux // LinuxForceNfTables is whether the node should use nftables for Linux
// netfiltering, unless overridden by the user. // netfiltering, unless overridden by the user.
LinuxForceNfTables atomic.Bool 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 // UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
@ -89,6 +94,7 @@ func (k *Knobs) UpdateFromNodeAttributes(selfNodeAttrs []tailcfg.NodeCapability,
silentDisco = has(tailcfg.NodeAttrSilentDisco) silentDisco = has(tailcfg.NodeAttrSilentDisco)
forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables) forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables) forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
) )
if has(tailcfg.NodeAttrOneCGNATEnable) { if has(tailcfg.NodeAttrOneCGNATEnable) {
@ -109,6 +115,7 @@ func (k *Knobs) UpdateFromNodeAttributes(selfNodeAttrs []tailcfg.NodeCapability,
k.SilentDisco.Store(silentDisco) k.SilentDisco.Store(silentDisco)
k.LinuxForceIPTables.Store(forceIPTables) k.LinuxForceIPTables.Store(forceIPTables)
k.LinuxForceNfTables.Store(forceNfTables) k.LinuxForceNfTables.Store(forceNfTables)
k.SeamlessKeyRenewal.Store(seamlessKeyRenewal)
} }
// AsDebugJSON returns k as something that can be marshalled with json.Marshal // AsDebugJSON returns k as something that can be marshalled with json.Marshal
@ -130,5 +137,6 @@ func (k *Knobs) AsDebugJSON() map[string]any {
"SilentDisco": k.SilentDisco.Load(), "SilentDisco": k.SilentDisco.Load(),
"LinuxForceIPTables": k.LinuxForceIPTables.Load(), "LinuxForceIPTables": k.LinuxForceIPTables.Load(),
"LinuxForceNfTables": k.LinuxForceNfTables.Load(), "LinuxForceNfTables": k.LinuxForceNfTables.Load(),
"SeamlessKeyRenewal": k.SeamlessKeyRenewal.Load(),
} }
} }

@ -1074,9 +1074,11 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
b.blockEngineUpdates(false) b.blockEngineUpdates(false)
} }
if st.LoginFinished() && wasBlocked { if st.LoginFinished() && (wasBlocked || b.seamlessRenewalEnabled()) {
// Auth completed, unblock the engine if wasBlocked {
b.blockEngineUpdates(false) // Auth completed, unblock the engine
b.blockEngineUpdates(false)
}
b.authReconfig() b.authReconfig()
b.send(ipn.Notify{LoginFinished: &empty.Message{}}) b.send(ipn.Notify{LoginFinished: &empty.Message{}})
} }
@ -1108,7 +1110,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
b.authURL = st.URL b.authURL = st.URL
b.authURLSticky = st.URL b.authURLSticky = st.URL
} }
if wasBlocked && st.LoginFinished() { if (wasBlocked || b.seamlessRenewalEnabled()) && st.LoginFinished() {
// Interactive login finished successfully (URL visited). // Interactive login finished successfully (URL visited).
// After an interactive login, the user always wants // After an interactive login, the user always wants
// WantRunning. // WantRunning.
@ -2456,8 +2458,10 @@ func (b *LocalBackend) popBrowserAuthNow() {
b.logf("popBrowserAuthNow: url=%v", url != "") b.logf("popBrowserAuthNow: url=%v", url != "")
b.blockEngineUpdates(true) if !b.seamlessRenewalEnabled() {
b.stopEngineAndWait() b.blockEngineUpdates(true)
b.stopEngineAndWait()
}
b.tellClientToBrowseToURL(url) b.tellClientToBrowseToURL(url)
if b.State() == ipn.Running { if b.State() == ipn.Running {
b.enterState(ipn.Starting) b.enterState(ipn.Starting)
@ -4176,6 +4180,9 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State) {
switch newState { switch newState {
case ipn.NeedsLogin: case ipn.NeedsLogin:
systemd.Status("Needs login: %s", authURL) systemd.Status("Needs login: %s", authURL)
if b.seamlessRenewalEnabled() {
break
}
b.blockEngineUpdates(true) b.blockEngineUpdates(true)
fallthrough fallthrough
case ipn.Stopped: case ipn.Stopped:
@ -5801,6 +5808,14 @@ func (b *LocalBackend) AdvertiseRoute(ipp netip.Prefix) error {
return err 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 ( var (
disallowedAddrs = []netip.Addr{ disallowedAddrs = []netip.Addr{
netip.MustParseAddr("::1"), netip.MustParseAddr("::1"),

@ -124,7 +124,8 @@ type CapabilityVersion int
// - 81: 2023-11-17: MapResponse.PacketFilters (incremental packet filter updates) // - 81: 2023-11-17: MapResponse.PacketFilters (incremental packet filter updates)
// - 82: 2023-12-01: Client understands NodeAttrLinuxMustUseIPTables, NodeAttrLinuxMustUseNfTables, c2n /netfilter-kind // - 82: 2023-12-01: Client understands NodeAttrLinuxMustUseIPTables, NodeAttrLinuxMustUseNfTables, c2n /netfilter-kind
// - 83: 2023-12-18: Client understands DefaultAutoUpdate // - 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 type StableID string
@ -2190,6 +2191,10 @@ const (
// netfilter management. // netfilter management.
// This cannot be set simultaneously with NodeAttrLinuxMustUseIPTables. // This cannot be set simultaneously with NodeAttrLinuxMustUseIPTables.
NodeAttrLinuxMustUseNfTables NodeCapability = "linux-netfilter?v=nftables" 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. // SetDNSRequest is a request to add a DNS record.

Loading…
Cancel
Save