Bonjour à tous,

Aujourd’hui, on va faire le tour de ma manière de gérer les mises à jour de mon infra perso. Elle a évolué, j’ai pas mal de choses plus ou moins compliquées à tenir à jour, du Debian, du BitWarden, du Windows, du OpnSense (FreeBSD) en plus de nginx compilés à la main et de mes certificats Let’s Encrypt. Il fallait donc que je centralise tout ça.

 

En plus de la centralisation, je voulais automatiser les mises à jour afin de plus m’occuper de mon bordel.

 

Tout ce qui sera mis ici dans l’article est disponible sur GitHub : https://github.com/stylersnico/own-ansible-update-tools

Cela vous évitera les erreurs d’indentation dans les copier/coller et vous pourrez mieux vous situer les fichiers de configuration.

 

Description de l’infra

  • 2 ESX avec 15 vms en tout
  • Un Vps hetzner
  • Du windows, avec entre autres mon AD et mon Exchange
  • Toujours du Windows pour Veeam et mes contrôleurs Unifi Wifi et Video
  • Du bitwarden sur VM
  • Nextcloud, un reverse proxy
  • Un OpnSense virtualisé pour le firewall
  • Et d’autres choses …

 

Les hôtes et les variables de connexion

Dans mon fichier d’hôte, j’ai bien séparé les hôtes par catégorie.

  • [ansible] : L’hôte ansible
  • [apt] : Mes serveurs Debian
  • [ngx-custom] : Mes serveurs ou je build NGINX à la pate
  • [letsencrypt] : Les serveurs NGINX ou il y’a des certificats Let’s Encrypt que je renouvelle
  • [bitwarden] : Le serveur Bitwarden, pour mettre à jour les images docker
  • [omv] : Le serveur OpenMediaVault ou j’ai mon Transmission que je coupe avant les mises à jour
  • [windows-no-ad] : Les serveurs Windows sans Active Directory
  • [windows-ad] : Les serveurs Windows dans l’Active Directory
  • [freebsd] : Le firewall OpnSense

 

Voici le détail :

[ansible]
localhost	ansible_connection=local

[apt]
localhost       ansible_connection=local
omv.nsh.ovh:22  ansible_connection=ssh  ansible_user=root
webhost.nicolas-simond.com:22      ansible_connection=ssh  ansible_user=root
192.168.1.7:22      ansible_connection=ssh  ansible_user=nsw-ansible
192.168.1.11:22      ansible_connection=ssh  ansible_user=nsw-ansible
192.168.1.12:22      ansible_connection=ssh  ansible_user=nsw-ansible
cloud.yaute.ninja:22      ansible_connection=ssh  ansible_user=root
192.168.1.38  ansible_connection=ssh  ansible_user=root
192.168.1.39 ansible_connection=ssh  ansible_user=root
192.168.1.40 ansible_connection=ssh  ansible_user=root

[ngx-custom]
192.168.1.7:22      ansible_connection=ssh  ansible_user=nsw-ansible
webhost.nicolas-simond.com:22      ansible_connection=ssh  ansible_user=root

[letsencrypt]
192.168.1.7:22      ansible_connection=ssh  ansible_user=nsw-ansible
webhost.nicolas-simond.com:22      ansible_connection=ssh  ansible_user=root

[bitwarden]
192.168.1.38  ansible_connection=ssh  ansible_user=root

[omv]
omv.nsh.ovh:22  ansible_connection=ssh  ansible_user=root

[windows-no-ad]
veeambackup.mail.ch
Ubiquiti-C.mail.ch

[windows-ad]
ex-dc02.mail.ch
ex-ms02.mail.ch


[freebsd]
192.168.1.254 ansible_connection=ssh ansible_user=root

[freebsd:vars]
ansible_python_interpreter="/usr/local/bin/python2.7"

 

Dans le détail, pour les Windows je suis obligé de faire deux fichiers de configuration séparés dans le dossier « group_vars »

Pour le FreeBSD j’indique directement le chemin du python à la fin.

 

Dans le fichier « /etc/ansible/group_vars/windows-no-ad.yml » je fait une connexion basique via WinRm avec l’administrateur local

ansible_become: false
ansible_user: Administrateur
ansible_password: XXX
ansible_port: 5986
ansible_connection: winrm
# The following is necessary for Python 2.7.9+ when using default WinRM self-signed certificates:
ansible_winrm_server_cert_validation: ignore

 

Dans le fichier « /etc/ansible/group_vars/windows-ad.yml » il y’a une subtilité. N’ayant pas réussi à faire parler du Debian 9 et du Serveur 2019 en kerberos je passe par une authentification NTLM classique.

ansible_become: false
ansible_user: Administrateur@mail.ch
ansible_password: XXX
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_transport: ntlm
# The following is necessary for Python 2.7.9+ when using default WinRM self-signed certificates:
ansible_winrm_server_cert_validation: ignore

 

Vous noterez que je laisse les mots de passe en clair dans mes fichiers de config et que je ne les chiffre pas. C’est en local chez moi et je veux y automatiser par la suite pour que tout ce mette à jour tout seul par la suite.

Vous allez sans doute préférer chiffre tout ça avec les vault ansible de votre côté.

J’en avais parlé dans mon article sur la gestion des Windows avec Ansible : https://www.abyssproject.net/2016/09/gerer-serveurs-windows-ansible/

 

Principes de découpage

Au début de chaque playbook, j’efface le fichier des logs et je coupe le démon transmission avant de lancer une activité internet avec les lignes suivantes :

- hosts: ansible
  become: yes
  become_method: sudo
  tasks:
    - name: Truncate logs
      shell: echo "" > /etc/ansible/ansible.log

- hosts: omv
  become: yes
  become_method: sudo
  tasks:
   - name: Stop transmission on OMV
     systemd:
       name: transmission-daemon
state: stopped

 

A la fin de chaque playbook je relance transmission et m’envoie le log Ansible par email pour savoir ce qu’il s’est passé, car, je vous rappelle que je ne lance pas les playbook à la main :

- hosts: omv 
  become: yes
  become_method: sudo
  tasks:
   - name: Start transmission on OMV
     systemd:    
       name: transmission-daemon
       state: started

- hosts: ansible
  become: yes
  become_method: sudo
  tasks:
   - name: Sending ansible.log by email
     mail:
       host: outbound-eu1.ppe-hosted.com
       port: 25 
       from: ansible@mail.ch
       to: me@mail.ch
       subject: Linux Servers update log
body: "{{ lookup('file', '/etc/ansible/ansible.log') }}"

 

Mise à jour des serveurs Debian, de NGINX et de BitWarden

On lance simplement les mises à jour sur les serveurs et on vérifie si tout redémarre bien en attendant en async que Ansible arrive à se reconnecter pour éviter des erreurs dans le playbook.

J’utilise Kexec pour un redémarrage plus rapide pour les serveurs où c’est installé.

- hosts: apt
  become: yes
  become_method: sudo
  tasks:
   - name: updates a server
     apt: update_cache=yes

   - name: upgrade a server
     apt: upgrade=dist dpkg_options='force-confold,force-confdef'

   - name: Check if a reboot is required
     register: reboot
     stat: path=/var/run/reboot-required get_md5=no

   - name: Check if kexec is used
     register: kexec
     stat: path=/usr/bin/kexec-reboot get_md5=no

   - name: Reboot the services
     shell: needrestart -ra -l
     when: reboot.stat.exists == false

   - name: Reboot the server using reboot
     command: /sbin/reboot
     async: 1
     poll: 0
     when: reboot.stat.exists == true and kexec.stat.exists == false


   - name: Wait for systems to become reachable
     wait_for_connection:

   - name: Reboot the server using kexec
     shell: kexec-reboot -lr
     async: 1
     poll: 0
     when: reboot.stat.exists == true and kexec.stat.exists == true

   - name: Wait for systems to become reachable
wait_for_connection:

 

Pour les serveurs Nginx, je lance simplement mon script de build depuis le serveur Ansible et attends le retour du build :
Le script est disponible sur GitHub : https://github.com/stylersnico/nginx-openssl-chacha-naxsi/blob/master/build.sh

Vous remarquerez que je crée une notification a la fin du script pour que Ansible se charge de redémarrer Nginx en cas de réussite du script.

- hosts: ngx-custom
  become: yes
  become_method: sudo
  tasks:
    - name: Build latest Nginx Release
      script: /etc/ansible/ressources/scripts/build-ngx.sh
      notify:
        - restart nginx

  handlers:
    - name: restart nginx
service: name=nginx state=restarted

 

Pour BitWarden, j’utilise le script fourni par les créateurs et passe des commandes shell directement à l’hôte à travers Ansible :

- hosts: bitwarden
  become: yes
  become_method: sudo
  tasks:
   - name: Self update on bitwarden
     shell: /root/bitwarden.sh updateself

   - name: Update bitwarden core and web
     shell: /root/bitwarden.sh update

 

Mise à jour des serveurs Windows

Ici c’est nettement plus simple que pour les Debian.

J’installe simplement les mises à jour critiques et les mises à jour de sécurité et attends le redémarrage des serveurs :

- hosts: windows-no-ad
  tasks:
   - name: install all critical and security updates
     win_updates:
       category_names:
       - CriticalUpdates
       - SecurityUpdates
       state: installed
     register: update_result

   - name: reboot if required
     win_reboot:
       reboot_timeout_sec: 180
     when: update_result.reboot_required


- hosts: windows-ad
  tasks:
   - name: install all critical and security updates
     win_updates:
       category_names:
       - CriticalUpdates
       - SecurityUpdates
       state: installed
     register: update_result

   - name: reboot if required
     win_reboot:
       reboot_timeout_sec: 180
     when: update_result.reboot_required

 

 

Mise à jour du firewall OpnSense

Ici pour OpnSense je ne passe pas par le système de mise à jour BSD mais par l’utilitaire OpnSense, je suis bien obligé.
J’invoque donc le shell local comme pour BitWarden (la connexion root de OpnSense doit se faire sur /bin/sh et non pas le choix par défaut) :

- hosts: freebsd
  become: yes
  become_method: sudo
  tasks:
   - name: Update OPNSense
shell: opnsense-update

 

Renouvellement des certificats Let’s Encrypt

ici je ne réinvente pas la roue, j’utilise simplement mon script bash que j’utilise depuis des années.
Un exemple est disponible ici : https://github.com/stylersnico/my-webserver/blob/master/root/renew-certs.sh

On va encore une fois invoquer le shell depuis Ansible et ensuite redémarrer les services NGINX sur les serveurs :

- hosts: letsencrypt
  become: yes
  become_method: sudo
  tasks:
   - name: Update certificats
     shell: /root/renew-certs.sh

   - name: restart nginx
service: name=nginx state=restarted

 

Concaténation

Si vous voulez tout lancer en une fois, faites donc un fichier global et importez vos playbook dedans :

nano /etc/ansible/playbook/update-all-servers.yml
- import_playbook: include/update-apt-servers.yml
- import_playbook: include/update-windows-servers.yml
- import_playbook: include/update-opnsense-servers.yml

 

Automatisation

Comme déjà expliqué, les mises à jour se font dans mon dos et ça me va bien.
Oui Ansible Tower / Semaphore gnagnagna. Si vous voulez faire simple, balancez un crontab depuis votre utilisateur ansible :

crontab -e

Personnellement je programme les mises à jour chaque jour à 7h et le renouvellement des certificats SSL chaque premier du mois comme ceci :

0 7 * * * /usr/bin/ansible-playbook /etc/ansible/playbooks/update-all-servers.yml
0 7 1 * * /usr/bin/ansible-playbook /etc/ansible/playbooks/include/letsencrypt.yml

Fin