first commit

This commit is contained in:
Nuxgrid
2026-07-22 21:01:19 +02:00
commit 8bb06f3737
57 changed files with 3529 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.claude/settings.local.json
+10
View File
@@ -0,0 +1,10 @@
[defaults]
inventory = inventory/host.ini
host_key_checking = False
interpreter_python = auto_silent
vars = groups_vars/
[privilege_escalation]
become = True
become_method = sudo
become_user = root
+75
View File
@@ -0,0 +1,75 @@
---
# Durcissement CIS Ubuntu 24.04 — AIDE (integrite fichiers) installe + verification quotidienne
# (checks 35758, 35759)
#
# IMPORTANT : /var/lib/docker (images/layers/volumes), les donnees de bases de donnees
# (postgresql, redis), wazuh-indexer, clamav et /var/log/audit sont exclus AVANT le premier
# aideinit — sans ca, le scan initial serait tres long et generait des faux positifs
# quotidiens (ces donnees changent en permanence par nature, leur integrite est geree par
# leurs propres mecanismes, pas par un FIM au niveau fichier).
#
# `aideinit` peut prendre plusieurs minutes (hash de la quasi-totalite du systeme de
# fichiers) — pas de reboot necessaire, mais consommation CPU/IO notable pendant le scan.
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-aide-hardening.yml --limit pre-prod-uber
- name: Durcissement AIDE - integrite fichiers (CIS Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
- name: Installer aide et aide-common
ansible.builtin.apt:
name:
- aide
- aide-common
state: present
update_cache: true
cache_valid_time: 3600
- name: Deployer les exclusions specifiques infra (avant le premier scan)
ansible.builtin.template:
src: ../templates/72_aide_infra_excludes.j2
dest: /etc/aide/aide.conf.d/72_aide_infra_excludes
owner: root
group: root
mode: "0644"
- name: Verifier si une base AIDE existe deja
ansible.builtin.stat:
path: /var/lib/aide/aide.db
register: aide_db
- name: Initialiser la base AIDE (aideinit) — peut prendre plusieurs minutes
ansible.builtin.command:
cmd: aideinit -y -f
when: not aide_db.stat.exists
changed_when: true
- name: Activer et demarrer la verification quotidienne (dailyaidecheck.timer)
ansible.builtin.systemd:
name: "{{ item }}"
masked: false
enabled: true
state: started
loop:
- dailyaidecheck.service
- dailyaidecheck.timer
- name: Verifier l'etat final
ansible.builtin.command:
cmd: systemctl show dailyaidecheck.timer -p LoadState,ActiveState
register: aide_timer_check
changed_when: false
failed_when: >
'LoadState=loaded' not in aide_timer_check.stdout or
'ActiveState=active' not in aide_timer_check.stdout
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: "✅ AIDE installé et vérification quotidienne active sur {{ inventory_hostname }}."
+83
View File
@@ -0,0 +1,83 @@
---
# Durcissement CIS Ubuntu 24.04 — AppArmor installe, active au boot, tous les profils enforcing
# (checks 35536-35539)
#
# CONTEXTE (verifie manuellement avant deploiement) : sur cette infra, apparmor_status montre
# des dizaines de profils en mode "unconfined"/"complain" qui correspondent tous a des paquets
# desktop jamais utilises sur des serveurs headless (navigateurs, Discord, Steam, transmission,
# runtimes conteneurs standalone comme runc/crun/podman non utilises directement — Docker confine
# deja ses containers via le profil "docker-default", separe et deja enforce). Aucun process
# reellement actif n'est concerne par le passage en enforce de ces profils inutilises.
#
# IMPORTANT GRUB : /etc/default/grub contient deja "audit=1 audit_backlog_limit=8192"
# (playbook-coredump-auditd-hardening.yml) — ce playbook AJOUTE apparmor=1 security=apparmor
# a la suite, ne remplace jamais la ligne existante.
#
# `aa-enforce /etc/apparmor.d/*` s'applique a chaud (pas de redemarrage necessaire pour le
# check CIS lui-meme), mais comme pour audit=1, le parametre GRUB apparmor=1/security=apparmor
# necessite un redemarrage pour etre reellement actif au niveau noyau (le check 35537 passe
# immediatement car il inspecte /etc/default/grub, pas l'etat runtime).
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-apparmor-hardening.yml --limit pre-prod-uber
- name: Durcissement AppArmor (CIS Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
- name: Installer apparmor et apparmor-utils
ansible.builtin.apt:
name:
- apparmor
- apparmor-utils
state: present
update_cache: true
cache_valid_time: 3600
- name: Sauvegarder /etc/default/grub avant modification
ansible.builtin.copy:
src: /etc/default/grub
dest: /etc/default/grub.bak-cis-apparmor
remote_src: true
owner: root
group: root
mode: "0644"
- name: Ajouter apparmor=1 security=apparmor a GRUB_CMDLINE_LINUX (sans ecraser audit=1 existant)
ansible.builtin.shell: |
set -e
if ! grep -q 'apparmor=1' /etc/default/grub; then
sed -i 's/^\(GRUB_CMDLINE_LINUX="[^"]*\)"/\1 apparmor=1 security=apparmor"/' /etc/default/grub
echo CHANGED
fi
register: grub_conf
changed_when: "'CHANGED' in grub_conf.stdout"
- name: Regenerer grub.cfg si la config a change
ansible.builtin.command:
cmd: update-grub
when: grub_conf.changed
changed_when: true
- name: Forcer tous les profils AppArmor en mode enforce
ansible.builtin.command:
cmd: aa-enforce /etc/apparmor.d/*
changed_when: true
- name: Verifier l'etat final (apparmor_status)
ansible.builtin.command:
cmd: apparmor_status
register: aa_status
changed_when: false
failed_when: >
'profiles are in complain mode' in aa_status.stdout and
not (aa_status.stdout | regex_search('^0\s*profiles are in complain mode', multiline=True))
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: "✅ AppArmor durci sur {{ inventory_hostname }} — reboot requis pour apparmor=1/security=apparmor au niveau noyau."
@@ -0,0 +1,73 @@
---
# Regles d'audit detaillees restantes (CIS 6.2.3.4-6.2.3.19) — appliquees a chaud, pas de reboot
#
# PREREQUIS : hardening/playbook-coredump-auditd-hardening.yml (auditd) et
# hardening/playbook-audit-rules-sudo-omnex.yml deja appliques.
#
# Couvre : date/heure, environnement reseau, acces fichier echoues, user/group (identity),
# permissions DAC (chmod/chown/xattr), montages, sessions, login/logout, suppressions fichiers,
# politique MAC (AppArmor), commandes chcon/setfacl/chacl/usermod, modules noyau.
#
# UID_MIN lu dynamiquement depuis /etc/login.defs de chaque host (1000 sur cette infra, mais
# pas suppose en dur pour rester correct si un host differe).
#
# `augenrules --load` recharge a chaud : PAS de redemarrage necessaire tant que le mode
# immutable (-e 2, check 35749) n'est pas active — volontairement PAS fait par ce playbook.
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-audit-rules-cis-rest.yml --limit pre-prod-uber
- name: Regles d'audit detaillees restantes CIS 6.2.3.x (Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
- name: Lire UID_MIN depuis /etc/login.defs
ansible.builtin.command:
cmd: awk '/^\s*UID_MIN/{print $2}' /etc/login.defs
register: uid_min_result
changed_when: false
- name: Deployer les regles d'audit CIS restantes
ansible.builtin.template:
src: ../templates/audit-52-cis-rest.rules.j2
dest: /etc/audit/rules.d/52-cis-rest.rules
owner: root
group: root
mode: "0640"
vars:
uid_min: "{{ uid_min_result.stdout }}"
register: cis_rest_rules
- name: Charger les nouvelles regles d'audit (augenrules --load)
ansible.builtin.command:
cmd: augenrules --load
when: cis_rest_rules.changed
changed_when: true
- name: Verifier que les cles principales sont actives (auditctl -l)
ansible.builtin.command:
cmd: auditctl -l
register: auditctl_check
changed_when: false
failed_when: >
'time-change' not in auditctl_check.stdout or
'system-locale' not in auditctl_check.stdout or
'identity' not in auditctl_check.stdout or
'perm_mod' not in auditctl_check.stdout or
'mounts' not in auditctl_check.stdout or
'session' not in auditctl_check.stdout or
'logins' not in auditctl_check.stdout or
'delete' not in auditctl_check.stdout or
'MAC-policy' not in auditctl_check.stdout or
'perm_chng' not in auditctl_check.stdout or
'usermod' not in auditctl_check.stdout or
'kernel_modules' not in auditctl_check.stdout
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: "✅ Règles d'audit CIS 6.2.3.x restantes actives sur {{ inventory_hostname }} (UID_MIN={{ uid_min_result.stdout }}, pas de reboot requis)."
@@ -0,0 +1,84 @@
---
# Regles d'audit detaillees (CIS 6.2.3.1-6.2.3.3) — surveillance sudo + surveillance dediee omnex
#
# PREREQUIS : necessite hardening/playbook-coredump-auditd-hardening.yml deja applique
# (auditd installe et actif).
#
# - Regles CIS standard : changements /etc/sudoers(.d), toute action "en tant qu'un autre
# utilisateur" (execve avec euid!=uid, couvre sudo/su), modifications de /var/log/sudo.log.
# - Regle custom omnex : capture TOUTE commande executee par omnex (execve filtre sur auid,
# qui persiste a travers sudo/su, donc suit omnex meme apres elevation) — uniquement sur
# les hosts ou l'utilisateur omnex existe (absent sur bdd-redis-prod/backup-mln, root direct).
# - `augenrules --load` recharge la config a chaud : PAS besoin de redemarrage ici (contrairement
# a audit=1 dans GRUB) tant que le mode immutable (-e 2) n'est pas active — volontairement pas
# configure par ce playbook (risque : plus aucune modif de regle possible sans reboot).
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-audit-rules-sudo-omnex.yml --limit pre-prod-uber
- name: Regles d'audit detaillees - sudo + omnex (CIS Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
- name: Deployer les regles d'audit sudo (CIS 6.2.3.1-6.2.3.3)
ansible.builtin.template:
src: ../templates/audit-50-sudo.rules.j2
dest: /etc/audit/rules.d/50-sudo.rules
owner: root
group: root
mode: "0640"
register: sudo_rules
- name: Verifier si l'utilisateur omnex existe sur cet host
ansible.builtin.getent:
database: passwd
key: omnex
ignore_errors: true
register: omnex_getent
- name: Deployer la regle d'audit dediee omnex (uniquement si l'utilisateur existe)
ansible.builtin.template:
src: ../templates/audit-51-omnex.rules.j2
dest: /etc/audit/rules.d/51-omnex.rules
owner: root
group: root
mode: "0640"
vars:
omnex_uid: "{{ ansible_facts.getent_passwd.omnex[1] }}"
when: omnex_getent is succeeded
register: omnex_rules
- name: Charger les nouvelles regles d'audit (augenrules --load)
ansible.builtin.command:
cmd: augenrules --load
when: sudo_rules.changed or (omnex_rules is defined and omnex_rules.changed)
changed_when: true
- name: Verifier que les regles sudo sont bien actives (auditctl -l)
ansible.builtin.command:
cmd: auditctl -l
register: auditctl_check
changed_when: false
failed_when: >
'scope' not in auditctl_check.stdout or
'user_emulation' not in auditctl_check.stdout or
'sudo_log_file' not in auditctl_check.stdout
- name: Verifier que la regle omnex est active (si applicable)
ansible.builtin.command:
cmd: auditctl -l
register: auditctl_omnex_check
changed_when: false
when: omnex_getent is succeeded
failed_when: "'omnex_actions' not in auditctl_omnex_check.stdout"
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: >-
✅ Règles d'audit sudo{{ ' + omnex' if omnex_getent is succeeded else '' }}
actives sur {{ inventory_hostname }} (auditctl -l vérifié, pas de reboot requis).
@@ -0,0 +1,117 @@
---
# Durcissement CIS Ubuntu 24.04 — core dumps restreints, Apport désactivé, auditd installé/actif
# (checks 35543, 35545, 35723-35726, 35756, 35757)
#
# IMPORTANT — REBOOT :
# - audit=1 et audit_backlog_limit=8192 sont ajoutés à /etc/default/grub puis `update-grub`
# régénère immédiatement /boot/grub/grub.cfg → les checks CIS 35725/35726 passent au VERT
# dès ce playbook exécuté (ils inspectent le fichier grub.cfg généré, pas le noyau en cours
# d'exécution).
# - MAIS la protection réelle (auditer les process qui démarrent avant auditd) ne prend effet
# qu'après un REDÉMARRAGE du serveur (le paramètre kernel n'est actif qu'au prochain boot).
# Ce playbook NE redémarre PAS automatiquement les serveurs — décision à prendre séparément,
# en fenêtre de maintenance, host par host (impact Docker/VPN/prod public à chaque reboot).
#
# - apport est PURGÉ (pas juste désactivé) : cohérent avec le choix déjà fait pour rsync/telnet/ftp
# dans ce même chantier. apport-core-dump-handler et python3-apport partent avec (dépendances),
# sans impact sur les services applicatifs de cette infra (aucun ne dépend d'apport).
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-coredump-auditd-hardening.yml --limit pre-prod-uber
- name: Durcissement core dumps + apport + auditd (CIS Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
# --- 1.5.3 Core dumps restreints ---
- name: Deployer fs.suid_dumpable=0
ansible.builtin.template:
src: ../templates/60-cis-coredump-sysctl.conf.j2
dest: /etc/sysctl.d/60-cis-coredump-sysctl.conf
owner: root
group: root
mode: "0644"
register: coredump_sysctl
- name: Appliquer fs.suid_dumpable
ansible.builtin.command:
cmd: sysctl --system
when: coredump_sysctl.changed
changed_when: true
- name: Deployer la limite hard core 0
ansible.builtin.template:
src: ../templates/60-cis-coredump-limits.conf.j2
dest: /etc/security/limits.d/60-cis-coredump.conf
owner: root
group: root
mode: "0644"
# --- 1.5.5 Apport (Automatic Error Reporting) ---
- name: Purger apport (et ses dependances python3-apport, apport-core-dump-handler)
ansible.builtin.apt:
name: apport
state: absent
purge: true
# --- 6.2.1.x auditd ---
- name: Installer auditd et audispd-plugins
ansible.builtin.apt:
name:
- auditd
- audispd-plugins
state: present
update_cache: true
cache_valid_time: 3600
- name: Demasquer, activer et demarrer auditd
ansible.builtin.systemd:
name: auditd
masked: false
enabled: true
state: started
- name: Verifier la presence de audit=1 dans GRUB_CMDLINE_LINUX
ansible.builtin.lineinfile:
path: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX='
line: 'GRUB_CMDLINE_LINUX="audit=1 audit_backlog_limit=8192"'
backup: true
register: grub_conf
- name: Regenerer grub.cfg si la config a change
ansible.builtin.command:
cmd: update-grub
when: grub_conf.changed
changed_when: true
- name: Verifier que audit=1 est bien present dans grub.cfg genere
ansible.builtin.command:
cmd: grep -q 'audit=1' /boot/grub/grub.cfg
changed_when: false
# --- 6.2.4.9 / 6.2.4.10 Ownership des outils d'audit ---
- name: Proprietaire/groupe root:root sur les outils d'audit
ansible.builtin.file:
path: "{{ item }}"
owner: root
group: root
loop:
- /sbin/auditctl
- /sbin/aureport
- /sbin/ausearch
- /sbin/autrace
- /sbin/auditd
- /sbin/augenrules
ignore_errors: true # certains binaires peuvent etre des symlinks vers /usr/sbin selon la distro
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: >-
✅ Core dumps/apport/auditd appliqués sur {{ inventory_hostname }}.
REDÉMARRAGE requis pour que audit=1 protège les process pré-auditd (non fait par ce playbook).
@@ -0,0 +1,96 @@
---
# Durcissement CIS Ubuntu 24.04 — retrait rsync/telnet/ftp + permissions cron + cron.allow/at.allow
# (checks 35573, 35585, 35587, 35594-35601)
#
# IMPORTANT :
# - rsync/telnet/ftp verifies avant retrait sur cette infra : aucun daemon actif, aucun cron/script
# n'en dependait (grep sur les crontabs + scripts de backup). Purge sans risque constate.
# - Le paquet 'at' n'est PAS installe sur cette infra et ce playbook ne l'installe PAS : ajouter un
# service juste pour le verrouiller ensuite serait contre-productif (surface d'attaque en plus
# pour un compliance-checkbox). /etc/at.allow est quand meme cree en defense en profondeur, mais
# le check CIS 35601 restera en echec par ce choix assume tant que 'at' n'est pas installe.
# - /etc/cron.allow doit avoir owner:group EXACTEMENT root:root pour satisfaire le check automatise
# 35600 — la documentation CIS mentionne le groupe "crontab" comme alternative acceptable, mais la
# regex du check SCA (`r:^root root$`) n'accepte que root:root en pratique (verifie empiriquement).
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-cron-packages-hardening.yml --limit pre-prod-uber
- name: Durcissement cron/at + retrait paquets insecurises (CIS Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
- name: Purger rsync, telnet et ftp (clients/services insecurises)
ansible.builtin.apt:
name:
- rsync
- telnet
- inetutils-telnet
- ftp
- tnftp
state: absent
purge: true
- name: Permissions /etc/crontab (0600 root:root)
ansible.builtin.file:
path: /etc/crontab
owner: root
group: root
mode: "0600"
- name: Permissions repertoires cron (0700 root:root)
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0700"
loop:
- /etc/cron.hourly
- /etc/cron.daily
- /etc/cron.weekly
- /etc/cron.monthly
- /etc/cron.d
- name: Verifier si /etc/cron.deny existe
ansible.builtin.stat:
path: /etc/cron.deny
register: cron_deny
- name: Creer /etc/cron.allow (crontab restreint aux utilisateurs autorises)
ansible.builtin.file:
path: /etc/cron.allow
state: touch
owner: root
group: root
mode: "0640"
access_time: preserve
modification_time: preserve
when: not cron_deny.stat.exists
- name: Permissions /etc/cron.deny si present
ansible.builtin.file:
path: /etc/cron.deny
owner: root
group: root
mode: "0640"
when: cron_deny.stat.exists
- name: Creer /etc/at.allow (defense en profondeur, meme si 'at' non installe)
ansible.builtin.file:
path: /etc/at.allow
state: touch
owner: root
group: daemon
mode: "0640"
access_time: preserve
modification_time: preserve
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: "✅ Durcissement cron/paquets appliqué sur {{ inventory_hostname }}"
@@ -0,0 +1,71 @@
---
# Durcissement CIS Ubuntu 24.04 — modules noyau reseau/filesystem inutilises + sysctl reseau
# (checks 35509, 35604-35607, 35608-35618)
#
# IMPORTANT :
# - net.ipv4.ip_forward est laisse ACTIF sur la plupart des hosts (Docker gere le NAT des ports
# publies, vpn-uber fait relais entre pairs WireGuard) — le desactiver casserait ces usages.
# Seul load-balancer (pas de Docker, pas de relais VPN) le desactive, via la variable
# `ip_forwarding_disabled` (group_vars, defaut false, true uniquement pour load-balancer).
# Le check CIS 35608 restera donc en echec par necessite fonctionnelle sur la plupart des hosts.
# - rp_filter est mis a 1 (strict) partout : verifie sans casse sur cette infra (Docker + WireGuard
# coexistent sans probleme de routage asymetrique ici), mais a re-tester si la topologie change.
# - Aucun des modules blacklistes n'est charge sur cette infra (verifie via lsmod avant deploiement) :
# zero impact fonctionnel attendu. Exception : fat/fuse sont compiles en dur dans le noyau Ubuntu
# (builtin) et resteront "disponibles" quoi qu'il arrive — check CIS 35509 restera en echec pour
# ces deux modules specifiquement, cas documente par la CIS elle-meme comme sans remediation
# possible ("if pre-compiled into the kernel, no remediation is necessary").
#
# `serial: 1` + `any_errors_fatal` : un host a la fois, arret immediat si un host casse.
#
# Usage recommande : tester sur un host avant le rollout complet :
# ansible-playbook hardening/playbook-kernel-sysctl-hardening.yml --limit pre-prod-uber
- name: Durcissement kernel modules + sysctl reseau (CIS Ubuntu 24.04)
hosts: prod,pre-prod,bdd-redis-prod,replica-prod,infra,infra-runner,infra-utils,load-balancer
become: true
serial: 1
any_errors_fatal: true
tasks:
- name: Deployer le blacklist des modules noyau inutilises
ansible.builtin.template:
src: ../templates/blacklist-unused-modules.conf.j2
dest: /etc/modprobe.d/cis-blacklist-unused.conf
owner: root
group: root
mode: "0644"
- name: Deployer le durcissement sysctl reseau
ansible.builtin.template:
src: ../templates/60-cis-network-hardening.conf.j2
dest: /etc/sysctl.d/60-cis-network-hardening.conf
owner: root
group: root
mode: "0644"
register: sysctl_conf
- name: Appliquer les parametres sysctl actifs
ansible.builtin.command:
cmd: sysctl --system
when: sysctl_conf.changed
changed_when: true
register: sysctl_apply
- name: Verifier qu'aucune erreur n'est remontee par sysctl --system
ansible.builtin.fail:
msg: "Erreur sysctl --system sur {{ inventory_hostname }} : {{ sysctl_apply.stdout }}"
when: sysctl_apply is defined and sysctl_apply.stdout is defined and 'error' in sysctl_apply.stdout | lower
- name: Verifier la valeur effective de ip_forward
ansible.builtin.command:
cmd: sysctl -n net.ipv4.ip_forward
register: ip_forward_check
changed_when: false
failed_when: >
(not (ip_forwarding_disabled | default(false)) and ip_forward_check.stdout != "1") or
((ip_forwarding_disabled | default(false)) and ip_forward_check.stdout != "0")
- name: Confirmer le succes pour cet host
ansible.builtin.debug:
msg: "✅ Durcissement kernel/sysctl appliqué sur {{ inventory_hostname }} — ip_forward={{ ip_forward_check.stdout }}"
+175
View File
@@ -0,0 +1,175 @@
---
# Durcissement PAM (CIS Ubuntu 24.04 — pam_faillock, pam_pwquality, pam_pwhistory)
#
# IMPORTANT — à lire avant d'exécuter :
# - Ce playbook touche l'authentification (sudo/su) sur TOUS les hosts ciblés.
# Une erreur dans un profil pam-auth-update peut casser sudo partout.
# - `serial: 1` traite un host à la fois : si un host casse, le run s'arrête
# avant de toucher les suivants (any_errors_fatal).
# - La dernière tâche force un reset du cache sudo (`sudo -K`) puis revalide
# un `become` réel, pour détecter une casse immédiatement plutôt que de la
# découvrir 15 min plus tard quand le cache sudo normal expire.
# - Volontairement AUCUNE expiration de mot de passe n'est appliquée sur les
# comptes existants (omnex/root) : ces comptes se connectent en SSH par clé
# (jamais par mot de passe), donc le mécanisme d'expiration PAM — qui ne se
# déclenche qu'au moment d'un prompt de mot de passe au login — ne se
# déclencherait jamais, et une expiration silencieuse casserait `sudo` sans
# aucun moyen interactif de le corriger à distance. PASS_MAX_DAYS n'est posé
# que dans /etc/login.defs (valeur par défaut pour les FUTURS comptes), pas
# rétroactivement via chage.
#
# Usage recommandé : tester d'abord sur UN seul host avant le rollout complet :
# ansible-playbook infra/playbook-pam-hardening.yml --limit pre-prod-uber
#
- name: Durcissement PAM (CIS Ubuntu 24.04)
hosts: bdd-redis-prod
become: true
serial: 1
any_errors_fatal: true
vars:
pam_faillock_deny: 5
pam_faillock_unlock_time: 900
pam_faillock_fail_interval: 900
pam_pwquality_minlen: 16
pam_pwquality_difok: 8
pam_pwhistory_remember: 24
tasks:
- name: Installer libpam-pwquality
ansible.builtin.apt:
name: libpam-pwquality
state: present
update_cache: true
cache_valid_time: 3600
- name: Sauvegarder l'état PAM actuel avant modification
ansible.builtin.shell: |
set -e
BACKUP_DIR="/root/pam-backup-$(date +%Y%m%d%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -a /etc/pam.d "$BACKUP_DIR/"
cp -a /usr/share/pam-configs "$BACKUP_DIR/"
cp /etc/login.defs "$BACKUP_DIR/" 2>/dev/null || true
[ -f /etc/security/faillock.conf ] && cp /etc/security/faillock.conf "$BACKUP_DIR/" || true
[ -f /etc/security/pwquality.conf ] && cp /etc/security/pwquality.conf "$BACKUP_DIR/" || true
echo "$BACKUP_DIR"
args:
executable: /bin/bash
changed_when: false
register: pam_backup
- name: Afficher le chemin de sauvegarde (rollback manuel possible)
ansible.builtin.debug:
msg: "Backup PAM : {{ pam_backup.stdout }}"
- name: Déployer le profil unix (retire nullok, garde use_authtok)
ansible.builtin.template:
src: ../templates/pam-configs-unix.j2
dest: /usr/share/pam-configs/unix
owner: root
group: root
mode: "0644"
register: pam_unix_profile
- name: Déployer le profil faillock (authfail)
ansible.builtin.template:
src: ../templates/pam-configs-faillock.j2
dest: /usr/share/pam-configs/faillock
owner: root
group: root
mode: "0644"
register: pam_faillock_profile
- name: Déployer le profil faillock_notify (preauth + account)
ansible.builtin.template:
src: ../templates/pam-configs-faillock-notify.j2
dest: /usr/share/pam-configs/faillock_notify
owner: root
group: root
mode: "0644"
register: pam_faillock_notify_profile
- name: Déployer le profil pwquality
ansible.builtin.template:
src: ../templates/pam-configs-pwquality.j2
dest: /usr/share/pam-configs/pwquality
owner: root
group: root
mode: "0644"
register: pam_pwquality_profile
- name: Déployer le profil pwhistory
ansible.builtin.template:
src: ../templates/pam-configs-pwhistory.j2
dest: /usr/share/pam-configs/pwhistory
owner: root
group: root
mode: "0644"
register: pam_pwhistory_profile
- name: Déployer /etc/security/faillock.conf
ansible.builtin.template:
src: ../templates/faillock.conf.j2
dest: /etc/security/faillock.conf
owner: root
group: root
mode: "0644"
- name: Déployer /etc/security/pwquality.conf
ansible.builtin.template:
src: ../templates/pwquality.conf.j2
dest: /etc/security/pwquality.conf
owner: root
group: root
mode: "0644"
- name: Activer les profils PAM (regénère common-auth/common-account/common-password)
ansible.builtin.command:
cmd: pam-auth-update --enable faillock --enable faillock_notify --enable pwquality --enable pwhistory
when: >
pam_unix_profile.changed or pam_faillock_profile.changed or
pam_faillock_notify_profile.changed or pam_pwquality_profile.changed or
pam_pwhistory_profile.changed
changed_when: true
- name: Configurer PASS_MAX_DAYS dans login.defs (nouveaux comptes uniquement)
ansible.builtin.lineinfile:
path: /etc/login.defs
regexp: '^PASS_MAX_DAYS'
line: "PASS_MAX_DAYS\t365"
- name: Configurer PASS_MIN_DAYS dans login.defs
ansible.builtin.lineinfile:
path: /etc/login.defs
regexp: '^PASS_MIN_DAYS'
line: "PASS_MIN_DAYS\t1"
- name: Configurer PASS_WARN_AGE dans login.defs
ansible.builtin.lineinfile:
path: /etc/login.defs
regexp: '^PASS_WARN_AGE'
line: "PASS_WARN_AGE\t7"
- name: Verrouillage par défaut des comptes inactifs (nouveaux comptes)
ansible.builtin.command:
cmd: useradd -D -f 30
changed_when: true
# --- Vérification critique : la stack PAM fonctionne-t-elle toujours ? ---
# On invalide le cache sudo existant pour forcer une vraie ré-authentification
# PAM, sinon un sudo déjà "chaud" masquerait une stack cassée pendant 15 min.
- name: Invalider le cache sudo pour forcer une vraie revalidation PAM
ansible.builtin.command:
cmd: sudo -K
become: false
changed_when: false
- name: Vérifier que sudo fonctionne toujours avec la nouvelle stack PAM
ansible.builtin.command:
cmd: whoami
register: pam_sudo_check
failed_when: pam_sudo_check.stdout != "root"
- name: Confirmer le succès pour cet host
ansible.builtin.debug:
msg: "✅ PAM durci avec succès sur {{ inventory_hostname }} — sudo toujours fonctionnel."
+76
View File
@@ -0,0 +1,76 @@
- name: SSH HARDENNING
hosts: load-balancer
become: true
gather_facts: true
tasks:
- name: Créer le groupe ssh
ansible.builtin.group:
name: ssh
state: present
- ansible.builtin.user:
name: "{{ user_ssh }}"
shell: /bin/bash
groups:
- sudo
- ssh
append: true
state: present
password: "{{ password_user_ssh }}"
update_password: on_create
create_home: true
home: /home/omnex
no_log: true
- name: Check si la paire de clé SSH existe
ansible.builtin.stat:
path: "{{ playbook_dir }}/../../.ssh/{{ user_ssh }}"
delegate_to: localhost
become: false
register: ssh_key_check
tags: [key]
- name: Générer la paire de clés SSH localement
community.crypto.openssh_keypair:
path: "{{ playbook_dir }}/../../.ssh/{{ user_ssh }}"
type: ed25519
delegate_to: localhost
become: false
when: not ssh_key_check.stat.exists
run_once: true
tags: [key]
- ansible.posix.authorized_key:
user: "{{ user_ssh }}"
state: present
key: "{{ lookup('file', playbook_dir + '/../../.ssh/' + user_ssh + '.pub') }}"
- name: Déployer la config sshd durcie
ansible.builtin.template:
src: ../templates/ssh_harden_conf.j2
dest: /etc/ssh/sshd_config
mode: "0644"
owner: root
group: root
validate: /usr/sbin/sshd -t -f %s
notify: restart ssh
- name: Delete cloud ssh config if exists
ansible.builtin.file:
path: /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
state: absent
- name: Déployer la config cloud-init durcie
ansible.builtin.template:
src: ../templates/cloud_harden.conf.j2
dest: /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
mode: "0644"
owner: root
group: root
notify: restart ssh
handlers:
- name: restart ssh
ansible.builtin.service:
name: ssh
state: restarted
+127
View File
@@ -0,0 +1,127 @@
---
- name: Génération du certificat SSL Let's Encrypt
hosts: infra
become: true
gather_facts: false
tasks:
- name: Installer certbot et le plugin nginx
ansible.builtin.apt:
name:
- certbot
- python3-certbot-nginx
- nginx
state: present
update_cache: yes
- name: Déployer la config Nginx HTTP simple (ACME challenge)
ansible.builtin.copy:
dest: /etc/nginx/sites-available/certbot
content: |
server {
listen 80;
listen [::]:80;
server_name {{ domain_name }} {{ subdomains | join(' ') }};
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 200 'ok';
add_header Content-Type text/plain;
}
}
- name: Activer le site certbot
ansible.builtin.file:
src: /etc/nginx/sites-available/certbot
dest: /etc/nginx/sites-enabled/certbot
state: link
force: yes
- name: Désactiver le site par défaut
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
- name: Tester la configuration Nginx
ansible.builtin.command: nginx -t
changed_when: false
- name: Redémarrer Nginx
ansible.builtin.systemd:
name: nginx
state: restarted
enabled: yes
- name: Ouvrir les ports dans UFW
ansible.builtin.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "22"
- "80"
- "443"
- name: Vérifier si le certificat existe déjà
ansible.builtin.stat:
path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
register: cert_file
tags: [gen]
- name: Générer le certificat avec Certbot (domaine + sous-domaines)
ansible.builtin.command: >
certbot certonly --nginx
-d {{ domain_name }}
{{ subdomains | map('regex_replace', '^(.*)$', '-d \1') | join(' ') }}
--non-interactive
--agree-tos
--email admin@{{ domain_name }}
when: not cert_file.stat.exists
tags: [gen]
- name: Vérifier que le certificat a bien été généré
ansible.builtin.stat:
path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
register: cert_check
- name: Afficher le résultat
ansible.builtin.debug:
msg: "{{ 'Certificat généré : /etc/letsencrypt/live/' + domain_name + '/fullchain.pem' if cert_check.stat.exists else 'ERREUR : certificat non trouvé' }}"
- name: Vérifier le renouvellement automatique
ansible.builtin.command: certbot renew --dry-run
changed_when: false
failed_when: false
when: cert_check.stat.exists
- name: Créer le dossier docker/certs
ansible.builtin.file:
path: "{{ docker_dir }}/nginx/certs"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0750"
tags: [create]
- name: Copier fullchain.pem dans docker/certs
ansible.builtin.copy:
src: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
dest: "{{ docker_dir }}/nginx/certs/fullchain.pem"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0644"
remote_src: yes
tags: [create]
- name: Copier privkey.pem dans docker/certs
ansible.builtin.copy:
src: "/etc/letsencrypt/live/{{ domain_name }}/privkey.pem"
dest: "{{ docker_dir }}/nginx/certs/privkey.pem"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
remote_src: yes
tags: [create]
+106
View File
@@ -0,0 +1,106 @@
---
- name: Installation et configuration du clamAV
hosts: infra
become: true
gather_facts: true
tasks:
- name: Vérifier si ClamAV existe dans les dépôts
ansible.builtin.command: apt-cache search ^clamav$
register: clamav_search
changed_when: false
- name: Afficher résultat
ansible.builtin.debug:
var: clamav_search.stdout
- name: Installer ClamAV si disponible
ansible.builtin.apt:
name:
- clamav
- clamav-daemon
state: present
update_cache: true
when: clamav_search.stdout != ""
- name: Démarrer ClamAV
ansible.builtin.service:
name: clamav-daemon
state: started
enabled: true
when: clamav_search.stdout != ""
- name: Arrêter clamav-freshclam avant la mise à jour
ansible.builtin.service:
name: clamav-freshclam
state: stopped
when: clamav_search.stdout != ""
- name: Mettre à jour la base de données virale (freshclam)
ansible.builtin.command: freshclam
register: freshclam_result
changed_when: "'Database updated' in freshclam_result.stdout"
failed_when: freshclam_result.rc != 0
when: clamav_search.stdout != ""
- name: Redémarrer clamav-freshclam après la mise à jour
ansible.builtin.service:
name: clamav-freshclam
state: started
enabled: true
when: clamav_search.stdout != ""
- name: Configurer ClamAV - activer la détection PUA
ansible.builtin.lineinfile:
path: /etc/clamav/clamd.conf
regexp: "^#?DetectPUA"
line: "DetectPUA yes"
backup: true
when: clamav_search.stdout != ""
notify: Redémarrer clamav-daemon
- name: Configurer ClamAV - activer les logs
ansible.builtin.lineinfile:
path: /etc/clamav/clamd.conf
regexp: "^#?LogFile "
line: "LogFile /var/log/clamav/clamav.log"
backup: true
when: clamav_search.stdout != ""
notify: Redémarrer clamav-daemon
- name: Configurer ClamAV - taille max des fichiers scannés
ansible.builtin.lineinfile:
path: /etc/clamav/clamd.conf
regexp: "^#?MaxFileSize"
line: "MaxFileSize 100M"
backup: true
when: clamav_search.stdout != ""
notify: Redémarrer clamav-daemon
- name: Planifier un scan quotidien via cron
ansible.builtin.cron:
name: "ClamAV daily scan"
minute: "30"
hour: "2"
job: >
ionice -c 3 nice -n 19
docker exec gestion-clamav
clamscan -r /home /tmp /app/uploads
--log=/var/log/clamav/daily_scan.log --quiet
user: root
when: clamav_search.stdout != ""
- name: Afficher résultat final
ansible.builtin.debug:
msg: "ClamAV installé et configuré avec succès"
when: clamav_search.stdout != ""
- name: Afficher message si ClamAV non disponible
ansible.builtin.debug:
msg: "ClamAV n'est pas disponible dans les dépôts"
when: clamav_search.stdout == ""
handlers:
- name: Redémarrer clamav-daemon
ansible.builtin.service:
name: clamav-daemon
state: restarted
+332
View File
@@ -0,0 +1,332 @@
---
- name: Installation et configuration du Gitea Act Runner
hosts: infra
become: false
gather_facts: true
vars:
act_runner_version: "0.2.11"
act_runner_binary: /usr/local/bin/act_runner
act_runner_workdir: /opt/gitea-runner
act_runner_config: /opt/gitea-runner/.runner
gitea_instance_url: "https://gitea.uber-stup.club"
runner_name: "{{ inventory_hostname }}-runner"
runner_labels: "ubuntu-latest:docker://catthehacker/ubuntu:act-22.04,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04"
# ── Utilisateur CI/CD ────────────────────────────────
ci_cd_user: "ci-cd"
ci_cd_user_shell: "/bin/bash"
ci_cd_user_groups:
- docker
ci_cd_ssh_pubkey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICJ4COozE+71IvOhzK7ijeQY5lmoDxYTA0+B0lEAO0tG xor_fakers@AsusXor" # ex: "ssh-ed25519 AAAAC3Nza... ci-cd@gitea"
tasks:
# ── Verrou dpkg (unattended-upgrades / cloud-init au boot) ──
- name: Attendre la libération du verrou dpkg/apt
become: true
shell: |
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 \
|| fuser /var/lib/dpkg/lock >/dev/null 2>&1 \
|| fuser /var/lib/apt/lists/lock >/dev/null 2>&1 \
|| fuser /var/cache/apt/archives/lock >/dev/null 2>&1; do
sleep 5
done
changed_when: false
args:
executable: /bin/bash
tags: always
# ── Prérequis ──────────────────────────────────────────
- name: Installer les paquets requis
become: true
apt:
name:
- curl
- ca-certificates
state: present
update_cache: yes
register: apt_prereqs_result
until: apt_prereqs_result is succeeded
retries: 10
delay: 15
# ── Docker (requis pour exécuter les jobs) ─────────────
- name: Vérifier si Docker est installé
become: true
command: docker --version
register: docker_check
changed_when: false
failed_when: false
- name: Installer Docker si absent
become: true
block:
- name: Ajouter la clé GPG Docker
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
- name: Ajouter le dépôt Docker
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
filename: docker
- name: Installer Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
update_cache: yes
register: apt_docker_result
until: apt_docker_result is succeeded
retries: 10
delay: 15
- name: Démarrer et activer Docker
systemd:
name: docker
state: started
enabled: yes
when: docker_check.rc != 0
# ── Utilisateur dédié CI/CD ─────────────────────────────
- name: Vérifier que ci_cd_ssh_pubkey est fourni
fail:
msg: "La variable 'ci_cd_ssh_pubkey' est requise. Relancez avec : -e ci_cd_ssh_pubkey='ssh-ed25519 AAAA...'"
when: ci_cd_ssh_pubkey is not defined or ci_cd_ssh_pubkey == ""
tags: ["ci-cd-user"]
- name: Créer l'utilisateur CI/CD
become: true
user:
name: "{{ ci_cd_user }}"
shell: "{{ ci_cd_user_shell }}"
groups: "{{ ci_cd_user_groups }}"
append: true
create_home: true
state: present
tags: ["ci-cd-user"]
- name: Créer le répertoire .ssh de l'utilisateur CI/CD
become: true
file:
path: "/home/{{ ci_cd_user }}/.ssh"
state: directory
owner: "{{ ci_cd_user }}"
group: "{{ ci_cd_user }}"
mode: "0700"
tags: ["ci-cd-user"]
- name: Déployer la clé publique SSH de l'utilisateur CI/CD
become: true
authorized_key:
user: "{{ ci_cd_user }}"
key: "{{ ci_cd_ssh_pubkey }}"
state: present
exclusive: false
tags: ["ci-cd-user"]
- name: Autoriser l'utilisateur CI/CD à utiliser Docker sans mot de passe (sudo ciblé, optionnel)
become: true
copy:
dest: "/etc/sudoers.d/{{ ci_cd_user }}-docker"
content: "{{ ci_cd_user }} ALL=(ALL) NOPASSWD: /usr/bin/docker\n"
mode: "0440"
validate: "visudo -cf %s"
tags: ["ci-cd-user"]
# ── Téléchargement act_runner ──────────────────────────
- name: Vérifier si act_runner est déjà installé
become: true
stat:
path: "{{ act_runner_binary }}"
register: runner_bin
tags: ["runner", "register-runner"]
- name: Récupérer la version installée
become: true
command: "{{ act_runner_binary }} --version"
register: installed_version
changed_when: false
failed_when: false
when: runner_bin.stat.exists
tags: ["runner", "register-runner"]
- name: Télécharger act_runner v{{ act_runner_version }}
become: true
get_url:
url: "https://dl.gitea.com/act_runner/{{ act_runner_version }}/act_runner-{{ act_runner_version }}-linux-amd64"
dest: "{{ act_runner_binary }}"
mode: "0755"
force: yes
tags: ["runner", "register-runner"]
when: >
not (runner_bin.stat.exists | default(false)) or
(installed_version.stdout is defined and act_runner_version not in installed_version.stdout)
# ── Répertoire de travail ──────────────────────────────
- name: Créer le répertoire de travail
become: true
file:
path: "{{ act_runner_workdir }}"
state: directory
mode: "0750"
tags: ["register-runner"]
# ── Enregistrement du runner ───────────────────────────
- name: Vérifier que gitea_runner_token est fourni
become: true
fail:
msg: "La variable 'gitea_runner_token' est requise. Relancez avec : -e gitea_runner_token=<token>"
when: gitea_runner_token is not defined or gitea_runner_token == ""
tags: ["register-runner"]
- name: Vérifier si le runner est déjà enregistré
become: true
stat:
path: "{{ act_runner_config }}"
register: runner_config
tags: ["register-runner"]
- name: Arrêter le service avant re-enregistrement
become: true
systemd:
name: gitea-runner
state: stopped
failed_when: false
when: gitea_runner_token is defined and runner_config.stat.exists
tags: ["register-runner"]
- name: Supprimer l'ancienne configuration du runner
become: true
file:
path: "{{ act_runner_config }}"
state: absent
when: gitea_runner_token is defined and runner_config.stat.exists
tags: ["register-runner"]
- name: Enregistrer le runner auprès de Gitea
become: true
command: >
{{ act_runner_binary }} register
--instance {{ gitea_instance_url }}
--token {{ gitea_runner_token }}
--name {{ runner_name }}
--labels {{ runner_labels }}
--no-interactive
args:
chdir: "{{ act_runner_workdir }}"
no_log: true
tags: ["register-runner"]
# ── Service systemd ────────────────────────────────────
- name: Créer le service systemd gitea-runner
become: true
copy:
dest: /etc/systemd/system/gitea-runner.service
mode: "0644"
content: |
[Unit]
Description=Gitea Act Runner
After=network.target docker.service
Requires=docker.service
[Service]
ExecStart={{ act_runner_binary }} daemon
WorkingDirectory={{ act_runner_workdir }}
Restart=always
RestartSec=5
User=root
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
notify: Recharger systemd
tags: ["register-runner"]
- name: Activer et démarrer le service gitea-runner
become: true
systemd:
name: gitea-runner
state: started
enabled: yes
daemon_reload: yes
tags: ["register-runner"]
# ── Vérification ───────────────────────────────────────
- name: Vérifier le statut du runner
become: true
command: systemctl status gitea-runner
register: runner_status
changed_when: false
failed_when: false
tags: ["register-runner"]
- name: Afficher le statut
become: true
debug:
msg: "{{ runner_status.stdout_lines }}"
tags: ["register-runner"]
- name: Check if swap is already active
become: true
ansible.builtin.command: swapon --show
register: swap_status
changed_when: false
tags: ["register-runner"]
- name: Create swapfile
become: true
ansible.builtin.command: "fallocate -l {{ swap_size }} {{ swap_file }}"
args:
creates: "{{ swap_file }}"
tags: ["register-runner"]
when: swap_status.stdout == ""
- name: Set swapfile permissions
become: true
ansible.builtin.file:
path: "{{ swap_file }}"
mode: "0600"
owner: root
group: root
tags: ["register-runner"]
- name: Format swap
become: true
ansible.builtin.command: mkswap {{ swap_file }}
tags: ["register-runner"]
when: swap_status.stdout == ""
- name: Enable swap
become: true
ansible.builtin.command: swapon {{ swap_file }}
tags: ["register-runner"]
when: swap_status.stdout == ""
- name: Ensure swap in fstab
become: true
ansible.builtin.lineinfile:
path: /etc/fstab
line: "{{ swap_file }} none swap sw 0 0"
state: present
tags: ["register-runner"]
- name: Set swappiness
become: true
ansible.builtin.sysctl:
name: vm.swappiness
value: "{{ swap_swappiness }}"
state: present
reload: yes
tags: ["register-runner"]
handlers:
- name: Recharger systemd
become: true
systemd:
daemon_reload: yes
+146
View File
@@ -0,0 +1,146 @@
---
- name: S3 backup pour la db de prod
hosts: infra
become: true
gather_facts: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- acl
state: present
tags: docker
- name: Create directory for Docker GPG key
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tags: docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
tags: docker
- name: Get system architecture
command: dpkg --print-architecture
register: system_arch
changed_when: false
- name: Get Ubuntu codename
command: lsb_release -cs
register: ubuntu_codename
changed_when: false
- name: Add Docker repository
apt_repository:
repo: "deb [arch={{ system_arch.stdout }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename.stdout }} stable"
state: present
filename: docker
tags: docker
- name: Update apt cache after adding repo
apt:
update_cache: yes
tags: docker
- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
tags: docker
- name: Ensure Docker service is started and enabled
systemd:
name: docker
state: started
enabled: yes
tags: docker
- name: Create docker group
group:
name: "{{ docker_group }}"
state: present
tags: docker
- name: Create docker user
user:
name: "{{ docker_user }}"
group: "{{ docker_group }}"
groups: docker
append: yes
shell: /bin/bash
create_home: yes
state: present
tags: docker
- name: Add ansible user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
when: ansible_user is defined
tags: docker
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
tags: docker
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
tags: docker
- name: Display versions
debug:
msg:
- "{{ docker_version.stdout }}"
- "{{ compose_version.stdout }}"
tags: docker
- name: Copy S3 project
ansible.builtin.copy:
src: ../monitoring/s3-backup/
dest: "{{ docker_dir }}/"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Ensure parent directory has correct permissions
ansible.builtin.file:
path: "{{ docker_dir }}"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Set correct permissions for Docker files
ansible.builtin.file:
path: "{{ docker_dir }}"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
recurse: yes
tags: project
+146
View File
@@ -0,0 +1,146 @@
---
- name: Déploiement OTA & S3 pour les build
hosts: infra
become: true
gather_facts: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- acl
state: present
tags: docker
- name: Create directory for Docker GPG key
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tags: docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
tags: docker
- name: Get system architecture
command: dpkg --print-architecture
register: system_arch
changed_when: false
- name: Get Ubuntu codename
command: lsb_release -cs
register: ubuntu_codename
changed_when: false
- name: Add Docker repository
apt_repository:
repo: "deb [arch={{ system_arch.stdout }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename.stdout }} stable"
state: present
filename: docker
tags: docker
- name: Update apt cache after adding repo
apt:
update_cache: yes
tags: docker
- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
tags: docker
- name: Ensure Docker service is started and enabled
systemd:
name: docker
state: started
enabled: yes
tags: docker
- name: Create docker group
group:
name: "{{ docker_group }}"
state: present
tags: docker
- name: Create docker user
user:
name: "{{ docker_user }}"
group: "{{ docker_group }}"
groups: docker
append: yes
shell: /bin/bash
create_home: yes
state: present
tags: docker
- name: Add ansible user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
when: ansible_user is defined
tags: docker
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
tags: docker
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
tags: docker
- name: Display versions
debug:
msg:
- "{{ docker_version.stdout }}"
- "{{ compose_version.stdout }}"
tags: docker
- name: Copy S3 project
ansible.builtin.copy:
src: ../monitoring/s3/
dest: "{{ docker_dir }}/"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Ensure parent directory has correct permissions
ansible.builtin.file:
path: "{{ docker_dir }}"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Set correct permissions for Docker files
ansible.builtin.file:
path: "{{ docker_dir }}"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
recurse: yes
tags: project
+155
View File
@@ -0,0 +1,155 @@
---
- name: Déploiement des solution de monitoring et de sécurité
hosts: infra
become: true
gather_facts: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- acl
state: present
tags: docker
- name: Create directory for Docker GPG key
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tags: docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
tags: docker
- name: Get system architecture
command: dpkg --print-architecture
register: system_arch
changed_when: false
- name: Get Ubuntu codename
command: lsb_release -cs
register: ubuntu_codename
changed_when: false
- name: Add Docker repository
apt_repository:
repo: "deb [arch={{ system_arch.stdout }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename.stdout }} stable"
state: present
filename: docker
tags: docker
- name: Update apt cache after adding repo
apt:
update_cache: yes
tags: docker
- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
tags: docker
- name: Ensure Docker service is started and enabled
systemd:
name: docker
state: started
enabled: yes
tags: docker
- name: Create docker group
group:
name: "{{ docker_group }}"
state: present
tags: docker
- name: Create docker user
user:
name: "{{ docker_user }}"
group: "{{ docker_group }}"
groups: docker
append: yes
shell: /bin/bash
create_home: yes
state: present
tags: docker
- name: Add ansible user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
when: ansible_user is defined
tags: docker
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
tags: docker
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
tags: docker
- name: Display versions
debug:
msg:
- "{{ docker_version.stdout }}"
- "{{ compose_version.stdout }}"
tags: docker
- name: Copy Docker project
ansible.builtin.copy:
src: ../monitoring/security/
dest: "{{ docker_dir }}/"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Ensure parent directory has correct permissions
ansible.builtin.file:
path: "{{ docker_dir }}"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Set correct permissions for Docker files
ansible.builtin.file:
path: "{{ docker_dir }}"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
recurse: yes
tags: project
- name: Set permissions for .env file
ansible.builtin.file:
path: "{{ docker_dir }}/.env"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
tags: project
ignore_errors: yes
+77
View File
@@ -0,0 +1,77 @@
---
- name: Mises à jour de sécurité automatiques (unattended-upgrades)
hosts: load-balancer
become: true
tasks:
- name: Installer unattended-upgrades et jq (notification Discord)
ansible.builtin.apt:
name:
- unattended-upgrades
- jq
state: present
update_cache: true
- name: Déployer la config des origines/exclusions (50unattended-upgrades)
ansible.builtin.template:
src: ../templates/unattended-upgrades-50.j2
dest: /etc/apt/apt.conf.d/50unattended-upgrades
owner: root
group: root
mode: "0644"
- name: Déployer la config d'activation périodique (20auto-upgrades)
ansible.builtin.template:
src: ../templates/unattended-upgrades-20.j2
dest: /etc/apt/apt.conf.d/20auto-upgrades
owner: root
group: root
mode: "0644"
- name: Déployer le script de notification Discord
ansible.builtin.template:
src: ../templates/unattended-upgrades-notify.sh.j2
dest: /usr/local/bin/unattended-upgrades-notify.sh
owner: root
group: root
mode: "0750"
- name: Déployer le service systemd de notification
ansible.builtin.template:
src: ../templates/unattended-upgrades-notify.service.j2
dest: /etc/systemd/system/unattended-upgrades-notify.service
owner: root
group: root
mode: "0644"
notify: Recharger systemd
- name: Déployer le timer systemd de notification
ansible.builtin.template:
src: ../templates/unattended-upgrades-notify.timer.j2
dest: /etc/systemd/system/unattended-upgrades-notify.timer
owner: root
group: root
mode: "0644"
notify: Recharger systemd
- name: Activer et démarrer le timer de notification
ansible.builtin.systemd:
name: unattended-upgrades-notify.timer
enabled: true
state: started
daemon_reload: true
- name: Vérifier la config avec un dry-run
ansible.builtin.command: unattended-upgrade --dry-run --debug
register: dry_run_result
changed_when: false
failed_when: false
- name: Afficher le résultat du dry-run
ansible.builtin.debug:
var: dry_run_result.stdout_lines
handlers:
- name: Recharger systemd
ansible.builtin.systemd:
daemon_reload: true
+132
View File
@@ -0,0 +1,132 @@
---
- name: Configure WireGuard VPN Client
hosts: load-balancer
become: true
vars:
vpn_ip: "10.0.0.12"
vpn_private_key: "+E1pcelkLIr0x6FcOofnAWAFr6g5qHu8QHpG1sY6mG4="
vpn_server_pubkey: "VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg="
vpn_server_ip: "131.123.42.158"
vpn_server_port: "51820"
vpn_network: "10.0.0.0/24"
vpn_private_ip: "10.0.0.1"
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install WireGuard and tools
apt:
name:
- wireguard
- wireguard-tools
state: present
- name: Create WireGuard config directory
file:
path: /etc/wireguard
state: directory
mode: '0700'
- name: Create WireGuard config
copy:
content: |
[Interface]
Address = {{ vpn_ip }}/32
ListenPort = 51821
PrivateKey = {{ vpn_private_key }}
[Peer]
PublicKey = {{ vpn_server_pubkey }}
Endpoint = {{ vpn_server_ip }}:{{ vpn_server_port }}
AllowedIPs = {{ vpn_network }}
PersistentKeepalive = 25
dest: /etc/wireguard/wg0.conf
mode: '0600'
register: wg_config
- name: Enable WireGuard service
systemd:
name: wg-quick@wg0
enabled: yes
state: started
- name: Redémarrer WireGuard si la config a changé (state:started ne recharge pas seul)
systemd:
name: wg-quick@wg0
state: restarted
when: wg_config.changed
- name: Enregistrer le pair load-balancer sur le serveur VPN
hosts: vpn-uber
become: true
vars:
vpn_ip: "10.0.0.12"
vpn_private_key: "+E1pcelkLIr0x6FcOofnAWAFr6g5qHu8QHpG1sY6mG4="
tasks:
- name: Calculer la clé publique du client à partir de sa clé privée
command: wg pubkey
args:
stdin: "{{ vpn_private_key }}"
delegate_to: localhost
become: false
register: client_pubkey
changed_when: false
no_log: true
tags: [vpn]
- name: Ajouter le pair dans wg0.conf
ansible.builtin.blockinfile:
path: /etc/wireguard/wg0.conf
marker: "# {mark} PEER load-balancer"
insertafter: EOF
block: |
[Peer]
PublicKey = {{ client_pubkey.stdout }}
AllowedIPs = {{ vpn_ip }}/32
register: peer_added
tags: [vpn]
- name: Appliquer la config sans couper les autres pairs
ansible.builtin.shell: wg syncconf wg0 <(wg-quick strip wg0)
args:
executable: /bin/bash
when: peer_added.changed
tags: [vpn]
- name: Vérifier la connectivité VPN du load-balancer
hosts: load-balancer
become: true
vars:
vpn_ip: "10.0.0.12"
vpn_private_ip: "10.0.0.1"
tasks:
- name: Wait for VPN connection to establish
pause:
seconds: 3
- name: Check VPN connection
command: ip addr show wg0
register: wg_status
changed_when: false
- name: Test VPN connectivity
command: ping -c 1 "{{ vpn_private_ip }}"
register: ping_result
ignore_errors: yes
- name: Display VPN status
debug:
msg: |
VPN Configuration Complete!
IP Address: {{ vpn_ip }}
WireGuard Status:
{{ wg_status.stdout }}
Ping result ({{ 'SUCCESS' if ping_result.rc == 0 else 'FAILED' }}):
{{ ping_result.stdout | default('Connection test deferred') }}
+15
View File
@@ -0,0 +1,15 @@
# ============================================================
# Variables de l'ancien déploiement natif (hosts demo-uber / uber-stup-web,
# playbooks playbook-backend.yml / playbook-frontend.yml / playbook-file.yml).
# Ces playbooks n'existent plus dans infra/, prod/ ou pre-prod/, et aucune de
# ces variables n'est référencée par un playbook actif.
# Conservées pour ne rien casser silencieusement — à confirmer puis supprimer.
# ============================================================
directory: "/home/ubuntu"
backend_dir: "/home/ubuntu/backend"
backend_binary: "/home/ubuntu/backend/main"
uploads_dir: "/home/ubuntu/backend/uploads"
frontend_port: 5173
user_web: "www-data"
discord_webhook_url: "REMPLACER_PAR_TON_WEBHOOK_DISCORD"
user_owner: "root"
+14
View File
@@ -0,0 +1,14 @@
# Docker — utilisé par tous les playbooks qui installent Docker (prod, pre-prod, infra)
docker_user: "ubuntu"
docker_group: "docker"
docker_dir: "/home/ubuntu/docker"
# Notification Discord infra (unattended-upgrades, etc.) — infra/playbook-unattended-upgrades.yml
# Laisser vide désactive silencieusement l'envoi (voir unattended-upgrades-notify.sh.j2).
discord_infra_webhook_url: "https://discord.com/api/webhooks/1526937190410621032/EUxNm2AMFByTPa9sWmwiX1fDpl41eEKO1ssj9gJxCLkqAwQ4laRSDXNxMeToseLQROAc"
# CIS 3.3.1 (ip forwarding) — hardening/playbook-kernel-sysctl-hardening.yml
# Par défaut laissé actif (false = ne pas désactiver) : la plupart des hosts font tourner Docker
# (NAT des ports publiés) ou servent de relais VPN. Surchargé à true uniquement pour les hosts
# sans aucun rôle de forwarding (voir group_vars/load-balancer).
ip_forwarding_disabled: false
@@ -0,0 +1,40 @@
# PostgreSQL
pg_version: "16"
db_user: admin_gestion_commande_db
db_name: gestion_commande_db
db_password: "1SWDxH20rV7K2Uc2PNlwCaCxfVZEtKomF0CK9OMh"
db_host: "localhost"
db_port: 5432
# Redis
redis_bind: "127.0.0.1"
redis_port: 6379
redis_maxmemory: "1024mb"
redis_maxmemory_policy: "allkeys-lru"
redis_password: "k6UYX9RtuXJVV1HUeefbSukMcSwjvVgRsh2qJGPh"
# Nginx / backend
nginx_enable_firewall: true
backend_local_port: 8080
backend_port: 80
nginx_proxy_timeout: 60
nginx_max_body_size: "10M"
# Sauvegarde Postgres (Plakar) — prod/playbook-backup.yml
plakar_version: "1.1.4"
plakar_go_version: "1.24.0"
plakar_backup_user: plakar-backup
plakar_home_dir: /var/lib/plakar-backup
plakar_dump_dir: /var/lib/plakar-backup/dumps
plakar_config_dir: /etc/plakar-backup
plakar_passphrase_file: /etc/plakar-backup/passphrase
# Dépôt Plakar (Kloset) distant — stocké sur MinIO via S3
plakar_store_name: minio-backup
plakar_s3_endpoint: "10.0.0.4:8080"
plakar_s3_bucket: backup-db-prod-mln
plakar_s3_prefix: postgresql
plakar_s3_access_key: admin
plakar_s3_secret_key: "admin@12345"
plakar_s3_use_tls: true
plakar_email: "free6nk33@gmail.com"
@@ -0,0 +1,4 @@
# Utilisé par infra/playbook-gitea-runner.yml (configuration du swap sur le runner CI)
swap_size: 4G
swap_file: /swapfile
swap_swappiness: 10
+4
View File
@@ -0,0 +1,4 @@
# Utilisé par infra/playbook-harden-ssh.yml
user_ssh: "omnex"
group_ssh: "ssh"
password_user_ssh: "$6$w6GBkTHgj8K84P7o$pToS4pRMGLKwfpRfnz6i.WpO72Lt5DLtFQWZJsKL2dKvQrdKdi2q8xRuWqq/W3ybFNLQapvAqN33HexVjajGw0" #aiQgIAcCVIy1E33ReRssfm414dm
@@ -0,0 +1,9 @@
# Utilisé par infra/playbook-harden-ssh.yml
user_ssh: "omnex"
group_ssh: "ssh"
password_user_ssh: "$6$w6GBkTHgj8K84P7o$pToS4pRMGLKwfpRfnz6i.WpO72Lt5DLtFQWZJsKL2dKvQrdKdi2q8xRuWqq/W3ybFNLQapvAqN33HexVjajGw0" #aiQgIAcCVIy1E33ReRssfm414dm
# CIS 3.3.1 — hardening/playbook-kernel-sysctl-hardening.yml
# load-balancer est un reverse proxy applicatif pur (pas de Docker, pas de relais VPN) :
# aucun besoin de forwarding kernel, peut être désactivé sans risque.
ip_forwarding_disabled: true
+3
View File
@@ -0,0 +1,3 @@
# Dossier source du projet Docker Compose copié sur l'hôte (pre-prod/playbook-pre-prod.yml)
# NOTE : ce dossier n'existe pas encore dans le repo (voir remarque précédente sur docker-pre-prod/ manquant)
compose_project_dir: "../docker-pre-prod/"
+7
View File
@@ -0,0 +1,7 @@
# Dossier source du projet Docker Compose copié sur l'hôte (prod/playbook-prod.yml)
compose_project_dir: "../docker-prod/"
# Utilisé uniquement par prod/playbook-s3-img.yml (les autres playbooks du groupe utilisent ansible_user)
user_deploy: "ubuntu"
docker_group: "docker"
docker_dir: "/home/ubuntu/docker/s3-img"
@@ -0,0 +1,4 @@
# Même stack que prod (prod/playbook-prod.yml cible désormais ce groupe)
compose_project_dir: "../../docker-prod/"
docker_group: "docker"
user_deploy: "ubuntu"
+36
View File
@@ -0,0 +1,36 @@
[prod]
prod-uber ansible_host=131.123.39.119 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.6 ansible_become_pass=aiQgIAcCVIy1E33ReRssfm414dm
[pre-prod]
pre-prod-uber ansible_host=131.123.39.112 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.7 ansible_become_pass=aiQgIAcCVIy1E33ReRssfm414dm
[bdd-redis-prod]
bdd-redis-prod ansible_host=132.243.162.62 ansible_user=root ansible_ssh_private_key_file=../.ssh/key_bdd_redis vpn_ip=10.0.0.5
[replica-prod]
replica-prod-uber ansible_host=132.243.162.161 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.9
[infra-monitoring-security]
monitoring-uber ansible_host=185.103.167.138 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.2
[infra-runner]
runner-uber ansible_host=185.103.164.200 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex
[infra-utils]
s3-uber ansible_host=80.96.58.164 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex ansible_become_pass=aiQgIAcCVIy1E33ReRssfm414dm
[vpn-uber]
vpn-uber ansible_host=131.123.42.158 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.1
[load-balancer]
load-balancer ansible_host=93.185.166.221 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex
[infra]
monitoring-uber ansible_host=185.103.167.138 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.2
vpn-uber ansible_host=131.123.42.158 ansible_user=omnex ansible_ssh_private_key_file=../.ssh/omnex vpn_ip=10.0.0.1
backup-mln ansible_host=85.121.176.241 ansible_user=root ansible_ssh_private_key_file=../.ssh/key_backup vpn_ip=10.0.0.4
[infra:children]
infra-monitoring-security
infra-runner
infra-utils
+9
View File
@@ -0,0 +1,9 @@
# Utilisé par infra/playbook-certbot.yml (certificat unique couvrant le domaine + sous-domaines)
# Valeurs héritées de l'ancien group_vars/all/vars.yml ("demo-uber.xyz") — ne correspondent
# pas au domaine réel du VPS monitoring documenté dans INFRA.md ("uber-stup.club").
# À CONFIRMER avant tout run de ce playbook, sous peine de générer un certificat pour le mauvais domaine.
domain_name: "demo-uber.xyz"
subdomains:
- wazuh.demo-uber.xyz
- dozzle.demo-uber.xyz
- beszel.demo-uber.xyz
+164
View File
@@ -0,0 +1,164 @@
---
- name: Déploiement de la pre-prod
hosts: pre-prod
become: true
gather_facts: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- acl
state: present
tags: docker
- name: Create directory for Docker GPG key
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tags: docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
tags: docker
- name: Get system architecture
command: dpkg --print-architecture
register: system_arch
changed_when: false
- name: Get Ubuntu codename
command: lsb_release -cs
register: ubuntu_codename
changed_when: false
- name: Add Docker repository
apt_repository:
repo: "deb [arch={{ system_arch.stdout }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename.stdout }} stable"
state: present
filename: docker
tags: docker
- name: Update apt cache after adding repo
apt:
update_cache: yes
tags: docker
- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
tags: docker
- name: Ensure Docker service is started and enabled
systemd:
name: docker
state: started
enabled: yes
tags: docker
- name: Create docker group
group:
name: "{{ docker_group }}"
state: present
tags: docker
- name: Create docker user
user:
name: "{{ docker_user }}"
group: "{{ docker_group }}"
groups: docker
append: yes
shell: /bin/bash
create_home: yes
state: present
tags: docker
- name: Add ansible user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
when: ansible_user is defined
tags: docker
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
tags: docker
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
tags: docker
- name: Display versions
debug:
msg:
- "{{ docker_version.stdout }}"
- "{{ compose_version.stdout }}"
tags: docker
- name: Copy Docker project
ansible.builtin.copy:
src: "{{ compose_project_dir }}"
dest: "{{ docker_dir }}/"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Ensure parent directory has correct permissions
ansible.builtin.file:
path: "{{ docker_dir }}"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Set correct permissions for Docker files
ansible.builtin.file:
path: "{{ docker_dir }}"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
recurse: yes
tags: project
- name: Set permissions for .env file
ansible.builtin.file:
path: "{{ docker_dir }}/.env.example"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
tags: project
ignore_errors: yes
- name: Set permissions for lbtelegram.env file
ansible.builtin.file:
path: "{{ docker_dir }}/lbtelegram.env"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
tags: project
ignore_errors: yes
+308
View File
@@ -0,0 +1,308 @@
---
# ============================================
# PostgreSQL Installation (vm-postgres uniquement)
# ============================================
- name: Installation et configuration de PostgreSQL
hosts: bdd-redis-prod
become: true
gather_facts: true
tasks:
# ============================================================
# Dépendances requises
# ============================================================
- name: Installer les dépendances pour le dépôt APT
ansible.builtin.apt:
name:
- gnupg2
- curl
- ca-certificates
- lsb-release
- python3-psycopg2
- acl
state: present
update_cache: yes
# ============================================================
# Ajout du dépôt officiel PostgreSQL
# ============================================================
- name: Ajouter la clé GPG du dépôt PostgreSQL
ansible.builtin.apt_key:
url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
state: present
- name: Ajouter le dépôt PostgreSQL 16
ansible.builtin.apt_repository:
repo: "deb http://apt.postgresql.org/pub/repos/apt {{ ansible_distribution_release }}-pgdg main"
state: present
filename: pgdg
# ============================================================
# Installation PostgreSQL 16
# ============================================================
- name: Installer PostgreSQL 16
ansible.builtin.apt:
name:
- "postgresql-{{ pg_version }}"
- "postgresql-client-{{ pg_version }}"
- "postgresql-contrib-{{ pg_version }}"
state: present
update_cache: yes
- name: Démarrer et activer PostgreSQL
ansible.builtin.systemd:
name: postgresql
state: started
enabled: yes
# ============================================================
# Configuration PostgreSQL
# ============================================================
- name: Configurer l'écoute sur localhost
ansible.builtin.lineinfile:
path: "/etc/postgresql/{{ pg_version }}/main/postgresql.conf"
regexp: "^#?listen_addresses"
line: "listen_addresses = 'localhost'"
notify: Restart postgresql
- name: Configurer le port
ansible.builtin.lineinfile:
path: "/etc/postgresql/{{ pg_version }}/main/postgresql.conf"
regexp: "^#?port"
line: "port = {{ db_port }}"
notify: Restart postgresql
# ============================================================
# Locale requise
# ============================================================
- name: Générer la locale fr_FR.UTF-8
ansible.builtin.command: locale-gen fr_FR.UTF-8
register: locale_gen
changed_when: "'Generation complete' in locale_gen.stdout"
- name: Mettre à jour les locales
ansible.builtin.command: update-locale
when: locale_gen.changed
# ============================================================
# Création de la base de données et configuration utilisateur
# ============================================================
- name: Définir le mot de passe de l'utilisateur postgres
become_user: postgres
ansible.builtin.postgresql_user:
name: "{{ db_user }}"
password: "{{ db_password }}"
state: present
- name: Vérifier si la base de données existe
become_user: postgres
ansible.builtin.command: psql -tAc "SELECT 1 FROM pg_database WHERE datname='{{ db_name }}'"
register: db_exists
changed_when: false
- name: Créer la base de données avec ICU
become_user: postgres
ansible.builtin.command: >
psql -c "CREATE DATABASE {{ db_name }}
OWNER {{ db_user }}
ENCODING 'UTF8'
LOCALE_PROVIDER icu
ICU_LOCALE 'fr-FR'
TEMPLATE template0"
when: db_exists.stdout != "1"
- name: Accorder tous les privilèges sur la base
become_user: postgres
ansible.builtin.postgresql_privs:
db: "{{ db_name }}"
role: "{{ db_user }}"
type: database
privs: ALL
state: present
# ============================================================
# Authentification pg_hba.conf
# ============================================================
- name: Configurer l'authentification md5 pour les connexions locales
ansible.builtin.postgresql_pg_hba:
dest: "/etc/postgresql/{{ pg_version }}/main/pg_hba.conf"
contype: host
databases: "{{ db_name }}"
users: "{{ db_user }}"
source: "127.0.0.1/32"
method: md5
notify: Restart postgresql
- name: Configurer l'authentification md5 pour IPv6 local
ansible.builtin.postgresql_pg_hba:
dest: "/etc/postgresql/{{ pg_version }}/main/pg_hba.conf"
contype: host
databases: "{{ db_name }}"
users: "{{ db_user }}"
source: "::1/128"
method: md5
notify: Restart postgresql
# ============================================================
# Vérification
# ============================================================
- name: Vérifier la version de PostgreSQL
become_user: postgres
ansible.builtin.command: psql --version
register: pg_version_check
changed_when: false
- name: Afficher la version
ansible.builtin.debug:
msg: "{{ pg_version_check.stdout }}"
- name: Vérifier que la base existe
become_user: postgres
ansible.builtin.command: psql -lqt
register: pg_databases
changed_when: false
- name: Afficher les bases de données
ansible.builtin.debug:
msg: "{{ pg_databases.stdout_lines }}"
handlers:
- name: Restart postgresql
ansible.builtin.systemd:
name: postgresql
state: restarted
# ============================================
# Redis Installation (vm-redis uniquement)
# ============================================
- name: Installation et configuration de Redis
hosts: bdd-redis-prod
become: true
gather_facts: true
tasks:
- name: Installer Redis
apt:
name:
- redis-server
- python3-redis
state: present
update_cache: yes
- name: Créer le répertoire de backup Redis
file:
path: /var/lib/redis/backup
state: directory
owner: redis
group: redis
mode: "0755"
- name: Configurer Redis - bind address (accepter connexions réseau)
lineinfile:
path: /etc/redis/redis.conf
regexp: "^bind"
line: "bind 127.0.0.1 {{ vpn_ip }}"
state: present
notify: Redémarrer Redis
- name: Configurer Redis - port
lineinfile:
path: /etc/redis/redis.conf
regexp: "^port"
line: "port {{ redis_port }}"
state: present
notify: Redémarrer Redis
- name: Configurer Redis - maxmemory
lineinfile:
path: /etc/redis/redis.conf
regexp: "^# ?maxmemory"
line: "maxmemory {{ redis_maxmemory }}"
state: present
notify: Redémarrer Redis
- name: Configurer Redis - maxmemory-policy
lineinfile:
path: /etc/redis/redis.conf
regexp: "^# ?maxmemory-policy"
line: "maxmemory-policy {{ redis_maxmemory_policy }}"
state: present
notify: Redémarrer Redis
- name: Configurer Redis - requirepass
lineinfile:
path: /etc/redis/redis.conf
regexp: "^# ?requirepass"
line: "requirepass {{ redis_password }}"
state: present
notify: Redémarrer Redis
- name: Activer AOF (Append Only File) pour Redis
lineinfile:
path: /etc/redis/redis.conf
regexp: "^appendonly"
line: "appendonly yes"
state: present
notify: Redémarrer Redis
- name: Configurer la fréquence de sync AOF
lineinfile:
path: /etc/redis/redis.conf
regexp: "^# ?appendfsync"
line: "appendfsync everysec"
state: present
notify: Redémarrer Redis
- name: Activer la persistance RDB (snapshots)
blockinfile:
path: /etc/redis/redis.conf
block: |
save 900 1
save 300 10
save 60 10000
marker: "# {mark} ANSIBLE MANAGED RDB PERSISTENCE"
notify: Redémarrer Redis
- name: Configurer le nom du fichier AOF
lineinfile:
path: /etc/redis/redis.conf
regexp: "^appendfilename"
line: 'appendfilename "appendonly.aof"'
state: present
notify: Redémarrer Redis
- name: Désactiver le mode protégé Redis
lineinfile:
path: /etc/redis/redis.conf
regexp: "^protected-mode"
line: "protected-mode no"
state: present
notify: Redémarrer Redis
- name: Configurer le répertoire de travail Redis
lineinfile:
path: /etc/redis/redis.conf
regexp: "^dir"
line: "dir /var/lib/redis"
state: present
notify: Redémarrer Redis
- name: Définir le niveau de log
lineinfile:
path: /etc/redis/redis.conf
regexp: "^loglevel"
line: "loglevel notice"
state: present
notify: Redémarrer Redis
- name: S'assurer que Redis est démarré
systemd:
name: redis-server
state: started
enabled: yes
handlers:
- name: Redémarrer Redis
systemd:
name: redis-server
state: restarted
+172
View File
@@ -0,0 +1,172 @@
---
- name: Installation et configuration du frontend et backend
hosts: replica-prod
become: true
gather_facts: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- acl
state: present
tags: docker
- name: Create directory for Docker GPG key
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tags: docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
tags: docker
- name: Get system architecture
command: dpkg --print-architecture
register: system_arch
changed_when: false
- name: Get Ubuntu codename
command: lsb_release -cs
register: ubuntu_codename
changed_when: false
- name: Add Docker repository
apt_repository:
repo: "deb [arch={{ system_arch.stdout }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename.stdout }} stable"
state: present
filename: docker
tags: docker
- name: Update apt cache after adding repo
apt:
update_cache: yes
tags: docker
- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
tags: docker
- name: Ensure Docker service is started and enabled
systemd:
name: docker
state: started
enabled: yes
tags: docker
- name: Create docker group
group:
name: "{{ docker_group }}"
state: present
tags: docker
- name: Create docker user
user:
name: "{{ docker_user }}"
group: "{{ docker_group }}"
groups: docker
append: yes
shell: /bin/bash
create_home: yes
state: present
tags: docker
- name: Add ansible user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
when: ansible_user is defined
tags: docker
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
tags: docker
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
tags: docker
- name: Display versions
debug:
msg:
- "{{ docker_version.stdout }}"
- "{{ compose_version.stdout }}"
tags: docker
- name: Copy Docker project
ansible.builtin.copy:
src: "{{ compose_project_dir }}"
dest: "{{ docker_dir }}/"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
- name: Ensure parent directory has correct permissions
ansible.builtin.file:
path: "{{ docker_dir }}"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Set correct permissions for Docker files
ansible.builtin.file:
path: "{{ docker_dir }}"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
recurse: yes
tags: project
- name: Set permissions for .env file
ansible.builtin.file:
path: "{{ docker_dir }}/.env.example"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
tags: project
ignore_errors: yes
- name: Set permissions for lbtelegram.env file
ansible.builtin.file:
path: "{{ docker_dir }}/.env.lbtelegram"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
tags: project
ignore_errors: yes
- name: Create certs folder
ansible.builtin.file:
path: "{{ docker_dir }}/certs"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0750"
tags: project
+155
View File
@@ -0,0 +1,155 @@
---
- name: Installation et configuration du frontend et backend
hosts: bdd-redis-prod
become: true
gather_facts: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- acl
state: present
tags: docker
- name: Create directory for Docker GPG key
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tags: docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
state: present
tags: docker
- name: Get system architecture
command: dpkg --print-architecture
register: system_arch
changed_when: false
- name: Get Ubuntu codename
command: lsb_release -cs
register: ubuntu_codename
changed_when: false
- name: Add Docker repository
apt_repository:
repo: "deb [arch={{ system_arch.stdout }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename.stdout }} stable"
state: present
filename: docker
tags: docker
- name: Update apt cache after adding repo
apt:
update_cache: yes
tags: docker
- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
tags: docker
- name: Ensure Docker service is started and enabled
systemd:
name: docker
state: started
enabled: yes
tags: docker
- name: Create docker group
group:
name: "{{ docker_group }}"
state: present
tags: docker
- name: Create docker user
user:
name: "{{ docker_user }}"
group: "{{ docker_group }}"
groups: docker
append: yes
shell: /bin/bash
create_home: yes
state: present
tags: docker
- name: Add ansible user to docker group
user:
name: "{{ user_deploy }}"
groups: docker
append: yes
when: user_deploy is defined
tags: docker
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
tags: docker
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
tags: docker
- name: Display versions
debug:
msg:
- "{{ docker_version.stdout }}"
- "{{ compose_version.stdout }}"
tags: docker
- name: Copy Docker project
ansible.builtin.copy:
src: ../../monitoring/s3-img
dest: "{{ docker_dir }}/"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Ensure parent directory has correct permissions
ansible.builtin.file:
path: "{{ docker_dir }}"
state: directory
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0755"
tags: project
- name: Set correct permissions for Docker files
ansible.builtin.file:
path: "{{ docker_dir }}"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
recurse: yes
tags: project-fin
- name: Set permissions for .env file
ansible.builtin.file:
path: "{{ docker_dir }}/.env"
owner: "{{ docker_user }}"
group: "{{ docker_group }}"
mode: "0640"
tags: project
ignore_errors: yes
+2
View File
@@ -0,0 +1,2 @@
# CIS Ubuntu 24.04 - core dumps restreints (1.5.3)
* hard core 0
+2
View File
@@ -0,0 +1,2 @@
# CIS Ubuntu 24.04 - core dumps restreints (1.5.3)
fs.suid_dumpable = 0
@@ -0,0 +1,29 @@
# CIS Ubuntu 24.04 - durcissement sysctl reseau (3.3.x)
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.tcp_syncookies = 1
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
{% if ip_forwarding_disabled | default(false) %}
# ip_forward desactive : ce host ne fait tourner ni Docker (NAT de ports publies) ni de relais VPN.
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
{% else %}
# ip_forward laisse actif (defaut systeme = 1) : ce host heberge Docker et/ou fait relais VPN,
# le desactiver casserait le NAT des ports publies ou le routage entre pairs WireGuard.
# CIS 35608 restera donc en echec ici par necessite fonctionnelle, pas par oubli.
{% endif %}
+18
View File
@@ -0,0 +1,18 @@
# Exclusions specifiques a cette infra (donnees volumineuses, evolution constante,
# integrite geree par leurs propres mecanismes — pas pertinent pour un check AIDE)
!/var/lib/docker$
!/var/lib/docker/.*
!/var/lib/wazuh-indexer$
!/var/lib/wazuh-indexer/.*
!/var/lib/postgresql$
!/var/lib/postgresql/.*
!/var/lib/redis$
!/var/lib/redis/.*
!/var/lib/clamav$
!/var/lib/clamav/.*
!/var/lib/beszel-agent$
!/var/lib/beszel-agent/.*
!/var/log/audit$
!/var/log/audit/.*
!/var/lib/containerd$
!/var/lib/containerd/.*
+12
View File
@@ -0,0 +1,12 @@
# CIS Ubuntu 24.04 - regles d'audit detaillees : surveillance sudo (6.2.3.1-6.2.3.3)
# 6.2.3.1 - changements de perimetre d'administration (sudoers)
-w /etc/sudoers -p wa -k scope
-w /etc/sudoers.d -p wa -k scope
# 6.2.3.2 - toute action executee en tant qu'un autre utilisateur (sudo -u, su, etc.)
-a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation
-a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation
# 6.2.3.3 - modifications du fichier de log sudo
-w /var/log/sudo.log -p wa -k sudo_log_file
+5
View File
@@ -0,0 +1,5 @@
# Regle custom (hors CIS) - surveillance dediee de l'utilisateur omnex
# auid (login uid) persiste a travers tout sudo/su : capture donc TOUTES les commandes
# executees par omnex, meme apres elevation de privileges.
-a always,exit -F arch=b64 -F auid={{ omnex_uid }} -S execve -k omnex_actions
-a always,exit -F arch=b32 -F auid={{ omnex_uid }} -S execve -k omnex_actions
+76
View File
@@ -0,0 +1,76 @@
# CIS Ubuntu 24.04 - regles d'audit detaillees restantes (6.2.3.4-6.2.3.19)
# UID_MIN lu dynamiquement depuis /etc/login.defs sur cet host : {{ uid_min }}
# 6.2.3.4 - changements date/heure
-a always,exit -F arch=b64 -S adjtimex,settimeofday -k time-change
-a always,exit -F arch=b32 -S adjtimex,settimeofday -k time-change
-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -k time-change
-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -k time-change
-w /etc/localtime -p wa -k time-change
# 6.2.3.5 - changements environnement reseau du systeme
-a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale
-a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale
-w /etc/issue -p wa -k system-locale
-w /etc/issue.net -p wa -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/networks -p wa -k system-locale
-w /etc/network/ -p wa -k system-locale
-w /etc/netplan/ -p wa -k system-locale
# 6.2.3.7 - tentatives d'acces fichier echouees
-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>={{ uid_min }} -F auid!=unset -k access
-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>={{ uid_min }} -F auid!=unset -k access
-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>={{ uid_min }} -F auid!=unset -k access
-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>={{ uid_min }} -F auid!=unset -k access
# 6.2.3.8 - modifications informations user/group
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/security/opasswd -p wa -k identity
-w /etc/nsswitch.conf -p wa -k identity
-w /etc/pam.conf -p wa -k identity
-w /etc/pam.d -p wa -k identity
# 6.2.3.9 - modifications permissions discretionnaires (DAC)
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>={{ uid_min }} -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>={{ uid_min }} -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>={{ uid_min }} -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>={{ uid_min }} -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>={{ uid_min }} -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>={{ uid_min }} -F auid!=unset -F key=perm_mod
# 6.2.3.10 - montages systeme de fichiers reussis
-a always,exit -F arch=b32 -S mount -F auid>={{ uid_min }} -F auid!=unset -k mounts
-a always,exit -F arch=b64 -S mount -F auid>={{ uid_min }} -F auid!=unset -k mounts
# 6.2.3.11 - informations d'initiation de session
-w /var/run/utmp -p wa -k session
-w /var/log/wtmp -p wa -k session
-w /var/log/btmp -p wa -k session
# 6.2.3.12 - evenements login/logout
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock -p wa -k logins
# 6.2.3.13 - suppressions de fichiers par les utilisateurs
-a always,exit -F arch=b64 -S rename,unlink,unlinkat,renameat -F auid>={{ uid_min }} -F auid!=unset -F key=delete
-a always,exit -F arch=b32 -S rename,unlink,unlinkat,renameat -F auid>={{ uid_min }} -F auid!=unset -F key=delete
# 6.2.3.14 - modifications du controle d'acces obligatoire (AppArmor)
-w /etc/apparmor/ -p wa -k MAC-policy
-w /etc/apparmor.d/ -p wa -k MAC-policy
# 6.2.3.15-17 - commandes chcon / setfacl / chacl
-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>={{ uid_min }} -F auid!=unset -k perm_chng
-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>={{ uid_min }} -F auid!=unset -k perm_chng
-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>={{ uid_min }} -F auid!=unset -k perm_chng
# 6.2.3.18 - commande usermod
-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>={{ uid_min }} -F auid!=unset -k usermod
# 6.2.3.19 - chargement/dechargement/modification de modules noyau
-a always,exit -F arch=b64 -S init_module,finit_module,delete_module,create_module,query_module -F auid>={{ uid_min }} -F auid!=unset -k kernel_modules
-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>={{ uid_min }} -F auid!=unset -k kernel_modules
@@ -0,0 +1,33 @@
# CIS Ubuntu 24.04 - modules noyau reseau et filesystem inutilises dans cette infra (3.2.1-3.2.4, 1.1.1.10)
install dccp /bin/false
blacklist dccp
install tipc /bin/false
blacklist tipc
install rds /bin/false
blacklist rds
install sctp /bin/false
blacklist sctp
install afs /bin/false
blacklist afs
install ceph /bin/false
blacklist ceph
install cifs /bin/false
blacklist cifs
install exfat /bin/false
blacklist exfat
install ext /bin/false
blacklist ext
# fat et fuse sont compiles en dur dans le noyau Ubuntu (builtin, pas de .ko) : cette entree
# ne change rien fonctionnellement et le check CIS 35509 restera en echec pour ces deux modules.
install fat /bin/false
blacklist fat
install fscache /bin/false
blacklist fscache
install fuse /bin/false
blacklist fuse
install gfs2 /bin/false
blacklist gfs2
install nfs_common /bin/false
blacklist nfs_common
install nfsd /bin/false
blacklist nfsd
+1
View File
@@ -0,0 +1 @@
PasswordAuthentication no
+5
View File
@@ -0,0 +1,5 @@
# Géré par Ansible (playbook-pam-hardening.yml) — CIS Ubuntu 24.04 (5.5.2/5.5.3)
deny = {{ pam_faillock_deny }}
unlock_time = {{ pam_faillock_unlock_time }}
fail_interval = {{ pam_faillock_fail_interval }}
even_deny_root
+48
View File
@@ -0,0 +1,48 @@
global
log stdout format raw local0
maxconn 4096
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
retries 2
frontend fe_https
bind *:443 ssl crt /etc/haproxy/certs/mln-uber.club.pem
mode http
option forwardfor
http-request set-header X-Forwarded-Proto https
# /api/* et /uploads/* -> pool backend (Go), tout le reste -> pool frontend (SPA)
acl is_api path_beg /api/ /uploads/ /webhook/telegram /webhook/nowpayment
use_backend be_api if is_api
default_backend be_frontend
# ============================================================
# Pool API — TODO : /api/health n'existe pas encore côté backend,
# ce check échouera tant que la route n'est pas ajoutée.
# ============================================================
backend be_api
mode http
option httpchk GET /api/health
http-check expect status 200
server prod-uber {{ hostvars['prod-uber']['vpn_ip'] }}:443 ssl verify none check inter 2s fall 3 rise 2
server replica-prod-uber {{ hostvars['replica-prod-uber']['vpn_ip'] | default('TODO-vpn_ip-manquant') }}:443 ssl verify none check inter 2s fall 3 rise 2 backup
# ============================================================
# Pool frontend statique (SPA) — check sur "/" (toujours servi par nginx même si l'API est down)
# ============================================================
backend be_frontend
mode http
option httpchk GET /
http-check expect status 200
server prod-uber {{ hostvars['prod-uber']['vpn_ip'] }}:443 ssl verify none check inter 2s fall 3 rise 2
server replica-prod-uber {{ hostvars['replica-prod-uber']['vpn_ip'] | default('TODO-vpn_ip-manquant') }}:443 ssl verify none check inter 2s fall 3 rise 2 backup
+9
View File
@@ -0,0 +1,9 @@
Name: Notify of failed login attempts and reset count upon success
Default: yes
Priority: 1024
Auth-Type: Primary
Auth:
requisite pam_faillock.so preauth
Account-Type: Primary
Account:
required pam_faillock.so
+6
View File
@@ -0,0 +1,6 @@
Name: Enable pam_faillock to deny access
Default: yes
Priority: 0
Auth-Type: Primary
Auth:
[default=die] pam_faillock.so authfail
+6
View File
@@ -0,0 +1,6 @@
Name: pwhistory password history checking
Default: yes
Priority: 1024
Password-Type: Primary
Password:
requisite pam_pwhistory.so remember={{ pam_pwhistory_remember }} enforce_for_root try_first_pass use_authtok
+7
View File
@@ -0,0 +1,7 @@
Name: Pwquality password strength checking
Default: yes
Priority: 1024
Conflicts: cracklib
Password-Type: Primary
Password:
requisite pam_pwquality.so retry=3
+23
View File
@@ -0,0 +1,23 @@
Name: Unix authentication
Default: yes
Priority: 256
Auth-Type: Primary
Auth:
[success=end default=ignore] pam_unix.so try_first_pass
Auth-Initial:
[success=end default=ignore] pam_unix.so
Account-Type: Primary
Account:
[success=end new_authtok_reqd=done default=ignore] pam_unix.so
Account-Initial:
[success=end new_authtok_reqd=done default=ignore] pam_unix.so
Session-Type: Additional
Session:
required pam_unix.so
Session-Initial:
required pam_unix.so
Password-Type: Primary
Password:
[success=end default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt
Password-Initial:
[success=end default=ignore] pam_unix.so obscure yescrypt
+8
View File
@@ -0,0 +1,8 @@
# Géré par Ansible (playbook-pam-hardening.yml) — CIS Ubuntu 24.04 (5.5.4)
minlen = {{ pam_pwquality_minlen }}
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
difok = {{ pam_pwquality_difok }}
enforce_for_root
+123
View File
@@ -0,0 +1,123 @@
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
# This sshd was compiled with PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented. Uncommented options override the
# default value.
Include /etc/ssh/sshd_config.d/*.conf
#Port 22
AddressFamily inet
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
# Logging
#SyslogFacility AUTH
#LogLevel INFO
# Authentication:
AllowGroups {{ group_ssh }}
AllowUsers {{ user_ssh }}
#LoginGraceTime 2m
PermitRootLogin no
#StrictModes yes
MaxAuthTries 3
MaxSessions 2
PubkeyAuthentication yes
# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
#AuthorizedPrincipalsFile none
#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes
# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication no
#PermitEmptyPasswords no
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
KbdInteractiveAuthentication no
# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the KbdInteractiveAuthentication and
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication via KbdInteractiveAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and KbdInteractiveAuthentication to 'no'.
UsePAM yes
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /run/sshd.pid
MaxStartups 10:30:60
PermitTunnel no
#ChrootDirectory none
#VersionAddendum none
# no default banner path
#Banner none
# Allow client to pass locale environment variables
AcceptEnv LANG LC_*
# override default of no subsystems
Subsystem sftp /usr/lib/openssh/sftp-server
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server
+5
View File
@@ -0,0 +1,5 @@
// Généré par ansible (infra/playbook-unattended-upgrades.yml) — ne pas éditer à la main.
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
+30
View File
@@ -0,0 +1,30 @@
// Généré par ansible (infra/playbook-unattended-upgrades.yml) — ne pas éditer à la main.
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Ubuntu,archive=${distro_codename}-security";
};
// Paquets sensibles exclus — mise à jour manuelle et review requises :
// - postgresql/redis : pas de tolérance à une régression sur la DB/cache
// - docker/containerd : un redémarrage du daemon peut interrompre les conteneurs en cours
// - wireguard : composant réseau critique du VPN inter-serveurs
Unattended-Upgrade::Package-Blacklist {
"postgresql*";
"redis*";
"docker-ce*";
"docker-ce-cli*";
"containerd.io";
"wireguard*";
};
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
Unattended-Upgrade::Automatic-Reboot-Time "00:00";
// Pas d'email (host headless) — notification Telegram via le script/timer dédié.
Unattended-Upgrade::Mail "";
@@ -0,0 +1,7 @@
[Unit]
Description=Notification Telegram post unattended-upgrades
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/unattended-upgrades-notify.sh
@@ -0,0 +1,38 @@
#!/bin/bash
# Généré par ansible (infra/playbook-unattended-upgrades.yml) — ne pas éditer à la main.
set -euo pipefail
LOG="/var/log/unattended-upgrades/unattended-upgrades.log"
TODAY="$(date +%Y-%m-%d)"
HOSTNAME="$(hostname)"
WEBHOOK_URL="{{ discord_infra_webhook_url }}"
if [ -z "$WEBHOOK_URL" ]; then
exit 0
fi
INSTALLED=""
if [ -f "$LOG" ]; then
INSTALLED=$(grep "^$TODAY" "$LOG" | grep -i "Packages that will be upgraded\|Packages that are upgraded" || true)
fi
REBOOT_STATUS="Pas de redémarrage requis"
if [ -f /var/run/reboot-required ]; then
REBOOT_STATUS="⚠️ Redémarrage requis (programmé à 00:00 UTC)"
fi
if [ -z "$INSTALLED" ]; then
MESSAGE="🔒 [$HOSTNAME] Unattended-upgrades — aucun paquet de sécurité à installer aujourd'hui."
else
MESSAGE="🔒 [$HOSTNAME] Unattended-upgrades — paquets mis à jour aujourd'hui :
${INSTALLED}
${REBOOT_STATUS}"
fi
PAYLOAD="$(jq -n --arg content "$MESSAGE" '{content: $content}')"
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
> /dev/null
@@ -0,0 +1,10 @@
[Unit]
Description=Déclenche la notification Telegram post unattended-upgrades
[Timer]
OnCalendar=*-*-* 00:30:00
Persistent=true
RandomizedDelaySec=120
[Install]
WantedBy=timers.target