mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-18 04:39:06 +02:00
[client] Make config reads pure and provision the identity explicitly
Reading a config wrote it back. profilemanager.readConfig persisted whatever apply() had filled in, and ReadConfig created and wrote the file outright when it was absent, so every reader was quietly a writer: a gate deciding whether to refuse a request, a UI listing profiles, a mobile getter reading one preference. The previous commit worked around that with a PeekConfig variant, which left two read functions with opposite side effects and the antipattern still there for everyone else. Only one thing in a read genuinely had to be persisted: apply() generated the WireGuard and SSH keys when it found them empty, and a generated key cannot be recomputed — losing it means the peer comes back with a different identity and registers again. Everything else apply() fills in is a deterministic default that the next read recomputes anyway. So identity provisioning is now its own step, Config.EnsureIdentity, and the callers that provision write the result out themselves, in the open: - Server.getConfig, the daemon's provisioning point; - the CLI's foreground login, which is about to dial management; - update() / directUpdate(), the config write paths — a stored profile can legitimately carry no identity, since a mobile logout clears the keys in place, and the next write is what has to mint a new one. ReadConfig and GetConfig no longer write anything, PeekConfig is gone, and the dry-run baseline no longer needs placeholder keys to keep apply() from minting real ones. One deliberate leftover: readConfig still calls util.EnforcePermission, which chmods a config file whose permissions are too broad. It changes no content and is idempotent, and dropping it would leave a legacy file world-readable until its first write.
This commit is contained in:
+20
-5
@@ -1162,10 +1162,9 @@ func (s *Server) storedLoginConfig(activeProf *profilemanager.ActiveProfileState
|
||||
// storedConfigAtPath reads a profile config file, yielding nil when it does not
|
||||
// exist yet.
|
||||
//
|
||||
// It peeks rather than reads: every caller here feeds a gate that may refuse
|
||||
// the request, and profilemanager.GetConfig writes the config back whenever it
|
||||
// has to fill in a default the file was missing. A refused request must leave
|
||||
// the profile file exactly as it found it.
|
||||
// Reading it has no side effect: profilemanager.GetConfig does not write, so a
|
||||
// request that the gates go on to refuse leaves the profile file as it found
|
||||
// it.
|
||||
func (s *Server) storedConfigAtPath(path string) (*profilemanager.Config, error) {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -1174,7 +1173,7 @@ func (s *Server) storedConfigAtPath(path string) (*profilemanager.Config, error)
|
||||
return nil, fmt.Errorf("stat profile config: %w", err)
|
||||
}
|
||||
|
||||
cfg, err := profilemanager.PeekConfig(path)
|
||||
cfg, err := profilemanager.GetConfig(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read profile config: %w", err)
|
||||
}
|
||||
@@ -1490,6 +1489,22 @@ func (s *Server) getConfig(activeProf *profilemanager.ActiveProfileState) (*prof
|
||||
return nil, false, fmt.Errorf("failed to get config: %w", err)
|
||||
}
|
||||
|
||||
// This is the daemon's provisioning point: the config resolved here is the
|
||||
// one the peer runs with, so it needs the keys that identify it, and those
|
||||
// have to reach disk — a key that stays in memory would come back different
|
||||
// on the next start and re-register the peer. Reads themselves are pure, so
|
||||
// the write is here, in the open, instead of hiding inside ReadConfig.
|
||||
generated, err := config.EnsureIdentity()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("ensure profile identity: %w", err)
|
||||
}
|
||||
|
||||
if generated || !configExisted {
|
||||
if err := profilemanager.WriteOutConfig(cfgPath, config); err != nil {
|
||||
return nil, false, fmt.Errorf("write out profile config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return config, configExisted, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -336,7 +336,7 @@ func TestLogin_ChangeThatAppearsMidRequestIsRefused(t *testing.T) {
|
||||
require.Equal(t, codes.Unavailable, gstatus.Code(err), "want the update-settings refusal, got %v", err)
|
||||
require.False(t, cancelled, "the refused login cancelled the login already in progress")
|
||||
|
||||
stored, err := profilemanager.PeekConfig(targetPath)
|
||||
stored, err := profilemanager.GetConfig(targetPath)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "https://mgmt.elsewhere.example:443", stored.ManagementURL.String(),
|
||||
"the refused login wrote the management URL it was asked for")
|
||||
|
||||
Reference in New Issue
Block a user