From eaec4d7b87d27cbc768bb03cbe03dcceccbf585f Mon Sep 17 00:00:00 2001 From: hogweed1 Date: Thu, 21 May 2026 02:10:57 +1000 Subject: [PATCH] ssh-certs users. --- .../proxmoxes/group_vars/all/projects.yml | 9 ++ playbooks/deploy_ssh_certs.yml | 94 +++++++++++++++++++ playbooks/deploy_ssh_user_certs.yml | 70 ++++++++++++++ playbooks/software/acme_sh.yml | 67 +++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 environments/proxmoxes/group_vars/all/projects.yml create mode 100644 playbooks/deploy_ssh_certs.yml create mode 100644 playbooks/deploy_ssh_user_certs.yml create mode 100644 playbooks/software/acme_sh.yml diff --git a/environments/proxmoxes/group_vars/all/projects.yml b/environments/proxmoxes/group_vars/all/projects.yml new file mode 100644 index 0000000..f7a456c --- /dev/null +++ b/environments/proxmoxes/group_vars/all/projects.yml @@ -0,0 +1,9 @@ +project_users: + - name: test-nigger + sudo: true + #- name: ivan + # sudo: false + +allowed_projects: + - zagon + - zagon2 \ No newline at end of file diff --git a/playbooks/deploy_ssh_certs.yml b/playbooks/deploy_ssh_certs.yml new file mode 100644 index 0000000..a52e574 --- /dev/null +++ b/playbooks/deploy_ssh_certs.yml @@ -0,0 +1,94 @@ +- hosts: all + become: yes + vars: + # Задайте пути к ключам на вашей локальной машине (где запускается Ansible), + # предварительно скачав их с сервера step-ca + local_ssh_user_ca_pub_path: "~/.step/secrets/ssh_user_ca_key.pub" + local_ssh_host_ca_key_path: "~/.step/secrets/ssh_host_ca_key" + + tasks: + # --- ЭТАП 1: УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ И КАТАЛОГАМИ --- + + - name: Создание системной директории для принципалов + file: + path: /etc/ssh/auth_principals + state: directory + owner: root + group: root + mode: '0755' + + - name: Создание локальных пользователей из списка проекта + user: + name: "{{ item.name }}" + shell: /bin/bash + create_home: yes + state: present + loop: "{{ project_users }}" + + - name: Генерация файлов auth_principals для каждого пользователя + copy: + dest: "/etc/ssh/auth_principals/{{ item.name }}" + content: "{{ allowed_projects | join('\n') }}\n" + owner: root + group: root + mode: '0644' + loop: "{{ project_users }}" + + - name: Настройка беспарольного sudo для администраторов проекта + copy: + dest: "/etc/sudoers.d/project-ssh-{{ item.name }}" + content: "{{ item.name }} ALL=(ALL) NOPASSWD:ALL" + validate: /usr/sbin/visudo -cf %s + mode: '0440' + loop: "{{ project_users }}" + when: item.sudo | bool + + # --- ЭТАП 2: РАСКАТКА ДОВЕРИЯ К USER CA --- + + - name: Копирование публичного ключа User CA на хост + copy: + src: "{{ local_ssh_user_ca_pub_path }}" + dest: /etc/ssh/ca.pub + owner: root + group: root + mode: '0644' + + # --- ЭТАП 3: ХОСТОВЫЕ СЕРТИФИКАТЫ (Опционально, локальный выпуск) --- + # Мы генерируем сертификат прямо на машине администратора с помощью локального step-cli + # и копируем готовый сертификат на целевой сервер. + + - name: Локальная генерация Host-сертификата для целевой ноды + delegate_to: localhost + become: no + shell: > + step ssh certificate {{ inventory_hostname }} /tmp/{{ inventory_hostname }}_host.pub + --host --sign --ca-key {{ local_ssh_host_ca_key_path }} --no-password --insecure + --principal {{ inventory_hostname }} --principal {{ ansible_host }} + args: + creates: "/tmp/{{ inventory_hostname }}-cert.pub" + # Примечание: Для работы этой задачи на вашем ПК должен быть настроен step-cli контекст. + # Если это сложно автоматизировать, этот блок можно пока пропустить. + + # --- ЭТАП 4: НАСТРОЙКА SSHD_CONFIG --- + + - name: Сбор имен всех разрешенных пользователей для AllowUsers + set_fact: + allow_users_list: "{{ project_users | map(attribute='name') | join(' ') }}" + + - name: Конфигурация OpenSSH Daemon + blockinfile: + path: /etc/ssh/sshd_config + block: | + TrustedUserCAKeys /etc/ssh/ca.pub + AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u + AllowUsers {{ allow_users_list }} hogweed1 + PasswordAuthentication no + PubkeyAuthentication yes + marker: "# {mark} ANSIBLE MANAGED ZERO TRUST BLOCK #" + notify: Restart SSH + + handlers: + - name: Restart SSH + service: + name: sshd + state: restarted \ No newline at end of file diff --git a/playbooks/deploy_ssh_user_certs.yml b/playbooks/deploy_ssh_user_certs.yml new file mode 100644 index 0000000..4d06ef8 --- /dev/null +++ b/playbooks/deploy_ssh_user_certs.yml @@ -0,0 +1,70 @@ +- hosts: all + become: yes + vars: + # Путь к ПУБЛИЧНОМУ ключу User CA на вашей Ansible-машине + local_ssh_user_ca_pub_path: "/etc/step-ca/ssh_user_ca_key.pub" + + tasks: + - name: Создание системной директории для принципалов + file: + path: /etc/ssh/auth_principals + state: directory + owner: root + group: root + mode: '0755' + + - name: Создание локальных пользователей из списка проекта + user: + name: "{{ item.name }}" + shell: /bin/bash + create_home: yes + state: present + loop: "{{ project_users }}" + + - name: Генерация файлов auth_principals с маппингом проектов + copy: + dest: "/etc/ssh/auth_principals/{{ item.name }}" + content: "{{ allowed_projects | join('\n') }}\n" + owner: root + group: root + mode: '0644' + loop: "{{ project_users }}" + + - name: Настройка беспарольного sudo для администраторов проекта + copy: + dest: "/etc/sudoers.d/project-ssh-{{ item.name }}" + content: "{{ item.name }} ALL=(ALL) NOPASSWD:ALL" + validate: /usr/sbin/visudo -cf %s + mode: '0440' + loop: "{{ project_users }}" + when: item.sudo | bool + + - name: Копирование публичного ключа User CA на хост + copy: + src: "{{ local_ssh_user_ca_pub_path }}" + dest: /etc/ssh/ca.pub + owner: root + group: root + mode: '0644' + + - name: Сбор имен всех разрешенных пользователей для AllowUsers + set_fact: + allow_users_list: "{{ project_users | map(attribute='name') | join(' ') }}" + + - name: Настройка sshd_config для авторизации пользователей по сертификатам + blockinfile: + path: /etc/ssh/sshd_config + block: | + TrustedUserCAKeys /etc/ssh/ca.pub + AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u + AllowUsers {{ allow_users_list }} hogweed1 + PasswordAuthentication no + PubkeyAuthentication yes + marker: "# {mark} ANSIBLE MANAGED USER CERTIFICATE BLOCK #" + notify: Restart SSH + + handlers: + - name: Restart SSH + service: + name: sshd + state: restarted \ No newline at end of file diff --git a/playbooks/software/acme_sh.yml b/playbooks/software/acme_sh.yml new file mode 100644 index 0000000..3427cb5 --- /dev/null +++ b/playbooks/software/acme_sh.yml @@ -0,0 +1,67 @@ +--- +#### TODO заставить работать + + +# 1. Готовим окружение и папки +- name: Ensure git and curl are installed + ansible.builtin.apt: + name: + - git + - curl + state: present + +- name: Create SSL directory for Angie + ansible.builtin.file: + path: "{{ angie_ssl_dir }}" + state: directory + owner: root + group: root + mode: '0755' + +# 2. Установка acme.sh (Идемпотентный способ через Git вместо curl | sh) +- name: Clone acme.sh repository + ansible.builtin.git: + repo: 'https://github.com' + dest: /root/acme.sh-src + version: master + depth: 1 + +- name: Check if acme.sh is already installed + ansible.builtin.stat: + path: /root/.acme.sh/acme.sh + register: acme_installed + +- name: Run acme.sh installer + ansible.builtin.command: + cmd: "./acme.sh --install -m {{ acme_email }}" + chdir: /root/acme.sh-src + when: not acme_installed.stat.exists + +# 3. Выпуск сертификата через DNS Reg.ru +- name: Issue Let's Encrypt certificate via Reg.ru DNS + ansible.builtin.command: + cmd: > + /root/.acme.sh/acme.sh --issue --dns dns_regru + -d "{{ acme_domain }}" + --server https://acme-v02.api.letsencrypt.org/directory + # Передаем секреты строго внутри контекста выполнения этой задачи + environment: + REGRU_API_Username: "{{ regru_username }}" + REGRU_API_Password: "{{ regru_password }}" + register: issue_result + # acme.sh вернет ошибку, если сертификат уже есть и еще свежий. + # Игнорируем эту ошибку, чтобы плейбук не падал при повторных запусках. + failed_when: + - issue_result.rc != 0 + - "'Sign failed: LE_OrderFinalize' not in issue_result.stderr" + - "'Create new order error' not in issue_result.stderr" + +# 4. Привязка (инсталляция) сертификата в папки Angie и настройка автопродления +- name: Install certificate to Angie directory and configure reload cmd + ansible.builtin.command: + cmd: > + /root/.acme.sh/acme.sh --install-cert -d "{{ acme_domain }}" + --key-file "{{ angie_ssl_dir }}/key.pem" + --fullchain-file "{{ angie_ssl_dir }}/cert.pem" + --reloadcmd "systemctl reload angie" + # service angie force-reload заменен на более современный systemctl reload \ No newline at end of file