Теория и практика.


Оглавление

  1. Пароли,
  2. Настройка роутеров и маршрутизаторв.
  3. Настройка безопасности ssh соединений.
  4. Фаерволы.
    1. Фаервол UFW.
    2. Фаервол Firewalld.
  5. Fail2ban.
    1. Установка и запуск.
    2. Базовая настройка.
    3. Фильтры.
    4. Действия.
    5. Настройка правил.
    6. NGINX DDoS (req limit).
    7. NGINX files.
    8. NGINX GZIP.
    9. NGINX X-XSS-Protection.
    10. NGINX X-Frame-Options.
    11. NGINX X-Permitted-Cross-Domain-Policies.
    12. NGINX Strict-Transport-Security.
    13. NGINX X-Content-Type-Options.
    14. NGINX SSL.
    15. Nginx Отключение серверных токенов.
    16. Nginx Задание размеров буферов.
    17. Работа со списком заблокированных адресов.
    18. Примеры правил.
    19. Удаление.
  6. Блокировка ip адреса или всей подсети.

Пароли.

Первое что каждый пользователь хочет услышать – ваше соединение максимально защищено. Но так ли это на самом деле? Так ли безопасны соединения с вашими серверами как мы думаем? Будь то сервер контроля версий GIT, файловый сервер или любой другой.

Наливайте приятную для вас жидкость, присаживайтесь поудобнее – будет интересно.

У любого продвинутого пользователя всегда имеется электронная почта. А если он ещё и по совместительству системный администратор, то скорее всего для упрощения рабочей нагрузки, он почти наверняка будет везде использовать одинаковые пароли. Конечно один пароль легче запомнить, но так делать категорически не стоит. Такое допущение упрощает работу злоумышленникам, а вам добавляет нервного напряжения, что наверняка скажется на вашем здоровье. Хорошенько задумайтесь перед тем, как использовать одинаковые пароли. Хорошо, если вы отделаетесь лёгким испугом, но надеятся на это никогда не стоит. Необходимо забодится о безопасности своих данных заблаговременно.

Мы можем бесконечно углубляться в тему паролей, но всё-таки коснёмся нескольких важных вопросов, которые повысят ваш уровень знаний, сохранят не толко ваши нервы, а злоумышленникам значительно усложнят работу.

С одной стороны все прекрасно знают какие комбинации для паролей не стоит использовать. С другой стороны запоминать гигантскую строку из различного набора знаков, которую вам создал генератор паролей также не является выходом, ведь вы можете забыть один из символов, что только прибавит вам головной боли пока вы всё вспомните. Сохранять пароль даже на USB носитель не всегда оправдывает себя, т. к. вашим носителем могут воспользоваться нечестные на руку коллеги по работе. Флеш-карту также легко можно случайно где-нибудь забыть или обранить. Нет, я конечно не призываю вас не сохранять ваши пароли или не использовать генераторы, я просто предостерегаю вас от таких казусов, до того как они произошли.

Так как же поступить в такой сложной ситуации, чтобы не запутаться и не забыть свой пароль, особенно если он не один?

Всё гениальное просто. Вам достаточно придумать и запомнить свою определенную цепочку символов из которых будут состоять ваши пароли, т. е. придумать алгоритм, по которому они и будут создаваться с одной только помощью вашей умной головы. А дальше дело техники. Восстановить подобные пароли по памяти не составит труда. Приведу простой пример. Пусть тестовая цепочка паролей будет состоять из следующего набора символов: 1) дата выхода одного из любимых сериалов вместе с точками, 2) Слитное написание фамилии и имени героя выбранного сериала в пункте 1. Такой комбинации уже будет достаточно. Например: «19.11.2020SamVinchester».

24 символа. Неплохо, правда? Такая задача злоумышленникам будет явно не по силам. Разумеется данную комбинацию уже не стоит использовать. Она приведена только в качестве примера, а алгоритм вы должны будете придумать самостоятельно под свои нужды. Теперь созданные наборы паролей можно записать на отдельную Флеш-карту и хранить дома в сейфе за 7 замками. Шучу конечно. Просто использовать лишь в крайнем случае.

Перейти к оглавлению.

Настройка роутеров и маршрутизаторв.

Теперь немного уделим внимания настройкам ваших роутеров и маршрутизаторов.

Многие из вас, дорогие друзья, даже не догадываются насколько они уязвимы. А ведь такое упущение может принести для вас много головной боли. Давайте же это исправим.

Кусайте печеньку, запивайте чайком, а я пока что приведу пример настроек роутера TP-Link WR741. В других роутера, например D-Link, ищите аналогичные меню и настройки.

Первое что необходимо изменить в конфигурации – это имя администратора и пароль. Теперь доступ к вашему роутеру ограничен, но пока ещё уязвим. После этого найдите настройку базовой защиты, а именно – межсетевой экран, VPN и ALG. Все указанные пункты необходимо установить в положение «Включить». Сохраните настройки и перейдите в следующий пункт настройки безопасности: «Защита от Dos атак» и также установите в положение «Включить». Не забудьте в этом же меню установить галочки напротив всех остальных пунктов с наименованием - «включить фильтрацию от атак…».

клик для увеличения клик для увеличения

Приведу ещё одну настройку, в которой можно легко ошибиться. В этом же меню безопасности имеется пункт «Локальное управление». По умолчанию настройка установлена в положение «Всем компьютерам в локальной сети…». Многие пользователи устанавливают её во второе положение - «Только указанным компьютерам…». Перед изменением этой настройки необходимо обратить внимание на кнопочку возле пункта «Mac-адрес вашего компьютера». Во многих роутерах на ней указано – «Добавить». В моём же роутере (TL-841L), на этой кнопке написано: «Копировать». Т.е. прошивка моего роутера не позволяет добавлять новые Mac-адреса. Будьте осторожны с этой настройкой. Иначе может возникнуть ситуация, что у вас самого не будет доступа к настройкам вашего маршрутизатора и его придётся сбрасывать к заводским настройкам. Вообщем, только потеряте время и деньги. Думаю, данную настройку стоит оставить по умолчанию.

клик для увеличения клик для увеличения

Также не забудьте про протокол UPnP.

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

С поддержкой UPnP они могут автоматически подключаться к сети, получать IP-адрес, находить другие устройства в сети и подключаться к ним, и это очень удобно.

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

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

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

Например, для каждого Torrent клиента (или сервера) порт подключения + ip адресс ПК, на котором используется тот или иной Torrent клиент. Иначе, рано или поздно, обнаружете что ваши torrent закачки попросту не работают.

По умолчанию порт uTorrent клиента ОС Windows 35691. Поэтому рекомендую сгенерировать случайный порт с помощью кнопочки "Случайный".

клик для увеличения клик для увеличения клик для увеличения клик для увеличения клик для увеличения клик для увеличения

Port Forwarding – или проброс портов, который также иногда называемый перенаправлением портов или туннелированием – это процесс пересылки трафика, адресованного конкретному сетевому порту с одного сетевого узла на другой. Этот метод позволяет внешнему пользователю достичь порта на частном IPv4-адресе (внутри локальной сети) извне, через маршрутизатор с поддержкой NAT.

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

Port Triggering - это параметр конфигурации маршрутизатора с поддержкой NAT, который управляет связью между внутренними и внешними хост-машинами в IP-сети. Он похож на переадресацию портов в том смысле, что позволяет перенаправлять входящий трафик на определенную внутреннюю хост-машину, хотя переадресованный порт не открыт постоянно, а целевая внутренняя хост-машина выбирается динамически.

Разница между Port Forwarding и Port Triggering в том, что первый это доступ из внешней сети к серверу во внутренней сети, а Port Triggering - это доступ из внутренней сети во внешнюю.

клик для увеличения клик для увеличения

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

Одновременно с этим, домашняя (частная) сеть остается закрытой за сетевым устройством и никаких изменений в ее работе нет.

клик для увеличения

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

Перейти к оглавлению.

Настройка безопасности ssh соединений.

Наконец мы подошли к главному вопросу дня – «Настройка безопасности ssh соединений».

Для начала необходимо установить пакет для ssh:

$: sudo pacman -S openssh
$: sudo yum –y install openssh-server openssh-clients
$: sudo apt-get update && sudo apt-get install openssh-server openssh-clients

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

$: /usr/sbin/ssh-keygen -A

А также сделайте резервную копию настроек sshd сервиса, перед его редактированием

$: sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.factory-defaults

Запустите sshd сервис и добавте его в атозагрузку:

$: sudo systemctl start sshd
$: sudo systemctl enable sshd

Перед тем, как отключить проверку пароля, обязательно скопируйте и вставте сгенерированный ключ с вашего удалённого пк на сервер ssh в конец файла пользователя к которому будете подключаться: «/home/user/.ssh/authorized_keys».

«user» - имя вашего созданного пользователя.

Для VPS или VDS команда отправки публичного ключа будет такой.

$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@host_ip
# Если не работает, возможно дополнительные параметры просто не нужны. Например, в Debian 11 не нужны.
$ ssh-copy-id user@host_ip

# Порт указывается через -p.
$ ssh-copy-id user@host_ip -p 2222
# Соответственно вставте свой порт, который вы настроили в sshd_config

В первое подключение у вас скорее всего будет суперпользователь (root). И после того как вы создадите пользователя, отправлять на сервер свой публичный ssh-ключ обязательно необходимо обоим пользователям, чтобы не потерять доступ.

$ ssh-copy-id root@host_ip
$ ssh-copy-id user@host_ip

# Или так, если вдруг поменяли порт по умолчанию.
$ ssh-copy-id root@host_ip -p 2222
$ ssh-copy-id user@host_ip -p 2222
# Соответственно вставте свой порт, который вы настроили в sshd_config

Или вручную, с помощью флеш-карты, если это ваш домашний сервер или в организации.

$: cat id_rsa.pub >> /home/user/.ssh/authorized_keys

«user» - имя вашего созданного пользователя.

Теперь внесем некоторые изменения от имени суперпользователя в файл: /etc/ssh/sshd_config

Протокол SSH первой версии имеет проблемы с безопасностью, которые закрыты во второй версии. Внесите эту строчку вручную, её нет в этом файле.

Protocol 2

Изменяем порт по умолчанию. Обязательно запомните его. Он понадобится при настройке фаервола.

Port 2222

Тип соединения (ipv4, ipv6, оба соединения одновременно).

AddressFamily inet
#AddressFamily inet6
#AddressFamily any

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

$ ssh -Q PubkeyAcceptedAlgorithms | grep rsa

А затем добавьте соответствующий параметр в «/etc/ssh/sshd_config».

PubkeyAcceptedKeyTypes=+ssh-rsa
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa

Отключение проверки пароля:

PasswordAuthentication no

Включаем аутентификацию по открытому ключу:

PubkeyAuthentication yes

Параметр AuthorizedKeysFile определяет файл, в котором содержатся публичные ключи, используемые для аутентификации пользователей по открытому ключу. В записи могут присутствовать переменные, например %h означает домашний каталог пользователя, а %u – имя пользователя. В дальнейшем мы планируем использование аутентификации по открытому ключу - раскомментируем эту строку.

AuthorizedKeysFile .ssh/authorized_keys

По умолчанию, все системные пользователи имеют возможность подключаться к системе по SSH. Например, разрешим SSH для пользователей root и networks:

AllowUsers root networks

Можно разрешить доступ всем, кроме указанных:

DenyUsers root networks

Выбранных пользователей можно добавить в группу и разрешить доступ только этой группе:

AllowGroups ssh_group

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

TCPKeepAlive yes
ClientAliveInterval 300
ClientAliveCountMax 3

Отключаем уязвимость в виде файла .RHOSTS со списком хостов и пользователей:

IgnoreRhosts yes

Отключаем аутентификацию на базе хоста, которая позволяет пользователю с определённого хоста подключаться к серверу:

HostbasedAuthentication no

Отключаем прямое подключение через root:

PermitRootLogin no

За настройку баннера отвечает параметр Banner

Запрещаем пустые пароли

PermitEmptyPasswords no

Анализ логов, для расширенного контроля над системой:

LogLevel INFO

PrintMotd выводит при подключении к sshd сообщение дня (message of the day) из файла «/etc/motd».

PrintMotd no # pam does that

Если UsePAM включен, то запустить sshd можно будет только от имени root.

UsePAM yes

Отключаем пересылку X11 через ssh, т.к. данный протокол не ориентирован на безопасность.

X11Forwarding no

Сначала нужно задать эти параметры в sshd_config. Поскольку мы вносим изменения в интерфейсы и удалённой, и клиентской системы, нам нужны права root с обеих сторон.

PermitRootLogin yes
PermitTunnel yes

Форвардинг портов. С помощью SSH туннелей вы можете строить целые цепочки для форвардинга портов. Включить или отключить SSH туннелирование можно следующими директивами.

AllowStreamLocalForwarding yes
# AllowTcpForwarding remote
AllowTcpForwarding yes
GatewayPorts yes
# AllowAgentForwarding yes

Сохраните файл и перезапустите sshd сервис:

$: sudo systemctl restart sshd

Подключиться к серверу теперь можно по ssh-ключу. Обязательно согласитесь на обмен fingerprint ключей.

$ ssh user@host_ip

# Или так, если вы меняли порт по умолчанию
$ ssh user@host_ip -p 2222
# Соответственно вставте свой порт, который вы настроили в sshd_config

Для передачи файлов по SSH воспользуемся следующими командами.

# Для передачи файла на сервер.
$ scp /your/path/file user@host_ip:/home/user/

# Для приёма файла с сервера.
$ scp user@host_ip:/home/user/file ./

При проблемах с передачей файлов по SSH:

scp: Connection closed

Просто добавьте в команду SCP сразу после неё ключ «-O» - для работы в режиме «Legacy».

# Для передачи файла на сервер.
$ scp -O /your/path/file user@host_ip:/home/user/

# Для приёма файла с сервера.
$ scp -O user@host_ip:/home/user/file ./

Если SCP по прежнему не работает, попробуйте следующую команду. Возможно она подскажет вам решение.

$ sudo sshd -t

Возможно у вас просто нет каталога «/var/empty» с нужными правами доступа.

$ sudo mkdir -p /var/empty
$ sudo chown root /var/empty

Перейти к оглавлению.


Фаерволы.

Наливайте ещё немного чая, осталось совсем немного.

Ну а я пока расскажу про 2 фаервола, один из которых вам обязательно понадобится, если вы хотите, чтобы ваш сервер был хоть как-то защищен.

Обратите внимание!
Не важно какой у вас используется фаервол - когда вы добавляете в любой фаервол какой-либо сервис - вы добавляете заранее настроеный порт по умолчанию для данного имени сервиса. Порт для каждого известного сервиса внесен в свой определенный отдельный конфигурационный файл. Изменить порт у какого-либо сервиса можно только вручную, изменяя конфигурационный файл нужного сервиса. Если у вас порт того или иного сервиса по какому-либо имени изменен - обязательно вносите в фаервол уже не имя сервиса, а конкретно порт и протокол (tcp или udp)!

Фаервол UFW.

Не самый хороший фаервол, но самый удобный в плане простоты настройки.

Удивительно, но Docker не работает из коробки с “Universal Firewall” Linux, или UFW.

Проблема в том, что UFW и Docker пытаются изменить одни и те же базовые правила брандмауэра, и этот конфликт требует дополнительной настройки, если вы хотите запустить UFW и Docker вместе.

Если вы настроите базовый брандмауэр UFW на запрет по умолчанию и разрешение HTTP и SSH, это будет выглядеть безопасным, но не будет блокировать запуск контейнеров Docker, привязанных к другим портам.

Эту проблему может быть трудно обнаружить, поскольку UFW и Docker – это отдельные системы.

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

Но если оно запущено через Docker, то по умолчанию оно будет видно на порту 8000 из любого места.

Это может стать серьезной проблемой, если вы не решите ее.

Для совмещения Docker-а и фаервола UFW воспользуйтесь статьёй: «Как использовать Docker с UFW параллельно».

Однако, не спешите переходить по ссылке. В данной статье используются решения, основанные на IPTABLES, фреймворке, который постепенно выходит из употребления. В итоге его заменит другое фреймворк - NFTABLES.

Не факт, что у вас получится, но если получится - вы счастливчик. Но, не спешите радоваться. Ибо ваша настройка продержится максимум лет 5...10, пока последний фреймворк полностью не заместит всеми любимый IPTABLES.

Не во всех ОС в репозиториях имеется графическая оболочка к консольной версии утилиты, но с консольной версией работается намного быстрее и удобнее. Установка выполняется следующей командой:

$ sudo pacman -S ufw gufw
$ sudo yum -y install ufw
$ sudo apt install ufw gufw

Теперь необходимо активировать фаервол. Мне в archlinux для автоматического запуска при перезагрузки системы понадобилось выполнить ещё одну команду.

$ sudo ufw enable
$ sudo systemctl enable ufw

Команды UFW:

app list - список доступных имён приложений;
enable - включить фаерволл и добавить его в автозагрузку;
disable - отключить фаерволл и удалить его из автозагрузки;
reload - перезагрузить файервол;
status - посмотреть состояние фаервола;
show - посмотреть один из отчётов о работе;
allow - добавить разрешающее правило;
deny - добавить запрещающее правило;
reject - добавить отбрасывающее правило;
limit - добавить лимитирующее правило;
delete - удалить правило;
insert - вставить правило.

Изменение стандартной внутренней политики.

$ sudo ufw default deny incoming

Изменение стандартной внешней политики

$ sudo ufw default allow outgoing

Например, чтобы открыть порт ufw для SSH, можно добавить одно из этих правил:

$ sudo ufw allow OpenSSH
$ sudo ufw allow 22
$ sudo ufw allow 22/tcp

Примеры других правил.

$ sudo ufw deny from 123.45.67.89
$ sudo ufw allow from 123.45.67.89/24
$ sudo ufw allow from 123.45.67.89/24 to any port 22
$ sudo ufw allow from 123.45.67.89 to any port 22
$ sudo ufw allow 3000:3100/tcp
$ sudo ufw allow 3000:3100/udp
$ sudo ufw allow 3000:3100
$ sudo ufw allow ftp
$ sudo ufw allow 21/tcp
$ sudo ufw allow https
$ sudo ufw allow 443

Сброс правил.

$ sudo ufw reset

Посмотреть правила с номерами.

$ sudo ufw status numbered

В Ufw есть опция сохранения логов — журнал событий.

$ sudo ufw logging on

Ufw поддерживает нескоько уровней логгирования:

Перейти к оглавлению.


Фаервол Firewalld.

Самый сильный и хороший фаервол. Единственный недостаток, он же и большой плюс - требует тщательной настройки и понимания как всё устроено!

Самое интересное в данном фаерволе то, что вы настраиваете только то, что будет разрешено.
Всё остальное по умолчанию будет запрещено. Обычно так во всех фаерволах.
Например, при использовании UFW и iptables - необходимо последовательно в последнем настроить и разрешения и запреты.
В качестве примера Firewalld можно привести правила маршрутизации, которые вносятся через его параметры. Вы вносите только разрешающие правила. Всё остальное по умолчанию запрещается.
Т.е. настраивать отдельные запреты необязательно.


Обратите внимание!
В данном блоке мы не будем рассматривать все варианты команд и примеров как их использовать. Мы рассмотрим конкретный случай, когда у вас установлен «wireguard + docker + docker-compose», чтобы вы могли быстро на конкретных примерах с подробными объяснениями понять как всё устроено и работает. И также быстро разобраться как и что делать у себя на своем сервере.
При этом не важно какой nginx у вас установлен - будь то просто «Nginx + certbot + python3-certbot-nginx + crontab» для «Let’s Encrypt» сертификтов или «Nginx proxy manager» в docker-контейнере или что-то другое.
Работать везде будет примерно одинаково.


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

Например, к сожалению, UFW не всегда блокирует входящий трафик по направлению к docker-контейнерам. При правильной настройке Firewalld можно не только ограничить доступ из-вне к определенным ресурсам внутри вашей системы, но и указать в каких случаях разрешать доступ. UFW такого не умеет. У него либо доступ есть, либо его нет.

Единственный недостаток Firewalld, на мой скромный взгляд, это необходимость работать только с ним, вместо iptables и ufw. Однако, это даёт намного больше пространства для маневров и более глубокую настройку поведения, которая недоступна в ufw.

Firewalld фильтрует входящий трафик по зонам в зависимости от примененных к зоне правил.
Если IP-адрес отправителя запроса соответствует правилам какой-либо зоны, то пакет будет отправляться через эту зону.
Если же адрес не соответствует ни одной из настроенных на сервере зоне, пакет будет обрабатываться зоной используемой по умолчанию.
При установке firewalld зона по умолчанию называется public.

В firewalld есть зоны, где уже предварительно настроены разрешения для различных служб. Можно использовать эти настройки или создавать собственные зоны.

Лучше, конечно, создавать собственные зоны и настраивать в них собственное поведение. Дело в том, что если вам понадобиться VPN-сервер, то в публичной зоне public не рекомендуется включать маскарадинг, т.е. NAT-преобразование адресов, во избежание проблем с внутренней адресацией сервисов, которые так или иначе всё равно будут обращаться к этой зоне, даже если по умолчанию будет установлена и настроена другая. Если попробовать включить маскарадинг в публичной зоне - то даже сам фаервол будет предупреждать вас, выдавая соответствующие «wrong» предупреждения - о том, что включать маскарадинг в стандартных зонах фаервола не рекомендуется.

Однако, такая практика существует и, при необходимости, вполне можно настроить.

Итак.

Зоны фаервола Firewalld.
drop минимальный уровень доверия. Все входящие соединения блокируются без ответа, допускаются только исходящие соединения;
block зона схожа с предыдущей, но при отклонении входящих запросов отправляется сообщение icmp-host-prohibited для Ipv4 или icmp6-adm-prohibited для Ipv6;
public представляет общественные, недоверенные сети. Можно разрешать избранные входящие соединения в индивидуальном порядке;
external внешние сети при использовании брандмауэра в качестве шлюза. Она настроена для маскирования NAT, поэтому ваша внутренняя сеть остается частной, но доступной;
internal антоним зоны external. Хост обладают достаточным уровнем доверия, доступен ряд дополнительных служб;
dmz используется для компьютеров, расположенных в DMZ (изолированные компьютеры без доступа к остальной сети). Разрешены только определенные входящие соединения;
work зона для рабочих машин (большинство компьютеров в сети доверенные);
home зона домашней сети. Можно доверять большинству ПК, но поддерживаются только определенные входящие соединения;
trusted доверять всем машинам в сети. Наиболее открытая из всех доступных опций, требует сознательного использования.

В firewalld используется два набора правил — постоянные и временные. Временные правила работают до перезагрузки сервера. По умолчанию при добавлении правил в firewalld, правила считаются временными «runtime». Чтобы добавить правило на постоянной основе нужно использовать флаг «--permanent». Такие правила будут применяться после перезагрузки сервера.

Итак, прежде чем установить этот фаервол, необходимо выключить и убрать всё что будет с ним конфликтовать. А именно - ufw и iptables.

Обратите внимание!

Маскировать или полностью удалять «iptables.service», как рекомендуют большинство источников интернета - не рекомендуется во избежание проблем не только с самим «Firewalld», но и с «Fail2Ban». «iptables.service», если он вообще есть, необходимо только выключить!

Дело в том, что «IPTABLES» и «Firewalld» взаимодействуют с системой следующим образом.

Теперь можно приступать к выполнению команд.

$ sudo ufw disable
$ sudo systemctl stop ufw
$ sudo systemctl disable ufw

$ sudo systemctl stop iptables
$ sudo systemctl disable iptables
# Проверяем
$ sudo systemctl status iptables

Устанавливаем Firewalld, запускаем и проверяем.

$ sudo apt -y install firewalld
$ sudo systemctl enable firewalld
$ sudo systemctl start firewalld
# Проверяем
$ sudo systemctl status firewalld

Далее я часто буду употреблять следующие 2 команды:

$ sudo firewall-cmd --list-all
# Покажет в активной зоне все настройки
# и
$ sudo firewall-cmd --get-active-zones
# покажет все активные зоны
$ sudo firewall-cmd --get-services
# Список всех сервисов доступных firewalld

Обязательно выполните эти 2 команды сверху, чтобы убедиться, что в данный момент у вас в активной зоне по умолчанию (а это скорее всего public) имеется сервис ssh или порт сервиса (если порт изменен). Иначе вы можете потерять доступ к своему серверу. Это важно!

Если ssh сервиса там нет, его можно добавить 2-мя следующими командами:

$ sudo firewall-cmd --permanent --zone=public --add-service=dhcp
$ sudo firewall-cmd --permanent --zone=public --add-service=dhcpv6
# Сначала DHCP-IPV4, т.к. по умолчанию там только DHCP-IPV6 (dhcpv6-client)
$ sudo firewall-cmd --permanent --zone=public --add-service=ssh
# Если порт отличается нужно заменить эту команду на следующую
$ sudo firewall-cmd --permanent --zone=public --add-port=2222/tcp
# Обязательно указывайте протокол для порта - иначе получите ошибку добавления

$ sudo firewall-cmd --reload
# Обязательно перезагружаем правила

К сожалению сервиса «dhclient» для ipv4 в Firewalld нет, т.е. «.xml»-файла с указанными портами и протоколами. Имеется введу конкретно сервис для клиента, чтобы сервер мог отдать клиенту заданные в настройках IP адреса.

Такой сервис можно создать самостоятельно.

# Создаем сервис для firewalld в виде xml-файла
$ sudo nano /usr/lib/firewalld/services/dhclient.xml

<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>dhclient</short>
  <description>This allows a DHCLIENT to accept messages from DHCP server and relay agents.</description>
  <port protocol="udp" port="67"/>
  <port protocol="udp" port="68"/>
</service>


# Сохраняем и выходим
CTRL + o
CTRL + x
# Обязательно перезагружаем правила
$ sudo firewall-cmd --reload
# Проверяем, что у нас получилось
$ sudo firewall-cmd --info-service=dhclient
dhclient
  ports: 67/udp 68/udp
  protocols:
  source-ports:
  modules:
  destination:
  includes:
  helpers:

После чего добавляем в нужную зону.

$ sudo firewall-cmd --permanent --zone=public --add-service=dhclient
# Обязательно перезагружаем правила
$ sudo firewall-cmd --reload
# Проверяем
$ sudo firewall-cmd --info-zone=public

В случае, если вы не меняли настройки сетевых карт сервера по умолчанию на ручные («/etc/network/interfaces»), то добавлять DHCP-сервисы обязательная процедура, чтобы сервер смог получить свой адрес. Если у вас ручные настройки сетевых карт, можно не добавлять ни один DHCP-сервис. Но, обычно в любой VPS или VDS не рекомендуется ставить ручные настройки сетевых карт, хотя это вполне возможно и нормально. Ручные настройки чаще всего прописывают на своих собственных серверах, на которых как раз и установлен DHCP-сервер, который как раз и раздаёт всем ПК в локальной сети IP адреса. Однако, в таком случае, все DHCP-сервисы необходимо добавлять в нужные зоны. Иначе ПК в сети не смогут получить свои адреса, а сервер раздать их.

И ещё раз убедитесь, что ssh или порт появились в зоне public.

$ sudo firewall-cmd --list-all
# или
$ sudo firewall-cmd --info-zone=public

Далее у вас уже должен быть запущен wireguard и docker сервисы, чтобы их сети были включены и доступны.

Сразу выполним команду, с помощью которой определим все необходимые нам наименования сетей - сеть по которому идёт интернет, сети ваших контейнеров (при появлении новых, обязательно вносить их в дальнейшие настройки), и сеть VPN wireguard.

$ ip a
# или
$ ls /sys/class/net

У меня в виртуальной машине используются следующие сети. У вас могут быть другие. Пока просто запомним их. Они нам далее пригодятся.

Теперь, перед тем, как продолжать необходимо понять несколько важных аспектов, т.к. без этих настроек у вас могут возникнуть БОЛЬШИЕ ПРОБЛЕМЫ.

Дело в том, что firewalld сервис стартует сразу после запуска сети. А docker сервис несколько позже. И также несколько позже стартует wireguard. Поэтому если вы просто добавить сети docker-а куда-либо, то после перезагрузки сервера мало того, что фаервол не запуститься, тык ещё и docker-сервис также не сможет запуститься.

Чтобы отрабатывал nginx - ему нужны сети, которые есть в docker-контейнерах. Сервису firewalld нужны все сети - и те что в docker-ах, и от wireguard-а. Ну а чтобы он правильно маршрутизировал трафик ему нужен запущенный nginx. И последний fail2ban-у нужен в первую очередь firewalld-сервис, чтобы правильно отрабатывали все ловушки.

Если вы попытаетесь добавить строку вида: «After=docker.service» в службу firewalld.service, то после перезагрузки сервера вы также получите те же ошибки и проблемы запуска.

Лучше всего будет firewalld.service запускать по таймеру systemd, что решит все проблемы.

Поэтому, чтобы всё правильно запускалось, в правильной последовательности и не вызывало ошибок - уберем из автозагрузки docker и nginx сервисы и создадим для них systemd-таймеры. Таким образом, сначала будет запускаться wireguard, затем docker, nginx, firewalld и только после этого fail2ban.

Убираем nginx и docker из автозагрузки.

$ sudo systemctl disable nginx
$ sudo systemctl disable docker

Создадим таймеры docker и nginx, добавим в автозагрузки и запустим их.

$ sudo nano /etc/systemd/system/docker.timer

[Unit]
Description=Starting Docker on after services wireguard
Wants=wg-quick.target
After=wg-quick.target

[Timer]
Unit=docker.service
OnBootSec=5s
AccuracySec=1s

[Install]
WantedBy=timers.target

# Сохранение настроек, выход
CTRL + o
CTRL + x

$ sudo nano /etc/systemd/system/nginx.timer

[Unit]
Description=Starting Nginx on after services docker
Wants=docker.timer
After=docker.timer

[Timer]
Unit=nginx.service
OnBootSec=10s
AccuracySec=1s

[Install]
WantedBy=timers.target

# Сохранение настроек, выход
CTRL + o
CTRL + x

$ sudo systemctl start docker.timer
$ sudo systemctl enable docker.timer
$ sudo systemctl start nginx.timer
$ sudo systemctl enable nginx.timer

Вот теперь переходим к таймеру firewalld.

# Выключим стандартный способ включения фаервола
$ sudo systemctl disable firewalld
# и создадим systemd таймер для нормального запуска firewalld сервиса без ошибок
$ sudo nano /etc/systemd/system/firewalld.timer

[Unit]
Description=Starting Firewalld on after services nginx
Wants=nginx.timer
After=nginx.timer

[Timer]
Unit=firewalld.service
OnBootSec=15s
AccuracySec=1s

[Install]
WantedBy=timers.target

# Сохранение настроек, выход
CTRL + o
CTRL + x
# Добавление таймера в автозапуск
$ sudo systemctl enable firewalld.timer
$ sudo systemctl start firewalld.timer

Таймер не обязательно должен называться точно также как и сервис, но расширение должно быть именно «.timer».

Параметры таймера:

Например, таймер, который будет запускаться один раз в неделю (в 12:00 в понедельник).

/etc/systemd/system/foo.timer

[Unit] Description=Run foo weekly [Timer] OnCalendar=weekly Persistent=true [Install] WantedBy=timers.target

Если требуется указать более точную дату и время, используйте следующий формат:

ДеньНедели Год-Месяц-День Часы:Минуты:Секунды

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

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

OnCalendar=Mon,Tue *-*-01..04 12:00:00

Запуск службы в первую субботу каждого месяца:

OnCalendar=Sat *-*-1..7 18:00:00

По крайней мере один день должен быть указан при использовании «ДеньНедели».

Таймер, который будет запускаться каждый день в 4 утра:

OnCalendar=*-*-* 4:00:00

Если необходимо запускать службу в разное время, то можно указать параметр OnCalendar несколько раз.

В этом примере служба запускается в 22:30 по рабочим дням и в 20:00 по выходным:

OnCalendar=Mon..Fri 22:30
OnCalendar=Sat,Sun 20:00

Указатели времени OnCalendar могут быть протестированы для того, чтобы проверить их правильность и вычислить следующее время срабатывания условия. Например, «systemd-analyze calendar weekly» или «systemd-analyze calendar "Mon,Tue *-*-01..04 12:00:00"».

Специальные выражения событий, такие как daily и weekly, относятся к конкретному времени начала и, таким образом, все таймеры, использующие эти выражения, запустятся одновременно. Таймеры, использующие специальные выражения, могут негативно сказаться на производительности системы, если сервисы, запускаемые таймерами, являются ресурсозатратными. Опция RandomizedDelaySec в разделе [Timer] помогает избежать подобных проблем посредством случайного выбора времени запуска каждого из таймеров.

Добавьте опцию AccuracySec=1us в раздел [Timer], чтобы не использовать значение погрешности 1m, установленное по умолчанию.

Можно использовать «systemd-run» для создания временных юнитов «.timer». То есть можно назначить запуск определённой команды в нужное время, не имея соответствующего файла службы.

Например, следующая команда создаст файл через 30 секунд:

$ systemd-run --on-active=30 /bin/touch /tmp/foo

Кроме того, можно указать предварительно существующий файл сервиса, при этом не имея файла таймера.

Например, запустим юнит, который называется «некоторыйюнит.service», через 12.5 часов:

$ systemd-run --on-active="12h 30m" --unit некоторыйюнит.service

Чтобы иметь возможность передавать параметры, т.е. аргументы командной строки, в сервис или таймер, необходимо в наименование сервиса перед расширением добавить собаку. Например, «status_email_user@.service».

Параметры(аргументы командной строки) передаются с помощью указания в строке запуска дополнительного ключа вместе с процентом: «%i».

Например, файл юнита для получения статуса.

/etc/systemd/system/status_email_user@.service

[Unit] Description=status email for %i to user [Service] Type=oneshot ExecStart=/usr/local/bin/systemd-email address %i User=nobody Group=systemd-journal

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

Теперь необходимо добавить все docker-сети к внутренней зоне фаервола internal. Т.е. к этим сетям будет иметь доступ только сам хост сервера. Из-вне доступа ни у кого не будет, кроме wireguard-а при подключении к нему. Всё нормально, мы этот момент дальше поправим, чтобы иметь к контейнерам доступ по доменам через 80 и 443 порт на входе в систему.

Это необходимо сделать не только ради ограничения доступа, но ещё по 1 причине.

Дело в том, что после перезагрузки firewalld сканирует все доступные сети и если не находит во всех имеющихся зонах какой-либо существующий интерфейс - автоматически создаст новую зону с этими наименованиями интерфейсов сетей.

Это проблема. Т.к. если будет существовать зона с указанными сетями - у вас будет случайный доступ из-вне к одному из ваших контейнеров (может даже ко всем контейнерам), даже если вы не указывали порт доступа.

Создавать скрипт и отдельный сервис ради удаления новой зоны бессмесленно, т.к. мы и устанавливали «firewalld» ради правильного и беспощадного ограничения доступа, какого нет в «ufw».

Кстати говоря, на заметку, именно так обычно и происходит в ufw фаерволе. Поэтому в нём ограничить доступ из-вне значительно сложнее.

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

С помощью команды «$ ip a» выше мы получили нужные нам списки сетей. Теперь используем их.

$ sudo firewall-cmd --permanent --zone=internal --add-interface=br-00aaad17d4b5
$ sudo firewall-cmd --permanent --zone=internal --add-interface=br-c2ecab4afa7c
$ sudo firewall-cmd --permanent --zone=internal --add-interface=docker0
# Обязательно перезагружаем правила
$ sudo firewall-cmd --reload

# и проверяем что у нас получилось
$ sudo firewall-cmd --info-zone=internal

Теперь можно создавать зоны фаервола с нужным нам доступом. Создадим зоны, например, «mysite» и «mywg». Пока поработаем надо зоной «mysite».

Добавим в эту зону «DHCP» и «SSH», несколько портов, включая «wireguard», а также включим «NAT», чтобы при подключении к «wireguard» у нас были свои IP адреса, заданные в «.conf»-файле. У меня порт «SSH» остался в виртуальной машине по умолчанию. Лучше так не делать и менять его на другой. Тогда вместо добавления сервиса надо будет добавлять конкретно порт, а не менять его в конфигурационных файлах, что будет более наглядно для вас самих и работать будет абсолютно также, но не правильнее.

$ sudo firewall-cmd --permanent --new-zone=mysite
$ sudo firewall-cmd --permanent --zone=mysite --add-service=dhcp
$ sudo firewall-cmd --permanent --zone=mysite --add-service=dhcpv6
$ sudo firewall-cmd --permanent --zone=mysite --add-service=ssh
$ sudo firewall-cmd --permanent --zone=mysite --add-service=http
$ sudo firewall-cmd --permanent --zone=mysite --add-service=https
$ sudo firewall-cmd --permanent --zone=mysite --add-port=51820/udp
$ sudo firewall-cmd --permanent --zone=mysite --add-masquerade

$ sudo firewall-cmd --reload

Мы ешё не закончили с этой зоной. Это только подготовительная часть - чтобы в процессе не потерять доступ.

Теперь добавим к этой зоне интерфейс и убедимся, что всё правильно работает. Вот теперь у нас есть доступ к нашим контейнерам по 80 и 443 портам по именам доменов + доступ к vpn сервису.

# Добавляем интерфейс, у вас будет свой
$ sudo firewall-cmd --permanent --zone=mysite --add-interface=enp0s3
# Меняем зону по умолчанию
$ sudo firewall-cmd --set-default-zone=mysite
# Обязательно перезагружаем правила
$ sudo firewall-cmd --reload

# Всё проверяем
$ sudo firewall-cmd --get-default-zone
mysite
$ sudo firewall-cmd --info-zone=mysite
mysite (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources: 
  services: dhcp dhcpv6-client http https ssh
  ports: 51820/udp
  protocols: 
  forward: no
  masquerade: yes
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:
$ sudo firewall-cmd --info-zone=public
public
  target: default
  icmp-block-inversion: no
  interfaces: 
  sources: 
  services: dhcp dhcpv6-client ssh
  ports: 
  protocols: 
  forward: no
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:
$ sudo firewall-cmd --get-active-zones
internal
  interfaces: br-00aaad17d4b5 br-c2ecab4afa7c docker0
mysite
  interfaces: enp0s3
$ sudo firewall-cmd --list-all
mysite (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources: 
  services: dhcp dhcpv6-client http http ssh
  ports: 51820/udp
  protocols: 
  forward: no
  masquerade: yes
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

Теперь займемся зоной «mywg». Здесь немного посложнее.

Здесь помимо всего остального доступа необходимо будет настроить таблицы маршрутизации, но т.к. у нас нет «iptables»,то это делается через «firewalld».

# Создаем новую зону
$ sudo firewall-cmd --permanent --new-zone=mywg

# Обязательно перезагружаем правила для применения зименений
$ sudo firewall-cmd --reload

# Добавляем в зону интерфейс
$ sudo firewall-cmd --permanent --zone=mywg --add-interface=wg0

# Перезагружаем правила
$ sudo firewall-cmd --reload

# Правило POSTROUTING добавлять не нужно, если уже включен маскарадинг в зоне.
# Правило POSTROUTING отвечает за изменение исходящих IP-адресов на маршрутизаторе или компьютере, который является шлюзом по умолчанию.
# Если маскарадинг уже включен в зоне, то этого достаточно для настройки NAT-преобразования IP-адресов.
# Однако, такая возможность существует и настраивается он не через iptables, а в firewalld следующим образом.

$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -o enp0s3 -j MASQUERADE

# Для разрешения пропуска трафика из одной сети в другую, необходимо добавить правило в FORWARD в firewalld.
# Это правило указывает, какой трафик должен быть разрешен для маршрутизации между различными сетями.
# Без правила FORWARD маршрутирование между сетями может быть заблокировано.

# Добавляем в iptables в таблицу FORWARD разрешение на пропуск трафика из одной сети в другую
$ sudo firewall-cmd --direct --permanent --add-rule ipv4 filter FORWARD 0 -i enp0s3 -o wg0 -j ACCEPT
$ sudo firewall-cmd --direct --permanent --add-rule ipv4 filter FORWARD 0 -i wg0 -o enp0s3 -m state --state RELATED,ESTABLISHED -j ACCEPT

# Перезагружаем весь фаервол для применение таблиц маршрутизации
$ sudo systemctl restart firewalld

# И смотрим на добавленные правила маршрутизации - если что-то не правильно - вы сразу увидите
$ sudo firewall-cmd --direct --get-all-rules

Добавим доступ по «SSH», откроем доступ к «Portainer-у» через «wireguard», если «portainer» имеется, и посмотрим на все внесенные изменения.

# Добавляем ssh и 9000 порт portainer-а и перезагружаем правила
$ sudo firewall-cmd --permanent --zone=mywg --add-service=ssh
$ sudo firewall-cmd --permanent --zone=mywg --add-service=http
$ sudo firewall-cmd --permanent --zone=mywg --add-service=https
$ sudo firewall-cmd --permanent --zone=mywg --add-port=9000/tcp
# Для Portainer-а, визуального контроля контейнеров, 9000 порт.
$ sudo firewall-cmd --reload

# Всё проверяем
$ sudo firewall-cmd --info-zone=mywg
$ sudo firewall-cmd --info-zone=public
$ sudo firewall-cmd --get-active-zones
internal
  interfaces: br-00aaad17d4b5 br-c2ecab4afa7c docker0
mysite
  interfaces: enp0s3
mywg
  interfaces: wg0

Всё! На этом настройки данного фаервола завершены.

Переходим к настройкам wireguard-а.

Настроим конфигурацию сервера. Закоментируем строки с маршрутизацией и перезапустим сервис.

$ sudo nano /etc/wireguard/wg0.conf

[Interface]
...
# PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE

# PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o enp0s3 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o enp0s3 -j MASQUERADE

...

$ sudo systemctl restart wg-quick@wg0

К сожалению, совместить как-то по другому, увы, нельзя. Ну а на этом настройки фаервола и wireguard для совместной работы завершены.

Перейти к оглавлению.


Fail2ban

Описывая Fail2ban в двух словах, можно сказать, что он позволяет на основе анализа логов блокировать тех, кто злоупотребляет доступностью сервера по сети. Например, защитить почтовые ящики от взлома путем перебора паролей или многократного запроса какого-либо ресурса.

Говоря о слабых сторонах Fail2Ban, вы должны знать, что он не очень хорошо работает против распределенных атак методом перебора. Причина в том, что мониторинг лог-файлов происходит примерно раз в секунду. Этого должно быть достаточно в большинстве случаев, однако можно получить больше отказов входа, чем указано в параметре “maxretry”.

5.1. Установка и запуск

У Fail2Ban довольно низкие системные требования. При конфигурации только с sshd и несколькими различными правилами, Fail2Ban требует около 500 Мб памяти и загружает 1 ядро CPU в среднем менее чем на 0,2%.

Установка производится следующей командой:

$ sudo yum install fail2ban
$ sudo apt-get install fail2ban
$ sudo pacman -S fail2ban

Для запуска службы вводим команду:

$ sudo systemctl start fail2ban

А вот сразу добавлять fail2ban в автозапуск - не рекомендую.

$ sudo systemctl enable fail2ban

Объясню небольшую проблему запуска fail2ban-а. Дело в том, что он запускается раньше всех, даже раньше firewalld и значительно раньше docker-а. В результате правила просто не действуют. А вот если бы он стартовал позже нашего фаервола, который уже настроен на запуск позже docker-сервиса - это было бы идеально. Поэтому создаём таймер аналогичный запуску фаервола, за исключением того, что указываем запуск после него.

$ sudo nano /etc/systemd/system/fail2ban.timer

[Unit]
Description=Starting Fail2Ban on after services Firewalld
Wants=firewalld.timer
After=firewalld.timer

[Timer]
Unit=fail2ban.service
OnBootSec=20s
AccuracySec=1s

[Install]
WantedBy=timers.target
				

Вот теперь запускаем.

$ sudo systemctl enable fail2ban.timer
$ sudo systemctl start fail2ban.timer

Перейти к оглавлению.


5.2. Базовая настройка.

Процесс настройки fail2ban не зависит от дистрибутива Linux. Основной конфигурационный файл находится по пути «/etc/fail2ban/jail.conf». Однако, его не рекомендуется менять и для настройки используют подключаемые файлы из каталога «/etc/fail2ban/jail.d».

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

$ sudo nano /etc/fail2ban/jail.d/default.conf

[DEFAULT]
maxretry = 5
findtime = 5m
bantime = 10m
action = firewallcmd-ipset
ignoreip = 127.0.0.1/8 192.168.0.100

* где:

* В секции [DEFAULT] хранятся общие настройки для всех правил. Каждую из настроек можно переопределить при конфигурировании самого правила.

Время указывается в следующих выборках:

В данном примере, если в течение 5 минут будет найдено 5 строк (maxretry = 5), содержащих критерий фильтра, Fail2ban заблокирует IP-адрес, с которого идет подключение на 10 минут.
Т.е. если вы ошибетесь 5 раз в течении 5 минут - вас заблокирует на 10 минут.

Перейти к оглавлению.


5.3. Фильтры.

По умолчанию в Fail2ban встроено несколько фильтров, позволяющих обеспечить базовую защиту большинству веб-интерфейсов вашего сервера: ssh, веб-серверы и т.д.

Фильтры можно найти в каталоге «/etc/fail2ban/filter.d».

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

Фильтр можно создать самостоятельно и далее использовать его в той или иной конфигурации настроек правил. Например, для отслеживания состояния доступа к вашему сайту.

Основной признак попыток — «подозрительная активность» в логах. Это могут быть регулярные попытки подключения с разных IP-адресов, запросы к различным портам сервера, запросы на те или иные ресурсы.

Фильтры, в основном, представляют набор регулярных выражений для поиска ключевых слов в файлах логов. Причем самое забавное, что это регулярные варажения BASH. Таким образом создать такое выражение будет не сильно сложным, и справиться сможет даже новичок. Нужно лишь соблюдать несколько простых правил.

Это основные правила создания BASH регулярных выражений. Перейдём к практике.

Начнём создавать регулярное выражение. Для 2 примеров будет отслеживать все ошибки NGINX в логах доступа и просто ошибки самого NGINX.

Сначала создадим регулярное выражение отслеживания ошибок.

Чтобы это сделать просмотрим либо стандартный файл логов NGINX, либо созданый вами для отдельной конфигурации. У меня же он стандартный - «/var/log/nginx/access.log». Однако, просматривать лог вручную - то ещё удовольствие. Поэтому немного его отфильтруем, для понимания того, какие виды строк нам вообще необходимо отслеживать.

Вообще у NGINX есть строго определённые виды статусов. Они имеют следующий вид:

Теперь можно попробовать хоть немного отфильтровать файл логов.

$ sudo cat /var/log/nginx/access.log | grep -Ei "(GET|POST|HEAD)" | grep -Ei "(4[0-9]{2}|5[0-9]{2})"

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

Оба выражения из утилиты «Grep» будем использовать в регулярном выражении для Fail2ban-а. В фильтре необходимо указать как минимум 3 строки, даже если одна из них будет пустой.

В принципе в конфигурациях Fail2ban-а пустая строка в определении какой-либо переменной - обычное дело. Т.е. это не bash, т.к. после равно мы ничего не указываем - оставляем пустое место, но переменная и знак равно должны быть. В bash-е обычно пустая строка указывается просто двумя кавычками, иногда двойными. А здесь если строка пустая - даже пробел не ставится, переменная и знак равно через пробел.

Итак, фильтр «/etc/fail2ban/filter.d/nginx-access.conf».

[Definition]

failregex = ^<HOST>.*"(GET|POST|HEAD).*" (4[0-9]{2}|5[0-9]{2}) .*$

ignoreregex =

Таким же образом отфильтруем просто ошибки NGINX - «/var/log/nginx/error.log», сначала вручную, а затем создадим на этой основе регулярное выражение и запишем его в новый фильтр Fail2ban-а.

[Definition]

failregex = ^.* client: <HOST>, server: .*$

ignoreregex =

После создания фильтра его обязательно необходимо проверить, т.е. протестировать.

Синтаксис команды для тестирования выглядит следующим образом:

$ sudo fail2ban-regex <Файл-лога> <Файл-фильтра> <Файл-исключений-из-правил>

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

Например, в первом и втором случае команды тестирования будут выглядить следующим образом.

$ sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-access.conf /etc/fail2ban/filter.d/nginx-access.conf
$ sudo fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-error.conf /etc/fail2ban/filter.d/nginx-error.conf

Если мы всё сделали правильно, на выходе будет примерно следующая информация для «access.log».

Lines: 437 lines, 0 ignored, 154 matched, 283 missed
[processed in 0.12 sec]

И для «error.log».

Lines: 13 lines, 0 ignored, 13 matched, 0 missed
[processed in 0.00 sec]

Теперь наш созданный фильтр можно использовать в jail-правилах.

Перейти к оглавлению.


5.4. Действия.

Действия Fail2ban-а имеют гораздо большее применение, чем кажется на первый взгляд.

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

Ничего не имею против такого способа. Однако, обращу ваше внимание на следующее. Со временем вывод команды Fail2ban может измениться. В результате регулярное выражение придётся переписывать. Это один момент.

Это ни разу не круто!

Зачем так сильно заморачиваться, если для того же самого результата придуманы так называемы экшены - action.d действия у Fail2ban-а.

Вы могли бы сразу начать менять стандартные действия - дописывая в них что-то своё.

Да, так можно делать, это будет прекрасно работать. Однако, после обновления все ваши старания слетят в круглый ноль и придётся настраивать всё заново.

Это второй момент.

Так как же тогда быть? Спросите вы.

Опять таки, всё гениально и просто - создать собственный custom action!

Приведу в пример экшен для своего скрипта, который просто добавляет ip адреса в некий заранее предопределенный файл в json формате. А затем опишу некоторые особенности создания таких экшенов.

# Fail2Ban configuration file
#
# Author: Mikhail Artamonov
# Modified by maximalisimus
# 
# /etc/fail2ban/action.d/blacksimple.conf

[INCLUDES]

after =

before =

[Init]

# Option:  Programs
# Notes.:  A script for working with a blacklist and whitelist.
# Values:  system command
#
blacklistip = blacklist

# Option: count
# Notes.: The number of locks after which the ip-address 
#         is entered in {IP,IP6,NF}TABLES.
# Values: NUM Default: 3
#
count = 3

# Option: mask
# Notes.: The network mask.
# Values: NUM Default: 32
#
mask = 32

# Option: ipv6
# Notes.: Select {IP6/NF}TABLES.
# Values: STRING Default: ""
#
ipv = ""

[Definition]

# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#
actionstart =

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop =

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck =

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionban = <blacklistip> -nft <ipv> black -ip <ip> -a -save

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionunban = <blacklistip> -nft <ipv> -c <count> service -start

[Init?family=inet6]

# Option:  ipv6
# Note:    Select {IP6/NF}TABLES.
# Values:  STRING Default: "-ipv6"
#
ipv = -ipv6

# Option: mask (ipv6)
# Notes.: 
# Values: NUM Default: 128
#
mask = 128

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

В секции INCLUDES указываются другие экшены, которые должны быть сюда подключены в качестве стартовых. Если файлов экшенов не существует - ошибки не будет. Обычно в других экшенх Fail2ban-а таким образом указываются пользовательские экшены с расширением «.local». Т.е. вы создали свой экшен и не задумываетесь о том, что он точно будет подключен в какой-нибудь стандартный экшен.

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

В моём случае указано наименование символической ссылки, которая расположена в «/usr/bin/». Ссылаться такая ссылка может на любую утилиту, любой ваш исполняемый скрипт. Соответственно и расположен может быть где угодно в системе - на ваше усмотрение.

В принципе здесь можно просто прописать полный путь к вашему исполняемому скрипту. Например, так.

[Init]

blacklistip = /etc/myscript-folder/myscrip.sh

В принципе всю секцию Init вместе с секцией Init?family=inet6 можно вынести в отдельный файл в ту же директорию с другим наименованием файла соответственно и затем просто указать наименование этого файла в секции INCLUDES.

Далее в секции Definition указываются действия при обнаружении особо активного и ретивого ip адреса.

Всего в экшенах существует 5 действий. Однако, самое интересное состоит в том, что в каждой такой строке действий можно прописать милион BASH-команд построчно. Я конечно утрирую, но всё таки. Все указанные в этих строках команды будут выполнены при определённых условиях. Например, при запуске самого Fail2ban-а или каждый раз при блокировке или разблокировке того или иного ip-адреса.

Вот эти 5 действий:

Например, так, как это сделано в модуле «iptables-allports.conf».

actionstart = <iptables> -N f2b-<name>
              <iptables> -A f2b-<name> -j <returntype>
              <iptables> -I <chain> -p <protocol> -j f2b-<name>

В угловых одинарных скобках соответственно указаны переменные секции Init. Эти же переменные вы вполне можете переопределить в файлах Jail-правил в квадратных скобках, при указании наименования action.d файла без расширения. Об этом будет рассказано в следующем пункте поподробнее.

Секция Init?family=inet6 переопределяет нужные вам переменные для протокола IPV6. Т.е. при обнаружении ip адреса с протоколом IPV6 - Fail2ban автоматически переопределит нужные вам переменные и сразу же их применит в секции Definition во всех строках исполняемого кода, будь блокировка actionban или разблокировка actionunban, запуск или завершение работы экшена ...

Обратите внимание ещё на одну вещь - ip адрес указывается угловых одинарных скобках - <ip>.

Самое забавное в том, что дополнительный скрипт использовать в принципе не обязательно. Просто напишите BASH команду в строке actionban вывода адреса в файл, а все остальные строки оставьте пустыми, т.е. после знака равно ничего не надо писать, но строки обязательно должны быть! Этого будет вполне достаточно.

Например, так.

actionban = echo <ip> >> /etc/blacklist.txt

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

actionunban = cat /etc/fail2ban/ip.blacklist | while read IP; do iptables -I f2b-<name> 1 -s $IP -j DROP; done

Тогда в секции Init обязательно прописать параметр <name>, также как это сделано в других подключаемых модулях экшенов - «*-common.conf» - директории «/etc/fail2ban/action.d/».

[Init]

# Default name of the chain
#

name = default

Ну и соответственно использовать «name» в правилах.

Обратите внимание ещё на одну вещь! В данном случае при каждой разблокировке одного какого-нибудь ip адреса - в IPTABLES будут многократно задублированы все пойманные ip адреса, которые записаны в заданный вами файл. Я бы так просто не стал указывать такой параметр при разблокировке ip адресов. Лучше сделать некий скрипт, который будет проверять есть ли такой адрес в IP(6)TABLES и только потом добавлять его туда, если его там нету.

Таким образом вы можете отслеживать ip адреса в Fail2ban-е, без особых усилий и дополнительных скриптов.

Перейти к оглавлению.


5.5. Настройка правил.

В Debian по умолчанию вместо стандартной настройки есть такой файл: «/etc/fail2ban/jail.d/defaults-debian.conf».

Обычно в нём заданы настройки SSH-ловушки по умолчанию. Сейчас она не работает. Мы её перезапишем в новой конфигурации: «/etc/fail2ban/jail.d/ssh.conf».

Не забудьте перед этим создать нужный файл лога и дать ему соответствующие права доступа. Иначе при перезапуске «fail2ban-сервиса» получите ошибку.

$ sudo touch /var/log/sshd.log
$ sudo chown root:adm /var/log/sshd.log
$ sudo chmod 640 /var/log/sshd.log

Обратите внимание! Перед настройкой конфигураций необходимо понимать ещё один важный момент.

Дело в том, что стандартный «action=firewallcmd...», т.е. через firewald не всегда хорошо отрабатывает. В процессе всесторонней проверке на виртуальной машине выяснилась одна проблема.

IP-адреса блокирует только один раз. В таблице маршрутизации фаервола адрес, который блокирован находится не по порядку, из-за чего - он по сути не блокируется.

Исправляет этот момент только перезапуском и фаервола и fail2ban-а. Чтобы такого не происходило - action вполне можно взять другой, пусть даже с iptables - это вполне нормально. У нас нет «iptables-сервиса», а не самого «iptables» как такового. Таким образом один бан другому мешать никак не будет. И разбан будет работать так как должен.

Выходит, что работать на блокировку будут таблицы маршрутизации «iptables», а не «firewalld». Т.е. пакет свободно пройдет через «firewalld» и остановится на «iptables». Фаервол и так уже нагружен, поэтому разгрузить его было бы хорошо. К тому же «iptables» по сути вообще не нагружен.

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

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

Теперь создаем файл конфигурации: «ssh.conf».

$ sudo nano /etc/fail2ban/jail.d/ssh.conf

[sshd]
enabled = true
port = ssh
# filter =
# action = iptables-ipset[name=sshd, port=ssh, protocol=tcp]
action = firewallcmd-ipset[name=sshd, port=ssh, protocol=tcp]
logpath = /var/log/sshd.log
ignoreregex =

* где:

Самое интересное в том, что в строке logpath вы можете указать несколько файлов логов. Для этого просто перенесите следующий файл на следующую строку пробелами примерно на один уровень с предыдущим логом.

Например.

logpath = /var/log/nginx/access.log
          /var/log/mylog/access.log

В строке action правил вы также можете указывать несколько действий по тому же принципу. Приведу полный текст примера правила для SSH соединений. Соответственно дополнительным будет новый экшен (action), созданный нами в предыдущем пункте - «/etc/fail2ban/actiond.d/blacksimple.conf». И здесь же переопределим одну нужную нам переменную - count. В моём скрипте приведённого примера экшена и правила переменная означает - сколько раз ip адрес был добавлен в json файл, после которого он должен попасть в черный список и навсегда блокироваться фаерволом.

[sshd]
enabled = true
port = ssh
action = firewallcmd-ipset[name=sshd, port=ssh, protocol=tcp]
         blacksimple[count=3]
logpath = /var/log/sshd.log

Таким образом вы можете добавлять любые дополнительные действия с ip адресами непосредственно внутри Fail2ban-а и при этом вам не нужны никакие дополнительные сервисы или скрипты с регулярными выражениями.

В моём же случае я с успехом использую полученную информацию для работы самодельного черного списка ip-адресов.

Чтобы изменения вступили в силу, перезапускаем сервис и проверяем.

$ sudo systemctl restart fail2ban
$ sudo fail2ban-client restart
# Этот вариант, в отличие от предыдущего, показывает ошибки при перезапуске, но не перезапускает свой сервис
$ sudo fail2ban-client status
# Если всё нормально должен показать следующее
Status
|- Number of jail:	1
`- Jail list:	sshd

Для гарантии, что fail2ban не заблокирут компьютер администратора или другой важный узел, предусмотрена настройка исключений с помощью опции ignoreip. Опция может быть применена как на глобальном уровне (default), так и для конкретного правила.

Для того, чтобы задать общую настроку, откроем наш файл «default.conf»:

$ sudo nano /etc/fail2ban/jail.d/default.conf

[DEFAULT]
...
ignoreip = 192.168.0.0/24 95.95.95.95

* в данном примере под фильтры не будут попадать адреса с 192.168.0.1 по 192.168.0.255 и адрес 95.95.95.95.

Файлы с настройкой действий находятся в каталоге «/etc/fail2ban/action.d». Чтобы блокировать адрес, Fail2ban создает правило в брандмауэре netfilter. Для этого, чаще всего, используются утилиты iptables или firewall-cmd. Последняя применяется в последних версиях CentOS / Red Hat / Fedora. Iptables более универсальная и может использоваться, почти, во всех системах Linux.

Остановимся на описании выше используемых действиях:

Перейти к оглавлению.


5.6. NGINX DDoS (req limit).

Данное правило поможет защитить веб-сервер nginx от DDoS-атак. В некоторых сборках, для данного правило может не оказаться готового фильтра, поэтому в данном примере, мы его создадим вручную.

Для начала, необходимо настроить NGINX:

$ sudo nano /etc/nginx/nginx.conf

В раздел http добавим:

http {
...
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
...

* данная настройка создает зону с интенсивностью запросов в 1 запрос в секунду.

После настраиваем лимит для конкретного виртуального домена в разделе server - location:

server {
...
location / {
...
limit_req zone=one burst=5 nodelay;
...

* данная настройка вместе с предыдущей зоной, созданной в секции http, позволит лимитировать запросы — 1 запрос в секунду при всплеске 5 запросов.

Проверяем конфигурационный файл nginx и перезапускаем сервис:

$ sudo nginx -t
$ sudo systemctl reload nginx

В лог-файле /var/log/nginx/error.log мы должны увидеть запись на подобие:

2020/11/16 19:11:08 [error] 1330844#1330844: *16640836 limiting requests, excess: 10.520 by zone "one", client: xxx.xxx.xxx.xxx, server: dmosk.ru, request: "GET / HTTP/1.1", host: "dmosk.ru", referrer: "https://dmosk.ru/page1"

Теперь можно приступать к настройке fail2ban. Создаем фильтр. Вообще в «Fail2Ban» уже есть встроенный фильтр для «Nginx» и он вполне нормальный, да и отрабатывает вполне неплохо. Но, на всякий случай, вот рабочая конфигурация строк, которые надо изменять.

$ sudo nano /etc/fail2ban/filter.d/nginx-limit-req.conf

[Definition]
ngx_limit_req_zones = [^"]+
failregex = ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>,
ignoreregex =

Создаем правило для fail2ban:

$ sudo nano /etc/fail2ban/jail.d/nginx-ddos.conf

[nginx-ddos]
enabled = true
port = http,https
filter = nginx-limit-req
action = iptables-multiport[name=nginxddos, port="http,https", protocol=tcp]
# action = firewallcmd-multiport[name=nginxddos, port="http,https", protocol=tcp]
logpath = /var/log/nginx/error.log

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

$ systemctl restart fail2ban

Перейти к оглавлению.


5.7. NGINX files.

Настройка upload файлов.

$ sudo nano /etc/nginx/nginx.conf


http {
	client_max_body_size 100M;
	client_body_buffer_size 256k;
	proxy_max_temp_file_size 0;

...

}

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

Более подробно о них написано в «Пункте 5.14».

Перейти к оглавлению.


5.8. NGINX GZIP.

Создайте файл «site_gzip.conf» со следующим содержимым

$ sudo nano /etc/nginx/site_gzip.conf

gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied    any;
gzip_vary       on;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;
# text/html is always compressed by gzip module

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

# gzip content
include /etc/nginx/site_gzip.conf;

Перейти к оглавлению.


5.9. NGINX X-XSS-Protection.

Заголовок X-XSS-Protection может предотвратить некоторые XSS-атаки.

Вы можете реализовать защиту XSS, используя три варианта в зависимости от конкретной потребности.

Вообще настройка добавляет в блок «server», но в принципе можно и в блок «http».

$ sudo nano /etc/nginx/nginx.conf

http {
  ...

  add_header X-XSS-Protection "1; mode=block";

  ...
}

Перейти к оглавлению.


5.10. NGINX X-Frame-Options.

Заголовок X-Frame-Options позволяет снизить уязвимость вашего сайта для clickjacking-атак. Этот заголовок служит инструкцией для браузера не загружать вашу страницу в frame/iframe. Не все браузеры поддерживают этот вариант.

Настроить X-Frame-Options можно тремя способами:

$ sudo nano /etc/nginx/nginx.conf

http {
  ...

  add_header X-Frame-Options "DENY";

  ...
}

Перейти к оглавлению.


5.11. NGINX X-Permitted-Cross-Domain-Policies.

Аналогично механизму браузеров блокировки стороннего контента Adobe Flash имеет свой. Он регулируется файлами crossdomain.xml сайта, начиная с корневого каталога. Проблема с механизмом в том, что на любом уровне вложенности корневой регулирующий файл (политика безопасности) может быть переопределен. Чтобы избежать таких ситуаций, необходимо задать этот HTTP-заголовок.

Доступно несколько вариантов настройки:

$ sudo nano /etc/nginx/nginx.conf

http {
  ...

  add_header X-Permitted-Cross-Domain-Policies master-only;

  ...
}

Перейти к оглавлению.


5.12. NGINX Strict-Transport-Security.

Заголовок Strict-Transport-Security запрещает использование незащищенного HTTP соединения на сайте, если есть защищенное HTTPS.

$ sudo nano /etc/nginx/nginx.conf

http {
  ...

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

  ...
}

Лучше включить его в «/etc/letsencrypt/options-ssl-nginx.conf». Ниже этот вариант тоже описан.

Перейти к оглавлению.


5.13. NGINX X-Content-Type-Options.

Рейтинг наиболее опасных к использованию возможностей браузера возглавляет возможность Internet Explorer «угадывать» тип файла, игнорируя его MIME-тип.

При передаче от сервера к браузеру все файлы имеют тот или иной тип, который прямо указывает на суть содержимого файла. Однако, Internet Explorer имеет встроенный механизм, который позволяет по-содержимому файла переопределить его тип.

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

$ sudo nano /etc/nginx/nginx.conf

http {
  ...

  add_header X-Content-Type-Options nosniff;

  ...
}

Перейти к оглавлению.


5.14. NGINX SSL.

SSL 90%.

Отредактируйте файл вашей конфигурации сервера. Например такой.

$ sudo nano /etc/nginx/sites-available/example

server {
	...
	listen 443 ssl http2; # Включить http2
    server_name example.com;
    ssl on;
    ssl_stapling_verify on;
	...
}
				

Теперь отредактируйте файл созданный certbot-ом.

$ sudo nano /etc/letsencrypt/options-ssl-nginx.conf
# HSTS. Лучше включить здесь, а не в http.
#add_header Strict-Transport-Security "max-age=604800; includeSubDomains" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains;";
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;" preload;

#ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1440m;
ssl_session_tickets off;

#ssl_protocols TLSv1.2 TLSv1.3;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;

#ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
	ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:
	ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
#ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 
EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW 
	!3DES !MD5 !EXP !PSK !SRP !DSS";
#ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384;
#ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:
	ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:
	DHE-RSA-AES256-GCM-SHA384;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:
	TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:
	ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:
	ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:
	DHE-RSA-AES256-GCM-SHA384;
ssl_ecdh_curve secp384r1;

# OCSP Stapling
ssl_stapling on;
resolver 8.8.8.8 8.8.4.4 valid=300s;

# gzip content
include /etc/nginx/site_gzip.conf;

# Expect CT (Optional)
add_header Expect-CT "max-age=0";

Наш GZIP, описанный выше, в принципе, можно подключить здесь.

Здесь, важны все строки, включая «Expect CT» и «OCSP Stapling».

Перейти к оглавлению.


5.15. Nginx Отключение серверных токенов.

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

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

$ curl -I 191.168.0.110

HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: ************************* GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive

# Или, может быть и такой ответ

HTTP/1.1 301 Moved Permanently
Server: nginx/1.18.0
Date: ************************* GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive

Давайте те устраним эту уязвимость. Для этого отредактируем стандартную конфигурацию Nginx.

$ sudo nano /etc/nginx/nginx.conf

http {
	server_tokens off;
	...
}

# Сохраняем и выходим
CTRL + o
CTRL + x

# Обязательно проверяем конфигурацию и перезагружаем сервер Nginx
$ sudo nginx -t
$ sudo systemctl restart nginx

Теперь ответ сервера на любой запрос будет такой.

$ curl -I 191.168.0.110

HTTP/1.1 200 OK
Server: nginx
Date: ************************* GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive

# Или такой

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: ************************* GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive

Отлично!

Перейти к оглавлению.


5.16. Nginx Задание размеров буферов.

Буферы Nginx задают возможность использования оперативной памяти для сохранения представляющей ценность информации. Их размер задается в конфигурационном файле и определяет будет ли использоваться оперативная память или все будет записываться на диск.

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

Размер буферов определяется четырьмя директивами в конфигурационном файле, в секции server либо в http:

  1. «client_body_buffer_size»
  2. «client_header_buffer_size»
  3. «client_max_body_size»
  4. «large_client_header_buffers»

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

large_client_header_buffers принимает два значения, разделенных пробелами: максимальное числом и размер буферов для чтения большого заголовка запроса клиента.

Значение размера буфера всегда должно быть меньше client_body_buffer_size, иначе возникнет ошибка 414 (Request-URI Too Large).

Также оно должно быть меньше client_header_buffer_size. В противном случае вернется ошибка 400 (Bad Request).

В конфигурационном файле для не самого мощного сервера значения буферов могут быть такими.

client_body_buffer_size 20K;
client_header_buffer_size 4k;
client_max_body_size 12m;
large_client_header_buffers 2 12k;

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

Задать их можно для каждого виртуального хоста — для каждого сайта (в секции server) или для всего сервера (в секции http - «/etc/nginx/nginx.conf»).

Перейти к оглавлению.


5.17. Fail2ban. Работа со списком заблокированных адресов.

Получить статистику заблокированных адресов можно следующей командой:

$ sudo fail2ban-client status <имя правила>

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

$ sudo fail2ban-client status

При наличие заблокированных IP-адресов мы увидим, примерно, следующее:

`- action
|- Currently banned: 2
| `- IP list: 31.207.47.55 10.212.245.29

С помощью iptables:

$ sudo iptables -L -n --line

С помощью firewall-cmd:

$ sudo firewall-cmd --direct --get-all-rules

Перейти к оглавлению.


5.18. Fail2ban. Примеры правил

Настройки Fail2ban - «/etc/fail2ban/jail.d/default.conf», при условии использования Wireguard. Хотя, последнее не обязательно. У вас просто появятся пару новых ip адресов в строке игнора, т.е. белых адресов.

[DEFAULT]
maxlines = 4
maxretry = 5
findtime = 5m
bantime = 10m
action = firewallcmd-ipset
ignoreip = 127.0.0.1/8 10.10.10.0/24

Конфигурация SSH - «/etc/fail2ban/jail.d/ssh.conf».

[sshd]
enabled = true
port = ssh
#action = iptables-ipset[name=sshd, port=ssh, protocol=tcp]
#action = nftables-allports[name=sshd, protocol=tcp]

action = firewallcmd-ipset[name=sshd, port=ssh, protocol=tcp]
logpath = /var/log/sshd.log

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

Настройка Nginx-DDOS - «/etc/fail2ban/jail.d/nginx-ddos.conf».

[nginx-ddos] enabled = true
port = http,https
maxretry = 5
findtime = 10s
bantime = 10m
filter = nginx-limit-req
#action = firewallcmd-ipset[name=nginxddos, port=ssh, protocol=tcp]
#action = firewallcmd-multiport[name=nginxddos, port="http,https", protocol=tcp]

action = iptables-multiport[name=nginxddos, port="http,https", protocol=tcp]
logpath = /var/log/nginx/error.log
ignoreregex = \.(jpg|jpeg|png|gif|js|css)

Отслеживание доступа в NGINX - «/etc/fail2ban/jail.d/access-nginx.conf», при помощи собственного фильтра.

[anginx]
enabled  = true
port = all
filter = nginx-access
logpath = /var/log/nginx/access.log
          /var/log/myservices/fwg/access.log
#action = firewallcmd-ipset[name=anginx, port=ssh, protocol=tcp]
#action = firewallcmd-multiport[name=anginx, port="http,https", protocol=tcp]
action = iptables-allports[name=anginx, protocol=all]
ignoreregex = \.(jpg|jpeg|png|gif|js|css)

Отдельное отслеживание ошибок NGINX - «/etc/fail2ban/jail.d/error-nginx.conf», при помощи собственного фильтра.

[enginx]
enabled  = true
port = all
filter = nginx-error
logpath = /var/log/nginx/error.log
#action = firewallcmd-ipset[name=enginx, port=ssh, protocol=tcp]
#action = firewallcmd-multiport[name=enginx, port="all", protocol=tcp]
action = iptables-allports[name=enginx, protocol=all]
ignoreregex = \.(jpg|jpeg|png|gif|js|css)

Перейти к оглавлению.


5.19. Fail2ban. Удаление.

Средствами fail2ban:

Для удаление адреса из списка вводим:

$ sudo fail2ban-client set <имя правила> unbanip

например:

$ sudo fail2ban-client set ssh unbanip 31.207.47.55

С помощью iptables:

$ sudo iptables -D <цепочка правил> -s IP-адрес

например:

$ sudo iptables -D fail2ban-ssh -s 10.212.245.29

С помощью firewall-cmd:

$ sudo firewall-cmd --direct --permanent --remove-rule <правило>

например:

$ sudo firewall-cmd --direct --permanent --remove-rule ipv4 filter f2b-sshd 0 -s 188.134.7.221

После необходимо перечитать правила:

$ sudo firewall-cmd --reload

Перейти к оглавлению.


6. Блокировка ip адреса или всей подсети.

С помощью Firewalld это будет выглядеть следующим образом.

$ sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='192.168.2.0/24' reject"
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all

С помощью IPTABLES так.

При этом у DROP в ответ запрашивающей стороне ничего не будет отправляться, а у REJECT будет автоматически сформирован ответ. Таким образом, если это злоумышленники, то при использовании последнего, т.е. REJECT, они просто будут знать о том, что вы их заблокировали. И тоже самое с блокировкой через Firewalld - лучше использовать DROP, а не REJECT.

Обратите внимание на ключ «-A».

$ sudo iptables -t filter -A INPUT -s 34.124.185.0/24 -j DROP
$ sudo iptables -t filter -A INPUT -s 45.79.221.110/24 -j REJECT

А вот так будет выглядить разрешающее ACCEPT правило правило.

$ sudo iptables -t filter -A INPUT -s 192.168.0.110/32 -j ACCEPT

При этом ключ «-A» используется для добавления значения в IPTABLES, а ключ «-D» удаляет значение из IPTABLES. Т.е. первый блокирует, второй разблокирует.

Разблокировка тех же адресов будет идентична, за исключением ключа «-A».

$ sudo iptables -t filter -D INPUT -s 34.124.185.0/24 -j DROP
$ sudo iptables -t filter -D INPUT -s 45.79.221.110/24 -j REJECT

Соответственно удаление разрешающего ACCEPT правила будет выглядеть так.

$ sudo iptables -t filter -D INPUT -s 192.168.0.110/32 -j ACCEPT

Для привязки любого вышеупомянутого правила к определённому входному сетевому интерфейсу необходимо дополнить команду сразу после цепочки INPUT ключом -i и наименованием сетевого инетерфейса.

Например, с сетевым интерфейсом «eth0» все вышеупомянутые команды будут выглядеть теперь так.

$ sudo iptables -t filter -A INPUT -i eth0 -s 34.124.185.0/24 -j DROP
$ sudo iptables -t filter -A INPUT -i eth0 -s 192.168.0.110/32 -j ACCEPT
$ sudo iptables -t filter -D INPUT -i eth0 -s 34.124.185.0/24 -j DROP
$ sudo iptables -t filter -D INPUT -i eth0 -s 192.168.0.110/32 -j ACCEPT

Блокировать с помощью «NFTABLES» немного сложнее, в том плане, что разблокировать придётся уже 2 командами, а не изменением ключа в одной команде.

Для начала нам нужно определиться с какой таблицей мы вообще работаем, а затем с какой цепочкой. Дело в том, что в «NFTABLES» все таблицы и цепочки вы создаете самостоятельно. Здесь нет ни стандартных таблиц, ни стандартных цепочек. Посмотрим на таблицы, которые вообще есть в системе, если они есть. Например, созданы по необходимости другим ПО.

$ sudo nft list tables

Если таблица есть, то скорее всего это будет «ip filter». Если нет, то её необходимо создать. Причём ip - это семейство nftables.

По умолчанию используется ip (IPv4), если семейство не указано.

Создадим таблицу, если у нас её нет.

$ sudo nft add table ip filter

Теперь посмотрим на цепочки данной таблицы.

$ sudo nft list table ip filter

Если такая таблица существовала, т.е. была создана другим ПО, то скорее всего в ней будет цепочка «chain INPUT». Если нет, то цепочку придётся создавать. Создаётся цепочка примерно так.

$ sudo nft add chain ip filter INPUT '{ type filter hook input priority 0; policy accept; }'

Теперь можно заблокировать надоедливый адрес, например, некой bootnet-сети.

$ sudo nft 'add rule ip filter INPUT ip saddr 165.22.60.249 counter drop'

А вот так будет выглядеть разрешающиее правило, аналогично разрешающему в IPATBLES.

$ sudo nft 'add rule ip filter INPUT ip saddr 192.168.0.110 counter accept'

Соответственно с привязкой к определённому входному сетевому интерфейсу, например, всё тот же «eth0»:

$ sudo nft 'add rule ip filter INPUT iifname "eth0" ip saddr 165.22.60.249 counter drop'
$ sudo nft 'add rule ip filter INPUT iifname "eth0" ip saddr 192.168.0.110 counter accept'

Чтобы разблокировать адрес, т.е. удалить его из NFTABLES, необходимо узнать его «handle». Сделать это можно так. Стандартная команда для определения рукопожатия и далее по конвееру небольшая фильтрация для более или менее приличного вида вывода. Все что идет по конвееру можно удалить, т.е. выполнять не обязательно.

# Вот так выглядит обычная команда.
$ sudo nft --handle --numeric list chain ip filter INPUT

# А вот так с конвеером.
$ sudo nft --handle --numeric list chain ip filter INPUT | grep -Ei "ip saddr|# handle" | sed 's/^[ \t]*//' | awk '!/^$/{print $0}'

В конце каждой строки напротив ip-адреса как раз и будет заветный «handle». Теперь зная его, можно удалить ip-адрес из NFTABLES.

$ sudo nft delete rule ip filter INPUT handle 10

Да, вот так просто удалить по «handle»-у.

Учтите! Что после перезагрузки все правила будут сброшены. Чтобы они сохранились их надо сохранить в файл, и загружать при старте системы дополнительным скриптом - примерно как IPATBLES, или отдельным bash скриптом и сервисом systemd.

Сохраним NFTABLES и посмотрим как восстанавливать внесённые изменения.

$ sudo mkdir /etc/nftables-conf/
$ sudo nft -s list ruleset >> /etc/nftables-conf/nftables_rules

# Восстановить
$ sudo nft -f /etc/nftables-conf/nftables_rules

Однако, лучше если вы сохраните свои правила (обязательно только добавить, а не заменять) в специальный конфигурационный файл - «/etc/nftables.conf».

$ sudo nft -s list ruleset >> /etc/nftables.conf

Применение с перезагрузкой NFTABLES.

# Загружаем правила с помощью службы systemd.
$ sudo systemctl enable nftables.service

$ sudo systemctl start nftables.service

Сохраним IPTABLES и посмотрим как восстанавливать внесенные изменения.

$ sudo iptables-save

$ sudo mkdir /etc/iptables-conf/
$ sudo iptables-save -f /etc/iptables-conf/iptables_rules.ipv4
# Или
$ sudo iptables-save > /etc/iptables-conf/iptables_rules2.ipv4

$ ls -la /etc/iptables-conf/

# Восстановить
$ sudo iptables-restore -vV /etc/iptables-conf/iptables_rules.ipv4
$ sudo iptables-restore -nvV /etc/iptables-conf/iptables_rules.ipv4

Применение с перезагрузкой IPTABLES.

$ sudo nano /etc/network/if-pre-up.d/iptables && sudo chmod +x /etc/network/if-pre-up.d/iptables

#!/bin/sh
/sbin/iptables-restore < /etc/iptables-conf/iptables_rules.ipv4

# Сохраняем, выходим
CTRL + o
CTRL + x
# Делаем исполняемым
$ sudo chmod +x /etc/network/if-pre-up.d/iptables
# Загружаем до запуска интерфейсов
$ sudo -t filter iptables -L

Либо для NFTABLES и IP(6)TABLES можете воспользоваться несколько другим способом.

Просто добавьте пару строчек в ручные настройки сети. Не важно как у вас настроены интерфейсы - на статичные адреса или динамические, главное чтобы после их настройки указать какой скрипт или скрипты необходимо запустить перед запуском того или иного интерфейса. И соответственно тут же можно указать какие скрипты запускать по окончании работы с интерфейсами.

Что можно в этих настройках писать, а что нельзя вы можете узнать в статье: «Базовые настройки серверов Linux: 4. Настройки сетевых соединений.».

Например, изменим мак-адрес сетевой карты eth0. Само собой, закоментированная строка представляет собой 2-й способ смены мак-адреса. Ну и, также, в комментариях помечен способ указания статичного ip-адреса.

3-мя точками обозначено то, что в файле могут имется и другие различные строки, которые не нужно менять. Просто оставьте их в значениях по умолчанию, потому что нас интересуют только настройки нужной сетевой карты. Например, в строках перед вашей сетевой картой могут быть настройки loopback-интерфейса.

$ sudo nano /etc/network/interfaces

...
allow-hotplug eth0
auto eth0
iface eth0 inet dhcp
# iface eth1 inet static
	# address 192.168.0.120
	# netmask 255.255.255.0
	# gateway 192.168.0.1
	# broadcast 192.168.0.255
	# dns-nameservers 192.168.0.254 8.8.8.8
# pre-up ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
pre-up ip link set eth0 address xx:xx:xx:xx:xx:xx
...

Допустим, у нас имеется сетевая карта «enp0s3». Адрес должен быть получен в виде DHCP. Посмотрим на запуск скрипта для IP(6)TABLES.

$ sudo nano /etc/iptables-restore.sh && sudo chmod +x /etc/iptables-restore.sh

#!/bin/sh
/sbin/iptables-restore < /etc/iptables-conf/iptables_rules.ipv4

CTRL + o
CTRL + x
$ sudo nano /etc/network/interfaces

...
allow-hotplug enp0s3
auto enp0s3
iface enp0s3 inet dhcp
pre-up /etc/iptables-restore.sh
...

CTRL + o
CTRL + x

Перейти к оглавлению.


Copyright © 14.01.2021 - 19.07.2023 by Mikhail Artamonov