🤖 PR generated for branch `fix/cyrillic-fonts-dev-certs-and-page-head`.
### Changes Summary
- **Fonts**: Add Cyrillic glyph subsets for Lato, EB Garamond, and Fira
Code.
- **Dev**: Support remote Root CA and encrypted dev CA key in
`.env.dev`.
- **UI & Kits**: Add size variant to `Breadcrumbs` component and enlarge
`PageHead` breadcrumbs with tuned collapse scroll threshold.
- **ESLint**: Add `no-hanging-particles` i18n & markdown typography rule
with auto-fixer.
- **Content & i18n**: Apply `ru-text` editorial and typography polish
(em dashes, number ranges, «ё» restoration, non-breaking spaces) across
`services/www` and `services/docs`.
4.8 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
Observability is an outbound push, not an exposed route. The optional alloy core service (Grafana Alloy, one agent per node) collects host metrics (a node_exporter-equivalent), container metrics (cadvisor), the systemd journal, and docker container logs, and pushes them to a central Prometheus and Loki that live on a designated node — the metrics via prometheus.remote_write to APEX_PROM_PUSH_URL, the logs via loki.write to APEX_LOKI_PUSH_URL. Both targets are authenticated with HTTP basic auth using APEX_OBS_BASIC_AUTH_USER / APEX_OBS_BASIC_AUTH_PASS. Because the push is outbound, Alloy attaches to the direct network (which has egress) rather than the internal enclave; there is no node-local log store and no /loki route to protect.
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: