Skip to content

Ansible

Documentation de Ansible

Tip

Ressources Essentielles - Ansible Galaxy : Trouvez des rôles et collections communautaires prêts à l'emploi. - Ansible Documentation : La documentation officielle, très complète.

Inventaire

Exemple de fichier d'inventaire (l'extention du fichier doit être en yaml ex: inventory.yaml) :

all:
  children:
    debian11:
      hosts:
        VMFSFTADEV01:
          ansible_host: 10.68.9.1
    alma:
      hosts:
        VMCENTREON:
          ansible_host: 10.255.9.1

On peut ensuite spécifier dans le fichier d'environnement le fichier d'inventaire à cible : /etc/ansible/ansible.cfg

[defaults]
inventory = /home/user/inventory.yaml

Playbook

Exemple de fichier PLaybook :

- name: SSO linux
  hosts: all
  become: true
  become_method: sudo
  vars:
    ansible_python_interpreter: /usr/bin/python3
  vars_files:
    - secret_vars.yml
  tasks:
    - name: Installer les dépendances nécessaires pour realm et sssd
      ansible.builtin.package:
        name:
          - realmd
          - sssd
          - sssd-tools
          - libnss-sss
          - libpam-sss
          - adcli
          - samba-common-bin 
          - krb5-user
          - packagekit-tools
          - python3-apt
          - python3-pexpect
        state: present
    - name: Vérifier si déjà joint au domaine organisation.com
      command: realm list
      register: realm_list
      changed_when: false
    - name: Joindre au domaine organisation.com
      ansible.builtin.expect:s
        command: realm join organisation.com -U "{{ domain_admin_user }}" -v
        responses:
         Password: "{{ domain_admin_password }}"
      no_log: true
      when: "'organisation.com' not in realm_list.stdout"
      register: realm_join
    - name: Configurer /etc/sssd/sssd.conf
      ansible.builtin.blockinfile:
        path: /etc/sssd/sssd.conf
        block: |
          [sssd]
          domains = organisation.com
          config_file_version = 2
          services = nss, pam

          [domain/organisation.com]
          default_shell = /bin/bash
          krb5_store_password_if_offline = True
          cache_credentials = True
          krb5_realm = organisation.com
          realmd_tags = manages-system joined-with-adcli
          id_provider = ad
          fallback_homedir = /home/%u@%d
          ad_domain = organisation.com
          use_fully_qualified_names = True
          ldap_id_mapping = True
          access_provider = simple
          simple_allow_groups = sec-adm-system-n3
        create: yes
        marker: "# {mark} ANSIBLE MANAGED BLOCK"
      notify: redémarrer sssd
    - name: Configurer /etc/pam.d/common-session
      ansible.builtin.blockinfile:
        path: /etc/pam.d/common-session
        block: |
          session [default=1]                     pam_permit.so
          session requisite                       pam_deny.so
          session required                        pam_permit.so
          session required        pam_unix.so
          session optional                        pam_sss.so
          session optional        pam_systemd.so
          session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
        marker: "# {mark} ANSIBLE MANAGED BLOCK"
        create: yes      
    - name: Configurer /etc/sudoers.d/domainadmin
      ansible.builtin.blockinfile:
        path: /etc/sudoers.d/domainadmin
        block: |
          %SEC-ADM-System-N3@organisation.com ALL=(ALL:ALL) ALL
        marker: "# {mark} ANSIBLE MANAGED BLOCK"
        create: yes
  handlers:
    - name: redémarrer sssd
      ansible.builtin.service:
        name: sssd
        state: restarted

Ce playbook doit activer la SSO sur les instances debian et utilise des mot de passe stocker dans "secret_vars.yml"

Pour éditer ce fichier crypter utiliser la commande :

ansible-vault edit secret_vars.yml

Voici un autre exemple pour installer la solution defender :

- name: Microsoft Defender pour point de terminaison Linux
  hosts: all
  become: true
  vars_files:
    - secret_vars.yml
  tasks:

    - name: Vérifier si le système est 32 bits
      set_fact:
        is_32_bit: "{{ ansible_architecture == 'i386' }}"

    - name: Marquer le playbook comme étant sauté si le système est 32 bits
      meta: end_play
      when: is_32_bit
      tags: skip_32_bit

    - name: Afficher un message si le système est 32 bits
      debug:
        msg: "Ce playbook ne supporte pas les systèmes 32 bits."
      when: is_32_bit

    - name: Vérifier si le répertoire 'mdatp' existe
      stat:
        path: /etc/opt/microsoft/mdatp/
      register: managed_dir_mdatp

    - name: Créer les répertoires MDATP
      file:
        path: /etc/opt/microsoft/mdatp/
        recurse: true
        state: directory
        mode: 0755
        owner: root
        group: root
      when: not managed_dir_mdatp.stat.exists

    - name: Installer les dépendances nécessaires pour extraire le "Onboarding Package"
      ansible.builtin.package:
        name:
          - unzip
        state: present
        update_cache: yes

    - name: Vérifier si le répertoire 'managed' existe
      stat:
        path: /etc/opt/microsoft/mdatp/managed/
      register: managed_dir

    - name: Créer le répertoire managed pour MDATP
      file:
        path: /etc/opt/microsoft/mdatp/managed/
        state: directory
        mode: 0600
        owner: root
        group: root
      when: not managed_dir.stat.exists

    - name: Copier mde_Onboarding
      copy:
        src: files/MicrosoftDefenderATPOnboardingLinuxServer.py
        dest: /etc/opt/microsoft/mdatp/MicrosoftDefenderATPOnboardingLinuxServer.py
        owner: root
        group: root
        mode: 0600

    - name: Copier mde_installer
      copy:
        src: files/mde_installer.sh
        dest: /etc/opt/microsoft/mdatp/mde_installer.sh
        owner: root
        group: root
        mode: 0700

    - name: Vérifiez si la commande mdatp existe
      command: which mdatp
      register: mdatp_command_check
      ignore_errors: true
      changed_when: false

    - name: Vérifiez si mdeattach_managed.json existe
      stat:
        path: /etc/opt/microsoft/mdatp/managed/mdeattach_managed.json
      register: mdeattach_managed_json_stat

    - name: Exécutez mde_installer.sh
      shell: /etc/opt/microsoft/mdatp/mde_installer.sh -i -y -c prod -o /etc/opt/microsoft/mdatp/MicrosoftDefenderATPOnboardingLinuxServer.py
      when: mdatp_command_check.failed or not mdeattach_managed_json_stat.stat.exists

    - name: Copiez le fichier du service mdatp-scan dans le répertoire systemd
      copy:
        src: files/mdatp-scan.service
        dest: /etc/systemd/system/mdatp-scan.service
        mode: '0644'
      register: service_file

    - name: Copiez le fichier mdatp-scan timer dans le répertoire systemd
      copy:
        src: files/mdatp-scan.timer
        dest: /etc/systemd/system/mdatp-scan.timer
        mode: '0644'
      register: timer_file

    - name: Recharger systemd pour appliquer de nouveaux fichiers d'unité
      command: systemctl daemon-reload
      when: service_file.changed or timer_file.changed

    - name: Activer mdatp-scan.timer pour démarrer au démarrage
      systemd:
        name: mdatp-scan.timer
        enabled: yes
        state: started
      when: service_file.changed or timer_file.changed

    - name: Démarrez le mdatp-scan.timer dès maintenant
      systemd:
        name: mdatp-scan.timer
        state: started
      when: service_file.changed or timer_file.changed

Ce playbook Ansible installe et configure Microsoft Defender pour Endpoint sur des systèmes Linux. Il commence par vérifier l'architecture du système, et s'arrête si celui-ci est en 32 bits. Ensuite, il prépare les répertoires nécessaires, installe des dépendances, et copie les scripts d'installation et d'onboarding requis. Si nécessaire, il exécute le script d'installation et configure des services systemd pour automatiser les scans de sécurité.

Variable

Ce fichier ansible.cfg situer par default dans /etc/ansible/ansible.cfg configure les paramètres par défaut pour Ansible. Il spécifie l'emplacement du fichier de mot de passe pour Vault, l'inventaire des hôtes, et l'interpréteur Python à utiliser. Les vérifications de clé hôte SSH sont désactivées. Il définit également le fichier de clé privée SSH à utiliser, l'utilisateur distant par défaut (it), et optimise les performances avec 10 forks et un timeout de 10 secondes.

[defaults]
vault_password_file = /etc/ansible/vault.txt
inventory = /opt/ansible/inventory.yaml
interpreter_python = /usr/bin/python3
host_key_checking = False
private_key_file = /etc/ansible/ssh_data/id_rsa
remote_user = it
forks = 10
timeout = 10

Lancer des playbook ansible

Pour lancer tous les playbook qui commence par any on peut utiliser la commande suivant su la vm ansible :

cd /opt/ansible
ansible-playbook any*

Modification du fichier de mot de passe

Le fichier secret_vars.yml contient tout les mots de passe nécessaire au fonctionnement de ansible pour modifier les variables dans ce fichier utiliser la commande suivante la vm ansible :

cd /opt/ansible
ansible-vault edit secret_vars.yml
# Préciser le mot de passe du vault

Résolution de problème

Pour résoudre des problème commun (ex pb de dépendance ext) bien précisé l'intépréteur python dans le playbook:

  vars:
    ansible_python_interpreter: /usr/bin/python3
 ```

 Pour éditier le vault via l'éditeur nano on peut changer les fichier : "/etc/bash.bashrc"
 et rajouter la variable `export EDITOR=nano`

## Agent Ansible

Ce script permet de configurer une machine pour qu'elle puisse être pilotée par Ansible, notamment à travers un tunnel SSH inversé (utile pour les machines derrière un NAT sans IP publique accessible).

Il effectue les actions suivantes :
*   Création de l'utilisateur dédié.
*   Configuration des privilèges sudo.
*   Mise en place des clés SSH (récupération depuis le serveur maître).
*   Établissement d'un tunnel SSH inversé vers le serveur Ansible.

```bash
#!/bin/bash

# set -x

# Configuration
REMOTE_SERVER="remote.domaine.org"
BASTION_HOST="bastion.domaine.org"
USERNAME="user"
REMOTE_USER="user"
BASTION_USER="user"
SSH_DIR="/home/$USERNAME/.ssh"
PRIVATE_KEY_PATH="$SSH_DIR/id_rsa"
BASTION_KEY_PATH="$PRIVATE_KEY_PATH"  # Par défaut, on utilise la même clé
REMOTE_KEY_PATH="/home/user/.ssh/id_rsa"
LOCAL_PORT="20022"
REMOTE_ANSIBLE_PORT="22"
HOSTNAME=$(hostname)
REMOTE_SCRIPT="infrastructure/script-ansible.sh"

# Fonction de logging unifiée
log() {
    local level="$1"
    local message="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    echo "$timestamp - $level - $message" | tee -a /tmp/ansible-agent.log
}

# Fonction pour vérifier si l'utilisateur existe
check_user_exists() {
    if id "$USERNAME" &>/dev/null; then
        log "INFO" "L'utilisateur $USERNAME existe déjà."
        return 0
    else
        log "WARNING" "L'utilisateur $USERNAME n'existe pas."
        return 1
    fi
}

# Fonction pour vérifier si l'utilisateur est sudoeur
check_sudo_privileges() {
    if groups "$USERNAME" | grep -q '\bsudo\b\|\bwheel\b'; then
        log "INFO" "L'utilisateur $USERNAME a les privilèges sudo."
        return 0
    else
        log "WARNING" "L'utilisateur $USERNAME n'a pas les privilèges sudo."
        return 1
    fi
}

# Fonction pour créer l'utilisateur
create_user() {
    log "INFO" "Création de l'utilisateur $USERNAME..."

    # Créer l'utilisateur avec un répertoire home
    if useradd -m -s /bin/bash "$USERNAME"; then
        log "INFO" "Utilisateur $USERNAME créé avec succès."
    else
        log "ERROR" "Échec de la création de l'utilisateur $USERNAME."
        exit 1
    fi

    # Demander et définir le mot de passe
    echo "Veuillez définir un mot de passe pour l'utilisateur $USERNAME :"
    if passwd "$USERNAME"; then
        log "INFO" "Mot de passe défini avec succès."
    else
        log "ERROR" "Échec de la définition du mot de passe."
        exit 1
    fi
}

# Fonction pour ajouter l'utilisateur au groupe sudo
add_sudo_privileges() {
    log "INFO" "Ajout des privilèges sudo pour l'utilisateur $USERNAME..."

    # Déterminer le groupe sudo selon la distribution
    if getent group sudo &>/dev/null; then
        SUDO_GROUP="sudo"
    elif getent group wheel &>/dev/null; then
        SUDO_GROUP="wheel"
    else
        log "ERROR" "Aucun groupe sudo/wheel trouvé sur ce système."
        exit 1
    fi

    if usermod -aG "$SUDO_GROUP" "$USERNAME"; then
        log "INFO" "Privilèges sudo ajoutés avec succès."
    else
        log "ERROR" "Échec de l'ajout des privilèges sudo."
        exit 1
    fi
}

# Fonction pour créer le répertoire .ssh avec les bonnes permissions
setup_ssh_directory() {
    log "INFO" "Configuration du répertoire .ssh..."

    # Créer le répertoire .ssh s'il n'existe pas
    if [[ ! -d "$SSH_DIR" ]]; then
        if sudo -u "$USERNAME" mkdir -p "$SSH_DIR"; then
            log "INFO" "Répertoire .ssh créé."
        else
            log "ERROR" "Échec de la création du répertoire .ssh."
            exit 1
        fi
    fi

    # Définir les permissions correctes
    sudo chown "$USERNAME:$USERNAME" "$SSH_DIR"
    sudo chmod 700 "$SSH_DIR"
    log "INFO" "Permissions du répertoire .ssh configurées (700)."
}

# Fonction pour créer le fichier de configuration SSH
create_ssh_allow_config() {
    log "INFO" "Création du fichier de configuration SSH pour autoriser l'utilisateur user..."

    SSH_CONFIG_DIR="/etc/ssh/sshd_config.d"
    CONFIG_FILE="$SSH_CONFIG_DIR/user_allow.conf"

    # Créer le répertoire si nécessaire
    if [[ ! -d "$SSH_CONFIG_DIR" ]]; then
        mkdir -p "$SSH_CONFIG_DIR"
    fi

    # Écrire le contenu
    echo "AllowUsers user@0.0.0.0/0 user@127.0.0.1 user@::1 user@localhost" > "$CONFIG_FILE"

    log "INFO" "Fichier de configuration SSH créé : $CONFIG_FILE"

    # Recharger la configuration SSH
    if systemctl reload ssh 2>/dev/null || service ssh reload 2>/dev/null || systemctl reload sshd 2>/dev/null; then
        log "INFO" "Configuration SSH rechargée avec succès."
    else
        log "WARNING" "Échec du rechargement de la configuration SSH."
    fi
}

# Fonction pour vérifier l'accès au bastion et configurer la clé si nécessaire
check_bastion_access() {
    log "INFO" "Vérification de l'accès au bastion $BASTION_HOST..."

    # Tester l'accès avec la clé actuelle (par défaut id_rsa)
    if ssh -i "$BASTION_KEY_PATH" -o BatchMode=yes -o StrictHostKeyChecking=no "${BASTION_USER}@${BASTION_HOST}" exit 2>/dev/null; then
        log "INFO" "Accès au bastion réussi avec $BASTION_KEY_PATH."
        return 0
    fi

    log "WARNING" "L'accès au bastion avec $BASTION_KEY_PATH a échoué."

    # Si id_rsa_bastion existe, essayer avec celle-là
    local CUSTOM_BASTION_KEY="$SSH_DIR/id_rsa_bastion"
    if [[ -f "$CUSTOM_BASTION_KEY" ]]; then
        if ssh -i "$CUSTOM_BASTION_KEY" -o BatchMode=yes -o StrictHostKeyChecking=no "${BASTION_USER}@${BASTION_HOST}" exit 2>/dev/null; then
            log "INFO" "Accès au bastion réussi avec $CUSTOM_BASTION_KEY."
            BASTION_KEY_PATH="$CUSTOM_BASTION_KEY"
            return 0
        fi
    fi


    # Copier la clé spécifique
    cp "$USER_BASTION_KEY" "$CUSTOM_BASTION_KEY"
    chown "$USERNAME:$USERNAME" "$CUSTOM_BASTION_KEY"
    chmod 600 "$CUSTOM_BASTION_KEY"

    BASTION_KEY_PATH="$CUSTOM_BASTION_KEY"
    log "INFO" "Clé du bastion configurée : $BASTION_KEY_PATH"

    # Vérifier à nouveau
    if ssh -i "$BASTION_KEY_PATH" -o BatchMode=yes -o StrictHostKeyChecking=no "${BASTION_USER}@${BASTION_HOST}" exit 2>/dev/null; then
        log "INFO" "Accès au bastion confirmé."
    else
        log "ERROR" "Toujours impossible de se connecter au bastion."
        exit 1
    fi
}

# Fonction pour récupérer la clé privée depuis le serveur distant
retrieve_private_key() {
    # Vérifier si les clés privée et publique existent déjà
    if [[ -f "$PRIVATE_KEY_PATH" ]] && [[ -f "$PRIVATE_KEY_PATH.pub" ]]; then
        log "INFO" "Les clés privée et publique existent déjà localement. Skipping retrieve_private_key."
        return 0
    fi

    # Demander le chemin de la clé privée locale pour la connexion au serveur distant
    echo "Veuillez entrer le chemin complet de la clé privée locale pour la connexion à $REMOTE_SERVER (via $BASTION_HOST) :"
    read -r LOCAL_AUTH_KEY_PATH

    if [[ -z "$LOCAL_AUTH_KEY_PATH" ]]; then
        log "ERROR" "Chemin de la clé privée locale non spécifié."
        exit 1
    fi

    local TEMP_KEY_PATH="/tmp/id_rsa_temp_$(date +%s)"
    local TEMP_PUB_KEY_PATH="/tmp/id_rsa.pub_temp_$(date +%s)"

    # Récupérer la clé privée via scp en tant qu'utilisateur actuel
    log "INFO" "Téléchargement de la clé privée depuis $REMOTE_SERVER vers un fichier temporaire..."
    if scp -v -o ProxyCommand="ssh -W %h:%p -i \"$LOCAL_AUTH_KEY_PATH\" -o StrictHostKeyChecking=no \"${BASTION_USER}@${BASTION_HOST}\"" -i "$LOCAL_AUTH_KEY_PATH" "$REMOTE_USER@$REMOTE_SERVER:$REMOTE_KEY_PATH" "$TEMP_KEY_PATH"; then
        log "INFO" "Clé privée téléchargée avec succès dans $TEMP_KEY_PATH."
    else
        log "ERROR" "Échec du téléchargement de la clé privée depuis $REMOTE_SERVER."
        exit 1
    fi

    # Déplacer la clé vers le répertoire de l'utilisateur cible et définir les permissions
    log "INFO" "Déplacement de la clé privée vers $PRIVATE_KEY_PATH et configuration des permissions..."
    if mv "$TEMP_KEY_PATH" "$PRIVATE_KEY_PATH"; then
        log "INFO" "Clé privée déplacée avec succès."
    else
        log "ERROR" "Échec du déplacement de la clé privée."
        exit 1
    fi

    sudo chown "$USERNAME:$USERNAME" "$PRIVATE_KEY_PATH"
    sudo chmod 600 "$PRIVATE_KEY_PATH"
    log "INFO" "Permissions de la clé privée configurées (600)."

    # Récupérer la clé publique via scp en tant qu'utilisateur actuel
    log "INFO" "Téléchargement de la clé publique depuis $REMOTE_SERVER vers un fichier temporaire..."
    if scp -v -o ProxyCommand="ssh -W %h:%p -i \"$LOCAL_AUTH_KEY_PATH\" -o StrictHostKeyChecking=no \"${BASTION_USER}@${BASTION_HOST}\"" -i "$LOCAL_AUTH_KEY_PATH" "$REMOTE_USER@$REMOTE_SERVER:$REMOTE_KEY_PATH.pub" "$TEMP_PUB_KEY_PATH"; then
        log "INFO" "Clé publique téléchargée avec succès dans $TEMP_PUB_KEY_PATH."
    else
        log "ERROR" "Échec du téléchargement de la clé publique depuis $REMOTE_SERVER."
        exit 1
    fi

    # Déplacer la clé publique vers le répertoire de l'utilisateur cible et définir les permissions
    log "INFO" "Déplacement de la clé publique vers $PRIVATE_KEY_PATH.pub et configuration des permissions..."
    if sudo mv "$TEMP_PUB_KEY_PATH" "$PRIVATE_KEY_PATH.pub"; then
        log "INFO" "Clé publique déplacée avec succès."
    else
        log "ERROR" "Échec du déplacement de la clé publique."
        exit 1
    fi

    sudo chown "$USERNAME:$USERNAME" "$PRIVATE_KEY_PATH.pub"
    sudo chmod 644 "$PRIVATE_KEY_PATH.pub"
    log "INFO" "Permissions de la clé publique configurées (644)."
}

# Fonction pour vérifier et activer le serveur SSH
check_and_enable_ssh() {
    log "INFO" "Vérification et activation du serveur SSH..."
    if systemctl is-active --quiet ssh; then
        log "INFO" "Le service SSH est déjà actif."
    else
        log "WARNING" "Le service SSH n'est pas actif. Tentative de démarrage..."
        if systemctl start ssh; then
            log "INFO" "Service SSH démarré avec succès."
        else
            log "ERROR" "Échec du démarrage du service SSH."
            exit 1
        fi
    fi

}

# Fonction principale
main_setup() {
    log "INFO" "Début de la configuration pour l'utilisateur $USERNAME"

    # Vérifier si le script est exécuté en tant que root
    if [[ $EUID -ne 0 ]]; then
        log "ERROR" "Ce script doit être exécuté en tant que root."
        exit 1
    fi

    # Vérifier si l'utilisateur existe
    user_exists=false
    if check_user_exists; then
        user_exists=true
    else
        create_user
        user_exists=true
    fi

    # Vérifier les privilèges sudo
    if ! check_sudo_privileges; then
        add_sudo_privileges
    fi

    # Configurer le répertoire .ssh
    setup_ssh_directory

    # Créer le fichier de configuration SSH
    create_ssh_allow_config

    # Récupérer la clé privée
    retrieve_private_key

    # Vérifier l'accès au bastion (C'est ici qu'on règle BASTION_KEY_PATH si besoin)
    check_bastion_access

    # Configurer authorized_keys
    setup_authorized_keys

    # Vérifier et activer le serveur SSH
    check_and_enable_ssh

    log "INFO" "Configuration terminée avec succès !"
    log "INFO" "L'utilisateur $USERNAME est maintenant configuré avec :"
    log "INFO" "  - Compte utilisateur créé"
    log "INFO" "  - Privilèges sudo accordés"
    log "INFO" "  - Répertoire .ssh configuré avec les bonnes permissions"
    log "INFO" "  - Clé privée installée depuis $REMOTE_SERVER"
    log "INFO" "  - Accès bastion vérifié (Clé: $BASTION_KEY_PATH)"
    log "INFO" "  - Serveur SSH vérifié et activé"
}

# Fonction pour configurer authorized_keys
setup_authorized_keys() {
    log "INFO" "Configuration du fichier authorized_keys..."

    AUTHORIZED_KEYS_FILE="$SSH_DIR/authorized_keys"

    # Créer le fichier authorized_keys s'il n'existe pas
    if [[ ! -f "$AUTHORIZED_KEYS_FILE" ]]; then
        touch "$AUTHORIZED_KEYS_FILE"
    fi

    # Ajouter la clé publique au fichier authorized_keys si elle n'y est pas déjà
    if [[ -f "$PRIVATE_KEY_PATH.pub" ]]; then
        if ! grep -q "$(cat "$PRIVATE_KEY_PATH.pub")" "$AUTHORIZED_KEYS_FILE"; then
            cat "$PRIVATE_KEY_PATH.pub" >> "$AUTHORIZED_KEYS_FILE"
            log "INFO" "Clé publique ajoutée à authorized_keys."
        else
            log "INFO" "Clé publique déjà présente dans authorized_keys."
        fi
    else
        log "WARNING" "Clé publique non trouvée, skipping setup_authorized_keys."
    fi

    # Définir les permissions correctes
    chown "$USERNAME:$USERNAME" "$AUTHORIZED_KEYS_FILE"
    chmod 600 "$AUTHORIZED_KEYS_FILE"
    log "INFO" "Permissions de authorized_keys configurées (600)."

    # Recharger la configuration SSH
    if systemctl reload ssh 2>/dev/null || service ssh reload 2>/dev/null || systemctl reload sshd 2>/dev/null; then
        log "INFO" "Configuration SSH rechargée après configuration authorized_keys."
    else
        log "WARNING" "Échec du rechargement de la configuration SSH."
    fi
}

# Établir le tunnel reverse SSH
establish_tunnel() {
    log "INFO" "Établissement du tunnel SSH reverse pour $HOSTNAME via $BASTION_HOST"

    # Tunnel reverse : le serveur pourra se connecter au poste via le port distant
    ssh -o ProxyCommand="ssh -W %h:%p -i \"$BASTION_KEY_PATH\" -o StrictHostKeyChecking=no \"${BASTION_USER}@${BASTION_HOST}\"" \
        -i "$PRIVATE_KEY_PATH"\
        -o ServerAliveInterval=60 \
        -o ServerAliveCountMax=3 \
        -o StrictHostKeyChecking=no \
        -o ExitOnForwardFailure=yes \
        -R ${LOCAL_PORT}:localhost:22 \
        -N ${USERNAME}@${REMOTE_SERVER} &

    SSH_PID=$!
    log "INFO" "Tunnel établi avec PID: $SSH_PID"

    # Attendre que le tunnel soit établi
    sleep 1

    # Nettoyer known_hosts avant la notification Ansible
    log "INFO" "Suppression de l'entrée known_hosts sur le serveur ansible"
    ssh -o ProxyCommand="ssh -W %h:%p -i \"$BASTION_KEY_PATH\" -o StrictHostKeyChecking=no \"${BASTION_USER}@${BASTION_HOST}\"" \
        -i "$PRIVATE_KEY_PATH" ${USERNAME}@${REMOTE_SERVER} "docker exec cronjob /bin/ash -c \"ssh-keygen -f /root/.ssh/known_hosts -R '[127.0.0.1]:20022'\""

    # Notifier le serveur Ansible que le poste est disponible
    notify_ansible_server

    # Maintenir le tunnel pendant 10 minutes
    # sleep 600

    # Fermer le tunnel
    kill $SSH_PID 2>/dev/null
    log "INFO" "Tunnel fermé"
}

# Notifier le serveur Ansible
notify_ansible_server(){
    log "INFO" "Correction des permissions de /root/.ssh/config sur le conteneur distant..."
    ssh -o ProxyCommand="ssh -W %h:%p -i \"$BASTION_KEY_PATH\" -o StrictHostKeyChecking=no \"${BASTION_USER}@${BASTION_HOST}\"" \
        -i "$PRIVATE_KEY_PATH" ${USERNAME}@${REMOTE_SERVER} "docker exec -u 0 cronjob /bin/sh -c 'chown root:root /root/.ssh/config && chmod 600 /root/.ssh/config && chmod 700 /root/.ssh'"

    log "INFO" "Exécution du script Ansible sur le serveur distant"

    # Exécuter le script Ansible sur le serveur distant via SSH
    ssh -o ProxyCommand="ssh -W %h:%p -i \"$BASTION_KEY_PATH\" -o StrictHostKeyChecking=no \"${BASTION_USER}@${BASTION_HOST}\"" \
        -i "$PRIVATE_KEY_PATH" ${USERNAME}@${REMOTE_SERVER} "$REMOTE_SCRIPT -l desktop" -a

    if [ $? -eq 0 ]; then
        log "INFO" "Script Ansible exécuté avec succès sur le serveur distant"
    else
        log "ERROR" "Erreur lors de l'exécution du script Ansible sur le serveur distant"
    fi

    # Tuer le processus SSH du tunnel après l'exécution du script Ansible sur le serveur distant
    # kill $SSH_PID 2>/dev/null
    # log "Tunnel fermé par le client."
}

# Vérifier si un tunnel existe déjà
check_existing_tunnel() {
    if pgrep -f "ssh.*-R.*${LOCAL_PORT}" > /dev/null; then
        log "WARNING" "Un tunnel existe déjà, arrêt du script"
        exit 0
    fi
}

# Script principal
main_tunnel() {
    log "INFO" "Démarrage du script de tunnel reverse"
    check_existing_tunnel
    establish_tunnel
    log "INFO" "Fin du script de tunnel reverse"
    stop_ssh_service
}

# Fonction pour arrêter le service SSH
stop_ssh_service() {
    if systemctl is-enabled --quiet ssh; then
        log "INFO" "Le service SSH est 'enabled', pas d'arrêt du service."
        return
    fi
    log "INFO" "Arrêt du service SSH..."
    if systemctl is-active --quiet ssh; then
        if systemctl stop ssh ssh.socket; then
            log "INFO" "Service SSH et socket arrêtés avec succès."
        else
            log "ERROR" "Échec de l'arrêt du service SSH."
        fi
    else
        log "INFO" "Le service SSH n'est pas actif, pas besoin de l'arrêter."
    fi
}

# Exécution du script principal
main() {
    main_setup
    main_tunnel
}

# Exécution du script principal
main "$@"

Liens Connexes

  • Cronjob Docker : Mise en place d'un conteneur pour l'exécution centralisée des tâches Ansible.