Карманный Ansible и защита от брутфорс-атак

от автора

Введение

Здравствуйте! В своей профессиональной деятельности я часто работаю с системами, находящимися в различных сетях, изолированных как друг от друга, так и от Интернета.

Часто эти сети содержат Linux-хосты с разнообразным функционалом, но, как правило, имеют ряд общих конфигураций. Например, настройка точек подключения к общим сетевым папкам, безопасность, зеркала репозиториев и другие аспекты — все это требует значительных временных затрат, особенно с учетом большого количества таких устройств.

Для управления многими Linux-хостами (да и Windows, кстати, тоже) существует отличный инструмент, который я очень люблю — Ansible. Однако для его использования требуется сервер, с которого будут запускаться плейбуки. Это подразумевает необходимость настройки рабочей машины на Linux или виртуальной машины.

Не всегда удобно и целесообразно носить с собой ноутбук, и я предпочитаю использовать Windows на своем рабочем устройстве, хотя хорошо ориентируюсь в терминале Linux. Я считаю, что от каждой системы стоит заимствовать лучшее: Windows для десктопа, а Linux — для серверов и open-source решений (это мое личное мнение, и я уверен, что многие с ним не согласятся, но о вкусах не спорят).

Итак, как же упростить себе жизнь? Один из вариантов — использовать OrangePi, что действительно удобно. Однако еще удобнее управлять всем этим непосредственно с мобильного телефона. К счастью, запуск настроенных плейбуков не представляет сложности даже на небольшом экране мобильного устройства.


Обо мне

Андрей, преподаватель курсов «Системный администратор» и «Ansible в системном администрировании« Школы IT-образования «Level UP» .
Работаю с инфраструктурами на базах Windows и Linux, с акцентом на автоматизацию и централизованное управление.


Содержание

  1. Настройка конфигурации и создание ролей Ansible для для Debian-хостов

  2. Настройка сервера Ansible на Orange PI и спряжение его с мобильным телефоном

Зачем это нужно?

Подытожим вышесказанное и определимся, чем может быть полезен такой проект:

  • Проведение аудита информационной безопасности

  • Работа с различными, изолированными друг от друга сетями

  • Работа в сети без интернета (при наличии локального зеркала репозитория)

  • Мобильность и простота применения

Что мы имеем

  1. Тестовый виртуальный хост

  2. Orange Pi 3 LTS

  3. Мобильный телефон с ОС Android

Настройка Ansible

В данной статье мы коснемся темы информационной безопасности и подготовим роли, централизованно закрывающие на указанных хостах все порты, кроме тех, которые нам нужны. Также мы защитим наши машины от брутфорса (перебора паролей) с помощью fail2ban. Обычно я настраиваю sudo и делаю авторизацию по RSA-ключам, но в этой статье мы рассматривать это не будем, чтобы не делать статью слишком объемной. Если этот вопрос вам интересен, напишите в комментариях, и мы его рассмотрим в дальнейшем.

Установим Ansible

sudo apt install ansible

Роли подготовим заранее в тестовой среде, а потом через git перенесем на наш мобильный сервер для дальнейшей работы. Начнем с окружения, которое крайне важно для Ansible. Для его корректной работы должна быть создана правильная структура каталогов. Конфигураций может быть несколько, но мой рабочий вариант без лишних деталей выглядит следующим образом:

.
├── ansible.cfg
├── inventory
│ ├── group_vars
│ ├── hosts
│ ├── host_vars
│ │ └── ansible-test
│ └── secrets.yml
├── playbooks
│ └── test
│ └── test.yml
├── requirements.txt
├── requirements.yml
├── roles
│ └── secure
│ ├── fail2ban
│ └── ufw
└── Vagrantfile

requirements.txt и requirements.yml

предназначены для загрузки зависимостей: requirements.yml используется для зависимостей с ansible-galaxy, а requirements.txt — для библиотек в виртуальное окружение Python. Также рекомендую использовать Ansible-lint — отличную библиотеку для проверки ролей. Настоятельно советую изучить этот инструмент, если вы хотите писать чистые роли и избегать возможных ошибок в дальнейшем применении.

ansible.cfg

В этом файле мы указываем пути к нашим ролям, ключам, методам авторизации, а также способу хранения кэша, что сокращает время тестирования новых ролей.

[defaults] skip_ansible_lint = True host_key_checking = False        inventory = inventory/hosts roles_path = roles/secure private_key_file = ~/.ssh/id_rsa become_method = sudo become_user = root  # кеширование фактов gathering = smart # 1 час  fact_caching_timeout = 3600  # кеш в json fact_caching = jsonfile      fact_caching_connection = /tmp/ansible_fact_cache

inventory

Директория в которой мы определяем с чем мы будем работать.

group_vars/all.yml удобен для указания переменных, общих для всех ролей. Это могут быть адреса серверов, имена служб, прокси и, в общем, все, что периодически нужно в наших ролях и что не хотелось бы дублировать.

Файлы, описывающие конфигурацию хостов, хранятся в директории host_vars, в файле secrets.yml удобно хранить пароли и прочие конфиденциальные данные. Эти факты я рекомендую хранить в шифрованном виде с использованием Ansible Vault. Также важно добавить этот файлы в .gitignore, чтобы хранить его локально на устройстве и не отправлять в удаленный репозиторий.

.gitignore

# Игнорировать все файлы в директории inventory/host_vars/

inventory/host_vars/*

При шифровании мы вводим пароль, который в дальнейшем будем использовать при запуске плейбуков:

ansible-vault encrypt inventory/secrets.yml # шифровать

ansible-vault edit inventory/secrets.yml    # редактировать

ansible-vault decrypt inventory/secrets.yml # расшифровать

hosts

В этом файле мы укажем хосты, а также группы, в которые входят эти хосты.

[test] ansible-test  [localhost] 127.0.0.1 ansible_connection=local

Также в этом файле можно указать адреса и пароли наших хостов, но с точки зрения безопасности это не самое лучшее решение, поэтому их мы укажем в отдельных файлах, соответствующих именам хостов в директории host_vars

host_vars/ansible-test

ansible_become: true ansible_host: 192.168.2.16 ansible_user: vagrant ansible_ssh_pass: vagrant ansible_become_pass: vagrant

Не забываем зашифровать файл через ansible-vault

ansible-vault encrypt inventory/host_vars/ansible-test

Vagrantfile

Этот файл мы настраиваем с учетом нашего гипервизора, к теме нашей статьи настройка vagrant не относится, но если кратко, то этот конфигурационный файл позволят описать все аспекты желаемой виртуальной машины и запустить ее одной командой.

Запускаем наш Vagrantfile на гипервизоре или создаем тестовую виртуальную машину самостоятельно. Я предпочитаю автоматизацию, поэтому доверю все заранее прописанному коду.

Создаем тестовую виртуальную машину

Создаем тестовую виртуальную машину

Для того чтобы использовать парольную аутентификацию Ansible вместо аутентификации по ключам, используемой по умолчанию, нам необходимо установить соответствующую утилиту:

sudo apt install sshpass

Теперь мы готовы проверить, насколько правильно настроен наш инвентарь. Не забывайте указывать запрос пароля для Ansible Vault:

ansible all -m ping --ask-vault-pass

Успешный отчет о тестировании связи с хостами

Успешный отчет о тестировании связи с хостами

Настройка ролей

Роль UFW

Есть два подхода в работе с Ansible, которые зависят от масштабности ваших задач. Если вы хотите выполнить простое действие, например, установить пакеты, скопировать файлы или перенести данные, вам будет достаточно использовать одиночный Ansible файл с указанием хостов, переменных и плейбуков. Он легко переносится, прост в оформлении и достаточно удобен для выполнения простых действий. Однако с ростом масштабов работать с такими файлами становится сложнее, и тут на помощь приходят роли.

Роли позволяют структурировать ваши плейбуки и упростить процесс управления конфигурацией, разбивая его на более мелкие и понятные части. Каждая роль представляет собой набор задач, обработчиков, переменных и других файлов, сгруппированных по вашему выбору. Это упрощает повторное использование кода и делает его более читаемым. Главное правило – каждая роль должна выполнять конкретное завершённое действие и быть максимально универсальной. В сложных ролях я, например, добавляю переменные опций для большей вариативности и универсальности, чтобы не создавать множество ролей, а объединять несколько смежных в одну.

В нашем примере мы создадим две роли: первая — ufw, которая настроит файрвол, и вторая — fail2ban, которая обеспечит защиту от брутфорс-атак.

Для создания всей структуры каталогов роли используем команду

ansible-galaxy role init roles/secure/ufw

перейдем в каталог роли

cd roles/secure/ufw

Изучим структуру

tree

.
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml

Роль простая, тут нам нужно будет исправить только 2 файла, файл с задачами tasks/main.yml и файл с переменными по умолчанию defaults/main.yml

tasks/main.yml

--- # tasks file for roles/common/ufw - name: Ensure UFW is installed   ansible.builtin.apt:     name: ufw     state: present   become: true  - name: Set default outgoing policy to allow   community.general.ufw:     default: allow     direction: outgoing   become: true  - name: Allow SSH connections   community.general.ufw:     rule: allow     port: 22     proto: tcp   become: true  - name: Ensure UFW is enabled and set to start on boot   community.general.ufw:     state: enabled   become: true  - name: Add custom rules   community.general.ufw:     rule: "{{ item.rule }}"     port: "{{ item.port }}"     proto: "{{ item.proto }}"   loop: "{{ ufw__rules }}"   become: true

В первой задаче мы убеждаемся, что ufw у нас установлен. Если он не установлен, Ansible выполнит его установку. Прекрасное свойство Ansible, называемое идемпотентностью, не позволит выполнить одно и то же действие несколько раз, поэтому мы можем не беспокоиться о возможных ошибках при повторном запуске кода.

Однако важно помнить, что это правило касается задач, запускаемых модулями Ansible, и не относится к командам, выполняемым через модули терминала!

Например такой модуль не будет иметь свойства идемпотентности

- name: Установить пакет nginx через команду      command: apt-get install -y ufw

А такой будет

- name: Ensure UFW is installed   ansible.builtin.apt:     name: ufw     state: present   become: true

Список всех модулей и опций можно просмотреть на официальном сайте Ansible

 name: Set default outgoing policy to allow   community.general.ufw:     default: allow     direction: outgoing   become: true

Эта задача устанавливает политику для исходящих соединений по умолчанию в состоянии «разрешить». Это означает, что все исходящие соединения (т.е. соединения, инициируемые с вашего сервера к внешним ресурсам) будут разрешены, если не указано иное в различных правилах брандмауэра.

- name: Allow SSH connections   community.general.ufw:     rule: allow     port: 22     proto: tcp   become: true

В этом примере мы разрешаем 22 порт для работы по ssh и включаем ufw в автозапуск, одновременно запуская его с настроенными правилами.

- name: Add custom rules   community.general.ufw:     rule: "{{ item.rule }}"     port: "{{ item.port }}"     proto: "{{ item.proto }}"   loop: "{{ ufw__rules }}"   become: true

Здесь уже интереснее, мы используем цикл для разрешения указанных портов, из переменной ufw__rules, которую укажем по умолчанию и сможем переназначать при запуске плейбука.

Зададим переменные по умолчанию:

defaults/main.yml

--- # defaults file for roles/common/ufw ufw__rules:      # настройка UFW   - { rule: 'allow', port: '80', proto: 'tcp' }   - { rule: 'allow', port: '443', proto: 'tcp' }

Роль готова, осталось ее протестировать. Для этого создадим плейбук и заполним его:

mkdir -p playbooks/test

touch playbooks/test/test.yml

playbooks/test/test.yml

- name: Testing ufw   hosts: test   roles:     - ufw    vars:     # - ufw     ufw__rules:             - { rule: 'allow', port: '53', proto: 'udp' }

В этом примере мы открываем порт для службы DNS, чтобы продемонстрировать, как игнорировать переменные по умолчанию из роли UFW.

Запуск плейбука — Testing ufw

После написания плейбука, запустим его с помощью следующей команды:

ansible-playbook playbooks/test/test.yml --ask-vault-pass

Из вывода команды можно сделать вывод, что все прошло успешно

Из вывода команды можно сделать вывод, что все прошло успешно

Из вывода команды можно сделать вывод, что все прошло успешно и все порты, кроме необходимых у нас закрыты.

Роль fail2ban

Для создания всей структуры каталогов роли используем команду

ansible-galaxy role init roles/secure/fail2ban

перейдем в каталог роли

cd roles/secure/fail2ban

tasks/main.yml

--- # tasks file for roles/secure/fail2ban # sshd logs - name: Update_apt_cache   ansible.builtin.apt:     update_cache: yes  - name: Install rsyslog   ansible.builtin.apt:     name: rsyslog     state: present  - name: Install iptables   ansible.builtin.apt:     name: iptables     state: present  - name: Ensure the log directory exists   ansible.builtin.file:     path: /var/log/sshd/     state: directory     mode: '0755'  - name: Create SSH log file   ansible.builtin.file:     path: /var/log/sshd/sshd.log     state: touch     owner: sshd     group: adm     mode: '0640'  - name: Configure rsyslog for SSH logging   ansible.builtin.copy:     dest: /etc/rsyslog.d/50-ssh.conf     content: |       if $programname == 'sshd' then /var/log/sshd/sshd.log       & stop     owner: root     group: root     mode: '0644'  - name: Restart rsyslog service   ansible.builtin.systemd:     name: rsyslog     state: restarted  - name: Enable log sshd   ansible.builtin.lineinfile:     path: /etc/ssh/sshd_config     regexp: '^#SyslogFacility AUTH'     line: 'SyslogFacility AUTH'     state: present  - name: Level log sshd   ansible.builtin.lineinfile:     path: /etc/ssh/sshd_config     regexp: '^#LogLevel INFO'     line: 'LogLevel INFO'     state: present  # fail2ban - name: Install Fail2Ban   ansible.builtin.apt:     name: fail2ban     state: present  - name: Restart sshd service to apply changes   ansible.builtin.systemd:     name: sshd     state: restarted  - name: Config fail2ban   ansible.builtin.template:     src: jail.local.j2     dest: /etc/fail2ban/jail.local     owner: root     group: root     mode: '0644'   notify:     - Restart_service  - name: Start and autostart fail2ban   ansible.builtin.service:     name: fail2ban     state: started     enabled: true  # test - name: Pause   ansible.builtin.pause:     seconds: 3   tags: test  - name: Check service status   ansible.builtin.service:     name: fail2ban     state: started   register: service_status   tags: test  - name: Info   ansible.builtin.assert:     that:       - service_status.status.ActiveState == 'active'     fail_msg: "[error] - Служба не запущена"     success_msg: "[info] - Служба запущена"

В этом файле задачи разбиты на 3 части:

  1. Установка и настройка rsyslog и iptables который читает fail2ban

  2. Настраивает fail2ban, а именно передает шаблон jinja конфигурационного фала

  3. Тестируем успешность проведенной операции путем проверки статуса службы

Изучим шаблон конфигурационного файла

templates/jail.local.j2

[DEFAULT] bantime  = {{ fail2ban__bantime }} findtime  = {{ fail2ban__findtime }} maxretry = {{ fail2ban__maxretry }} allowipv6 = true [sshd] enabled = true port = ssh filter = sshd action = iptables-multiport[name=sshd, port=ssh, protocol=tcp] logpath = /var/log/sshd/sshd.log

В этот шаблон мы передаем 3 переменные, которые определим по умолчаниюю.

defaults/main.yml

--- # defaults file for roles/secure/fail2ban fail2ban__bantime: 600  # время бана fail2ban__findtime: 600 # частота бана fail2ban__maxretry: 5   # число неудачных попыток

Не забываем про обработчик которой вызываем в задаче под именем Restart_service

handlers/main.yml

--- # handlers file for roles/secure/fail2ban - name: Restart_service   ansible.builtin.systemd:     name: fail2ban     state: restarted

Добавляем в плейбук нашу роль

playbooks/test/test.yml

- name: Testing ufw + fail2ban   hosts: test   roles:     - ufw     - fail2ban    vars:     # - ufw     ufw__rules:             - { rule: 'allow', port: '53', proto: 'udp' }     # - fail2ban     fail2ban__bantime: 600  # время бана     fail2ban__findtime: 600 # частота бана     fail2ban__maxretry: 3   # число неудачных попыток

Я люблю указывать ключевые переменные в плейбуках для большей универсальности.

Запуск плейбука — Testing ufw + fail2ban

ansible-playbook playbooks/test/test.yml --ask-vault-pass

Все  прошло успешно

Все прошло успешно

Проверим работу fail2ban умышленно ошибаясь в авторизации по ssh при подключении к нашему тестовому серверу

После трех попыток неверного ввода учетных данных наш хост заблокирован на 10 минут

После трех попыток неверного ввода учетных данных наш хост заблокирован на 10 минут

1. Fail2ban через логи ssh обнаружил 3 неверные попытки авторизации

2. Fail2ban создал правило для блокировки IP-адреса на уровне брандмауэра iptables, что позволяет блокировать доступ к порту 22 (SSH) для IP-адреса

Для демонстрации важности защиты хоста о брутфорса я продемонстрирую лог со своего сервера в облаке, без настроенного fail2ban:

sudo journalctl -r | grep 'invalid user' | head -n 20

Каждые пару минут хост в сети атакуют боты

Каждые пару минут хост в сети атакуют боты

Настройка сервера Ansible на Orange PI

Для установки Orenge Pi зайдем на официальный сайт и загрузим последний актуальный дистрибутив с драйверами по адресу

Записываем образ на SD карту с помощью Rufus (или аналогичной программы). После этого вставляем карту в Orange Pi, загружаем устройство и подключаем его к нашей сети с помощью кабеля. При наличии DHCP сервера найдем IP-адрес, выданного устройству, и подключимся через любимый SSH клиент (Putty, PowerShell), введя логин и пароль по умолчанию (логин: orangepi, пароль: orangepi). Если возникнут какие-либо проблемы, можно подключить монитор и клавиатуру непосредственно к устройству и настроить сеть локально.

Настройка сети

По умолчанию на Orange Pi установлен NetworkManager, который настроен на получение IP-адреса по DHCP. Мы не будем настраивать статический IP на проводном сетевом адаптере, а подключимся к Wi-Fi точке доступа нашего телефона. Для этого просканируем доступные Wi-Fi сети:

nmcli device wifi list

Из списка доступных точек доступа выберем нужную и введем пароль для доступа.

nmcli device wifi connect --ask

Также настроим автоподключение к точке доступа мобильного телефона, чтобы устройство автоматически подключалось к сети при перезагрузке.

nmcli connection modify connection.autoconnect yes

Конфиг беспроводного подключения доступен по адресу /etc/NetworkManager/system-connections/

1.2 Настройка системы

В первую очередь сменим репозитории с Китайских на стандартные

sudo nano /etc/apt/sources.list

deb http://deb.debian.org/debian/ bookworm main non-free-firmware deb-src http://deb.debian.org/debian/ bookworm main non-free-firmware  deb http://security.debian.org/debian-security bookworm-security main non-free-firmware deb-src http://security.debian.org/debian-security bookworm-security main non-free-firmware  deb http://deb.debian.org/debian/ bookworm-updates main non-free-firmware deb-src http://deb.debian.org/debian/ bookworm-updates main non-free-firmware

Обновим кэш репозиториев и саму систему

sudo apt update sudo apt upgrade

Создадим нового пользователя взамен стандартного (в моем случаем логин пользователя будет «ch«)

sudo useradd -m -s /bin/bash ch groups # просмотрим в каких группах дефолтный пользователь sudo usermod -aG tty,disk,dialout,sudo,audio,video,plugdev,games,users,systemd-journal,input,netdev,ssh ch sudo passwd ch

И удалим старого пользователя

sudo userdel -r orangepi sudo rm -rf /var/mail/orangepi

Настроим время

sudo timedatectl set-timezone Europe/Moscow timedatectl

Имя хоста

sudo hostnamectl set-hostname opi

Не забываем сделать правки в файлах /etc/hosts и /etc/hostname после чего перезагружаем систему

sudo reboot

1.3 Подключение к Github

Создадим rsa ключи для доступа к закрытому репозиторию на GitHub

ssh-keygen -t rsa

Открываем публичный ключ, копируем его содержимое в личный кабинет на Github в разделе Settings -> SSH and GPG keys -> SSH keys

sudo cat ~/.ssh/id_rsa.pub

После чего тестируем соединение

ssh -T git@github.com

После авторизации, копируем репозиторий с нашими рабочими ролями Ansible

git@github.com:/.gite

Подключение телефона к Orange PI и запуск плейбука

Для подключения по ssh с мобильного телефона на ОС Android я использую 2 приложения, ConnectBot — как ssh клиент, и Network Utilites — для определения адреса клиента.

Для работы нам нужно:

  1. Определить сеть точки доступа

    Наши клиенты подключаются к сети 192.168.222.38

    Наши клиенты подключаются к сети 192.168.222.38
  2. Сканировать ее и найти адрес клиента

    Адрес клиента 192.168.222.127

    Адрес клиента 192.168.222.127
  3. Подключится к клиенту по ssh

    Открываем ssh сессию

    Открываем ssh сессию
    1. Разрешаем Fingerprint

  4. Редактируем файл инвентаря и group_vars

    С телефона работать можно, все необходимые кнопки в приложении есть

    С телефона работать можно, все необходимые кнопки в приложении есть
  5. Запускаем плейбук и наслаждаемся отчетом!

    Как это выглядит

    Как это выглядит

Если у вас есть вопросы по статье или вы хотите подробнее узнать об инструментах, которые мы не успели рассмотреть, напишите об этом в комментариях. Мы рассмотрим ваши вопросы в следующих статьях! Спасибо за внимание!

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Какие инструменты и практики вы бы хотели рассмотреть с следующих публикациях?

100% Создание сложных ролей Ansible1
0% Работа с Ansible-lint0
0% Больше информационной безопасности0
100% Создание локального зеркала репозиториев для изолированных сетей1
100% Мониторинг и оповещения в сетевой инфраструктуре1

Проголосовал 1 пользователь. Воздержался 1 пользователь.

ссылка на оригинал статьи https://habr.com/ru/articles/848006/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *