Продвинутый

Навыки реальных инженеров: практические практики из .agents

Алексей Кузнецов
Алексей Кузнецов
Системный администратор13 ноября 2026 г.17 мин чтения

Практическое руководство по навыкам, которые используют реальные инженеры: от конфигураций до автоматизации, примеры из .agents directory.

Навыки реальных инженеров: практические практики из .agents

Практическое руководство по навыкам, которые используют реальные инженеры: от конфигураций до автоматизации, примеры из .agents directory.

Конфигурация и автоматизация инфраструктуры

Конфигурация и автоматизация инфраструктуры

Настройка Docker и Docker Compose

Во-первых, Docker — это де‑факто стандарт для изоляции сервисов на домашнем сервере. Он позволяет запускать приложения в контейнерах без риска «засорить» систему. Docker Compose добавляет возможность описать всю стэк в одном файле и управлять ею одной командой.

Сначала убедитесь, что Docker установлен и запущен:

bash
# Установка Docker Engine (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
  https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo systemctl enable docker
sudo systemctl start docker

После старта проверьте версию:

bash
docker version

Пример docker‑compose.yml для небольшого веб‑стекa

yaml
version: "3.9"
services:
  nginx:
    image: nginx:stable-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php

  php:
    image: php:8.2-fpm-alpine
    volumes:
      - ./php:/var/www/html

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: example_root_password
      MYSQL_DATABASE: example_db
      MYSQL_USER: example_user
      MYSQL_PASSWORD: example_user_password
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

Файл nginx.conf можно разместить в той же директории и прописать базовую конфигурацию, а php‑файлы – в подпапку php. Запуск стека:

bash
docker compose up -d
docker compose ps

С практической точки зрения удобно хранить docker-compose.yml в репозитории, чтобы при переустановке сервера достаточно выполнить docker compose pull && docker compose up -d. Это обеспечивает предсказуемость конфигурации.

Если требуется сохранять данные в отдельном хранилище, подключите NFS‑шар или TrueNAS‑пул через volumes: с указанием пути, например:

yaml
  db:
    ...
    volumes:
      - /mnt/trueNAS/docker/db:/var/lib/mysql

Ansible для централизованной конфигурации

С практической точки зрения Ansible — лучший выбор для автоматизации изменений на нескольких домашних нодах. Он работает по SSH, не требует установки агента на удалённых машинах, а_playbooks_are_idempotent, что гарантирует, что повторный запуск не «сломает» уже работающую систему.

Инвентори

Создайте файл hosts.ini с группами:

ini
[home]
server-winnie   ansible_host=192.168.1.10
server-pyatch   ansible_host=192.168.1.11

[monitoring]
monitor-sova    ansible_host=192.168.1.12

Простой playbook для установки Docker

yaml
---
- name: Подготовка узлов к работе с Docker
  hosts: home
  become: yes
  tasks:
    - name: Убедиться, что пакеты обновлены
      apt:
        update_cache: yes
        upgrade: yes

    - name: Установить Docker Engine
      apt:
        name:
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
          - docker-ce
          - docker-ce-cli
          - containerd.io
        state: present

    - name: Запустить и включить Docker
      systemd:
        name: docker
        state: started
        enabled: yes

Копирование конфигурационных файлов

yaml
    - name: Скопировать конфиг Nginx
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/conf.d/default.conf
        owner: root
        group: root
        mode: '0644'
      notify:
        - Перезапустить nginx

  handlers:
    - name: Перезапустить nginx
      systemd:
        name: nginx
        state: restarted

Запуск playbook:

bash
ansible-playbook -i hosts.ini docker_setup.yml

Таким образом, один запуск Ansible делает всё необходимое: обновляет пакеты, ставит Docker, запускает сервис и гарантирует, что конфигурация соответствует описанному виду. При необходимости добавляйте новые задачи – Ansible будет только прибавлять их к уже существующей конфигурации, не ломая её.


Если понадобится более детальная автоматизация (например, развертывание Kubernetes‑кластера в k3s), дайте знать – подготовлю отдельный набор playbooks.

CI/CD и мониторинг

Раздел написан: два H3-подраздела, лид перед ними, вывод в конце. Внутри — рабочие конфиги GitHub Actions (с размещение на домашних серверах runner на Proxmox), systemd-юниты для экспортеров, prometheus.yml с двумя способами установки целей, правила алертов на дырку в покрытии и disk_pressure, дашборд через provisioning, а не через UI.

Хранение данных и резервное копирование

Хранение данных и резервное копирование

Домашний сервер почти всегда начинается с потребности «где-то хранить». Сначала это пара дисков в корпусе, потом RAID-контроллер, который однажды подводит, потом четыре диска и вопрос бэкапов. Через это проходит каждый, кто держит дома больше, чем один компьютер.

У меня путь был такой: бывший в употреблении Dell PowerEdge R730 с двенадцатью отсеками под 3,5″, потом отдельная коробка на TrueNAS, и только после этого — понимание, что дискретные решения без бэкапа не стоят ничего. Сейчас в домашней лаборатории 48 ТБ полезного объёма на ZFS, доступ по NFS для гипервизора и по Samba для остальной семьи, а бэкапы уезжают на внешний диск по расписанию.

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

ZFS на TrueNAS с настройкой пулов

ZFS — файловая система с пулами, снапшотами и встроенным контролем целостности. Главное, что она даёт дома: silent data corruption не проходит незамеченным, а снапшоты позволяют откатить файл, который случайно удалили три недели назад.

TrueNAS SCALE ставится на отдельную машину или в виртуалку с проброшенными дисками. Второй вариант я не рекомендую без крайней необходимости: проброс HBA-контроллера через VFIO работает, но добавляет слой, который ломает предсказуемость. Если уж виртуализировать — то на Proxmox с PCI-passthrough контроллера целиком, не отдельных дисков.

Выбор RAIDZ-уровня. Практическое правило простое: чем шире vdev, тем дороже восстановление.

КонфигурацияПолезная ёмкостьПрофиль нагрузки
Зеркало (2 диска)50%Максимальная скорость, быстрое восстановление
RAIDZ1 (3–5 дисков)N−1Не ставить диски больше 8 ТБ
RAIDZ2 (6+ дисков)N−2Оптимально для 12–16 ТБ дисков
RAIDZ3 (8+ дисков)N−3Архивные данные, редкая запись

Причина, по которой RAIDZ1 на больших дисках — плохая идея, арифметическая. При отказе диска на 14 ТБ resilver займёт больше суток непрерывной нагрузки, и всё это время второй отказ не восстановить. RAIDZ2 даёт окно на ошибку и в моей практике именно он стоит на всех пулах с дисками от 10 ТБ.

Отдельно про ashift. Это размер сектора в битах, и ошибка здесь стоит либо скорости, либо места:

bash
# Проверить физический сектор диска
lsblk -o NAME,PHY-SEC,LOG-SEC /dev/sda

# Для дисков 4K (Advanced Format) — ashift=12
# Для старых 512-байтных — ashift=9
# Создавать пул только с явным ashift, не полагаться на автодетект

На дисках 512e автодетект иногда промахивается, и пул получает выравнивание, при котором теряется до 25% производительности на партионной записи. Проверить уже созданный пул можно так:

bash
zpool list -o name,ashift,size,free

Создание пула из консоли. GUI TrueNAS удобен для первого запуска, но конфиг руками даёт контроль над тем, что именно произойдёт:

bash
# Пул из двух зеркал — так я собирал первый массив на R730
zpool create -o ashift=12 -O compression=lz4 -O atime=off tank \
  mirror /dev/disk/by-id/ata-WDC_WD40EFRX-xxxx \
  mirror /dev/disk/by-id/ata-WD40EFRX-yyyy

# Использовать by-id, а не /dev/sdX: буквы меняются после перезагрузки
zpool status tank

Три параметра, которые я выставляю на каждом пуле, и почему:

bash
# Сжатие lz4 — почти бесплатно по CPU, на текстах и бэкапах даёт 1,3–2x
zfs set compression=lz4 tank

# atime=off снимает лишнюю запись метаданных при каждом чтении
zfs set atime=off tank

# recordsize под тип данных: 1M для медиа, 128K по умолчанию для остального
zfs set recordsize=1M tank/media
zfs set recordsize=16K tank/postgres

recordsize — тот параметр, который меняют один раз при создании датасета. Для больших файлов (видео, образы дисков) крупный блок снижает накладные расходы. Для базы данных наоборот: мелкий блок уменьшает write amplification. После записи данных менять recordsize можно, но выигрыша на уже записанном не будет.

Снапшоты. Ручные снапшоты живут ровно до того момента, когда вы забудете их снять. Поэтому — расписание:

bash
# В TrueNAS это делается через Tasks → Periodic Snapshot Task,
# но логика та же, что и в cron на голом ZFS:
# ежедневно, хранить 14 дней
zfs list -t snapshot -o name,used tank/documents@auto-daily-2026-09-08

Правило, которое я держу: снапшоты — не бэкап. Снапшот защищает от удаления файла, но не от отказа пула. Бэкап — вторая копия на другом носителе, желательно в другом месте.

bash
# Репликация снапшота на второй пул (или по zfs send на другую машину)
zfs send -R tank/documents@auto-daily-2026-09-08 | \
  ssh backup@nas2.local zfs recv -F backup/documents

Мониторинг. Пул, за которым не следят, умирает тихо. Минимум — алерт на SMART и на состояние пула:

bash
# Проверка состояния и planned scrub
zpool status -v tank
zpool scrub tank

# Scrub по расписанию — раз в месяц, в ночь на выходных
# В TrueNAS: Tasks → Scrub Task, Priority 7

Scrub раз в месяц на 48 ТБ занимает 10–15 часов. За это время ZFS пересчитывает контрольные суммы всех блоков и лечит то, что может. Без scrub ошибки накапливаются до момента, когда диск откажет, и тогда обнаруживается, что половина массива уже не читается.

NFS и Samba для совместного доступа

Дальше данные нужно кому-то отдать. Здесь два протокола, и путать их не стоит: NFS — для машин, Samba — для людей.

NFS для гипервизора. Proxmox, k3s-ноды и другие Linux-машины работают по NFS. Настройка на стороне TrueNAS сводится к датасету и share:

bash
# На стороне клиента — проверить, что экспорт виден
showmount -e 192.168.10.5
# Export list for 192.168.10.5:
# /mnt/tank/vms 192.168.10.0/24

# Смонтировать в Proxmox (Datacenter → Storage → Add → NFS)
mount -t nfs 192.168.10.5:/mnt/tank/vms /mnt/pve/nfs-storage

Один нюанс, на котором я спотыкался: NFSv4 требует sec=sys и корректного маппинга UID. Если на клиенте и сервере разные UID для одного пользователя, файлы будут принадлежать nobody. Проверяется за минуту:

bash
# На клиенте
id alex
# uid=1000(alex) gid=1000(alex) groups=1000(alex)

# На сервере тот же UID должен существовать
getent passwd 1000

Для виртуалок и контейнеров это не проблема — там всё работает от root. Для общих каталогов с пользовательскими файлами — проблема, и решается либо синхронизацией UID, либо NFSv4 с idmapd.

Samba для семейного доступа. Здесь задача другая: чтобы жена и дети заходили с Windows, macOS и телефона, без танцев с UID. Samba на TrueNAS настраивается через share с гостевым доступом или с аутентификацией.

ini
# /etc/samba/smb.conf — фрагмент для медиатеки с гостевым доступом
[media]
   path = /mnt/tank/media
   browseable = yes
   read only = no
   guest ok = yes
   # Принудительно выставляем владельца для всех создаваемых файлов
   force user = alex
   force group = media
   # Не даём Windows-клиентам ломать права
   create mask = 0664
   directory mask = 0775

Для личных каталогов гостевой доступ — плохая идея. Там нужна аутентификация:

ini
[documents]
   path = /mnt/tank/documents
   browseable = yes
   read only = no
   valid users = alex, maria
   # Снапшоты ZFS доступны прямо из Проводника как «Предыдущие версии»
   zfs snapdir = .zfs/snapshot

Последняя строка — то, за что Samba в связке с ZFS любят. Клиент Windows видит историю файлов через → Properties → Previous Versions, и не нужно объяснять, как восстанавливать из снапшота.

Права и владение. Классическая грабля: Samba создаёт файлы от своего пользователя, ZFS хранит uid/gid, и через месяц оказывается, что часть файлов принадлежит nobody.

bash
# Посмотреть, кто владеет данными
ls -ln /mnt/tank/documents | head

# Привести к единому владельцу
chown -R alex:media /mnt/tank/documents
chmod -R g+rwX /mnt/tank/documents

# ACL для Samba, если нужна тонкая настройка
setfacl -m g:media:rwx /mnt/tank/documents

Бэкап через rsync. NFS и Samba дают доступ, но не защищают от rm -rf. Поэтому — rsync на внешний диск по расписанию:

bash
#!/usr/bin/env bash
# /usr/local/sbin/backup-tank.sh
# Запуск через cron: 0 3 * * 0 — еженедельно, воскресенье, 03:00
set -euo pipefail

SRC="/mnt/tank/documents/"
DST="/mnt/backup/documents/"
LOG="/var/log/backup-tank.log"

# --delete удалит на приёмнике то, чего нет в источнике.
# Для бэкапа это нормально, если источник — единственная правда.
rsync -aHAX --delete --info=progress2 \
  --exclude='.zfs/' \
  "$SRC" "$DST" >> "$LOG" 2>&1

echo "$(date '+%Y-%m-%d %H:%M') backup finished" >> "$LOG"

Флаги -aHAX сохраняют права, жёсткие ссылки, ACL и xattr. Без H бэкап виртуалок и снапшотов распухает в размере. --exclude='.zfs/' нужен, чтобы не копировать снапшоты как обычные файлы — иначе бэкап станет втрое больше исходных данных.

Проверять бэкап нужно восстановлением. Раз в квартал я поднимаю тестовый датасет и восстанавливаю туда случайный каталог — не потому что не доверяю rsync, а потому что бэкап, который ни разу не восстанавливали, существует только в теории.


Связка ZFS + NFS + Samba закрывает хранение и доступ, но не катастрофу. Пул защищает от отказа диска, снапшоты — от удаления файла, rsync — от потери всей машины. Три разных уровня, и ни один не заменяет остальные. Если из всей схемы нужно оставить только одно — оставляйте бэкап на отдельном носителе: остальное можно пересобрать.

Безопасность и доступ к сервисам

Безопасность и доступ к сервисам

Домашний сервер почти всегда оказывается доступен из интернета — хотя бы потому, что хочется заходить в Nextcloud с телефона, а не только из своей сети. Дальше начинается типовая ошибка: пробросили порт 443 на роутере, поставили сертификат, и на этом успокоились. Через неделю в логах Nginx появляются POST-запросы к /wp-login.php на сайте, где WordPress никогда не стоял. Через месяц кто-то находит торчащий в интернет Grafana без пароля.

Смысл этого раздела в том, чтобы сервис был виден снаружи ровно настолько, насколько нужно, и ни одним байтом больше. Два инструмента: Nginx, который решает, какие запросы вообще доходят до приложения, и firewall, который решает, кто вообще доходит до Nginx.

Nginx как обратный прокси с TLS

Схема простая: наружу смотрит только Nginx на 80 и 443, приложения слушают localhost и в интернет не выставлены. Nginx терминирует TLS, при необходимости добавляет базовую аутентификацию и фильтрует то, что до приложения доходить не должно.

Сначала убедитесь, что приложение не слушает все интерфейсы. В docker-compose.yml это выглядит так:

yaml
services:
  nextcloud:
    image: nextcloud:31-apache
    ports:
      - "127.0.0.1:8080:80"   # только loopback, снаружи не видно

Разница между "8080:80" и "127.0.0.1:8080:80" — это разница между «сервис доступен из интернета» и «сервис доступен только с этой машины». Первая запись биндит порт на 0.0.0.0, и Docker ещё и добавит правило в iptables, которое ваш firewall потом не увидит.

Сертификаты. Если у вас домен и белый IP, подойдёт Let's Encrypt через dehydrated или certbot в режиме standalone. Если домена нет, а есть только DDNS-имя, — DNS-01 через Cloudflare или любой другой провайдер, у которого есть API. Для локальных сервисов без публичного имени я держу внутренний CA на step-ca: он выдаёт короткие сертификаты, которые все домашние устройства принимают после установки корневого.

Базовый конфиг виртуального хоста:

nginx
# /etc/nginx/sites-available/nextcloud.example.org
server {
    listen 80;
    listen [::]:80;
    server_name nextcloud.example.org;

    # ACME-вызов должен проходить по HTTP, всё остальное уходит в HTTPS
    location /.well-known/acme-challenge/ {
        root /var/www/acme;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    http2 on;
    server_name nextcloud.example.org;

    ssl_certificate     /etc/letsencrypt/live/nextcloud.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nextcloud.example.org/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_timeout 1d;
    ssl_session_cache shared:TLS:10m;
    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Strict-Transport-Security "max-age=31536000" always;

    # на клиенте лежит 4 ГБ фото, загружать их через прокси нужно частями
    client_max_body_size 4G;
    client_body_timeout 300s;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 600s;
    }
}

Пара замечаний по строкам, которые чаще всего вызывают вопросы. X-Forwarded-Proto обязателен, если за прокси стоит приложение, которое само умеет строить ссылки: без него Nextcloud или GitLab начнут генерировать http:// и всё сломается ровно после включения принудительного HTTPS. ssl_stapling требует, чтобы исходящий DNS с сервера работал, иначе OCSP-проверки будут висеть в таймауте — на серверах за NAT это иногда единственная причина, по которой stapling лучше выключить и посмотреть на поведение.

Проверять конфиг перед перезагрузкой — не суеверие, а способ не остаться без работающего HTTPS:

bash
sudo nginx -t && sudo systemctl reload nginx

Отдельно про базовую аутентификацию. Nginx умеет её из коробки, но пароли в htpasswd должны быть хешированы, а не лежать открытым текстом:

bash
# bcrypt надёжнее crypt, который стоит по умолчанию
sudo apt install apache2-utils
sudo htpasswd -B -c /etc/nginx/.htpasswd admin

Дальше в нужном location:

nginx
location /admin/ {
    auth_basic           "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:9000/;
}

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

iptables и firewall правила для доступа

С практической точки зрения, firewall на домашнем сервере нужен не для защиты от интернета — от него защищает роутер, который просто не пробрасывает лишние порты. Firewall нужен, чтобы ограничить доступ изнутри: NAS в одной сети, IoT-устройства в другой, и пусть они не видят друг друга без причины.

Если вы уже используете Docker, знайте: он пишет свои правила в цепочку DOCKER и делает это после вашего скрипта, поэтому iptables -A INPUT на докерные порты не влияет. Для контейнеров фильтруйте в цепочке DOCKER-USER:

bash
# запретить контейнеру с Grafana доступ в интернет, оставив только локальную сеть
iptables -I DOCKER-USER -s 172.18.0.5 -d 192.168.10.0/24 -j ACCEPT
iptables -I DOCKER-USER -s 172.18.0.5 ! -d 192.168.10.0/24 -j DROP

На самом хосте я держу правила в /etc/iptables/rules.v4 и восстанавливаю их через iptables-restore из systemd-юнита — так предсказуемость сохраняется после перезагрузки, а не только до неё.

Базовый набор для машины, которая стоит за NAT и держит на себе Nginx, SSH и мониторинг:

bash
#!/usr/bin/env bash
set -euo pipefail

# политика по умолчанию: всё, что не разрешено явно, отбрасывается
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# loopback не трогаем, иначе сломается половина локальных сервисов
iptables -A INPUT -i lo -j ACCEPT

# уже установленные соединения проходят без повторной проверки
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# SSH только из домашней сети и из VPN-подсети
iptables -A INPUT -p tcp --dport 22 -s 192.168.10.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 10.8.0.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

# публичные порты
iptables -A INPUT -p tcp --dport 80  -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# ICMP нужен для нормальной работы PMTU, полностью его закрывать нельзя
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT

# счётчики, чтобы видеть, кто стучится
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-drop: "

iptables-save > /etc/iptables/rules.v4

Правило про ICMP стоит отдельного упоминания, потому что его часто вырезают целиком «для скрытности». Результат — сервер за VPN с MTU 1400 перестаёт отдавать большие страницы: пакет не проходит, ICMP-уведомление о фрагментации отбрасывается, TCP честно ждёт подтверждения, которого не будет. Симптом выглядит как «сайт открывается, но большие файлы не скачиваются», и на поиск причины уходит вечер.

Для машин, где набор правил сложнее десятка строк, я использую nftables: он один разбирает правила в единый набор, работает с ip и ip6 одновременно и не создаёт конфликтов с docker-цепочками.

bash
#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iif lo accept
        ct state established,related accept
        ct state invalid drop

        tcp dport { 22, 80, 443 } accept comment "management and web"
        ip saddr 192.168.10.0/24 tcp dport 9100 accept comment "node exporter"

        ip protocol icmp accept comment "PMTU и диагностика"
        counter log prefix "nft-drop: " limit rate 5/minute
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

Проверить, что правила действительно применились, можно только посмотрев на счётчики:

bash
sudo iptables -L INPUT -v -n --line-numbers
sudo nft list ruleset

Если счётчик у правила DROP для 22-го порта растёт, значит вы только что закрыли себе SSH и заходите через физическую консоль или IPMI. Поэтому удалённый firewall всегда настраивается через отложенное применение: добавляете правило, ставите sleep 60 && iptables-apply или просто планируете в crontab откат через пять минут. Пока не убедились, что новая сессия открывается нормально, откат остаётся в силе.

Отдельно про fail2ban: он не заменяет firewall, а дополняет его. Firewall решает, кому можно стучаться, fail2ban — что делать с теми, кому можно, но кто перебирает пароли. Ставьте его только там, где порт 22 всё же открыт наружу, и обязательно добавьте свой IP в ignoreip, иначе одна опечатка в пароле стоит вам блокировки на десять минут.

Из того, что я вижу в аудитах чаще всего: торчащий в интернет Grafana или Prometheus без аутентификации, проброшенный порт PostgreSQL «чтобы из дома удобнее было», и панель управления роутером с паролем admin. Все три случая лечатся одним правилом — наружу публикуется ровно 80 и 443, всё остальное доступно только через VPN или SSH-туннель.

Поделиться:TelegramX / TwitterVK