Рабочие заметки и инструкции
Тут описываю механики безболезненной репликации серверов с использованием Ansible и плейбуков.
Вообще суть такого подхода в том, чтобы создавать сценарии взаимодействия с сервером под управлением Linux. И Ansible обладает рядом преимуществ по сравнению с обычными шелл-скриптами.
На управляемых серверах необходимо создать пользователя в группе wheel, пароль для sudo команд у которого не будет запрашиваться.
На тестовом сервере создаём файл подкачки
dd if=/dev/zero of=/swapfile bs=512M count=2
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
И обновляем vim /etc/fstab
vim /etc/fstab
/swapfile none swap sw 0 0
Обновляем систему
dnf upgrade -y
Создаём пользователя ansible на каждом из серверов
useradd ansible
passwd ansible
usermod -aG wheel ansible
Разрешаем исполнение sudo без пароля
echo "ansible ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/ansible
Установка пакетов
sudo dnf install -y epel-release python3-pip
Установка зависимостей (локально для юзера ansible; использовать sudo su - ansible)
pip3 install passlib
Установка пакетов
sudo dnf install -y epel-release python3-pip
Установка Ansible (локально для юзера ansible; использовать sudo su - ansible)
sudo dnf install -y pipx
pipx ensurepath
pipx install --include-deps ansible
ansible --version
pip3 install passlib
Генерируем пару ключей для управляемых серверов,
используем имя ansible-key для примера;
отправляем ключ на сервера, которыми будем управлять.
cd ~/.ssh/
ssh-keygen -t rsa -b 4096 -f ~/.ssh/ansible-key
ssh-copy-id -i ~/.ssh/ansible-key.pub user@server
Создаём конфигурацию SSH на управляющем сервере. Задача этой конфигурации - указать, с каким ключом и под каким юзером будет происходить подключение. Сохраняется в ~/.ssh/config
Host prod.khudoley.pro
User ansible
Port 22
IdentityFile ~/.ssh/ansible-key
Host test.khudoley.pro
User ansible
Port 22
IdentityFile ~/.ssh/ansible-key
Создаём в корне домашнего раздела пользователя файл конфигурации ~/ansible.cfg
[defaults]
inventory = /home/ansible/hosts.ini
remote_user = ansible
host_key_checking = False
Там же создаём файл инвентаря ~/hosts.ini
[prod]
live_server ansible_host=prod.khudoley.pro ansible_user=ansible
[test]
test_server ansible_host=test.khudoley.pro ansible_user=ansible
Создаём тестовый плейбук и проверяем подключение к серверам ~/playbooks/test.yml
---
- name: Проверка соединения с серверами
hosts: all
gather_facts: false
tasks:
- name: Тест соединения с хостами
ansible.builtin.ping:
Запускаем проверку
ansible-playbook ~/playbooks/test.yml
В ответ должно вернуться нечто вроде этого
PLAY [Проверка соединения с серверами] *********************
TASK [Тест соединения с хостами] ***************************
ok: [live_server]
ok: [test_server]
PLAY RECAP *************************************************
live_server: ok=1 ...
test_server: ok=1 ...
Если всё окей - можно переходить к созданию плейбуков.
---
- name: Ensure SELinux is enabled and enforcing
hosts: all
become: true
tasks:
- name: Check SELinux status
command: sestatus
register: selinux_status
- name: Enable SELinux in config if disabled
lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: 'SELINUX=enforcing'
when: "'disabled' in selinux_status.stdout"
- name: Reboot system to enable SELinux
reboot:
when: "'disabled' in selinux_status.stdout"
- name: Set SELinux to enforcing if permissive
command: setenforce 1
when: "'permissive' in selinux_status.stdout"
- name: Confirm SELinux is enforcing
command: sestatus
register: selinux_final_status
- name: Debug SELinux final status
debug:
msg: "SELinux final status: "
Устанавливаем необходимые коллекции
ansible-galaxy collection install community.general ansible.posix
Копируем плейбук
---
- name: Universal CentOS 9 Stream Setup
hosts: all
become: true
vars:
username: anley # Имя пользователя, создаваемого на сервере.
user_password: "<ENCRYPTED_PASSWORD>" # Пароль для пользователя в зашифрованном виде.
root_password: "<ENCRYPTED_PASSWORD>" # Пароль для root в зашифрованном виде.
hostname: test # Имя хоста, устанавливаемое на сервере.
my_ip: "<YOUR_IP_ADDRESS>" # Твой IP-адрес.
master_ip: "<SERVER_IP>" # IP-адрес управляющего сервера.
ssh_port: 22323 # Пользовательский порт для SSH.
packages:
- screen
- vim
- nftables
- policycoreutils-python-utils
network_interface: ens3 # Сетевой интерфейс управляемого сервера для настройки NAT.
tasks:
- name: Set root user password
user:
name: root
password: ""
# Устанавливает пароль для root-пользователя.
- name: Create user '' and add to wheel group
user:
name: ""
state: present
password: ""
groups: wheel
append: true
# Создаёт пользователя с заданным паролем и добавляет его в группу wheel для административного доступа.
- name: Set system hostname to ''
hostname:
name: ""
# Устанавливает имя хоста для системы.
- name: Update /etc/hosts file
copy:
dest: /etc/hosts
content: |
127.0.0.1 localhost localhost.localdomain
::1 localhost localhost.localdomain
127.0.1.1
# Обновляет файл hosts для поддержки локального разрешения имени хоста.
- name: Upgrade all packages
dnf:
name: "*"
state: latest
# Обновляет все установленные пакеты до последних версий.
- name: Install EPEL repository
dnf:
name: epel-release
state: present
# Устанавливает репозиторий EPEL для расширенного набора пакетов.
- name: Install necessary packages
dnf:
name: ""
state: present
# Устанавливает необходимые пакеты из переменной packages.
- name: Stop firewalld service
systemd:
name: firewalld
state: stopped
enabled: false
# Отключает и останавливает firewalld, так как используется nftables.
- name: Enable and start nftables service
systemd:
name: nftables
state: started
enabled: true
# Включает и запускает сервис nftables для настройки файрвола.
- name: Configure nftables rules
copy:
dest: /etc/sysconfig/nftables.conf
content: |
table inet main {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
}
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state invalid drop
ct state { established, related } accept
tcp flags syn limit rate 50/second accept
icmp type { destination-unreachable, echo-request, router-advertisement, router-solicitation, time-exceeded, parameter-problem } limit rate 10/second accept
tcp dport { 22, 80, 443 } ct state new accept
ip saddr { , } tcp dport ct state new accept
counter log prefix "input_drop: " reject
}
chain output {
type filter hook output priority filter; policy accept;
}
chain forward {
type filter hook forward priority filter; policy accept;
}
chain postrouting {
type filter hook postrouting priority mangle; policy accept;
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "" ip saddr 10.8.0.0/24 counter masquerade
}
}
# Настраивает правила файрвола в конфигурационном файле.
- name: Load nftables rules from configuration file
command: nft -f /etc/sysconfig/nftables.conf
# Загружает правила nftables из конфигурационного файла.
- name: Configure SELinux for custom SSH port
command: semanage port -a -t ssh_port_t -p tcp
args:
creates: "/var/lib/selinux/targeted/active/ports"
# Добавляет пользовательский порт SSH в SELinux.
- name: Configure SSH to disable password login for ansible user
lineinfile:
path: /etc/ssh/sshd_config.d/04-passwordauth.conf
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
create: true
# Отключает вход по паролю для ansible пользователя.
- name: Configure SSH to disable root login
lineinfile:
path: /etc/ssh/sshd_config.d/01-permitrootlogin.conf
regexp: '^#?PermitRootLogin'
line: 'PermitRootLogin no'
create: true
# Запрещает вход по SSH для root-пользователя.
- name: Configure SSH port
lineinfile:
path: /etc/ssh/sshd_config.d/02-port.conf
regexp: '^#?Port'
line: 'Port '
create: true
# Устанавливает пользовательский порт для SSH.
- name: Configure SSH client alive settings
copy:
dest: /etc/ssh/sshd_config.d/03-clientalive.conf
content: |
ClientAliveInterval 1800
ClientAliveCountMax 16
# Настраивает параметры для поддержания активности SSH-клиентов.
- name: Restart SSH service
systemd:
name: sshd
state: restarted
# Перезапускает SSH для применения изменений конфигурации.
Для использования этого плейбука нам понадобится ip адрес свой, ip адрес управляющего сервера, имена сетевых интерфейсов, смотрящих наружу, на управляемых серверах, а также пароли, которые мы предварительно зашифруем для безопасного использования.
Внешний IP и интерфейс, который его получил, узнаём командой
ip addr
IP адреса, соответственно, для управляющего сервера используем “как есть”, а для себя устанавливаем по маске /24
Шифруем пароли командами
ansible-vault encrypt_string 'пароль юзера' --name 'user_password'
ansible-vault encrypt_string 'пароль рута' --name 'root_password'
Меняем остальные переменные при необходимости.
Создаём файл для хранения пароля от хранилища (не секурно, но вводить его каждый раз при запуске плейбука - твоё право, а я не хочу; если уж ломанут этот сервер - то ключ с подключением по ssh лежит перед носом)
echo 'Ключ шифрования паролей ansible-vault' > ~/.vault_pass
chmod 600 ~/.vault_pass
Теперь, имея на руках плейбук с заполненными переменными для каждого сервера, мы можем выполнить его сначала для тестового, а потом, убедившись в результате, для живого сервера.
ansible-playbook ~/path/to/your_playbook.yml --vault-password-file ~/.vault_pass
После того, как предыдущий плэйбук отработал, необходимо закрыть вход под рутом, запретить вход по паролю, увеличить окно жизни сессии SSH.
Перед этим генерим и отправляем на сервер клиентские ключи, чтобы не потерять доступ.
--
- name: Update SSH configuration
hosts: all
become: true
tasks:
- name: Configure SSH to disable password login for ansible user
lineinfile:
path: /etc/ssh/sshd_config.d/04-passwordauth.conf
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
create: true
tags:
- ssh
- security
- name: Configure SSH to disable root login
lineinfile:
path: /etc/ssh/sshd_config.d/01-permitrootlogin.conf
regexp: '^#?PermitRootLogin'
line: 'PermitRootLogin no'
create: true
tags:
- ssh
- security
- name: Configure SSH client alive settings
copy:
dest: /etc/ssh/sshd_config.d/03-clientalive.conf
content: |
ClientAliveInterval 1800
ClientAliveCountMax 16
tags:
- ssh
- name: Restart SSH service to apply changes
service:
name: sshd
state: restarted
tags:
- ssh
- restart