Skip to content

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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:

  1. Environment files are neither read nor versioned in plaintext.
  2. 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.
  3. 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

Dernière vérification de cette page : 2026-09-11