Implement phase 1 and 2 review actions (DMARC UI CSS fixes, bounded DMARC ingest stdin, rate-limit eviction hardening, settings/user handler cleanup, shared atomic/supervisor helpers) and phase 3 documentation/changelog updates including roadmap and accepted-risk notes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,10 +7,11 @@ package postfix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/mixeme/selfpost/internal/supervisor"
|
||||
)
|
||||
|
||||
// Postfix manages the on-disk Postfix state the panel is responsible for. After
|
||||
@@ -154,17 +155,5 @@ func assertMapSafe(address, login string) error {
|
||||
// Arguments are fixed literals — no user input is interpolated into the command,
|
||||
// and it never goes through a shell (security.md).
|
||||
func reloadViaSupervisor() error {
|
||||
cmd := exec.Command("supervisorctl",
|
||||
"-c", "/etc/supervisor/supervisord.conf",
|
||||
"start", "postfix-reload")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
// A reload already in flight is not a failure: that pending run reloads
|
||||
// Postfix after our file is in place (the file is written before this).
|
||||
if strings.Contains(string(out), "already started") {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("reload postfix via supervisor: %w: %s", err, strings.TrimSpace(string(out)))
|
||||
}
|
||||
return nil
|
||||
return supervisor.Start("postfix-reload")
|
||||
}
|
||||
|
||||
@@ -1,47 +1,14 @@
|
||||
package postfix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mixeme/selfpost/internal/atomicfile"
|
||||
)
|
||||
|
||||
// writeFileAtomic writes data to path via a temp file in the same directory
|
||||
// followed by a rename, so a concurrent Postfix reload only ever sees the
|
||||
// complete old or new map, never a partial write (security.md). It mirrors the
|
||||
// same primitive used for the OpenDKIM tables.
|
||||
// complete old or new map, never a partial write (security.md).
|
||||
func writeFileAtomic(path string, data []byte, perm os.FileMode) error {
|
||||
dir := filepath.Dir(path)
|
||||
tmp, err := os.CreateTemp(dir, ".tmp-*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("create temp in %s: %w", dir, err)
|
||||
}
|
||||
tmpName := tmp.Name()
|
||||
cleanup := true
|
||||
defer func() {
|
||||
if cleanup {
|
||||
_ = os.Remove(tmpName)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := tmp.Write(data); err != nil {
|
||||
tmp.Close()
|
||||
return fmt.Errorf("write temp %s: %w", tmpName, err)
|
||||
}
|
||||
if err := tmp.Chmod(perm); err != nil {
|
||||
tmp.Close()
|
||||
return fmt.Errorf("chmod temp %s: %w", tmpName, err)
|
||||
}
|
||||
if err := tmp.Sync(); err != nil {
|
||||
tmp.Close()
|
||||
return fmt.Errorf("sync temp %s: %w", tmpName, err)
|
||||
}
|
||||
if err := tmp.Close(); err != nil {
|
||||
return fmt.Errorf("close temp %s: %w", tmpName, err)
|
||||
}
|
||||
if err := os.Rename(tmpName, path); err != nil {
|
||||
return fmt.Errorf("rename %s -> %s: %w", tmpName, path, err)
|
||||
}
|
||||
cleanup = false
|
||||
return nil
|
||||
return atomicfile.Write(path, data, perm)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user