4.4 KiB
title, description, weight
| title | description | weight |
|---|---|---|
| Security model | Privilege isolation, firewall management, intrusion detection, and secret handling. | 18 |
apex hardens a node in independent layers: filesystem privilege isolation, a managed host firewall, intrusion detection, container-level containment, and a strict secrets boundary. Each layer is applied by an action and is therefore reproducible and re-runnable.
Privilege isolation
Every project runs as its own locked noroot-<project> system user; shared storage is group-writable with the sticky bit so projects can exchange files without being able to delete each other's. The full mechanism — users, groups, and the ownership matrix — is described in Concept: Tiered storage.
Host firewall
apex configure/ufw applies the node's firewall from the configs/ufw/ pillar in three parts:
- Host rules (
configs/ufw/host/host.rules) — plainufwcommands, one per line, applied in order. - Kernel-level rule blocks (
configs/ufw/host/before.rules,after.rules) — injected into/etc/ufw/before.rulesand/etc/ufw/after.rulesas managed marker blocks. Injection is self-healing: every previous managed block — including blocks left by predecessor tooling — is removed before the fresh block is inserted after ufw's# End required linesanchor, and a timestamped.bakof the target is kept beside it. - Container rules (
configs/ufw/docker/*.rules) — applied throughufw-docker, which translates them into rules that survive Docker's own iptables manipulation.configure/baseinstalls the helper to~/.local/bin/ufw-docker.
Missing configuration is an error, not a silent skip: an absent docker/ or host/ directory exits 66 when its counterpart exists, and a missing /etc/ufw target file aborts the run. The action finishes with a reminder that ufw must be restarted to take effect — and that the SSH port must be allowed first.
Intrusion detection
apex configure/crowdsec installs CrowdSec through its official installer when cscli is absent, renders everything under configs/crowdsec/ into /etc/crowdsec/ (acquisition sources, parsers, bouncer configuration), and installs the crowdsecurity/linux and crowdsecurity/traefik collections. When the node's configs include a firewall-bouncer configuration, the matching bouncer package is installed as well, closing the loop from detection to blocking.
Container containment
The core composition encodes container-level hardening:
- The edge proxy never touches the Docker socket: it discovers backends through a filtered socket proxy that exposes read-only container/service state on an isolated network.
enclaveandsocketareinternal: truenetworks — no egress, no host port exposure.- Core services set
no-new-privilegesand run under the project's noroot identity wherever the workload allows; capabilities are granted per service, not globally.
Observability
The optional alloy service sends host and container metrics to
APEX_PROM_PUSH_URL and logs to APEX_LOKI_PUSH_URL. Configure compatible
endpoints and HTTP basic-auth credentials through APEX_OBS_BASIC_AUTH_USER
and APEX_OBS_BASIC_AUTH_PASS. The service requires outbound connectivity to
those endpoints; their hosting and access policy are deployment choices.
Secrets boundary
- Secrets live only in per-project
.envfiles, which are symlinks into tier-1 storage — outside the repository working tree and covered by.gitignore(**/.env). - Each node repository documents its expected keys in
SECRETS.md; the file documents names, never values. - Committed configuration (
apex.env) is restricted to non-secret scalars. - The shared composition marks critical secrets with
?error, so a missing value fails fast at compose-config time. - Capture-up commits are safe by construction: the ignored
.envlinks and tier data never enter the git index.
See also: