--- # ============================================================================== # ЭТАП 1: Сбор сертификатов с хоста storm.bit-it.ru на ваш ПК # ============================================================================== - name: Безопасный сбор всех SSL-сертификатов с эталонного хоста hosts: storm.bit-it.ru become: true vars: local_tmp_dir: "/tmp/ansible_certs" target_certs: [] ansible_python_interpreter: /usr/bin/python3 tasks: - name: Динамический поиск папок доменов ansible.builtin.find: paths: "/etc/letsencrypt/live" file_type: directory recurse: false excludes: "all,README" register: found_live_dirs - name: Формирование списка доменов с учетом фильтрации ansible.builtin.set_fact: dynamic_domains: "{{ (found_live_dirs.files | map(attribute='path') | map('basename') | list) if target_certs | length == 0 else (found_live_dirs.files | map(attribute='path') | map('basename') | intersect(target_certs) | list) }}" cacheable: true - name: Проверка, что список доменов для переноса не пуст ansible.builtin.meta: end_play when: dynamic_domains | length == 0 - name: Вывод списка доменов, выбранных для синхронизации ansible.builtin.debug: msg: "Будут синхронизированы следующие сертификаты: {{ dynamic_domains }}" - name: МАКСИМАЛЬНАЯ БЕЗОПАСНОСТЬ - Создание изолированной папок на вашем ПК ansible.builtin.file: path: "{{ local_tmp_dir }}/{{ item }}" state: directory mode: '0700' delegate_to: localhost become: false loop: [ 'archive', 'live', 'renewal' ] # --- РЕШЕНИЕ ПРОБЛЕМЫ С ПАПКАМИ --- - name: Поиск всех файлов внутри папок archive для выбранных доменов ansible.builtin.find: paths: "/etc/letsencrypt/archive/{{ item }}" file_type: file recurse: true loop: "{{ dynamic_domains | list }}" register: found_archive_files - name: Скачивание файлов archive на ваш ПК ansible.builtin.fetch: src: "{{ item.1.path }}" dest: "{{ local_tmp_dir }}/archive/{{ inventory_hostname }}/{{ item.1.path | relpath('/') }}" flat: true loop: "{{ found_archive_files.results | subelements('files') }}" - name: Скачивание конфигураций обновления renewal на ваш ПК ansible.builtin.fetch: src: "/etc/letsencrypt/renewal/{{ item }}.conf" dest: "{{ local_tmp_dir }}/renewal/{{ item }}.conf" flat: true loop: "{{ dynamic_domains | list }}" - name: Скачивание файлов из папок live на ваш ПК ansible.builtin.fetch: src: "/etc/letsencrypt/live/{{ item.0 }}/{{ item.1 }}.pem" dest: "{{ local_tmp_dir }}/live/{{ item.0 }}/{{ item.1 }}.pem" flat: true loop: "{{ dynamic_domains | product(['cert', 'chain', 'fullchain', 'privkey']) | list }}" # ============================================================================== # ЭТАП 2: Раскатка сертификатов СТРОГО на выбранные хосты # ============================================================================== - name: Раскатка SSL-сертификатов с ПК на целевые хосты и зачистка hosts: "{{ target_certs if (target_certs | length > 0) else 'all:!storm.bit-it.ru' }}" become: true vars: local_tmp_dir: "/tmp/ansible_certs" source_host_inventory_name: "storm.bit-it.ru" ansible_python_interpreter: /usr/bin/python3 tasks: - name: Основной block раскатки block: - name: Сбор информации о запущенных сервисах на целевом хосте ansible.builtin.service_facts: - name: Автоматическое создание всей иерархии папок Let's Encrypt на целевом хосте ansible.builtin.file: path: "/etc/letsencrypt/{{ item }}" state: directory owner: root group: root mode: '0755' loop: [ 'archive', 'live', 'renewal' ] - name: Копирование содержимого папки archive на целевой сервер ansible.builtin.copy: src: "{{ local_tmp_dir }}/archive/{{ source_host_inventory_name }}/etc/letsencrypt/archive/{{ inventory_hostname }}/" dest: "/etc/letsencrypt/archive/{{ inventory_hostname }}/" owner: root group: root mode: '0600' when: inventory_hostname in hostvars[source_host_inventory_name]['dynamic_domains'] - name: Копирование конфигураций renewal на целевой сервер ansible.builtin.copy: src: "{{ local_tmp_dir }}/renewal/{{ inventory_hostname }}.conf" dest: "/etc/letsencrypt/renewal/{{ inventory_hostname }}.conf" owner: root group: root mode: '0644' when: inventory_hostname in hostvars[source_host_inventory_name]['dynamic_domains'] - name: Создание папок доменов внутри директории live ansible.builtin.file: path: "/etc/letsencrypt/live/{{ inventory_hostname }}" state: directory owner: root group: root mode: '0700' when: inventory_hostname in hostvars[source_host_inventory_name]['dynamic_domains'] - name: Нативная сборка относительных символических ссылок в папке live ansible.builtin.file: src: "../../archive/{{ inventory_hostname }}/{{ item }}1.pem" dest: "/etc/letsencrypt/live/{{ inventory_hostname }}/{{ item }}.pem" state: link force: true loop: [ 'cert', 'chain', 'fullchain', 'privkey' ] when: inventory_hostname in hostvars[source_host_inventory_name]['dynamic_domains'] - name: Автоматический перезапуск активных веб- и почтовых служб ansible.builtin.systemd: name: "{{ item }}" state: restarted loop: [ 'dovecot', 'nginx', 'apache2', 'httpd' ] when: - ansible_facts.services[item + '.service'] is defined - ansible_facts.services[item + '.service'].state == 'running' - inventory_hostname in hostvars[source_host_inventory_name]['dynamic_domains'] always: - name: МАКСИМАЛЬНАЯ БЕЗОПАСНОСТЬ - Полное удаление секретных ключей с ПК администратора ansible.builtin.file: path: "{{ local_tmp_dir }}" state: absent delegate_to: localhost become: false run_once: true