Park Security – Architectural Note¶
Machine translation of the French original.
What this page says, and what it deliberately omits
It describes the security systems, their connections, and their flows. It does not provide any IP addresses, hostnames, account names, or service FQDNs. Hosts are identified by their role.
This choice is not mere politeness: even behind authentication, documentation pages eventually end up being copied into a ticket, screenshot, or clipboard. An architecture remains an architecture without its coordinates; a list of public FQDNs, on the other hand, is a ready-made attack map.
The coordinates reside in the private vault and the vault. See Read a secret from the vault.
The same rule applies to the sub-pages of this section, with an additional clause: no open vulnerabilities are listed there. They describe the architecture, the principles, and how they are verified; the current state – what is in progress, what is failing – resides in the private vault and on the compliance dashboard.
The Guiding Principle¶
The park does not have a single perimeter to defend. It has layers that fail independently: an attacker who breaches one must still breach the next, and each breach leaves a trace in a system that the previous layer does not control.
Four rules govern the whole:
- A single identity authority. No service should manage its own accounts if it can delegate. If there is a departure, a compromised password, or a second factor, there should be only one place to contact.
- What detects is not necessarily what executes. Logs from a host go elsewhere. A compromised host can lie about itself, but cannot erase what has already gone.
- Silence is a suspicious state. A probe that says nothing and a dead probe produce the same absence. Therefore, each detection chain should have a [functional witness](glossaire.md#temoin: not a counter that says "I am alive," but a known case, deliberately injected, that must be detected each time it passes.
- Prove, not declare. Each control is measured by a probe that has been seen to fail at least once; its verdict is entered into a record that cannot rewrite it so that it's not obvious, and the "compliant" status is calculated based on the evidence – it never actually assesses anything. See Compliance and evidence.
A quick overview of the section¶
The park's defense is organized into four functions, and a fifth that verifies them all:
flowchart LR
classDef p fill:#0f766e,stroke:#0f766e,color:#fff
classDef d fill:#7c2d12,stroke:#7c2d12,color:#fff
classDef r fill:#9a3412,stroke:#9a3412,color:#fff
classDef c fill:#1e40af,stroke:#1e40af,color:#fff
classDef g fill:#3f3f46,stroke:#3f3f46,color:#fff
prev["PRÉVENIR<br/>périmètre · identité · secrets<br/>chaîne d'approvisionnement · correctifs"]:::p
det["DÉTECTER<br/>SIEM · exécution · réseau<br/>fichiers · vulnérabilités"]:::d
rep["RÉPONDRE<br/>notification unique · réponse automatique<br/>investigation · registre d'incidents"]:::r
cont["SURVIVRE<br/>sauvegardes immuables · restauration chronométrée<br/>clé maîtresse hors ligne"]:::c
prouv["PROUVER<br/>catalogue de contrôles · sondes<br/>registre chaîné · ancre git"]:::g
prev --> det --> rep --> cont
prouv -.->|"mesure"| prev
prouv -.->|"mesure"| det
prouv -.->|"mesure"| rep
prouv -.->|"mesure"| cont
| Page | What you'll find |
|---|---|
| This page | The architecture: the layers, their connections, their flows – and prevention |
| Détection | Une question, un moteur : SIEM, exécution, réseau, fichiers, vulnérabilités — et leurs témoins |
| Réponse | Notification, réponse automatique bornée, investigation à chaud, registre d'incidents |
| Continuité | Niveaux de sauvegarde, immuabilité, exercice de restauration, clé maîtresse |
| Conformité et preuves | NIS2 et SOC 2 comme grille, contrôles, sondes, registre chaîné, audit |
| Glossaire | Chaque terme, et ce qu'il désigne dans ce parc |
Overview¶
flowchart TB
classDef edge fill:#0f766e,stroke:#0f766e,color:#fff
classDef det fill:#7c2d12,stroke:#7c2d12,color:#fff
classDef id fill:#1e40af,stroke:#1e40af,color:#fff
classDef data fill:#3f3f46,stroke:#3f3f46,color:#fff
client["Client Internet"]
subgraph perimetre["1 · Périmètre"]
dns["DNS public<br/>zone déléguée au registrar"]
nat["Routeur — NAT entrant<br/>un seul port ouvert"]
edge["Proxy de bordure<br/>terminaison TLS · sur le NAS"]:::edge
proxy["Proxy interne<br/>routage · en-têtes · journalisation"]:::edge
ips["CrowdSec<br/>analyse des journaux → 403"]:::edge
end
subgraph identite["2 · Identité"]
idp["Fournisseur d'identité<br/>OIDC · SAML · MFA"]:::id
fa["forward_auth<br/>mur, sans identité applicative"]:::id
end
subgraph prive["3 · Réseau privé"]
cp["Plan de contrôle VPN<br/>auto-hébergé sur le VPS"]:::edge
mesh["Maillage WireGuard<br/>chiffré de bout en bout"]:::edge
dnsf["Filtrage DNS<br/>listes + horizon dédoublé"]:::edge
end
subgraph contenu["4 · Contenu"]
av["Moteur antivirus central<br/>un seul daemon, protocole INSTREAM"]:::det
yara["Analyse statique profonde<br/>dépliage récursif · YARA"]:::det
quar["Quarantaine partagée<br/>sur le NAS"]:::data
end
subgraph detection["5 · Détection et réponse"]
siem["SIEM / XDR<br/>intégrité fichiers · CVE · MITRE"]:::det
fleet["Collecte journaux + métriques"]:::det
rt["Détection à l'exécution<br/>appels système · eBPF"]:::det
ids["IDS réseau<br/>signatures sur le trafic"]:::det
ar["Réponse automatique<br/>blocage à durée limitée"]:::det
hub["Répartiteur d'alertes"]:::det
chat["Messagerie d'équipe<br/>deux destinations"]:::det
dfir["Investigation à chaud<br/>client sur chaque machine"]:::det
end
subgraph socle["6 · Socle"]
coffre["Coffre de secrets"]:::data
sops["Secrets chiffrés au repos<br/>dans le dépôt"]:::data
backup["Dépôts de sauvegarde chiffrés<br/>ajout seul · copies hors site"]:::data
smart["Santé des disques"]:::data
preuve["Registre de preuves<br/>chaîné · ancré dans git"]:::data
end
client --> dns --> nat --> edge --> proxy
proxy --> ips
ips -->|"IP décidée"| proxy
proxy --> idp
proxy -.->|"dernier recours"| fa
idp --> proxy
mesh --- cp
mesh --- dnsf
av --> quar
yara --> quar
proxy --> siem
av --> siem
yara --> siem
fleet --> siem
siem --> hub --> chat
smart --> hub
rt -.->|"après mesure du bruit"| hub
ids -.->|"après mesure du bruit"| hub
siem -->|"identifiant de règle"| ar
chat -.->|"enquête"| dfir
coffre -.-> sops
backup -.-> quar
siem -.->|"sondes"| preuve
backup -.->|"sondes"| preuve
The diagram is read from top to bottom, in the order that a malicious request encounters the defenses. Layers 5 and 6 each have their own page: Detection, Response, Resilience and Compliance and evidence.
1 · Perimeter and Exposure¶
The Entry Point¶
A request originating from the Internet passes through four devices before reaching a container: the public resolver, the NAT router, a border proxy hosted on the NAS, and then an internal proxy on the main Docker host.
The WAN border is not the Docker host
This is the most counterintuitive aspect of the architecture, and the one that takes the most time to overlook. Two consequences:
- Any L3 filtering performed on the Docker host blocks nothing coming from the Internet: the source address it sees is that of the border proxy, never that of the client.
- Querying a service by its name from the LAN does not test the WAN path. The resolution is duplicated: the same name points elsewhere depending on where you are requesting from. Testing the WAN requires forcing the resolution to the public address.
Details of the complete path: Reverse proxy chain and DNS and resolution.
Certificates¶
Certificates are issued by ACME via DNS-01 challenge, through the registrar's API. This choice has a direct security consequence: no public A record is required to issue a certificate. A service can therefore be served over TLS on the LAN without its name ever being published in a public DNS zone — the transparency of the certificates remains public.
Application Detection and Remediation¶
CrowdSec reads the logs of the internal proxy, makes a decision, and enforces that decision through a module integrated into the proxy, which responds with a 403. It also uses a community-maintained blocklist.
It was deployed after a superficial public audit that identified 74 exposed names and no intrusion detection system. This is the only layer that acts on the content of an HTTP request; upstream, the NAT has no knowledge of HTTP.
What CrowdSec can't see
The internal proxy only sees the real client address if the edge proxy transmits it in a header, and if the internal proxy trusts that header. An incorrect configuration in this area does not cause any errors; it simply blocks the edge, or blocks no one. The symptom is a system that appears to be working.
2 · Identity and Access¶
A single identity provider (Authentik) manages accounts, groups, two-factor authentication, and sessions. Two connection modes coexist, and the order of preference is a rule, not a preference:
| Mode | What the service learns | When to use |
|---|---|---|
| Native OIDC / SAML | Identity, groups, roles, clean logout | By default, as soon as the service supports it |
forward_auth |
Nothing. A wall in front of the application | Last resort, for a service without any notion of accounts |
Never put both on the same service
The authentication proxy intercepts OAuth callback URLs before they reach the application. The result is a connection loop produced by three components that all appear to be working correctly — each does exactly its job. This is a flaw that does not show up in any error logs.
The local login for a service is only closed after a successful initial federated connection. The reverse order causes problems.
3 · Private Network¶
Hosts do not connect using their local network addresses when leaving the network. A WireGuard mesh connects them, with a self-hosted control plane running on the public VPS — thus, without any dependency on a third-party service for network membership.
This leads to two properties:
- A host outside the network speaks to the SIEM using the mesh address, never using a local address that does not exist for it. This is a common error during agent enrollment.
- The name of the control plane and the suffix of the machines must be two distinct domains. A client takes control of the suffix resolution: a control plane below it would become unreachable from a connected node. The server also refuses to start in this configuration.
DNS filtering (block lists, internal rewrites) serves the local network and doubles the resolution: the same name makes the internal address accessible within, and the public address accessible outside.
A device within the announced subnet
Activating route acceptance on a machine already located in the subnet that the mesh announces causes it to lose network access. The symptom resembles an internet outage; the cause is a learned route that covers the local route.
4 · Content: Antivirus and File Analysis¶
One engine, for a reason that is not about cost¶
The park uses a single antivirus daemon. The other machines do not install any binaries or signature databases; instead, they send the content over a dedicated channel (INSTREAM protocol) and receive a verdict.
Why just one, and why a private mirror?
The park has a single public IP address. Multiple update clients querying the signature distribution network from this address end up with rate limiting, followed by being blacklisted.
The symptom is not an error; it's a signature database that silently ages. An antivirus with a three-month-old database responds, responds quickly, and responds incorrectly.
Therefore, only one process from the park connects to the internet to retrieve signatures, twice a day, and serves them to everyone from an internal mirror. The others have their internet access cut off by configuration.
The client logs, not the server¶
In INSTREAM, the daemon only receives a stream of bytes: its own log indicates that a signature was found in "a stream," without a path or machine. The only entity that holds the complete information is the client. Therefore, it is the client that writes to the system log, in the exact format expected by the SIEM decoder.
Consequences for the perimeter
A central engine is a network service: it only sees what is sent to it. A host without a client is not "protected" – it is outside the perimeter. Coverage is therefore determined by the list of clients and their monitored zones, never by the daemon's status.
Detection: quarantine, not deletion¶
A file detected is moved to quarantine on a shared space of the NAS, with restricted access and retention. Deletion is possible, but it is not the root of the problem, for two reasons observed in this environment:
- False positives have already targeted healthy files (an ebook, application archives); deletion would have been futile;
- Some monitored areas are synchronized to other devices: moving a file from the monitored area will propagate its disappearance everywhere. This is intended for a real threat, but a false positive would be harmless.
Deep Static Analysis¶
Where an antivirus makes a judgment about a file, a second static analysis engine does what the antivirus does not: it recursively unpacks (archive within archive, document, embedded object, macro, executable) and runs dozens of scanners on each extracted object, including a YARA rule engine with thousands of rules. No code is executed: this is static analysis, not a sandbox.
A set of rules that doesn't load says nothing
The engine compiles its rule set into a single block. A single invalid rule invalidates everything. The container then appears to be in good health and no longer detects anything – a perfectly silent failure.
A counter of loaded rules is not sufficient to detect this: it tells you how many rules are in memory, but not whether they still recognize anything. The canary rule is placed among the others, and its triggering is required with each pass – it is what distinguishes "nothing to report" from "nothing works anymore". See Detection.
5 · The macOS Workstation¶
The macOS system is treated as a standard host within the network, not as an exception.
| Layer | Role |
|---|---|
| SIEM Agent | File integrity checks, configuration monitoring, reporting to management |
| Antivirus Client | Sending the content of risky zones to the central engine, on a schedule |
| Process-based Outbound Firewall | Little Snitch — each binary requests permission to connect |
| Persistence Enumerator | KnockKnock, connected to the central antivirus engine |
KnockKnock connected to the central engine¶
KnockKnock identifies everything that runs automatically on a Mac: startup agents and daemons, kernel extensions, authorization plugins, connection elements, and browser extensions. This is the question "What is running without my knowledge," which is exactly the question asked after a compromise.
Currently, the tool queries a public reputation service based on file fingerprints. The network uses a derivative version that sends the content to the internal antivirus engine instead:
- Nothing leaves the local network, neither fingerprints nor content;
- No API key is required, so there are no rate limits;
- The verdict is based on the actual content of the file, not what a third party has already seen.
A process-based outbound firewall makes failures silent
On this system, any new network tool is silent until a rule explicitly allows it, and the failure looks exactly like a network outage: the connection is held up waiting for a decision to be displayed in a window — which is invisible to a scheduled task or a remote session.
Therefore, a design rule: a task without an interface must go through a pre-authorized interpreter, never a newly installed binary. An interactive application, on the other hand, can open its own connection: someone is there to respond to the window.
Full disk access
The complete enumeration of persistence points requires the "full disk access" permission, granted by a binary and manually, in the system settings. No command can grant it remotely – it is a property of the operating system, not a limitation of the tool.
A partial audit is partial
The enumerator traverses a dozen categories in sequence. If a system call remains stuck in uninterrupted waiting – a state where the process no longer responds to either a stop request or a forced shutdown – then a single category can take over the entire audit. A scheduled task that "kills and restarts" then stacks a process at each iteration.
The rule that applies: a category that blocks is explicitly excluded, while the others continue to run, and the list of unvisited categories is placed in the notification body. An audit that has not been fully traversed is not a "green" audit – and "nothing detected" looks almost identical to "not checked".
6 · Detection, correlation, alerting¶
The SIEM¶
A SIEM/XDR manager collects agents from all hosts. It provides four things that raw logs do not:
- Monitoring of critical paths for integrity;
- Configuration checks against hardening baselines;
- Vulnerability detection through correlation between installed packages and CVE databases;
- Correlation and mapping on an attack technique matrix.
A second channel collects logs and metrics towards a separate search engine. The two do not answer the same question: one answers "Is this a known attack?", while the other answers "What happened, minute by minute."
Beyond the SIEM¶
The SIEM sees the state of the hosts. Three engines cover what it doesn't see: Falco monitors the system calls of containers during execution, Suricata applies signatures to network traffic, and Velociraptor allows querying the entire fleet during an incident. Their roles, their witnesses, and their design limitations are described in Detection and Response.
The path to detection¶
sequenceDiagram
autonumber
participant Z as Zone surveillée
participant C as Client de scan
participant M as Moteur antivirus
participant J as Journal système
participant A as Agent SIEM
participant S as Manager SIEM
participant H as Répartiteur d'alertes
participant D as Zulip + Telegram
Z->>C: fichier nouveau ou modifié
C->>M: contenu, en INSTREAM
M-->>C: verdict + signature
Note over C: le serveur ignore le chemin<br/>et la machine : c'est le client<br/>qui détient le contexte
C->>Z: mise en quarantaine
C->>J: ligne au format attendu par le décodeur
A->>J: lecture
A->>S: événement
S->>S: règle → niveau de sévérité
S->>H: intégration filtrée par identifiant de règle
H->>D: un envoi, deux destinations
Two traps in this chain, both silent
The integration filter. It targets a rule identifier, never a group. A filter per group ingests neighboring rules and posts duplicates on the same channel.
The severity level. It decides what goes through. A level that is too high makes two different paths post twice; a level that is too low silences the detection. This setting is verified by intentionally triggering a detection with a harmless test file, not by re-reading the configuration.
The notification¶
All alerts converge towards a single dispatcher: a sender sends once, and the dispatcher duplicates to the destinations. When a destination changes, only the channel map needs to be updated – no sender needs to be modified. The detail – and what the system does automatically when a rule dictates – is in Response.
An alert that repeats says nothing
Any recurring notification uses hysteresis and a duration. An alert that triggers every hour for three weeks is no longer an alert; it's just noise that the eye filters out, and it obscures the real one.
7 · Secrets¶
| Where | What | Property |
|---|---|---|
| Self-hosted vault | Passwords, tokens, keys, notes | Source of truth, unlocked by a dedicated service |
| Encrypted files in the repository | Environment variables of stacks | Encrypted at rest, versioned, and decryptable upon deployment |
| Pre-commit and pre-push analysis | Multiple secret detectors stacked on each diff, then the entire history in continuous integration | Blocks before the secret enters the history |
| Host scanning | Secrets remaining in plaintext on disks | Severity based on file permissions |
Three rules that cannot be negotiated:
- Environment files are neither read nor versioned in plaintext.
- A secret never passes through a URL. The error trace of a workflow engine logs the resolved URL, including tokens – and this trace survives the rotation of the secret.
- A value read from the vault passes through a pipe, never through the command line: it does not traverse the process table, the shell history, or the transcript of an assisted session.
Procedure: Read a secret from the vault.
8 · Supply Chain¶
This is the area that has changed the most in recent years, and one that no firewall can protect.
Quarantine of versions. No packages published within less than seven days are installed. This is the only defense that would have stopped the widespread distribution of compromised packages in recent months: they were all detected and removed within hours or a few days. The delay turns a race into a non-event.
The quarantine is set in three different places
The three package managers in the ecosystem have this setting, but with three units and three locations – one based on days, another on minutes, and the third on seconds; one global to the machine, and another only per repository.
Practical consequence: a repository can have no quarantine, and nothing will indicate it. The configuration file that sets it up is therefore part of the skeleton of a new project, just like the version pinning file.
Images frozen by fingerprint. Container images are pinned by their digest, not by a tag. A tag is a mutable pointer: stable today's tag is not stable tomorrow's, and nothing in the composition file indicates this.
Internal registry. Home-built images live in a private registry, and a public registry mirrors the private one – which eliminates as many direct dependencies on a third-party service at startup.
Known vulnerabilities and inventory. The running images and repositories are scanned daily, and each finding is categorized according to three axes – severity, confirmed exploitation, and exposure – rather than solely on its score. Each running image has its SBOM. Details: Detection.
9 · Patches and material exposure¶
Patches. Hosts follow an unsupervised patching rotation, spread out overnight with a random offset per host. Components whose patching restarts a critical service are deliberately excluded and handled manually.
A major version upgrade disables third-party repositories
This happened in this environment: a system upgrade disabled the third-party repositories, leaving three months without security updates on the affected components – without any visible errors. After any major upgrade, the presence of the repositories is explicitly verified.
Disks. The SMART status of all physical disks is tracked and alerts are generated before failure. A point of attention: not all manufacturer indicators speak the same language, and a "failed" status may simply be a misinterpreted mapping table. A replacement is decided based on a trend, never on a single reading.
Backups. Each host pushes a encrypted repository to the NAS, with retention, integrity verification, and restoration via navigation. The encryption is done by the client, before sending: the repository server does not hold the keys. The repository rejects any deletion from the hosts, copies exist offsite, and a real restoration is regularly timed: see Continuity.
10 · The flows, without addresses¶
| From | To | Channel | Object |
|---|---|---|---|
| Client Internet | Edge Proxy | HTTPS | All public requests |
| Edge Proxy | Internal Proxy | HTTPS | Application routing |
| Internal Proxy | Identity Provider | Internal HTTP | Authentication |
| Internal Proxy | CrowdSec | Local API | Blocking decision |
| Scanning Clients | Antivirus Engine | Dedicated TCP, IN-STREAM | Content to be analyzed |
| Update Process | Signature Distribution | Outgoing HTTPS | Only outgoing internet traffic from the antivirus chain |
| Agents | SIEM Manager | Encrypted agent channel | Events, integrity, inventory |
| Agents outside LAN | SIEM Manager | Same, via mesh | Same |
| SIEM Manager | Alert Distributor | Internal HTTP | Alerts filtered by rule ID |
| Distributor | Team Messaging | Outgoing HTTPS | Notification, two destinations |
| Hosts | Backup Server | Internal HTTPS | Data already encrypted |
| Hosts | Secret Vault | Internal HTTPS | Reading a secret, via a pipe |
| Runtime Detection | Local file, then distributor | Local, then Internal HTTP | System call events, notified after noise measurement |
| Traffic Convergence Point | Network IDS | Passive listening | Signatures applied during transit |
| Investigation Clients | Investigation Server | Encrypted channel, via mesh outside LAN | Investigation queries and results |
| Probe Launcher | Evidence Repository | Authenticated API | Dated verdicts |
| Evidence Repository | Two remote Git repositories | Outgoing HTTPS | Daily commit |
What this architecture does not cover¶
A design note that only lists its strengths is essentially a brochure. The structural limitations – those that depend on architectural choices, not the state of the day – are clearly stated:
- The NAS is both the WAN edge, the primary storage, and the backup repository. This is a single point of failure that is deliberately assumed, and it is also the most exposed machine. This is why the backup repository refuses to delete anything from the hosts, and why copies exist off-site.
- A single public IP address. The entire signature update chain depends on it, and a ban from the provider side degrades detection without any visible errors.
- No dynamic sandbox. File analysis is static. A binary that only reveals its behavior at runtime passes both engines.
- The antivirus coverage is a list of zones, not a host property. A newly created directory is not monitored until someone adds it.
- The macOS endpoint relies on manually granted permissions (full disk access, outbound firewall rules). A reinstall will lose all of them silently.
- CrowdSec operates on HTTP. A service exposed on another protocol is only covered by network filtering.
Next to read¶
- Detection, Response, Continuity, Compliance and Evidence – details of layers 5 and 6.
- Glossary – each term in this section.
- Reverse proxy chain – the detailed path of a request.
- DNS and resolution – the double-edged sword, and why testing from the LAN doesn't test anything.
- Publishing a new service – the procedure that applies the authentication rules described here.
- Patching a host – rotating patches, step by step.
- Reading a secret from the vault – without having it pass through a transcript.
Dernière vérification de cette page : 2026-09-11