Archive Legacy Setup And Add Ansible Control Plane

This commit is contained in:
Dmitry
2026-07-08 12:47:37 +03:00
parent 841d56380b
commit 4bafa7d09e
203 changed files with 1663 additions and 11 deletions
@@ -0,0 +1,16 @@
- name: Base server setup
hosts: servers
gather_facts: true
become: false
tasks:
- name: Ping hosts
ansible.builtin.ping:
- name: Check expected LAN IP
ansible.builtin.assert:
that:
- expected_lan_ip in ansible_all_ipv4_addresses
fail_msg: "{{ inventory_hostname }} must have {{ expected_lan_ip }}; found: {{ ansible_all_ipv4_addresses | join(', ') }}"
success_msg: "{{ inventory_hostname }} has {{ expected_lan_ip }}"
when: expected_lan_ip is defined
@@ -0,0 +1,10 @@
# Запускать один раз на новом сервере от имени root или пользователя с полным sudo:
# ansible-playbook -i inventory/hosts.yml playbooks/bootstrap.yml -K
#
# После выполнения: Ansible-плейбуки всё равно требуют -K (become),
# но деплой через GitOps (docker compose) работает без пароля.
- name: Bootstrap servers
hosts: servers
become: true
roles:
- bootstrap
@@ -0,0 +1,119 @@
- name: Directory structure — cloud-pc
hosts: cloud-pc
become: true
vars:
data_user: ada
data_group: ada
tasks:
- name: Create base mount points
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ data_user }}"
group: "{{ data_group }}"
mode: "0755"
loop: "{{ storage_mounts | default([]) }}"
- name: Mount base storage disks
ansible.posix.mount:
path: "{{ item.path }}"
src: "{{ item.src }}"
fstype: "{{ item.fstype }}"
opts: "{{ item.opts | default('defaults,nofail') }}"
state: mounted
loop: "{{ storage_mounts | default([]) }}"
- name: Create service data and config directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ data_user }}"
group: "{{ data_group }}"
mode: "0755"
loop:
- /opt/data/postgres
- /opt/data/nextcloud
- /opt/data/gitea
- /opt/data/gitea-runner
- /opt/data/syncthing
- /opt/data/zerotier
- /opt/samba/media
- /opt/samba/documents
- /opt/samba/backups_win
- /opt/configs
- /opt/configs/nextcloud/app
- /opt/configs/syncthing
- name: Create /opt/services
ansible.builtin.file:
path: /opt/services
state: directory
owner: "{{ data_user }}"
group: "{{ data_group }}"
mode: "0755"
- name: Create backup directories (3TB)
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ data_user }}"
group: "{{ data_group }}"
mode: "0755"
loop:
- /mnt/backup/restic/services
- /mnt/backup/restic/samba
- /mnt/backup/devices/proxmox
- /mnt/backup/devices/ada-x1
- name: Directory structure — mini-pc
hosts: mini-pc
become: true
vars:
data_user: ada
data_group: ada
tasks:
- name: Create service directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ data_user }}"
group: "{{ data_group }}"
mode: "0755"
loop:
- /opt/services
- /opt/data
- /opt/configs
- /opt/data/vaultwarden
- /opt/data/adguard/work
- /opt/data/caddy
- /opt/data/gitea-runner
- /opt/data/npm
- /opt/data/uptime-kuma
- /opt/data/zerotier
- /opt/configs/adguard
- /opt/configs/caddy
- /opt/configs/caddy/lego
- /opt/configs/npm/letsencrypt
- name: Directory structure — ru-vps
hosts: ru-vps
become: true
vars:
data_user: ada
data_group: ada
tasks:
- name: Create service directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ data_user }}"
group: "{{ data_group }}"
mode: "0755"
loop:
- /opt/services
- /opt/data
- /opt/configs
- /opt/data/caddy
- /opt/data/gitea-runner
- /opt/data/zerotier
- /opt/configs/caddy
@@ -0,0 +1,9 @@
- name: Install rclone
hosts: servers
become: true
tasks:
- name: Install rclone package
ansible.builtin.apt:
name: rclone
state: present
update_cache: true
@@ -0,0 +1,36 @@
- name: Install resticprofile
hosts: servers
become: true
tasks:
- name: Install restic package
ansible.builtin.apt:
name: restic
state: present
update_cache: true
- name: Create restic config directory
ansible.builtin.file:
path: /etc/restic
state: directory
owner: root
group: root
mode: "0755"
- name: Create resticprofile log directory
ansible.builtin.file:
path: /var/log/resticprofile
state: directory
owner: root
group: root
mode: "0755"
- name: Download resticprofile install script
ansible.builtin.get_url:
url: https://raw.githubusercontent.com/creativeprojects/resticprofile/master/install.sh
dest: /tmp/resticprofile-install.sh
mode: "0755"
- name: Install resticprofile binary
ansible.builtin.command:
cmd: /tmp/resticprofile-install.sh -b /usr/local/bin
creates: /usr/local/bin/resticprofile
@@ -0,0 +1,5 @@
bootstrap_user: ada
bootstrap_ssh_public_keys: [ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWNsUQmppB6cQccXX1ZaBbFIcmM6RmghsTVbG9TgoZB ada@ada-x1]
docker_registry_mirrors:
- https://mirror.gcr.io
- https://dockerhub.timeweb.cloud
@@ -0,0 +1,4 @@
- name: restart sshd
ansible.builtin.service:
name: sshd
state: restarted
@@ -0,0 +1,85 @@
- name: Ensure user exists
ansible.builtin.user:
name: "{{ bootstrap_user }}"
shell: /bin/bash
groups: sudo
append: true
create_home: true
state: present
# docker group создаётся только после установки Docker.
# Добавление происходит в роли docker, не здесь.
- name: Add SSH authorized keys
ansible.posix.authorized_key:
user: "{{ bootstrap_user }}"
key: "{{ item }}"
state: present
loop: "{{ bootstrap_ssh_public_keys }}"
when: bootstrap_ssh_public_keys | length > 0
- name: Gather service facts
ansible.builtin.service_facts:
- name: Ensure Docker config directory exists
ansible.builtin.file:
path: /etc/docker
state: directory
mode: "0755"
- name: Configure Docker registry mirrors
ansible.builtin.copy:
dest: /etc/docker/daemon.json
content: "{{ {'registry-mirrors': docker_registry_mirrors} | to_nice_json }}\n"
mode: "0644"
register: docker_daemon_config
when: docker_registry_mirrors | length > 0
- name: Restart Docker after daemon config change
ansible.builtin.service:
name: docker
state: restarted
when:
- docker_daemon_config is changed
- "'docker.service' in ansible_facts.services"
# Команды, которые реально нужны без пароля:
# apt / apt-get — обновление пакетов
# systemctl — управление сервисами
# ufw — фаервол
# resticprofile — установка systemd-таймеров бэкапа
#
# Ansible become НЕ входит в этот список намеренно:
# для плейбуков запускать с -K. Это сознательный компромисс
# между удобством и тем, чтобы не давать python3 NOPASSWD (= root).
- name: Configure sudoers — NOPASSWD for specific commands
ansible.builtin.copy:
dest: /etc/sudoers.d/{{ bootstrap_user }}
content: |
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/bin/systemctl
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/bin/ufw
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/local/bin/resticprofile
validate: /usr/sbin/visudo -cf %s
mode: "0440"
# Drop-in конфиг — не трогаем основной sshd_config.
# Требует Debian 12+ (OpenSSH 8.9+).
- name: Harden SSH
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/99-hardening.conf
content: |
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
mode: "0644"
notify: restart sshd
- name: Ensure sshd_config.d is included
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
line: Include /etc/ssh/sshd_config.d/*.conf
state: present
insertbefore: BOF
validate: /usr/sbin/sshd -t -f %s
notify: restart sshd