From 258f16f84b3713fde23b43affd5e0814584d3f35 Mon Sep 17 00:00:00 2001 From: Sonia Appasamy Date: Fri, 8 Sep 2023 12:04:54 -0400 Subject: [PATCH] ipn/ipnlocal: add tailnet MagicDNS name to ipn.LoginProfile Start backfilling MagicDNS suffixes on LoginProfiles. Updates #9286 Signed-off-by: Sonia Appasamy --- ipn/ipnlocal/local.go | 10 +++++----- ipn/ipnlocal/network-lock.go | 2 +- ipn/ipnlocal/network-lock_test.go | 18 +++++++++--------- ipn/ipnlocal/peerapi_test.go | 2 +- ipn/ipnlocal/profiles.go | 12 ++++++++++-- ipn/ipnlocal/profiles_test.go | 10 +++++----- ipn/ipnlocal/state_test.go | 2 +- ipn/prefs.go | 8 ++++++++ types/netmap/netmap.go | 3 +++ 9 files changed, 43 insertions(+), 24 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index d2000c6ac..df380cfe0 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1038,7 +1038,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control // Perform all mutations of prefs based on the netmap here. if prefsChanged { // Prefs will be written out if stale; this is not safe unless locked or cloned. - if err := b.pm.SetPrefs(prefs.View()); err != nil { + if err := b.pm.SetPrefs(prefs.View(), st.NetMap.MagicDNSSuffix()); err != nil { b.logf("Failed to save new controlclient state: %v", err) } } @@ -1089,7 +1089,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control b.mu.Lock() prefs.WantRunning = false p := prefs.View() - if err := b.pm.SetPrefs(p); err != nil { + if err := b.pm.SetPrefs(p, st.NetMap.MagicDNSSuffix()); err != nil { b.logf("Failed to save new controlclient state: %v", err) } b.mu.Unlock() @@ -1484,7 +1484,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error { newPrefs := opts.UpdatePrefs.Clone() newPrefs.Persist = oldPrefs.Persist().AsStruct() pv := newPrefs.View() - if err := b.pm.SetPrefs(pv); err != nil { + if err := b.pm.SetPrefs(pv, b.netMap.MagicDNSSuffix()); err != nil { b.logf("failed to save UpdatePrefs state: %v", err) } b.setAtomicValuesFromPrefsLocked(pv) @@ -2358,7 +2358,7 @@ func (b *LocalBackend) migrateStateLocked(prefs *ipn.Prefs) (err error) { // Backend owns the state, but frontend is trying to migrate // state into the backend. b.logf("importing frontend prefs into backend store; frontend prefs: %s", prefs.Pretty()) - if err := b.pm.SetPrefs(prefs.View()); err != nil { + if err := b.pm.SetPrefs(prefs.View(), b.netMap.MagicDNSSuffix()); err != nil { return fmt.Errorf("store.WriteState: %v", err) } } @@ -2906,7 +2906,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn } prefs := newp.View() - if err := b.pm.SetPrefs(prefs); err != nil { + if err := b.pm.SetPrefs(prefs, b.netMap.MagicDNSSuffix()); err != nil { b.logf("failed to save new controlclient state: %v", err) } b.lastProfileID = b.pm.CurrentProfile().ID diff --git a/ipn/ipnlocal/network-lock.go b/ipn/ipnlocal/network-lock.go index 6d5f9335f..11cebcca3 100644 --- a/ipn/ipnlocal/network-lock.go +++ b/ipn/ipnlocal/network-lock.go @@ -578,7 +578,7 @@ func (b *LocalBackend) NetworkLockForceLocalDisable() error { newPrefs := b.pm.CurrentPrefs().AsStruct().Clone() // .Persist should always be initialized here. newPrefs.Persist.DisallowedTKAStateIDs = append(newPrefs.Persist.DisallowedTKAStateIDs, stateID) - if err := b.pm.SetPrefs(newPrefs.View()); err != nil { + if err := b.pm.SetPrefs(newPrefs.View(), b.netMap.MagicDNSSuffix()); err != nil { return fmt.Errorf("saving prefs: %w", err) } diff --git a/ipn/ipnlocal/network-lock_test.go b/ipn/ipnlocal/network-lock_test.go index e1f002471..db5a7f0f6 100644 --- a/ipn/ipnlocal/network-lock_test.go +++ b/ipn/ipnlocal/network-lock_test.go @@ -151,7 +151,7 @@ func TestTKAEnablementFlow(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) b := LocalBackend{ capTailnetLock: true, varRoot: temp, @@ -191,7 +191,7 @@ func TestTKADisablementFlow(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) temp := t.TempDir() tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID)) @@ -383,7 +383,7 @@ func TestTKASync(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) // Setup the tka authority on the control plane. key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2} @@ -605,7 +605,7 @@ func TestTKADisable(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) temp := t.TempDir() tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID)) @@ -696,7 +696,7 @@ func TestTKASign(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) // Make a fake TKA authority, to seed local state. disablementSecret := bytes.Repeat([]byte{0xa5}, 32) @@ -785,7 +785,7 @@ func TestTKAForceDisable(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) temp := t.TempDir() tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID)) @@ -880,7 +880,7 @@ func TestTKAAffectedSigs(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) // Make a fake TKA authority, to seed local state. disablementSecret := bytes.Repeat([]byte{0xa5}, 32) @@ -1013,7 +1013,7 @@ func TestTKARecoverCompromisedKeyFlow(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: nlPriv, }, - }).View())) + }).View(), "")) // Make a fake TKA authority, to seed local state. disablementSecret := bytes.Repeat([]byte{0xa5}, 32) @@ -1104,7 +1104,7 @@ func TestTKARecoverCompromisedKeyFlow(t *testing.T) { PrivateNodeKey: nodePriv, NetworkLockKey: cosignPriv, }, - }).View())) + }).View(), "")) b := LocalBackend{ varRoot: temp, logf: t.Logf, diff --git a/ipn/ipnlocal/peerapi_test.go b/ipn/ipnlocal/peerapi_test.go index beae08e18..d6b4eafc9 100644 --- a/ipn/ipnlocal/peerapi_test.go +++ b/ipn/ipnlocal/peerapi_test.go @@ -660,7 +660,7 @@ func TestPeerAPIReplyToDNSQueries(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, - }).View()) + }).View(), "") if !h.ps.b.OfferingExitNode() { t.Fatal("unexpectedly not offering exit node") } diff --git a/ipn/ipnlocal/profiles.go b/ipn/ipnlocal/profiles.go index f99f8bee8..30f4c59f8 100644 --- a/ipn/ipnlocal/profiles.go +++ b/ipn/ipnlocal/profiles.go @@ -206,7 +206,12 @@ func init() { // SetPrefs sets the current profile's prefs to the provided value. // It also saves the prefs to the StateStore. It stores a copy of the // provided prefs, which may be accessed via CurrentPrefs. -func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView) error { +// +// If tailnetMagicDNSName is provided non-empty, it will be used to +// enrich the profile with the tailnet's MagicDNS name. The MagicDNS +// name cannot be pulled from prefsIn directly because it is not saved +// on ipn.Prefs (since it's not a field that is configurable by nodes). +func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, tailnetMagicDNSName string) error { prefs := prefsIn.AsStruct() newPersist := prefs.Persist if newPersist == nil || newPersist.NodeID == "" || newPersist.UserProfile.LoginName == "" { @@ -250,6 +255,9 @@ func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView) error { cp.ControlURL = prefs.ControlURL cp.UserProfile = newPersist.UserProfile cp.NodeID = newPersist.NodeID + if tailnetMagicDNSName != "" { + cp.TailnetMagicDNSName = tailnetMagicDNSName + } pm.knownProfiles[cp.ID] = cp pm.currentProfile = cp if err := pm.writeKnownProfiles(); err != nil { @@ -589,7 +597,7 @@ func (pm *profileManager) migrateFromLegacyPrefs() error { return fmt.Errorf("load legacy prefs: %w", err) } pm.dlogf("loaded legacy preferences; sentinel=%q", sentinel) - if err := pm.SetPrefs(prefs); err != nil { + if err := pm.SetPrefs(prefs, ""); err != nil { metricMigrationError.Add(1) return fmt.Errorf("migrating _daemon profile: %w", err) } diff --git a/ipn/ipnlocal/profiles_test.go b/ipn/ipnlocal/profiles_test.go index a75571fd4..fa5c89b63 100644 --- a/ipn/ipnlocal/profiles_test.go +++ b/ipn/ipnlocal/profiles_test.go @@ -41,7 +41,7 @@ func TestProfileCurrentUserSwitch(t *testing.T) { LoginName: loginName, }, } - if err := pm.SetPrefs(p.View()); err != nil { + if err := pm.SetPrefs(p.View(), ""); err != nil { t.Fatal(err) } return p.View() @@ -96,7 +96,7 @@ func TestProfileList(t *testing.T) { LoginName: loginName, }, } - if err := pm.SetPrefs(p.View()); err != nil { + if err := pm.SetPrefs(p.View(), ""); err != nil { t.Fatal(err) } return p.View() @@ -157,7 +157,7 @@ func TestProfileDupe(t *testing.T) { reauth := func(pm *profileManager, p *persist.Persist) { prefs := ipn.NewPrefs() prefs.Persist = p - must.Do(pm.SetPrefs(prefs.View())) + must.Do(pm.SetPrefs(prefs.View(), "")) } login := func(pm *profileManager, p *persist.Persist) { pm.NewProfile() @@ -379,7 +379,7 @@ func TestProfileManagement(t *testing.T) { }, NodeID: nid, } - if err := pm.SetPrefs(p.View()); err != nil { + if err := pm.SetPrefs(p.View(), ""); err != nil { t.Fatal(err) } return p.View() @@ -506,7 +506,7 @@ func TestProfileManagementWindows(t *testing.T) { }, NodeID: tailcfg.StableNodeID(strconv.Itoa(int(id))), } - if err := pm.SetPrefs(p.View()); err != nil { + if err := pm.SetPrefs(p.View(), ""); err != nil { t.Fatal(err) } return p.View() diff --git a/ipn/ipnlocal/state_test.go b/ipn/ipnlocal/state_test.go index 193c43909..0ac687bb5 100644 --- a/ipn/ipnlocal/state_test.go +++ b/ipn/ipnlocal/state_test.go @@ -923,7 +923,7 @@ func TestEditPrefsHasNoKeys(t *testing.T) { LegacyFrontendPrivateMachineKey: key.NewMachine(), }, - }).View()) + }).View(), "") if p := b.pm.CurrentPrefs().Persist(); !p.Valid() || p.PrivateNodeKey().IsZero() { t.Fatalf("PrivateNodeKey not set") } diff --git a/ipn/prefs.go b/ipn/prefs.go index b8589fe90..356359533 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -755,6 +755,14 @@ type LoginProfile struct { // It is filled in from the UserProfile.LoginName field. Name string + // TailnetMagicDNSName is filled with the MagicDNS suffix for this + // profile's node (even if MagicDNS isn't necessarily in use). + // It will neither start nor end with a period. + // + // TailnetMagicDNSName is only filled from 2023-09-09 forward, + // and will only get backfilled when a profile is the current profile. + TailnetMagicDNSName string + // Key is the StateKey under which the profile is stored. // It is assigned once at profile creation time and never changes. Key StateKey diff --git a/types/netmap/netmap.go b/types/netmap/netmap.go index a3570ff74..d8747d0dc 100644 --- a/types/netmap/netmap.go +++ b/types/netmap/netmap.go @@ -171,6 +171,9 @@ func MagicDNSSuffixOfNodeName(nodeName string) string { // // It will neither start nor end with a period. func (nm *NetworkMap) MagicDNSSuffix() string { + if nm == nil { + return "" + } return MagicDNSSuffixOfNodeName(nm.Name) }