Automatiser la gestion des comptes d’administrateurs locaux avec Ansible – Partie 4

Mon état des lieux des comptes sur les serveurs étant fait, il est temps de commencer à mettre un peu d’ordre dans les accès à ces serveurs.

Pour rappel, je veux obtenir ceci :

  • modifier le mot de passe du compte root pour le rendre difficile à utiliser (long et compliqué) et en interdire l’usage distant par ssh autrement qu’avec une clé
  • supprimer les comptes qui n’ont rien à faire là, ainsi que le repertoire home de ces comptes s’il existe
  • supprimer des clés publiques parmi les authorized_keys de root (que j’ai identifiées par un procédé similaire à ce que j’ai fait dans mon état des lieux pour les fichiers passwd)
  • m’assurer que les administrateurs système ont un compte nominatif autorisé à utiliser sudo

 

Configuration des comptes admin

Cette configuration étant un prérequis commun à l’ensemble des serveurs, je vais créer des tâches dans le rôle common.

Dans le fichier roles/common/tasks/main.yml j’appelle deux tâches. Une pour configurer les comptes admin et les clés ssh associées, et l’autre pour configurer sudo :

- name: include vars
  include_vars: all

# roles applicable a tous les serveurs
- import_tasks: configure_admins.yml
- import_tasks: configure_sudoers.yml

Mon fichier roles/common/tasks/configure_admins.yml ressemble à ceci :

# file: roles/common/tasks/configure_admins.yml
- name: include vars
  include_vars: "{{ ansible_os_family }}"

- name: comptes admins qui doivent etre presents
  user:
    name: "{{ item.name }}"
    state: present
    password: "{{ item.password }}"
    groups: "{{ sudo_admin_group }}"
  with_items:
    - { name: 'admin1', password: 'mettre_ici_la_chaine_chiffree_du_mdp_de_admin1' }
    - { name: 'admin2', password: 'mettre_ici_la_chaine_chiffree_du_mdp_de_admin2' }

- name: admins qui doivent avoir une authentification par cle ssh
  authorized_key:
    user: "{{ item.name }}"
    state: present
    key: "{{ lookup('file', '{{ item.name }}_id_rsa.pub') }}"
  with_items:
    - { name: 'admin1' }
    - { name: 'admin2' }

- name: comptes qui doivent etre supprimes
  include_tasks: remove_linux_user_with_home.yml
  with_items:
    - oldadmin1
    - oldadmin2

- name: nettoyage authorized_keys de root
  lineinfile:
    path: /root/.ssh/authorized_keys
    regexp: ".*{{ item }}$"
    state: absent
  with_items:
    - motif1@machine
    - motif2@machine

- name: changer le mdp de root par un truc costaud
  user:
    name: root
    state: present
    password: 'mettre_ici_la_chaine_chiffree_pour_le_mdp_de_root

Que fait donc ce script ?  Eh bien, dans l’ordre :

  • il s’assure que soient créé les comptes pour les admins souhaités, et de les mettre dans le bon groupe pour leur permettre d’utiliser sudo, et pour ceux d’entre eux qui veulent utiliser une authentification par clé, il met la clé publique dans le fichier authorized_keys de leur compte
  • il détruit les comptes admin qui n’ont plus lieu d’être, et supprime en même temps leur dossier home ; pour cela je fais appel à un autre script pour chaque entrée with_items de la section
  • il retire les entrées indésirables du fichier authorized_keys de root
  • et enfin il change le mot de passe root

Je vais détailler un peu.

Gestion des mots de passe

Dans chaque valeur “password” il faut mettre une chaine chiffrée que vous pourrez obtenir avec la commande suivante :

python -c "import crypt, getpass, pwd; print crypt.crypt('mon_mot_de_passe', '\$6\$Rrz4zg0v\$')"

La valeur bizarre à la fin est le “salt” qui est là pour mettre un peu d’entropie dans les chaines chiffrées. Il est nécessaire de le préciser, mais vous pouvez mettre ce que vous voulez. Dans l’exemple ci-dessus la chaine chiffrée obtenue est :

$6$Rrz4zg0v$ph/URcNJaepwt5wBDfcA4wez4iQZn/.YoUkZ0CwVjOc.1bbO4IeB1By.S1xbx3i.nzghGhnAZobIGYRhqCCQ/

et c’est bien une chaîne de cette forme qu’il faut mettre entre les quotes.

Gestion de l’authentification par clés ssh

Si pour un compte admin il est souhaité faire une authentifcation par clé RSA, il faut :

  • que l’admin en question commence par créer une paire de clés RSA avec ssh-keygen (il est évidemment fortement préférable que la clé privée soit protégée par mot de pass)
  • que la clé publique soit déposée dans le dossier roles/common/files sous un nom au format suivant : admin1_id_rsa.pub
  • et ajouter le nom du compte dans la section with_items qui va bien

Nettoyage du aythorized_keys de root

Il s’agit ici simplement de retirer les lignes de ce fichier que je veux voir disparaitre. Pour cela, j’ai préalablement repéré les motifs qui se trouvent en fin de ligne de chaque clé publique, motif qui est souvent de la forme utilisateur@machine, et c’est ce motif que je mets dans la section with_items.

Suppression des comptes admin indésirables

Pour cela je m’appuie sur un autre script ansible, qui est appelé pour chaque entrée with_tems de cette section. Procéder ainsi me permet de m’assurer que l’ensemble des tâches qu’il contient est bien appliqué pour chaque compte.

Voici à quoi ressemble mon fichier roles/common/tasks/remove_linux_user_with_home.yml :

# file: roles/common/tasks/remove_linux_user_with_home.yml

- set_fact: outer_loop="{{item}}"

- debug: msg="outer item={{outer_loop}} inner item={{item}}"

- name: get user homedir
  shell: >
    getent passwd {{ item }} | cut -d: -f6
  register: user_home
  changed_when: user_home.stdout != ""

- debug:
    var: user_home

- name: suppression du homedir
  file: path={{ user_home.stdout }} state=absent
  when: user_home.changed

- name: suppression du maildir
  file: path="/var/spool/mail/{{ item }}" state=absent

- name: suppression du user
  user:
    name: "{{ item }}"
    state: absent

Ce script s’assure de supprime un compte, mais aussi le dossier HOME associé (en allant chercher dans le fichier /etc/passwd son emplacement), et le dossier mail du user.

 

Configuration du fichier sudoers

J’ai fait le choix de préserver l’essentiel du fichier sudoers, plutôt que de le regéréner car je sais que sur les serveurs sur lesquels je vais pousser mes configurations certains auront des paramétrages spécifiques dans le fichier sudoers. Le regénérer était exclu. Je cherche donc juste à faire deux choses :

  • m’assurer que le groupe “sudo” ou “wheel” est bien autorisé à faire du sudo sans mot de passe
  • placer le compte admin dans le bon groupe

Pour déterminer quel est le nom du groupe, qui diffère selon les distributions, j’ai fait placé cette variable dans des fichiers au nom de la famille de distribution (tel que rapporté par les facts ansible).

Ainsi, dans un fichier roles/common/vars/RedHat je mets :

---
# file: common/vars/RedHat
sudo_admin_group: wheel

et, bien que j’aie très peu de Debian, dans un fichier roles/common/vars/Debian, je mets :

---
# file: common/vars/Debian
sudo_admin_group: sudo

Le bon fichier est utilisé par ansible par la mention suivante en début de rôle :

  include_vars: "{{ ansible_os_family }}"

Ensuite, pour la modification du fichier sudoers, j’ai un fichier roles/common/tasks/configure_sudoers.yml qui contient ceci :

# file: roles/common/tasks/configure_sudoers.yml
- name: include vars
include_vars: "{{ ansible_os_family }}"

- name: Configure sudo sans mdp pour wheel 1/2
replace:
path: /etc/sudoers
regexp: '^%wheel\s*ALL=\(ALL\)\s*ALL'
replace: '# %wheel ALL=(ALL) ALL'
validate: visudo -cf %s
when: ansible_os_family == "RedHat"

- name: Configure sudo sans mdp pour wheel 2/2
lineinfile:
path: /etc/sudoers
state: present
regexp: '%wheel.*NOPASSWD'
line: '%wheel ALL=(ALL) NOPASSWD: ALL'
validate: visudo -cf %s
when: ansible_os_family == "RedHat"

- name: Pas encore implemente
fail: msg="Pas encore implemente pour cette distribution {{ ansible_os_family }}"
when: ansible_os_family != "RedHat"

Ce script permet de s’assurer que dans le fichier sudoers la ligne qui permet au groupe wheel d’utiliser sudo sans mot de passe est bien décommentée (ou le groupe sudo pour debian mais pas encore fait au moment de la rédaction de l’article).

Configuration de sshd

Dans tout ce qui précède, je n’ai pas traité la configuration du démon sshd. Je souhaite accepter les connexions par clé ssh sur le compte root mais pas l’authentification par mot de passe. Cette fois, j’accepte de regénérer un fichier sshd_config et d’écraser le précédent, et pour me faciliter la vie je vais utiliser des scripts disponibles sur ansible galaxy. J’ai utilisé willshersystems.sshd, que j’ai configuré comme ceci.

L’installation se fait via la commande suivante :

ansible-galaxy install willshersystems.sshd

Ansible-galaxy se charge de télécharger le module et le range dans une arborescence distincte de celle que j’ai montée dans l’article, dans le dossier ~/.ansible/roles/willshersystems.sshd/. A l’utilisation, cela ne fait pas de différence, et il est possible d’utiliser le rôle willshersystems.sshd de la même manière que le rôle common par exemple.

La configuration du rôle willshersystems.sshd se fait à travers des variables que j’ai définies dans le fichier de configuration de site.

Configuration de site

A travers ce fichier, il s’agit de décrire les rôles que je veux voir jouer aux serveurs. Comme je veux que tous les serveurs bénéficient de cette même configuration de comptes admin, tous les serveurs auront les rôles common et willshersystems.sshd. Mon fichier site.yml ressemble donc à ceci :

---
# file: site.yml
- hosts: all
  remote_user: root
  any_errors_fatal: false

  vars:
    sshd_skip_defaults: false
    sshd:
      ListenAddress:
        - "0.0.0.0"
        - "::"
      PermitRootLogin: without-password

  roles:
    - common
    - willshersystems.sshd

 

C’est parti !!!

Bon, c’est bien gentil, mais maintenant il faut pousser la conf vers les serveurs.

Comme je suis prudent, je commence par y aller serveur par serveur, et sur des serveurs de test ou de pré-production, avec la commande suivante :

ansible-playbook -i preprod.yml site.yml --limit unemachine-de-test

ce qui signifie que je vais appliquer la configuration de site aux machines de preprod, mais en limitant à une seule machine.

Une fois content du résultat sur quelques machines, je peux lacher les chevaux (et croiser les doigts) et retirer la limitation à une seule machine.

 

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.