Набори IP переглянуті
Уже є відповідь, де згадуються набори IP. Однак він досить одномірний, оскільки фокусується на підвищенні продуктивності в порівнянні з класичними правилами та на тому, що набори IP пом'якшують проблему, що виникає з великою кількістю індивідуальних IP-адрес, які не можуть бути легко виражені як підмережа в нотаціях CIDR.
Позначення, що використовуються нижче
Бо ipset
я буду використовувати позначення, прочитані ipset restore
і написані ipset save
.
Відповідно для iptables
(і ip6tables
) правил я буду використовувати позначення, прочитані iptables-restore
і написані автором iptables-save
. Це робить коротшими позначення, і це дозволяє мені виділити потенційні правила, що стосуються лише IPv4 (префіксованого -4
) або лише для IPv6 (префіксованого -6
).
У деяких прикладах ми будемо перенаправляти потік пакетів в інший ланцюг. Припускається, що ланцюг існує в цій точці, тому рядки для створення ланцюгів не виробляються (також не згадується назва таблиці або команди COMMIT
в кінці).
Розширені набори IP
Набори IP можуть зробити набагато більше, ніж було зазначено в іншій відповіді, і ви обов'язково повинні ознайомитися з документацією щодо встановлення IP-адрес ( ipset(8)
), а також iptables-extensions(8)
додатково до цього короткого запису тут.
Наприклад , я буду в основному зосереджений на три типи набору: hash:ip
, hash:net
і list:set
, але є більше , ніж ті , і всі вони мають дійсні випадки використання.
Наприклад, ви можете також відповідати номерам портів, а не лише IP-адресам .
Збереження та відновлення наборів IP як iptables-save
іiptables-restore
Ви можете створювати декларації набору IP оптом та імпортувати їх, перекладаючи їх у ipset restore
. Якщо ви хочете зробити свою команду більш стійкою до вже існуючих записів, використовуйте ipset -exist restore
.
Якщо ваші правила містяться у файлі, який default.set
ви називаєте :
ipset -exist restore < default.set
Такий файл може містити записи до create
наборів та add
записів до них. Але, як правило, більшість команд з командного рядка, здається, мають у файлах відповідну версію. Приклад (створення набору серверів DNS):
create dns4 hash:ip family inet
create dns6 hash:ip family inet6
# Google DNS servers
add dns4 8.8.8.8
add dns4 8.8.4.4
add dns6 2001:4860:4860::8888
add dns6 2001:4860:4860::8844
Тут один набір створений для IPv4 ( dns4
) і один для IPv6 ( dns6
).
Часи очікування на IP-набори
Часи очікування в наборах IP можуть бути встановлені як за замовчуванням для набору, а також для запису. Це дуже корисно для сценаріїв, коли ви хочете тимчасово заблокувати когось (наприклад, для сканування портів або спроби грубої сили вашого SSH-сервера).
Це працює таким чином (за замовчуванням під час створення IP-наборів):
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
Ми повернемося до цих конкретних наборів нижче та обґрунтування того, чому вони встановлені таким, яким вони є.
Якщо ви хочете встановити час очікування на конкретну IP-адресу, ви можете просто сказати:
add ssh_dynblock4 1.2.3.4 timeout 7200
Блокувати IP 1.2.3.4 на дві години замість (встановленого) півгодини за замовчуванням.
Якщо ви подивитесь на це ipset save ssh_dynblock4
через короткий час, ви побачите щось уздовж:
create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 1.2.3.4 timeout 6954
Застереження про час очікування
- тайм-аути - це функція для будь-якого набору. Якщо набір не створений із підтримкою тайм-аута, ви отримаєте помилку (наприклад
Kernel error received: Unknown error -1
).
- тайм-аути задаються в секундах. Використовуйте арифметичні вирази Баша, щоб, наприклад, пройти від хвилин до секунд. Наприклад:
sudo ipset add ssh_dynblock4 1.2.3.4 timeout $((120*60))
Перевірка наявності запису в даному IP-наборі
Всередині ваших сценаріїв може бути корисним, щоб дізнатись, чи вже існує запис. Цього можна досягти, при ipset test
якому повертається нуль, якщо запис існує, а не нульове в іншому випадку. Тож у сценарії можна застосувати звичайні чеки:
if ipset test dns4 8.8.8.8; then
echo "Google DNS is in the set"
fi
Однак у багатьох випадках ви хочете скористатися -exist
перемикачем ipset
, щоб направити його не скаржитися на існуючі записи.
Популяція наборів IP з iptables
правил
Це, на мій погляд, одна з вбивчих особливостей наборів IP. Ви не тільки можете співставити записи з набором IP, ви можете також додати нові записи до існуючого набору IP.
Наприклад, у цій відповіді на це запитання у вас є:
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT
... з наміром обмежити швидкість спроб підключення до SSH (порт 22 TCP). Використовуваний модуль recent
відстежує останні спроби з'єднання. Замість state
модуля я все ж віддаю перевагу conntrack
модулю.
# Say on your input chain of the filter table you have
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
# Then inside the SSH chain you can
# 1. create an entry in the recent list on new connections
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
# 2. check whether 3 connection attempts were made within 2 minutes
# and if so add or update an entry in the ssh_dynblock4 IP set
-4 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock4 src --exist
-6 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock6 src --exist
# 3. last but not least reject the packets if the source IP is in our
# IP set
-4 -A SSH -m set --match-set ssh_dynblock4 src -j REJECT
-6 -A SSH -m set --match-set ssh_dynblock6 src -j REJECT
У цьому випадку я перенаправляю потік до SSH
ланцюга таким чином, що мені не доведеться повторюватись -p tcp --dport ssh
за кожним правилом.
Повторюю:
-m set
робить в iptables
курсі , що ми використовуємо перемикачі з set
модуля (який обробляє набори IP)
--match-set ssh_dynblock4 src
повідомляє iptables
відповідати адресу джерела ( src
) проти названого набору ( ssh_dynblock4
)
- це відповідає
sudo ipset test ssh_dynblock4 $IP
(де $IP
міститься вихідна IP-адреса пакета)
-j SET --add-set ssh_dynblock4 src --exist
додає або оновлює адресу джерела ( src
) з пакету в набір IP ssh_dynblock4
. Якщо запис існує ( --exist
), він буде просто оновлений.
- це відповідає
sudo ipset -exist add ssh_dynblock4 $IP
(де $IP
міститься вихідна IP-адреса пакета)
Якщо ви хочете, щоб замість цільової / цільової адреси замість цього, ви використовуєте dst
замість src
. Для отримання додаткових варіантів зверніться до посібника.
Набори наборів
Набори IP можуть містити інші набори. Тепер, якщо ви дотримувались статті до цього пункту, ви задумалися, чи можна комбінувати набори. І звичайно, це так. Для IP-наборів зверху ми можемо створити два спільних набори ssh_dynblock
і, ssh_loggedon
відповідно, містити набори лише для IPv4 та лише для IPv6:
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
# Sets of sets
create ssh_loggedon list:set
create ssh_dynblock list:set
# Populate the sets of sets
add ssh_loggedon ssh_loggedon4
add ssh_loggedon ssh_loggedon6
add ssh_dynblock ssh_dynblock4
add ssh_dynblock ssh_dynblock6
Наступне питання, яке повинно з’явитися у вашій свідомості, - чи це дозволяє нам співставляти та маніпулювати наборами IP в АГ-версії IP-версії.
І відповідь на це звучить: ТАК! (на жаль, востаннє це не було задокументовано)
Отже, правила з попереднього розділу можна переписати, щоб прочитати:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
що набагато більш стисло. І так, це перевірено і працює як шарм.
Зведення все це разом: захист грубої сили SSH
На своїх серверах у мене працює сценарій як cron
завдання, яке займає купу імен хостів і вирішує їх на IP-адреси, а потім подає їх у IP-набір для "довірених хостів". Ідея полягає в тому, що довірені хости отримують більше спроб увійти в сервер і не обов'язково блокуються до тих пір, як ніхто інший.
І навпаки, у мене цілі країни заблоковані підключенням до мого SSH-сервера, за винятком (потенційного) виключення надійних хостів (тобто порядок питань має значення).
Однак це залишається як вправа для читача. Тут я хотів би додати акуратне рішення, яке використовуватиме набори, що містяться в ssh_loggedon
наборі, щоб дозволяти подальшим спробам з'єднання передаватись, а не піддаватися такому ж уважному контролю, як інші пакети.
Важливо пам’ятати про затримки за замовчуванням 90 хвилин ssh_loggedon
і 30 хвилин для ssh_dynblock
перегляду таких iptables
правил:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m set --match-set ssh_loggedon src -j ACCEPT
-A SSH -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
На даний момент ви повинні запитати себе, як з'єднувальна IP-адреса закінчується в ssh_loggedon
підмножинах. Тож читайте далі ...
Бонус: додавання IP-адреси, з якою ви входите під час входу в SSH
Якщо ви експериментували з sshrc
друзями та з друзями, ви дізналися про його недоліки. Але PAM приходить на допомогу. Модуль з назвою pam_exec.so
дозволяє нам викликати скрипт під час входу в SSH в точці, коли ми знаємо, що користувач прийнятий в систему.
В /etc/pam.d/sshd
нижче pam_env
і pam_selinux
записи додайте наступний рядок:
session optional pam_exec.so stdout /path/to/your/script
і переконайтеся, що ваша версія сценарію ( /path/to/your/script
вище) існує та працює у виконанні.
PAM використовує змінні середовища для повідомлення про те, що відбувається, тому ви можете використовувати простий сценарій, як цей:
#!/bin/bash
# When called via pam_exec.so ...
SETNAME=ssh_loggedon
if [[ "$PAM_TYPE" == "open_session" ]] && [[ -n "$PAM_RHOST" ]]; then
[[ "x$PAM_RHOST" != "x${PAM_RHOST//:/}" ]] && SETNAME="${SETNAME}6" || SETNAME="${SETNAME}4"
ipset -exist add $SETNAME "$PAM_RHOST"
fi
На жаль, ipset
утиліта, схоже, не має вбудованих смартфонів netfilter. Отже, нам потрібно розрізняти IPv4 та IPv6 набір IP при додаванні нашого запису. В іншому випадку ipset
припустимо, що ми хочемо додати ще один набір до набору, а не IP. І звичайно, навряд чи знайдеться набір, названий на честь IP-адреси :)
Тому ми перевіряємо :
IP-адресу та додаємо 6
до встановленого імені в такому випадку та в 4
іншому випадку.
Кінець.