Supposons qu'un client vous ait demandé d'aider à la migration de script pour déployer un fichier sudoers centralisé sur les serveurs RHEL et AIX.

Eh bien, c'est un scénario très courant, et avec son exemple, vous pouvez démontrer l'utilisation des fonctionnalités avancées d'Ansible, ainsi que la façon dont l'approche change - d'un script qui effectue une certaine tâche, à une description idempotente (sans apporter de modifications) et en surveillant la conformité avec l'état de l'instance.
Prenez le script:
Il y a 212 lignes de code, sans contrôle de version dans le fichier sudoers. Le client dispose déjà d'un processus qui s'exécute une fois par semaine et vérifie la somme de contrôle du fichier pour assurer la sécurité. Bien que le script contienne une référence à Solaris, pour ce client, nous n'avons pas eu à transférer cette exigence.
Commençons par créer un rôle et placer le fichier sudoers dans Git pour le contrôle de version. Entre autres choses, cela nous permettra de nous débarrasser de la nécessité de monter des volumes NFS.
Avec les paramètres de validation et de sauvegarde pour les modules de
copie et de
modèle , nous pouvons éliminer la nécessité d'écrire du code pour sauvegarder et restaurer le fichier. Dans ce cas, la validation est effectuée avant que le fichier ne soit placé au point de destination, et si la validation échoue, le module génère une erreur.
Pour chaque rôle, nous devons spécifier des tâches, des modèles et des variables. Voici la structure du fichier correspondant:

Le fichier de script de rôle (playbook),
sudoers.yml , a une structure simple:
---
##
# Role playbook
##
- hosts: all
roles:
- sudoers
...
Les variables de rôle se trouvent dans le
fichier vars / main.yml . Ce fichier contient le fichier de contrôle et inclut / exclut les directives qui seront utilisées pour créer une logique spéciale pour ignorer les hôtes Lawson et inclure le fichier sudoers.d uniquement dans les hôtes hd.
Voici le contenu du
fichier vars / main.yml :
---
MD5FILE: /root/.sudoer.md5
EXCLUDE: la
INCLUDE: hd
...
Si nous utilisons les
modules copy et
lineinfile , le rôle ne sera pas idempotent. Le module de copie installera le fichier de base et lineinfile réinsérera include à chaque démarrage. Comme ce rôle sera lancé sur
Ansible Tower , l'idempotence est un must. Nous convertirons le fichier en un modèle jinja2.
Dans la première ligne, nous ajoutons la commande suivante pour
contrôler les espaces et les retraits :
#jinja2: lstrip_blocks: True, trim_blocks: True
Notez que les versions plus récentes du module de
modèle incluent des paramètres pour
trim_blocks (ajoutés dans Ansible 2.4).
Voici le code qui insère la ligne d'
inclusion à la fin du fichier:
{% if ansible_hostname[0:2] == INCLUDE %}
#includedir /etc/sudoers.d
{% endif %}
Nous utilisons la construction conditionnelle ({% if%}, {% endif%}) pour la commande shell qui insère une ligne pour les hôtes dont les noms commencent par les caractères "hd". Nous utilisons des faits Ansible et un filtre [0: 2] pour analyser le nom d'hôte.
Passons maintenant aux tâches. Tout d'abord, vous devez établir un fait pour analyser le nom d'hôte. Nous utiliserons le fait «parhost» dans la construction conditionnelle.
---
##
# Parse hostnames to grab 1st 2 characters
##
- name: "Parse hostname's 1st 2 characters"
set_fact: parhost={{ ansible_hostname[0:2] }}
Il n'y a pas de paramètre
csum sur le serveur de stock RHEL. Si nécessaire, nous pouvons utiliser un autre fait pour indiquer conditionnellement le nom du fichier binaire avec la somme de contrôle. Veuillez noter qu'un code supplémentaire peut être requis si ces fonctionnalités sont différentes sous AIX, Solaris et Linux.
De plus, nous devons résoudre le problème avec les différences de groupes racine sous AIX et RHEL.
##
# Conditionally set name of checksum binary
##
- name: "set checksum binary"
set_fact:
csbin: "{{ 'cksum' if (ansible_distribution == 'RedHat') else 'csum' }}"
##
# Conditionally set name of root group
##
- name: "set system group"
set_fact:
sysgroup: "{{ 'root' if (ansible_distribution == 'RedHat') else 'sys' }}"
L'utilisation de blocs nous permet de définir la condition pour l'ensemble de la tâche. Nous utiliserons la condition à la fin du bloc pour exclure les hôtes «la».
##
# Enclose in block so we can use parhost to exclude hosts
##
- block:
Le module modèle valide et installe le fichier. Nous fixons le résultat afin de pouvoir déterminer si la tâche a changé. L'utilisation du paramètre de validation dans ce module vous permet de vérifier la validité du nouveau fichier sudoer avant de le placer sur l'hôte.
##
# Validate will prevent bad files, no need to revert
# Jinja2 template will add include line
##
- name: Ensure sudoers file
template:
src: sudoers.j2
dest: /etc/sudoers
owner: root
group: "{{ sysgroup }}"
mode: 0440
backup: yes
validate: /usr/sbin/visudo -cf %s
register: sudochg
Si un nouveau modèle a été installé, exécutez le script shell pour générer le fichier de somme de contrôle. La construction conditionnelle met à jour le fichier de somme de contrôle lors de l'installation du modèle sudoers, ou si le fichier de somme de contrôle est manquant. Étant donné que le processus en cours surveille également d'autres fichiers, nous utilisons le code shell fourni dans le script source:
- name: sudoers checksum
shell: "grep -v '/etc/sudoers' {{ MD5FILE }} > {{ MD5FILE }}.tmp ; {{ csbin }} /etc/sudoers >> {{ MD5FILE }} ; mv {{ MD5FILE }}.tmp {{ MD5FILE }}"
when: sudochg.changed or MD5STAT.exists == false
Le module de fichiers vérifie l'installation des autorisations nécessaires:
- name: Ensure MD5FILE permissions
file:
path: "{{ MD5FILE }}"
owner: root
group: "{{ sysgroup }}"
mode: 0600
state: file
Étant donné que le paramètre de sauvegarde ne fournit aucune option pour le traitement des sauvegardes précédentes, nous devrons nous occuper de créer le code approprié nous-mêmes. Dans l'exemple ci-dessous, nous utilisons pour cela le paramètre «register» et le champ «stdout_lines».
##
# List and clean up backup files. Retain 3 copies.
##
- name: List /etc/sudoers.*~ files
shell: "ls -t /etc/sudoers*~ |tail -n +4"
register: LIST_SUDOERS
changed_when: false
- name: Cleanup /etc/sudoers.*~ files
file:
path: "{{ item }}"
state: absent
loop: "{{ LIST_SUDOERS.stdout_lines }}"
when: LIST_SUDOERS.stdout_lines != ""
Achèvement du bloc:
##
# This conditional restricts what hosts this block runs on
##
when: parhost != EXCLUDE
...
Le cas d'utilisation prévu est de jouer ce rôle sur Ansible Tower. Les alertes Ansible Tower peuvent être configurées de sorte qu'en cas d'échec de l'exécution du travail, les alertes soient envoyées par e-mail, à Slack ou d'une autre manière. Ce rôle s'exécute dans Ansible, Ansible Engine ou Ansible Tower.
En conséquence, nous avons supprimé tout ce qui était superflu du script et créé un rôle complètement idempotent qui peut fournir l'état souhaité du fichier sudoers. L'utilisation de SCM permet un contrôle de version, fournit une gestion des changements et une transparence plus efficaces. Les CI / CD avec Jenkins ou d'autres outils vous permettent de configurer des tests automatisés du code Ansible pour les modifications futures. Le rôle d'auditeur dans Ansible Tower vous permet de surveiller et d'appliquer les exigences organisationnelles.
Le code de travail avec les sommes de contrôle pourrait être supprimé du script, mais pour cela, le client devrait d'abord consulter son service de sécurité. Si nécessaire, le modèle sudoers peut être protégé avec Ansible Vault. Enfin, l'utilisation de groupes évite d'écrire la logique en utilisant les inclusions et les exclusions.
→ Vous pouvez télécharger le rôle depuis GitHub sur ce
lien