Security hardening: input validation, secrets protection, NATS auth

Config injection prevention:
- Add FQDN validation for domain names (RFC 1123) in Register/Update —
  rejects newlines, spaces, shell metacharacters that could inject into
  HAProxy/dnsmasq configs
- Add backend address validation (valid host:port format, valid IP or
  hostname, port 1-65535). DNS-only backends allow bare IPs.
- Add header key/value validation — keys must be HTTP token chars only,
  values must not contain newlines or NULs
- Add WireGuard peer name validation (alphanumeric + hyphens + underscores)
- Add defense-in-depth domain validation in certbot Provision()

Secrets protection:
- Remove ?raw=true bypass on GET /api/v1/secrets — secrets are now always
  redacted in API responses regardless of query parameters
- Update test to verify redaction cannot be bypassed

NATS authentication:
- Generate random auth token on first startup, store in secrets.yaml
- Pass token to embedded NATS server via Authorization option
- Internal client connects with the same token
- External NATS clients (Wild Cloud) must now authenticate

Security headers:
- Add X-Content-Type-Options: nosniff
- Add X-Frame-Options: DENY
- Add Cache-Control: no-store
This commit is contained in:
2026-07-14 12:38:17 +00:00
parent 4500a1a45e
commit 60f3ca4a3a
8 changed files with 149 additions and 16 deletions

15
main.go
View File

@@ -19,6 +19,7 @@ import (
"github.com/wild-cloud/wild-central/internal/frontend"
"github.com/wild-cloud/wild-central/internal/logging"
"github.com/wild-cloud/wild-central/internal/natsbus"
"github.com/wild-cloud/wild-central/internal/secrets"
)
var startTime time.Time
@@ -99,9 +100,19 @@ func main() {
fmt.Sscanf(v, "%d", &natsPort)
}
// Load or generate NATS auth token from secrets
secretsMgr := secrets.NewManager(filepath.Join(dataDir, "secrets.yaml"))
natsToken, _ := secretsMgr.GetSecret("nats.authToken")
if natsToken == "" {
natsToken, _ = secrets.GenerateSecret(32)
_ = secretsMgr.SetSecret("nats.authToken", natsToken)
slog.Info("generated NATS auth token", "component", "startup")
}
natsSrv, err := natsbus.Start(natsbus.Config{
Port: natsPort,
DataDir: natsDataDir,
Port: natsPort,
DataDir: natsDataDir,
AuthToken: natsToken,
})
if err != nil {
slog.Error("failed to start NATS server", "error", err)