diff --git a/cmd/server/config.go b/cmd/server/config.go index bb5c4353..038a29cc 100644 --- a/cmd/server/config.go +++ b/cmd/server/config.go @@ -996,9 +996,15 @@ func SaveGeoFilter(configDir string, gf *GeoFilterConfig) error { } out = append(out, '\n') - // Atomic write: temp file + rename. + // Atomic write: temp file + rename. Keep the original file's mode: + // config.json holds the API key and broker passwords, and an operator + // who made it 0600 should not find it 0644 after a geo-filter save. + mode := os.FileMode(0644) + if fi, err := os.Stat(configPath); err == nil { + mode = fi.Mode().Perm() + } tmp := configPath + ".tmp" - if err := os.WriteFile(tmp, out, 0644); err != nil { + if err := os.WriteFile(tmp, out, mode); err != nil { return fmt.Errorf("write config: %w", err) } if err := os.Rename(tmp, configPath); err != nil { diff --git a/cmd/server/config_mode_test.go b/cmd/server/config_mode_test.go new file mode 100644 index 00000000..601ef765 --- /dev/null +++ b/cmd/server/config_mode_test.go @@ -0,0 +1,40 @@ +package main + +import ( + "os" + "path/filepath" + "testing" +) + +// SaveGeoFilter rewrites config.json through a temp file. The file holds the +// API key and broker passwords, so the rewrite must keep whatever mode the +// operator set rather than resetting it to 0644. +func TestSaveGeoFilterPreservesFileMode(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "config.json") + if err := os.WriteFile(path, []byte(`{"apiKey":"secret-key-that-is-long-enough"}`+"\n"), 0600); err != nil { + t.Fatal(err) + } + gf := &GeoFilterConfig{Polygon: [][2]float64{{0, 0}, {0, 1}, {1, 1}, {1, 0}}} + if err := SaveGeoFilter(dir, gf); err != nil { + t.Fatalf("SaveGeoFilter: %v", err) + } + fi, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if got := fi.Mode().Perm(); got != 0600 { + t.Fatalf("config.json mode after save = %o, want 0600", got) + } + // Default stays 0644 for a file that was 0644. + if err := os.Chmod(path, 0644); err != nil { + t.Fatal(err) + } + if err := SaveGeoFilter(dir, nil); err != nil { + t.Fatalf("SaveGeoFilter(nil): %v", err) + } + fi, _ = os.Stat(path) + if got := fi.Mode().Perm(); got != 0644 { + t.Fatalf("config.json mode after save = %o, want 0644", got) + } +}