0.28.0 — operational hardening (plan D1–D5): CI deploy gate, versioned images, rate limiting, resource limits, health probes
D1: deploy.sh CI gate — read-only SHA via git ls-remote, Gitea commit-status
poll, portable mkdir deploy lock (macOS, no flock), TOCTOU guard, token
passed via curl --config - (not argv), graceful misconfig tolerance.
D2: version-tagged images — OIKOS_VERSION=v$VERSION, keep-last-3 prune derived
from 'docker compose config --images'; VERSION read after pull.
D3: per-IP rate limiting — new internal/httpapi/ratelimit.go (x/time/rate),
rightmost-XFF, /healthz exempt, ctx-driven sweep; disabled by default.
D4: mem_limit/cpus on all 10 compose services.
D5: staleness-aware health probes — new internal/health package wired into
scheduler (:8093) and notifier (:8094); nomos already had :8092.
Two /review passes hardened the deploy lock, TOCTOU guard, token hygiene,
and XFF handling.
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/dtoro/oikos/internal/config"
|
||||
"github.com/dtoro/oikos/internal/db"
|
||||
"github.com/dtoro/oikos/internal/db/sqlcgen"
|
||||
"github.com/dtoro/oikos/internal/health"
|
||||
"github.com/dtoro/oikos/internal/observability"
|
||||
"github.com/dtoro/oikos/internal/remote"
|
||||
"github.com/google/uuid"
|
||||
@@ -48,11 +49,22 @@ func Run(ctx context.Context, pool *db.Pool, cfg config.Config) {
|
||||
sshUser = "root"
|
||||
}
|
||||
|
||||
// Liveness probe (plan D5): staleness is 3x the interval so a single
|
||||
// slow check pass (one host hung on SSH) doesn't flap the container
|
||||
// unhealthy before the next scheduled tick.
|
||||
stale := 3 * interval
|
||||
if stale < 90*time.Second {
|
||||
stale = 90 * time.Second
|
||||
}
|
||||
probe := health.New(stale)
|
||||
probe.Serve(ctx, cfg.HealthListen)
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Immediate first pass
|
||||
runCheckPass(ctx, pool)
|
||||
probe.Bump()
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -61,6 +73,7 @@ func Run(ctx context.Context, pool *db.Pool, cfg config.Config) {
|
||||
return
|
||||
case <-ticker.C:
|
||||
runCheckPass(ctx, pool)
|
||||
probe.Bump()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -586,7 +599,7 @@ func checkDisk(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) checkRes
|
||||
// checkCertExpiry checks TLS certificate expiry.
|
||||
func checkCertExpiry(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) checkResult {
|
||||
cfg := struct {
|
||||
Host string `json:"host"`
|
||||
Host string `json:"host"`
|
||||
// Dial is an optional explicit dial address (the TLS terminator's IP)
|
||||
// for when the hostname doesn't resolve/reach from the scheduler — the
|
||||
// container has no mesh interface and the host resolver doesn't know
|
||||
|
||||
Reference in New Issue
Block a user