fix: P2+P3 bug fixes, hardening, and cleanup (18 files)
Bug fixes: - Add applyEnvOverrides to LoadFromBytes (M05) - Set state=failed on compose-up failure in selfupdate (M16) - Clamp usableMB to min 0 in memory check (M22) - Remove "manual" schedule from triggerAllCrossBackups (M23) - Add mmcblk device handling for partition paths (M21) - Fix stripPartition for mmcblk devices (L25) - Fix TruncateStr for UTF-8 and negative maxLen (L05/L06) - Fix AllDone to return false for empty restore plans (L14) - Fix PushOnce to return actual errors (L39) - Restore pending events on save failure in DrainPendingEvents (M03) - Add duplicate check in AddStoragePath (M04) - Call CleanupTempMounts after drive scan (H13) - Log SetStep save errors (M25) Hardening: - Guard scheduler Start() against double-start (M14) - Acquire mutex in scheduler Stop() before reading cancel (L24) - Cap log lines parameter to 10000 (L31) - Require POST for logout (L32) - Use sync.Once for Server.Close() (L49) - Panic on crypto/rand.Read failure in setup CSRF (L40) - Validate Bearer token against Hub API key in CSRF (H16 fix) - Replace custom hasPrefix with strings.HasPrefix (L13) - Replace simpleHash with crc32.ChecksumIEEE (L48) Cleanup: - Remove dead imageName function (L02) - Remove dead detectHostIPViaRoute function (L03) - Rename shadowed copy variable to cp (L07) - Copy DefaultEnabledEvents in GetNotificationPrefs early return (L09) - Update BUGHUNT.md with comprehensive audit results Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,7 @@ const csrfFormField = "_csrf"
|
||||
func generateCSRFToken() string {
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
// Fallback to time-based (extremely unlikely)
|
||||
return "fallback-csrf-token"
|
||||
panic("crypto/rand.Read failed: " + err.Error())
|
||||
}
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package setup
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -11,46 +10,17 @@ import (
|
||||
// Inside a Docker container, the network interfaces only show the bridge IP
|
||||
// (e.g. 172.18.0.4), which is useless for users. Instead, we:
|
||||
// 1. Check HOST_IP env var (set by docker-compose.yml)
|
||||
// 2. Try to detect the Docker host gateway via `ip route`
|
||||
// 3. Fall back to interface enumeration as last resort
|
||||
// 2. Fall back to interface enumeration as last resort
|
||||
func DetectLocalIPs() []string {
|
||||
// Option 1: explicit HOST_IP from environment
|
||||
if hostIP := os.Getenv("HOST_IP"); hostIP != "" {
|
||||
return []string{hostIP}
|
||||
}
|
||||
|
||||
// Option 2: detect Docker host gateway IP via default route
|
||||
// Inside a container, `ip route | grep default` gives the host gateway.
|
||||
// Then we check the host's IP by looking at what IP routes to that gateway.
|
||||
if ip := detectHostIPViaRoute(); ip != "" {
|
||||
return []string{ip}
|
||||
}
|
||||
|
||||
// Option 3: fallback to interface enumeration (works on bare metal)
|
||||
// Option 2: fallback to interface enumeration (works on bare metal)
|
||||
return detectInterfaceIPs()
|
||||
}
|
||||
|
||||
// detectHostIPViaRoute tries to find the Docker host's LAN IP.
|
||||
// Inside a container, the default gateway is the Docker host.
|
||||
// We read /host-etc/hostname or use the gateway as a hint.
|
||||
func detectHostIPViaRoute() string {
|
||||
// Try: ip route get 1.0.0.0 — shows the source IP used for routing
|
||||
out, err := exec.Command("ip", "route", "get", "1.0.0.0").Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
// Output: "1.0.0.0 via 172.18.0.1 dev eth0 src 172.18.0.4"
|
||||
// The gateway (172.18.0.1) is the Docker host — but that's the bridge IP.
|
||||
// We need the host's actual LAN IP.
|
||||
|
||||
// Better approach: read /proc/net/route or parse `ip route` for the gateway,
|
||||
// then the gateway itself is the Docker host — but we need its external IP.
|
||||
// Since we can't easily get the host's LAN IP from inside the container,
|
||||
// return empty and let the fallback handle it or rely on HOST_IP env.
|
||||
_ = out
|
||||
return ""
|
||||
}
|
||||
|
||||
func detectInterfaceIPs() []string {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
|
||||
@@ -266,6 +266,11 @@ func countValid(results []DriveBackup) int {
|
||||
func (s *Server) runDriveScan() {
|
||||
results, err := ScanDrivesForInfraBackups(s.logger, s.isDebug())
|
||||
|
||||
// Clean up any temporary mounts created during scan
|
||||
if results != nil {
|
||||
CleanupTempMounts(results, s.logger)
|
||||
}
|
||||
|
||||
s.scanMu.Lock()
|
||||
defer s.scanMu.Unlock()
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ func (s *SetupState) SetStep(step string) {
|
||||
s.Step = step
|
||||
s.mu.Unlock()
|
||||
if err := s.Save(); err != nil {
|
||||
// Best effort — don't crash
|
||||
log.Printf("[WARN] Failed to save setup step %q: %v", step, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user