bitenforcer
bitenforcer lit une politique de durcissement déclarative et répond complètement à une seule question :
« Si j'appliquais ceci à cette machine, qu'est-ce qui changerait exactement — et quel effet cela pourrait-il avoir sur mon accès ? »
| Version | 0.1.0-ga, commit 4c6ce93, compilée avec go1.25.12 |
| Plateformes | Linux amd64/arm64 uniquement — macOS et Windows ne sont pas publiés, et pourquoi |
| Chaîne d'approvisionnement | Compilation reproductible, SBOM CycloneDX, signature cosign, attestation du SBOM, SHA256SUMS signé |
| Forme | Outil en ligne de commande à exécution unique. Pas de démon, pas de télémétrie, pas de battement de cœur |
bitenforcer v1 livre validate et apply --dry-run. Il planifie chaque changement,
valide le contenu qu'il écrirait, exécute ses garde-fous anti-verrouillage — puis s'arrête.
Il n'écrit pas sur l'hôte, et aucune option ne l'y contraint.
C'est une décision de périmètre délibérée : ce n'est ni un bogue, ni une panne, ni un réglage qui vous manquerait. Pourquoi mérite dix minutes de votre temps avant de planifier un déploiement.
Les garde-fous eux-mêmes, et l'exposé complet des raisons pour lesquelles l'application est hors périmètre, se trouvent sur Les garde-fous, et pourquoi la v1 n'applique rien.
Ce qu'il fait
Pointez-le sur une politique YAML :
bitenforcer validate --config policy.yaml # is the policy well-formed?
bitenforcer apply --config policy.yaml --dry-run # what would it do to THIS host?
Le contrôle préalable nomme chaque fichier qu'il écrirait et les octets qu'il y écrirait, chaque unité systemd qu'il activerait, désactiverait, masquerait ou dans laquelle il déposerait un fragment de durcissement, chaque règle de pare-feu, chaque sysctl, chaque compte et chaque permission — dans l'ordre où ils seraient appliqués.
Là où un vrai validateur existe, le contenu candidat y est passé :
| Artefact | Validateur |
|---|---|
sshd_config | sshd -t -f <candidate> |
| sudoers | visudo -c |
| jeu de règles nftables | nft -c -f <candidate> |
| jeu de règles iptables | iptables-restore --test |
Ainsi, « cette politique produit un sshd_config que sshd rejettera » est détecté avant que quiconque en porte la responsabilité — et non à 3 h du matin quand le démon refuse de redémarrer.
Parallèlement, les garde-fous anti-verrouillage rapportent ce que votre politique ferait à votre propre accès, mesuré par rapport à la session dans laquelle vous êtes réellement en train de travailler.
Ce qu'il ne fera pas, c'est deviner. Un module dont les prérequis sont absents produit une
erreur au lieu de supposer. Une session qui ne peut pas être mesurée est rapportée comme non
mesurée, jamais comme « personne n'est en danger ». Un validateur qui ne peut pas s'exécuter
indique NOT VALIDATED fichier par fichier, avec la raison, plutôt que de laisser croire que
le contenu a été vérifié.
Sortie réelle d'un contrôle préalable
Il s'agit d'une exécution réelle du binaire 0.1.0-ga livré, contre le modèle standard, sur
un hôte Ubuntu ordinaire, en tant qu'utilisateur non root. Seule l'adresse IP de l'opérateur a
été remplacée par une adresse de documentation, et un très long bloc d'erreur nft a été
abrégé.
$ bitenforcer generate --template standard --output-file policy.yaml
Example configuration written to policy.yaml
$ bitenforcer validate --config policy.yaml
Configuration 'standard-hardening' is valid.
Environment : staging
Compliance : [CIS-L1]
A valid policy is not a safe one: run `bitenforcer apply --dry-run` to see what it
would change and which guards fire.
(FR) Politique valide. Une politique valide n'est pas pour autant sûre : utilisez
`apply --dry-run`.
$ bitenforcer apply --config policy.yaml --dry-run
=== PRE-FLIGHT: every change this run would make ===
operator "ubuntu" is on an SSH session whose peer socket was NOT measured
(SSH_CONNECTION/SSH_CLIENT in this process's environment (an inherited, settable
environment variable -- a hint, NOT a measured socket)); any address in the
environment is a hint, not evidence
NOT EXAMINED on this host (the rest of the pre-flight below is unaffected):
selinux: SELinux prerequisites check failed: SELinux tools not found
(getenforce not in PATH)
These modules were SKIPPED, not found compliant — this run says nothing
about whether the host matches that part of the policy.
(FR) Modules NON EXAMINÉS sur cet hôte : ignorés, et non déclarés conformes.
1. [command] users /etc/passwd
chmod 0644 /etc/passwd
2. [command] users /etc/passwd
chown root:root /etc/passwd
3. [command] users /etc/shadow
chmod 0640 /etc/shadow
4. [command] users /etc/shadow
chown root:root /etc/shadow
5. [command] users /etc/group
chmod 0644 /etc/group
6. [command] users /etc/group
chown root:root /etc/group
7. [command] users /etc/gshadow
chmod 0640 /etc/gshadow
8. [command] users /etc/gshadow
chown root:root /etc/gshadow
9. [file-write] ssh /etc/ssh/sshd_config
1194 bytes, mode 0600, validated with sshd -t -f <candidate>; NOT VALIDATED in
this dry run (the validator could not be run in this context: sshd -t needs root
to read the host keys (sshd: no hostkeys available -- exiting.)) — re-run the
dry run as root to validate
10. [file-write] firewall /etc/nftables.conf
566 bytes, mode 0600, validated with nft -c -f <candidate>; NOT VALIDATED in
this dry run (nft -c needs root […]) — re-run the dry run as root to validate
11. [file-write] kernel /etc/sysctl.d/99-bitenforcer.conf
672 bytes, mode 0644, validated with sysctl key syntax check
12. [command] kernel /etc/sysctl.d/99-bitenforcer.conf
sysctl -p /etc/sysctl.d/99-bitenforcer.conf
=== PRE-FLIGHT ADVISORIES (the guards; they did NOT block this run) ===
These are the lockout guards. They are advisory: they report the shapes they
recognise, and seven independent reviewers each found one they did not — which is
why this release does not apply anything. Treat a finding as a reason to change the
policy, never as the full list of ways it could strand you. Re-run with
--guards=blocking to have findings exit non-zero, e.g. to gate a policy in CI.
1. [kernel-remote-access-loss] this run would set net.ipv4.conf.all.rp_filter=1,
which drops any packet whose reply route does not leave by the interface it
arrived on
evidence: bitenforcer measures the interface carrying your session (eth0)
but not this host's routing table, so it cannot show that the route back
to 198.51.100.24 leaves by that same interface. On a host with two
uplinks, a VPN, or asymmetric routing, strict reverse-path filtering
drops your session and every future connection from that address
remedy: run `ip route get 198.51.100.24` and confirm it leaves by eth0;
if it does, say so for this one parameter, and keep a second session
open while you apply
override: re-run with --acknowledge kernel-remote-access-loss (this guard only)
2. [kernel-remote-access-loss] this run would set net.ipv4.ip_forward=0, and
bitenforcer cannot establish whether this host routes the network your session
arrives through
…
DRY RUN: nothing above was applied and this host was not changed.
This is the only mode bitenforcer v1 runs in; enforcement is not part of this release.
(FR) SIMULATION : rien n'a été appliqué et cet hôte n'a pas été modifié.
Trois éléments de cette sortie constituent tout le produit :
NOT EXAMINEDest une réponse, pas une omission. L'outillage SELinux est absent d'une Ubuntu standard : le module selinux est donc rapporté comme ignoré, et explicitement non déclaré conforme, et le reste du contrôle préalable se poursuit. Une exécution qui ne dit rien d'un contrôle est très différente d'une exécution qui déclare ce contrôle satisfaisant.NOT VALIDATEDnomme la raison, fichier par fichier.sshd -ta besoin des droits root pour lire les clés d'hôte : en tant qu'utilisateur ordinaire, l'outil vous dit donc que le contenu n'a pas été vérifié et ce qu'il faut faire pour y remédier — au lieu de laisser croire discrètement qu'il l'a été.- Le constat d'un garde-fou apporte une preuve, une remédiation et une dérogation propre à ce garde-fou. Il ne dit pas « risqué ». Il dit quel paramètre, ce qu'il a mesuré, ce qu'il n'a pas pu mesurer, et la commande exacte à exécuter pour trancher la question.
Ce que cela vous apporte aujourd'hui
Il est facile de lire « n'applique pas les changements » comme « ne fait rien ». Voici ce qu'un outil limité au contrôle préalable vous apporte réellement :
- Un artefact pour votre fenêtre de changement. Joignez la sortie du contrôle préalable à la demande de changement. Chaque fichier, unité, règle et sysctl, dans l'ordre, avec les octets.
- Un verrou en CI.
--guards=blockingfait échouer le pipeline lorsqu'un socle couperait l'accès, avant qu'il n'atteigne un hôte. - La détection précoce d'un contenu de politique invalide.
sshd -tetnft -csont exécutés contre le contenu candidat : une politique qui produit une configuration inanalysable est donc détectée dès la revue. - Une dérive sur laquelle agir, sans danger. Lancez la simulation sur tout un parc et lisez ce que chaque hôte rapporte comme devant changer — sans aucune possibilité de modifier quoi que ce soit par accident.
- Une carte de couverture honnête.
NOT EXAMINEDvous dit là où l'outil n'a rien à dire, pour que vous ne preniez pas le silence pour de la conformité.
La politique en tant que code
bitenforcer generate --template standard --output-file policy.yaml
Modèles : minimal, standard, strict. Ce sont des points de départ figés, à éditer — ils ne
lisent pas l'hôte et ne dérivent aucune posture de la machine sur laquelle vous les avez
exécutés. Les politiques générées font un aller-retour complet : redonnez la sortie à validate
et elle passe.
validate va plus loin qu'une vérification de schéma : une politique qui définit une clé que
rien n'implémente est refusée au chargement, nommément, avec l'énoncé de ce qui ne se
produira pas. Un réglage qui est analysé mais inerte est précisément le défaut que ces produits
ont passé un mois à retirer d'eux-mêmes.
validate lit le fichier de politique et rien d'autre — il ne regarde pas cet hôte. Exécutez
bitenforcer apply --config <file> --dry-run pour savoir ce qu'elle changerait ici.
Plateformes
0.1.0-ga publie deux binaires signés, et l'écart est délibéré :
| Plateforme | Publiée | Pourquoi |
|---|---|---|
Linux amd64 | Oui | |
Linux arm64 | Oui | |
macOS amd64/arm64 | Non | Retenue — voir ci-dessous |
Windows amd64 | Non | Retenue — voir ci-dessous |
bitenforcer est réservé à Linux parce que tout son sujet est Linux. Chaque module qu'il
planifie porte sur un mécanisme Linux : jeux de règles iptables/nftables, clés sysctl sous
/proc/sys, unités systemd, SELinux et AppArmor, ainsi que /etc/passwd, /etc/shadow,
/etc/gshadow et sudoers avec leurs modes et propriétaires POSIX. Il en va de même pour les
validateurs auxquels il soumet le contenu candidat — sshd -t, visudo -c, nft -c,
iptables-restore --test.
Une compilation macOS ou Windows fonctionnerait. C'est précisément le problème : elle
démarrerait, accepterait une politique et produirait un contrôle préalable dans lequel chaque
module serait NOT EXAMINED — un rapport qui ressemble à une exécution réussie et ne dit rien
de l'hôte. Puisque cet outil existe pour vous dire ce qu'un changement ferait avant que vous ne
le fassiez, un binaire dont la sortie honnête est « je n'ai rien à dire sur cette machine » est
pire que pas de binaire du tout. Il n'est pas publié, afin que personne ne le prenne pour une
couverture.
Si vous avez besoin de la posture de durcissement d'un hôte macOS, c'est le collecteur posture
de bitcollector, qui est livré sur macOS et rapporte, contrôle par
contrôle, s'il a été observé, non observé, ou non pris en charge par l'agent là-bas.
Étapes suivantes
- Les garde-fous, et pourquoi la v1 n'applique rien — les dix-huit garde-fous anti-verrouillage, le verrou en CI, et pourquoi le chemin d'application est délibérément absent de cette version.
- Vérifier vos téléchargements — à faire avant la première exécution.
- bitcollector — la moitié qui rapporte, et d'où viennent les preuves de posture en continu.
- Référentiels de conformité — comment les contrôles se mettent en correspondance.
Cette page vous a-t-elle été utile ?