Набори 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іншому випадку.
Кінець.