Files
Steffen Skui 9277db02a5
Build and push soc-collector / build (push) Successful in 7s
collector: tag CVEs yours/upstream/update + stop flagging the edge
cve: each image now carries an UPDATE STATUS in its title — [yours] (git.skui.io image,
fix in its repo), [update] (third-party, a NEWER image is published -> pull it), or
[upstream] (already running the latest published image -> clears only when the maintainer
rebuilds). 'Fixable' just means a patched package exists, not that you can apply it; the
status makes that explicit, and actionable findings now sort above the can't-touch ones.

config: the designated edge (SOC_EDGE, default 'traefik') is allowed to own 0.0.0.0:80/443
-- that is its job -- so it's reported as good rather than a false-positive finding.
2026-06-24 21:46:24 +02:00

68 lines
3.0 KiB
Python

"""Config-drift source: read-only Docker socket -> 0.0.0.0 binds, privileged, rw-socket mounts.
The designated edge proxy (SOC_EDGE, default 'traefik') is SUPPOSED to own 0.0.0.0:80/443 —
that's its whole job — so its public binds are reported as good, not flagged (false positive)."""
import os
from soc import finding
from . import _docker
# Containers that are the public edge: allowed to bind 0.0.0.0 (they front the estate).
EDGE = {x.strip() for x in os.environ.get("SOC_EDGE", "traefik").replace(";", ",").split(",") if x.strip()}
def run():
if not _docker.available():
return None # socket not mounted -> skip
conts = _docker.get("/containers/json") or []
findings, edge_ok = [], []
for c in conts:
name = (c.get("Names") or ["?"])[0].lstrip("/")
cid = c.get("Id", "")[:12]
try:
d = _docker.get(f"/containers/{cid}/json")
except Exception:
continue
hc = d.get("HostConfig", {}) or {}
# Ports published on all interfaces (0.0.0.0 / ::) — i.e. exposed off-host.
exposed = []
for port, binds in ((d.get("NetworkSettings", {}) or {}).get("Ports") or {}).items():
for b in (binds or []):
if b.get("HostIp") in ("0.0.0.0", "::", ""):
exposed.append(f"{b.get('HostIp') or '0.0.0.0'}:{b.get('HostPort')}->{port}")
if exposed and name in EDGE:
edge_ok.append(f"{name} ({'; '.join(exposed)})") # the edge — public by design
elif exposed:
findings.append(finding(
f"drift-bind-{name}", "high", f"{name} publishes ports on all interfaces",
host=name, evidence="; ".join(exposed),
impact=["reachable from outside the host, bypassing the reverse proxy"],
fix=["bind to 127.0.0.1, or drop host ports and route via the proxy"]))
if hc.get("Privileged"):
findings.append(finding(
f"drift-priv-{name}", "high", f"{name} runs --privileged",
host=name, evidence="Privileged=true",
impact=["a container escape becomes full host root"],
fix=["drop --privileged; grant only the specific capabilities needed"]))
for m in (d.get("Mounts") or []):
if m.get("Source") == "/var/run/docker.sock" and m.get("RW"):
findings.append(finding(
f"drift-sock-{name}", "high", f"{name} mounts the Docker socket read-write",
host=name, evidence="/var/run/docker.sock (rw)",
impact=["the container can control the host's Docker = host root"],
fix=["mount it :ro, or put a scoped socket-proxy in front"]))
good = [f"edge {e} binds 0.0.0.0:80/443 by design (allowed)" for e in edge_ok]
if not findings and not good:
good.append("No 0.0.0.0 binds, privileged, or rw-socket containers")
return {
"scope": f"{len(conts)} container(s)",
"findings": findings,
"good": good,
}