start
This commit is contained in:
1 parent
4b8bb1427a
commit
975d3e9d1d
84 files changed
+2507
-351
No files matched your search
@@ -0,0 +1,84 @@
|
||||
---
|
||||
# Rôle ssh — reprend 01-ssh (sshd_config, ssh_config, banner, motd, clés
|
||||
# autorisées) et le durcissement du projet terraform-proxmox-serveur-debian-12.
|
||||
#
|
||||
# Principe : les accès distants sont exclusivement en SSH par clé publique ;
|
||||
# le mot de passe des comptes reste utilisable en local (console, su),
|
||||
# le rôle ne le verrouille jamais.
|
||||
|
||||
ssh_port: 22
|
||||
# yes | prohibit-password | no
|
||||
# prohibit-password : root par clé uniquement (nécessaire si Ansible se
|
||||
# connecte en root) ; le mot de passe root reste utilisable sur la console.
|
||||
ssh_permit_root_login: prohibit-password
|
||||
|
||||
# Comptes autorisés en SSH. Chaque entrée : name (obligatoire), groups (liste,
|
||||
# « sudo_or_wheel » est traduit selon l'OS), sudo_nopasswd (bool), shell,
|
||||
# password_hash (optionnel, pour un mot de passe local — jamais en clair).
|
||||
# Les clés publiques sont lues dans files/keys/<name>/*.pub.
|
||||
ssh_users:
|
||||
- name: root
|
||||
|
||||
ssh_keys_dir: "{{ playbook_dir }}/files/keys"
|
||||
|
||||
# Clés centralisées dans /etc/ssh/authorized_keys/<utilisateur>, appartenant à
|
||||
# root : un utilisateur ne peut pas ajouter de clé lui-même (reprise de
|
||||
# l'ancien script, qui posait un lien symbolique vers /root/.ssh).
|
||||
# false : clés classiques dans ~/.ssh/authorized_keys.
|
||||
ssh_central_authorized_keys: true
|
||||
|
||||
# Groupes supplémentaires autorisés en SSH (en plus des ssh_users).
|
||||
ssh_allow_groups: []
|
||||
|
||||
# Durcissement du démon.
|
||||
ssh_max_auth_tries: 3
|
||||
ssh_max_sessions: 4
|
||||
ssh_max_startups: "10:30:60"
|
||||
ssh_login_grace_time: 30
|
||||
ssh_client_alive_interval: 300
|
||||
ssh_client_alive_count_max: 2
|
||||
ssh_x11_forwarding: false
|
||||
ssh_agent_forwarding: false
|
||||
ssh_tcp_forwarding: false
|
||||
ssh_log_level: VERBOSE
|
||||
|
||||
# Algorithmes (Debian/Ubuntu uniquement : sous Red Hat ils sont imposés par
|
||||
# crypto-policies, ne pas les redéfinir dans sshd_config).
|
||||
ssh_kex_algorithms:
|
||||
- [email protected]
|
||||
- curve25519-sha256
|
||||
- [email protected]
|
||||
- diffie-hellman-group16-sha512
|
||||
- diffie-hellman-group18-sha512
|
||||
ssh_ciphers:
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
- aes256-ctr
|
||||
ssh_macs:
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
ssh_host_key_algorithms:
|
||||
- ssh-ed25519
|
||||
- [email protected]
|
||||
- rsa-sha2-512
|
||||
- rsa-sha2-256
|
||||
ssh_pubkey_accepted_algorithms: "{{ ssh_host_key_algorithms }}"
|
||||
|
||||
# Bannière d'avertissement (avant authentification) et MOTD (après).
|
||||
ssh_banner: true
|
||||
ssh_banner_organisation: "l'organisation"
|
||||
# Logo ASCII affiché en tête de bannière (chaîne multiligne, vide = pas de logo).
|
||||
ssh_banner_logo: ""
|
||||
ssh_motd: true
|
||||
# Sous Ubuntu : désactiver les MOTD publicitaires (motd-news, help-text).
|
||||
ssh_motd_disable_ubuntu_extras: true
|
||||
|
||||
# Durcissement du client ssh (ssh_config) : pas d'agent forwarding ni de X11
|
||||
# par défaut, known_hosts hachés.
|
||||
ssh_client_hardening: true
|
||||
|
||||
# Après redémarrage du démon, ouvrir une NOUVELLE connexion depuis le
|
||||
# contrôleur pour vérifier qu'on ne s'est pas enfermé dehors.
|
||||
ssh_verify_new_connection: true
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Géré par Ansible (rôle ssh) — repris de 01-ssh/00-basic.
|
||||
# get load averages
|
||||
IFS=" " read LOAD1 LOAD5 LOAD15 <<<$(awk '{ print $1,$2,$3 }' /proc/loadavg)
|
||||
# get free memory
|
||||
IFS=" " read USED AVAIL TOTAL <<<$(free -htm | awk '/Mem/ { print $3,$7,$2 }')
|
||||
# get processes
|
||||
PROCESS=$(ps -eo user=|sort|uniq -c | awk '{ print $2 " " $1 }')
|
||||
PROCESS_ALL=$(echo "$PROCESS"| awk {'print $2'} | awk '{ SUM += $1} END { print SUM }')
|
||||
PROCESS_ROOT=$(echo "$PROCESS" | awk '/root/ { print $2}')
|
||||
PROCESS_USER=$(echo "$PROCESS" | awk '!/root/ { SUM += $2} END { print SUM }')
|
||||
# get processors
|
||||
PROCESSOR_NAME=$(awk -F": " '/model name/ { print $2 }' /proc/cpuinfo | head -1)
|
||||
PROCESSOR_COUNT=$(grep -ioPc 'processor\t:' /proc/cpuinfo)
|
||||
|
||||
# colors
|
||||
W="\e[0;39m"
|
||||
G="\e[1;32m"
|
||||
R="\e[1;31m"
|
||||
dim="\e[2m"
|
||||
undim="\e[0m"
|
||||
|
||||
echo -e "${W}System info:
|
||||
$W Hostname$dim····$undim: $W${HOSTNAME}
|
||||
$W Distro$dim······$undim: $W$(. /etc/os-release; echo "$PRETTY_NAME")
|
||||
$W Kernel$dim······$undim: $W$(uname -sr)
|
||||
$W Uptime$dim······$undim: $W$(uptime -p)
|
||||
$W Load$dim········$undim: $G$LOAD1$W (1m), $G$LOAD5$W (5m), $G$LOAD15$W (15m)
|
||||
$W Processes$dim···$undim: $G$PROCESS_ROOT$W (root), $G$PROCESS_USER$W (user), $G$PROCESS_ALL$W (total)
|
||||
$W CPU$dim·········$undim: $W$PROCESSOR_NAME ($G$PROCESSOR_COUNT$W vCPU)
|
||||
$W Memory$dim······$undim: $G$USED$W used, $G$AVAIL$W avail, $G$TOTAL$W total"
|
||||
|
||||
# config
|
||||
max_usage=90
|
||||
bar_width=50
|
||||
|
||||
# disk usage: ignore zfs, squashfs & tmpfs
|
||||
printf "\nDisk usage:\n"
|
||||
|
||||
while read line; do
|
||||
# get disk usage
|
||||
usage=$(echo "$line" | awk '{print $2}' | sed 's/%//')
|
||||
used_width=$((($usage*$bar_width)/100))
|
||||
# color is green if usage < max_usage, else red
|
||||
if [ "${usage}" -ge "${max_usage}" ]; then
|
||||
color=$R
|
||||
else
|
||||
color=$G
|
||||
fi
|
||||
# print green/red bar until used_width
|
||||
bar="[${color}"
|
||||
for ((i=0; i<$used_width; i++)); do
|
||||
bar+="="
|
||||
done
|
||||
# print dimmmed bar until end
|
||||
bar+="${W}${dim}"
|
||||
for ((i=$used_width; i<$bar_width; i++)); do
|
||||
bar+="·"
|
||||
done
|
||||
bar+="${undim}]"
|
||||
# print usage line & bar
|
||||
echo "${line}" | awk '{ printf("%-31s%+3s used out of %+4s\n", $1, $2, $3); }' | sed -e 's/^/ /'
|
||||
echo -e "${bar}" | sed -e 's/^/ /'
|
||||
done < <(df -H -x zfs -x squashfs -x tmpfs -x devtmpfs -x overlay -x nfs -x nfs4 -x cifs --output=target,pcent,size | tail -n+2)
|
||||
|
||||
printf "\n"
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: Redémarrer sshd
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ ssh_service_name[ansible_facts['os_family']] }}"
|
||||
state: restarted
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
- name: Déposer la bannière d'avertissement (SSH et console)
|
||||
ansible.builtin.template:
|
||||
src: banner.j2
|
||||
dest: "{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- /etc/issue.net
|
||||
- /etc/issue
|
||||
when: ssh_banner | bool
|
||||
|
||||
- name: "Déposer le script MOTD (Debian/Ubuntu : update-motd.d)"
|
||||
ansible.builtin.copy:
|
||||
src: 00-basic
|
||||
dest: /etc/update-motd.d/00-basic
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when:
|
||||
- ssh_motd | bool
|
||||
- ansible_facts['os_family'] == 'Debian'
|
||||
|
||||
- name: Désactiver les MOTD annexes d'Ubuntu
|
||||
ansible.builtin.file:
|
||||
path: "/etc/update-motd.d/{{ item }}"
|
||||
mode: "0644"
|
||||
loop:
|
||||
- 10-help-text
|
||||
- 50-motd-news
|
||||
- 50-landscape-sysinfo
|
||||
- 88-esm-announce
|
||||
- 91-contract-ua-esm-status
|
||||
failed_when: false
|
||||
when:
|
||||
- ssh_motd | bool
|
||||
- ssh_motd_disable_ubuntu_extras | bool
|
||||
- ansible_facts['distribution'] == 'Ubuntu'
|
||||
|
||||
- name: Désactiver motd-news (Ubuntu)
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/default/motd-news
|
||||
regexp: '^ENABLED='
|
||||
line: "ENABLED=0"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when:
|
||||
- ssh_motd | bool
|
||||
- ssh_motd_disable_ubuntu_extras | bool
|
||||
- ansible_facts['distribution'] == 'Ubuntu'
|
||||
|
||||
- name: "Déposer le script MOTD (Red Hat : profile.d)"
|
||||
ansible.builtin.copy:
|
||||
src: 00-basic
|
||||
dest: /usr/local/bin/motd-basic
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when:
|
||||
- ssh_motd | bool
|
||||
- ansible_facts['os_family'] == 'RedHat'
|
||||
|
||||
- name: Appeler le MOTD à l'ouverture d'un shell interactif (Red Hat)
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/profile.d/zz-motd.sh
|
||||
content: |
|
||||
# {{ ansible_managed }}
|
||||
[ -n "$PS1" ] && [ -x /usr/local/bin/motd-basic ] && /usr/local/bin/motd-basic
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when:
|
||||
- ssh_motd | bool
|
||||
- ansible_facts['os_family'] == 'RedHat'
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
- name: Créer le répertoire central des clés autorisées
|
||||
ansible.builtin.file:
|
||||
path: "{{ ssh_central_keys_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Écrire le fichier de clés de chaque compte
|
||||
ansible.builtin.template:
|
||||
src: authorized_keys.j2
|
||||
dest: "{{ ssh_central_keys_dir }}/{{ item.name }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: ssh_users_keys[item.name] | length > 0
|
||||
|
||||
- name: Supprimer le fichier central d'un compte sans clé
|
||||
ansible.builtin.file:
|
||||
path: "{{ ssh_central_keys_dir }}/{{ item.name }}"
|
||||
state: absent
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: ssh_users_keys[item.name] | length == 0
|
||||
|
||||
- name: Lister les fichiers de clés d'anciens comptes
|
||||
ansible.builtin.find:
|
||||
paths: "{{ ssh_central_keys_dir }}"
|
||||
file_type: file
|
||||
register: ssh_central_existing
|
||||
|
||||
- name: Supprimer les fichiers de clés des comptes qui ne sont plus gérés
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
state: absent
|
||||
loop: "{{ ssh_central_existing.files }}"
|
||||
loop_control:
|
||||
label: "{{ item.path | basename }}"
|
||||
when: item.path | basename not in ssh_allow_users_effective
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
- name: Installer le serveur OpenSSH
|
||||
ansible.builtin.package:
|
||||
name: openssh-server
|
||||
state: present
|
||||
|
||||
- name: Préparer les comptes et leurs clés
|
||||
ansible.builtin.import_tasks: users.yml
|
||||
|
||||
- name: Configurer le démon sshd
|
||||
ansible.builtin.import_tasks: sshd.yml
|
||||
|
||||
- name: Déployer la bannière et le MOTD
|
||||
ansible.builtin.import_tasks: banner_motd.yml
|
||||
|
||||
- name: Durcir le client ssh
|
||||
ansible.builtin.template:
|
||||
src: ssh_config.j2
|
||||
dest: "{{ ssh_client_dropin_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: ssh_client_hardening | bool
|
||||
|
||||
- name: Appliquer la configuration sshd maintenant
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Vérifier qu'une nouvelle connexion SSH aboutit
|
||||
ansible.builtin.import_tasks: verify.yml
|
||||
when: ssh_verify_new_connection | bool
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
- name: Lire la version du paquet openssh-server
|
||||
ansible.builtin.package_facts:
|
||||
manager: auto
|
||||
|
||||
- name: Extraire la version d'OpenSSH
|
||||
ansible.builtin.set_fact:
|
||||
ssh_version: "{{ ansible_facts['packages']['openssh-server'][0].version | regex_search('([0-9]+\\.[0-9]+)') }}"
|
||||
|
||||
- name: Déterminer si sshd prend en charge Include (OpenSSH >= 8.2)
|
||||
ansible.builtin.set_fact:
|
||||
ssh_supports_include: "{{ ssh_version is version('8.2', '>=') }}"
|
||||
|
||||
- name: Sauvegarder le sshd_config d'origine une seule fois
|
||||
ansible.builtin.copy:
|
||||
src: /etc/ssh/sshd_config
|
||||
dest: /etc/ssh/sshd_config.avant-ansible
|
||||
remote_src: true
|
||||
force: false
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
|
||||
- name: Appliquer le durcissement sshd
|
||||
block:
|
||||
- name: Créer le répertoire des drop-ins sshd
|
||||
ansible.builtin.file:
|
||||
path: "{{ ssh_dropin_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: ssh_supports_include | bool
|
||||
|
||||
- name: S'assurer que sshd_config inclut le répertoire des drop-ins
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^\s*Include\s+/etc/ssh/sshd_config\.d/\*\.conf'
|
||||
line: "Include /etc/ssh/sshd_config.d/*.conf"
|
||||
insertbefore: BOF
|
||||
when: ssh_supports_include | bool
|
||||
notify: Redémarrer sshd
|
||||
|
||||
- name: Déposer le drop-in de durcissement
|
||||
ansible.builtin.template:
|
||||
src: sshd_durcissement.conf.j2
|
||||
dest: "{{ ssh_dropin_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
when: ssh_supports_include | bool
|
||||
notify: Redémarrer sshd
|
||||
|
||||
- name: Insérer le durcissement en tête de sshd_config (OpenSSH < 8.2)
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
block: "{{ lookup('ansible.builtin.template', 'sshd_durcissement.conf.j2') }}"
|
||||
marker: "# {mark} DURCISSEMENT ANSIBLE"
|
||||
insertbefore: BOF
|
||||
when: not ssh_supports_include | bool
|
||||
notify: Redémarrer sshd
|
||||
|
||||
- name: Neutraliser dans sshd_config les directives qui contrediraient le durcissement
|
||||
ansible.builtin.replace:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^(\s*(?:PermitRootLogin|PasswordAuthentication|AuthorizedKeysFile|X11Forwarding|Banner|PrintMotd)\s.*)$'
|
||||
replace: '#\1'
|
||||
after: "# END DURCISSEMENT ANSIBLE"
|
||||
when: not ssh_supports_include | bool
|
||||
notify: Redémarrer sshd
|
||||
|
||||
- name: Valider la configuration sshd
|
||||
ansible.builtin.command:
|
||||
cmd: sshd -t
|
||||
changed_when: false
|
||||
rescue:
|
||||
- name: Retirer le drop-in invalide
|
||||
ansible.builtin.file:
|
||||
path: "{{ ssh_dropin_file }}"
|
||||
state: absent
|
||||
when: ssh_supports_include | bool
|
||||
|
||||
- name: Restaurer sshd_config
|
||||
ansible.builtin.copy:
|
||||
src: /etc/ssh/sshd_config.avant-ansible
|
||||
dest: /etc/ssh/sshd_config
|
||||
remote_src: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
when: not ssh_supports_include | bool
|
||||
|
||||
- name: Arrêter sur configuration sshd invalide
|
||||
ansible.builtin.fail:
|
||||
msg: "sshd -t a rejeté la configuration : elle a été retirée, le démon n'a pas été redémarré."
|
||||
|
||||
- name: Autoriser le port SSH dans SELinux
|
||||
community.general.seport:
|
||||
ports: "{{ ssh_port }}"
|
||||
proto: tcp
|
||||
setype: ssh_port_t
|
||||
state: present
|
||||
when:
|
||||
- ansible_facts['os_family'] == 'RedHat'
|
||||
- ansible_facts['selinux'].status | default('disabled') == 'enabled'
|
||||
- ssh_port | int != 22
|
||||
|
||||
- name: Désactiver l'activation par socket (Ubuntu >= 22.10)
|
||||
ansible.builtin.systemd:
|
||||
name: ssh.socket
|
||||
enabled: false
|
||||
state: stopped
|
||||
when:
|
||||
- ansible_facts['distribution'] == 'Ubuntu'
|
||||
- ansible_facts['distribution_version'] is version('22.10', '>=')
|
||||
notify: Redémarrer sshd
|
||||
|
||||
- name: Lister les clés privées d'hôte
|
||||
ansible.builtin.find:
|
||||
paths: /etc/ssh
|
||||
patterns: "ssh_host_*_key"
|
||||
register: ssh_host_keys
|
||||
|
||||
- name: Restreindre les droits des clés privées d'hôte
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
loop: "{{ ssh_host_keys.files }}"
|
||||
loop_control:
|
||||
label: "{{ item.path | basename }}"
|
||||
|
||||
- name: Activer et démarrer sshd
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ ssh_service_name[ansible_facts['os_family']] }}"
|
||||
enabled: true
|
||||
state: started
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
- name: Recenser les clés publiques disponibles par compte
|
||||
ansible.builtin.set_fact:
|
||||
ssh_users_keys: >-
|
||||
{{ ssh_users_keys | default({}) | combine({
|
||||
item.name: lookup('ansible.builtin.fileglob', ssh_keys_dir ~ '/' ~ item.name ~ '/*.pub', wantlist=True)
|
||||
}) }}
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
|
||||
- name: Calculer la liste AllowUsers
|
||||
ansible.builtin.set_fact:
|
||||
ssh_allow_users_effective: "{{ ssh_users | map(attribute='name') | list }}"
|
||||
|
||||
- name: Refuser une configuration qui enfermerait Ansible dehors
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ssh_users | length > 0
|
||||
- (ssh_users_keys | default({})).values() | map('length') | sum > 0
|
||||
- ssh_ansible_login in ssh_allow_users_effective or ssh_allow_groups | length > 0
|
||||
- ssh_users_keys[ssh_ansible_login] | default([]) | length > 0 or ssh_allow_groups | length > 0
|
||||
- not (ssh_ansible_login == 'root' and ssh_permit_root_login == 'no')
|
||||
fail_msg: >-
|
||||
Le compte de connexion Ansible « {{ ssh_ansible_login }} » doit figurer dans ssh_users
|
||||
avec au moins une clé dans {{ ssh_keys_dir }}/{{ ssh_ansible_login }}/*.pub,
|
||||
et ssh_permit_root_login ne peut pas valoir « no » si Ansible se connecte en root.
|
||||
vars:
|
||||
ssh_ansible_login: "{{ ansible_user | default('root') }}"
|
||||
|
||||
- name: Créer les comptes (hors root)
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.name }}"
|
||||
shell: "{{ item.shell | default('/bin/bash') }}"
|
||||
groups: "{{ item.groups | default([]) | map('replace', 'sudo_or_wheel', common_sudo_group_effective) | list }}"
|
||||
append: true
|
||||
password: "{{ item.password_hash | default(omit) }}"
|
||||
create_home: true
|
||||
state: present
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when: item.name != 'root'
|
||||
no_log: true
|
||||
|
||||
- name: Autoriser sudo sans mot de passe pour les comptes de service
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/sudoers.d/90-{{ item.name }}"
|
||||
content: |
|
||||
# {{ ansible_managed }}
|
||||
{{ item.name }} ALL=(ALL) NOPASSWD:ALL
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
validate: /usr/sbin/visudo -cf %s
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- item.name != 'root'
|
||||
- item.sudo_nopasswd | default(false) | bool
|
||||
|
||||
- name: Retirer le sudo sans mot de passe des comptes qui ne le demandent plus
|
||||
ansible.builtin.file:
|
||||
path: "/etc/sudoers.d/90-{{ item.name }}"
|
||||
state: absent
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- item.name != 'root'
|
||||
- not (item.sudo_nopasswd | default(false) | bool)
|
||||
|
||||
- name: Déployer les clés autorisées (répertoire central)
|
||||
ansible.builtin.import_tasks: keys_central.yml
|
||||
when: ssh_central_authorized_keys | bool
|
||||
|
||||
- name: Déployer les clés autorisées (~/.ssh)
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ item.name }}"
|
||||
key: "{{ ssh_users_keys[item.name] | map('ansible.builtin.file') | join('\n') }}"
|
||||
exclusive: true
|
||||
manage_dir: true
|
||||
loop: "{{ ssh_users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
when:
|
||||
- not ssh_central_authorized_keys | bool
|
||||
- ssh_users_keys[item.name] | length > 0
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Construire la commande de vérification
|
||||
ansible.builtin.set_fact:
|
||||
ssh_verify_argv: >-
|
||||
{{ ['ssh', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=15', '-o', 'StrictHostKeyChecking=accept-new',
|
||||
'-o', 'ControlPath=none', '-p', ssh_port | string]
|
||||
+ (['-i', ansible_ssh_private_key_file] if ansible_ssh_private_key_file is defined else [])
|
||||
+ [(ansible_user | default('root')) ~ '@' ~ (ansible_host | default(inventory_hostname)), 'true'] }}
|
||||
|
||||
- name: Ouvrir une nouvelle session SSH depuis le contrôleur
|
||||
ansible.builtin.command:
|
||||
argv: "{{ ssh_verify_argv }}"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
register: ssh_verify
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Signaler un échec de connexion après durcissement
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
Une NOUVELLE connexion SSH vers {{ inventory_hostname }} échoue (code {{ ssh_verify.rc }}).
|
||||
La session courante reste ouverte : vérifier {{ ssh_dropin_file }} et
|
||||
{{ ssh_central_keys_dir if ssh_central_authorized_keys else '~/.ssh/authorized_keys' }}
|
||||
avant de la fermer.
|
||||
when: ssh_verify.rc != 0
|
||||
@@ -0,0 +1,5 @@
|
||||
# {{ ansible_managed }}
|
||||
# Source : files/keys/{{ item.name }}/*.pub
|
||||
{% for key_file in ssh_users_keys[item.name] | sort %}
|
||||
{{ lookup('ansible.builtin.file', key_file) | trim }}
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
{% if ssh_banner_logo | length > 0 %}
|
||||
{{ ssh_banner_logo | regex_replace('\\s+$', '') }}
|
||||
|
||||
{% endif %}
|
||||
AVERTISSEMENT : L'accès à ce système est réservé aux utilisateurs
|
||||
dûment autorisés.
|
||||
|
||||
Toute tentative d'accès à ce système sans autorisation, ou de maintien
|
||||
frauduleux dans ce système, fera l'objet de poursuites conformément à
|
||||
la politique de {{ ssh_banner_organisation }}.
|
||||
|
||||
Tout utilisateur autorisé est informé et reconnaît que ses actions
|
||||
peuvent être enregistrées, conservées et auditées conformément aux
|
||||
chartes et politiques internes de {{ ssh_banner_organisation }}.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# {{ ansible_managed }}
|
||||
Host *
|
||||
ForwardAgent no
|
||||
ForwardX11 no
|
||||
HashKnownHosts yes
|
||||
GSSAPIAuthentication no
|
||||
StrictHostKeyChecking ask
|
||||
SendEnv LANG LC_*
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 3
|
||||
@@ -0,0 +1,62 @@
|
||||
# {{ ansible_managed }}
|
||||
# Première valeur lue = valeur retenue : ce fichier est lu avant sshd_config.
|
||||
|
||||
Port {{ ssh_port }}
|
||||
LogLevel {{ ssh_log_level }}
|
||||
|
||||
# Authentification : clé publique uniquement, jamais de mot de passe à distance.
|
||||
PermitRootLogin {{ ssh_permit_root_login }}
|
||||
PubkeyAuthentication yes
|
||||
AuthenticationMethods publickey
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
HostbasedAuthentication no
|
||||
IgnoreRhosts yes
|
||||
StrictModes yes
|
||||
UsePAM yes
|
||||
{% if ssh_central_authorized_keys %}
|
||||
AuthorizedKeysFile {{ ssh_central_keys_dir }}/%u
|
||||
{% else %}
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
{% endif %}
|
||||
AllowUsers {{ ssh_allow_users_effective | join(' ') }}
|
||||
{% if ssh_allow_groups | length > 0 %}
|
||||
AllowGroups {{ ssh_allow_groups | join(' ') }}
|
||||
{% endif %}
|
||||
|
||||
# Limites anti-force-brute et sessions inactives.
|
||||
MaxAuthTries {{ ssh_max_auth_tries }}
|
||||
MaxSessions {{ ssh_max_sessions }}
|
||||
MaxStartups {{ ssh_max_startups }}
|
||||
LoginGraceTime {{ ssh_login_grace_time }}
|
||||
ClientAliveInterval {{ ssh_client_alive_interval }}
|
||||
ClientAliveCountMax {{ ssh_client_alive_count_max }}
|
||||
|
||||
# Surface réduite.
|
||||
X11Forwarding {{ 'yes' if ssh_x11_forwarding else 'no' }}
|
||||
AllowAgentForwarding {{ 'yes' if ssh_agent_forwarding else 'no' }}
|
||||
AllowTcpForwarding {{ 'yes' if ssh_tcp_forwarding else 'no' }}
|
||||
PermitTunnel no
|
||||
GatewayPorts no
|
||||
PermitUserEnvironment no
|
||||
UseDNS no
|
||||
{% if ansible_facts['os_family'] == 'Debian' %}
|
||||
DebianBanner no
|
||||
|
||||
# Algorithmes (Red Hat : gérés par crypto-policies).
|
||||
KexAlgorithms {{ ssh_kex_algorithms | join(',') }}
|
||||
Ciphers {{ ssh_ciphers | join(',') }}
|
||||
MACs {{ ssh_macs | join(',') }}
|
||||
HostKeyAlgorithms {{ ssh_host_key_algorithms | join(',') }}
|
||||
PubkeyAcceptedAlgorithms {{ ssh_pubkey_accepted_algorithms | join(',') }}
|
||||
{% endif %}
|
||||
|
||||
# Affichage.
|
||||
{% if ssh_banner %}
|
||||
Banner /etc/issue.net
|
||||
{% else %}
|
||||
Banner none
|
||||
{% endif %}
|
||||
PrintMotd no
|
||||
PrintLastLog yes
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
ssh_service_name:
|
||||
Debian: ssh
|
||||
RedHat: sshd
|
||||
ssh_dropin_dir: /etc/ssh/sshd_config.d
|
||||
# « 00- » : les fichiers inclus sont lus par ordre lexical et la PREMIÈRE
|
||||
# valeur rencontrée l'emporte. Nos réglages passent donc avant
|
||||
# 50-cloud-init.conf (Ubuntu) et 50-redhat.conf (Red Hat).
|
||||
ssh_dropin_file: "{{ ssh_dropin_dir }}/00-durcissement.conf"
|
||||
ssh_client_dropin_file: /etc/ssh/ssh_config.d/00-durcissement.conf
|
||||
ssh_central_keys_dir: /etc/ssh/authorized_keys
|
||||
Reference in new issue
Block a user