119 lines
4.9 KiB
YAML
119 lines
4.9 KiB
YAML
|
|
---
|
||
|
|
# Devis d'etancheite de la frontiere — RELEVE seul. Ne modifie rien (D-23).
|
||
|
|
#
|
||
|
|
# Question posee : la frontiere laisse-t-elle passer ce que la politique n'autorise PAS ?
|
||
|
|
# `devis-expositions.yml` pose la question inverse — chaque exposition declaree repond-elle.
|
||
|
|
# Les deux sont necessaires : un pare-feu peut servir tout ce qu'on lui demande *et*
|
||
|
|
# laisser passer tout le reste.
|
||
|
|
#
|
||
|
|
# LES CIBLES SONT LES PORTS REELLEMENT EN ECOUTE, releves ici. Sonder un port ferme ne
|
||
|
|
# prouverait rien du pare-feu : le refus viendrait de la machine, pas de la bordure.
|
||
|
|
#
|
||
|
|
# LE CONTROLE D'ABORD. La sonde interroge une adresse ou personne n'ecoute avant tout
|
||
|
|
# verdict ; si elle repond, le releve est declare NUL. C'est la lecon du 2026-08-09 :
|
||
|
|
# `connect()` a menti deux jours durant a travers un `synproxy` herite.
|
||
|
|
#
|
||
|
|
# make frontiere-mesurer
|
||
|
|
|
||
|
|
- name: Relevé des ports réellement en écoute
|
||
|
|
hosts: hotes_actifs
|
||
|
|
become: true
|
||
|
|
gather_facts: false
|
||
|
|
|
||
|
|
tasks:
|
||
|
|
- name: Recenser les ports TCP en écoute
|
||
|
|
ansible.builtin.shell:
|
||
|
|
executable: /bin/bash
|
||
|
|
# `|| true` en fin de chaîne : avec `pipefail`, un `grep -v` qui ne garde rien
|
||
|
|
# tuerait la tâche. Un hôte sans écoute publique est un relevé vide, pas une panne.
|
||
|
|
cmd: >-
|
||
|
|
set -o pipefail;
|
||
|
|
ss -ltnH | awk '{print $4}'
|
||
|
|
| grep -vE '^127\.|^\[::1\]' | sed 's/.*://' | sort -un | tr '\n' ' ' || true
|
||
|
|
register: devis_frontiere_ss
|
||
|
|
changed_when: false
|
||
|
|
|
||
|
|
- name: Retenir l'écoute de cet hôte
|
||
|
|
ansible.builtin.set_fact:
|
||
|
|
devis_frontiere_ecoute:
|
||
|
|
ip: "{{ ansible_host }}"
|
||
|
|
ports: "{{ devis_frontiere_ss.stdout.split() | map('int') | list }}"
|
||
|
|
|
||
|
|
- name: Sondage depuis les deux points de vue
|
||
|
|
hosts: localhost
|
||
|
|
connection: local
|
||
|
|
become: false
|
||
|
|
gather_facts: false
|
||
|
|
|
||
|
|
tasks:
|
||
|
|
- name: Rassembler l'écoute de toute la flotte
|
||
|
|
ansible.builtin.set_fact:
|
||
|
|
devis_frontiere_ports: >-
|
||
|
|
{{ dict(groups['hotes_actifs']
|
||
|
|
| map('extract', hostvars, 'devis_frontiere_ecoute')
|
||
|
|
| list | zip(groups['hotes_actifs']) | map('reverse') | list) }}
|
||
|
|
|
||
|
|
# Forme `argv` et non `cmd` : le chemin du dépôt peut contenir une espace, et `cmd`
|
||
|
|
# découpe dessus. Mesuré ici même — « /home/.../Espace Chezlepro/... » cassait.
|
||
|
|
- name: Dériver les cibles à sonder depuis la politique
|
||
|
|
ansible.builtin.command:
|
||
|
|
argv:
|
||
|
|
- python3
|
||
|
|
- "{{ playbook_dir }}/../../scripts/devis_frontiere.py"
|
||
|
|
- --cibles
|
||
|
|
stdin: "{{ devis_frontiere_ports | to_json }}"
|
||
|
|
register: devis_frontiere_cibles
|
||
|
|
changed_when: false
|
||
|
|
|
||
|
|
- name: Sonder depuis le VLAN d'administration (le poste de l'exploitant)
|
||
|
|
ansible.builtin.command:
|
||
|
|
argv:
|
||
|
|
- python3
|
||
|
|
- "{{ playbook_dir }}/../../scripts/sonde_tcp.py"
|
||
|
|
stdin: >-
|
||
|
|
{{ {'controle': (devis_frontiere_cibles.stdout | from_json).controle,
|
||
|
|
'cibles': (devis_frontiere_cibles.stdout | from_json).admin} | to_json }}
|
||
|
|
register: devis_frontiere_admin
|
||
|
|
changed_when: false
|
||
|
|
|
||
|
|
# Play distinct, et ce n'est pas cosmétique. Dans un play `connection: local`, un
|
||
|
|
# `delegate_to` hérite de cette connexion : la sonde « tenant » s'exécutait en réalité sur
|
||
|
|
# le poste. Elle rendait donc la frontière JOIGNABLE — depuis le VLAN d'administration,
|
||
|
|
# elle l'est. Mesuré et corrigé le 2026-08-09 ; c'est la même famille de piège que le
|
||
|
|
# `connect()` : vérifier d'où l'instrument mesure avant de croire ce qu'il dit.
|
||
|
|
- name: Sondage depuis un hôte du tenant (le sens sortant)
|
||
|
|
# Un seul hôte suffit : le devis mesure la bordure, pas les hôtes. Les règles sortantes
|
||
|
|
# portent sur l'alias du tenant, donc sur tous ses hôtes indistinctement.
|
||
|
|
# Motif `groupe[0]` et non un template : un `hosts:` templaté échoue quand l'inventaire
|
||
|
|
# est vide — c'est ce qui cassait `ansible-lint`, qui s'exécute sans inventaire.
|
||
|
|
hosts: hotes_actifs[0]
|
||
|
|
become: false
|
||
|
|
gather_facts: false
|
||
|
|
|
||
|
|
tasks:
|
||
|
|
- name: Sonder les destinations internes interdites
|
||
|
|
ansible.builtin.script:
|
||
|
|
cmd: >-
|
||
|
|
"{{ playbook_dir }}/../../scripts/sonde_tcp.py"
|
||
|
|
'{{ {"controle": (hostvars["localhost"].devis_frontiere_cibles.stdout
|
||
|
|
| from_json).controle,
|
||
|
|
"cibles": (hostvars["localhost"].devis_frontiere_cibles.stdout
|
||
|
|
| from_json).tenant} | to_json }}'
|
||
|
|
args:
|
||
|
|
executable: python3
|
||
|
|
register: devis_frontiere_tenant
|
||
|
|
changed_when: false
|
||
|
|
|
||
|
|
- name: Déposer le relevé sur le contrôleur
|
||
|
|
ansible.builtin.copy:
|
||
|
|
dest: "{{ playbook_dir }}/../../instance/devis-frontiere.json"
|
||
|
|
mode: "0600"
|
||
|
|
content: >-
|
||
|
|
{{ {
|
||
|
|
'admin': hostvars['localhost'].devis_frontiere_admin.stdout | from_json,
|
||
|
|
'tenant': devis_frontiere_tenant.stdout | from_json,
|
||
|
|
'sonde_depuis': inventory_hostname,
|
||
|
|
} | to_nice_json }}
|
||
|
|
delegate_to: localhost
|
||
|
|
become: false
|