Как Match переопределяет настройки sshd_config
Продолжаем тему с приоритетом чтения конфигов. С SSH мы разобрались в этом посте, давай посмотрим другие неочевидные вещи в других реализациях.
читать первым в телеграм читать первым в макс
Файл 00-security.conf защищает только от последующих глобальных объявлений. Абсолютной гарантии он не даёт. Например:
Include /etc/ssh/sshd_config.d/*.conf
Match Address 192.168.1.0/24
PermitRootLogin yes
Match способен переопределить глобальную настройку.
Если в /etc/ssh/sshd_config.d/00-security.conf будет PermitRootLogin no, для обычных подключений результат будет no. Но для подключения из сети 192.168.1.0/24 сработает блок Match, и значение станет yes.
Это не нарушение правила «первое значение побеждает». Match — отдельный контекст. Подходящие параметры из него переопределяют глобальные.
Match — отдельный контекст.При этом, когда подходят сразу несколько блоков Match, для каждого параметра побеждает его первое объявление среди совпавших блоков. PermitRootLogin разрешено использовать внутри Match.
То есть условие из предыдущего поста можно сформулировать точнее — 00-security.conf гарантирует глобальное значение PermitRootLogin no, пока его не переопределяет подходящий блок Match или параметр запуска самого sshd.
Команда sudo sshd -T | grep permitrootlogin показывает глобальный результат, но сама по себе не проверяет все возможные Match. Для проверки конкретного подключения есть -C:
sudo sshd -T \
-C user=root,addr=192.168.1.50,host=bashdays.ru | grep permitrootlogin
А затем можно проверить внешний адрес:
sudo sshd -T \
-C user=root,addr=203.0.113.50,host=bashdays.ru | grep permitrootlogin
И получить разные результаты.
-C позволяет передать пользователя, адрес клиента, hostname, локальный адрес и порт, после чего sshd применит подходящие блоки Match.
sshd -t при этом проверяет только синтаксис и ключи, а не доказывает, что итоговое значение нужного параметра безопасно.
Еще настройку может переопределить командная строка. Даже идеальные конфиги не помогут, если демон запущен так:
/usr/sbin/sshd -o PermitRootLogin=yes
Параметры командной строки имеют приоритет над конфигурационным файлом. Поэтому при особо параноидальной проверке стоит посмотреть в сторону:
ps auxww | grep '[s]shd'
systemctl cat ssh.service
systemctl cat sshd.service
Тут особенно интересны ExecStart и systemd-override файлы.
Не все параметры подчиняются простому first wins, в документации явно прописано: «если не указано обратное».
Например, некоторые параметры накапливаются:
AllowUsers alice
AllowUsers bob
В результате разрешены оба пользователя. Каждое объявление AllowUsers добавляет элементы в список.
Аналогично несколько объявлений разрешены у ряда других списочных директив, например AcceptEnv. Поэтому правило «всё после первого значения игнорируется» нельзя механически применять ко всем параметрам.
Дела дела…
А еще grep легко наебать. Такой поиск будет ненадёжен:
grep -R "PermitRootLogin yes" /etc/ssh
Он может пропустить:
permitrootlogin yes
PermitRootLogin yes
PermitRootLogin=yes
Или найти закомментированную строку:
# PermitRootLogin yes
Ключевые слова в конфигурации ssh нечувствительны к регистру. Поэтому источником истины должен быть не grep, а вывод самого парсера sshd -T с проверкой нужных сценариев через -C.
Где используется похожий подход
Клиентский ~/.ssh/config
Это практически полный аналог, первое найденное значение тоже побеждает. Поэтому частные настройки размещают выше общих:
Host production
User deploy
Port 2222
Host *
User ubuntu
Port 22
Если поставить Host * первым, его User и Port уже займут значения, а соответствующие параметры из Host production не сработают. Кроме того, порядок источников такой: командная строка, пользовательский конфиг, системный конфиг.
В systemd-networkd. Для файлов .network и .link применяется первый подходящий файл в алфавитном порядке, а все последующие совпавшие файлы игнорируются:
10-server.network
99-default.network
Поэтому здесь ранний цифровой префикс тоже позволяет перехватить устройство раньше стандартной конфигурации.
В nginx подход используется частично. Регулярные location проверяются по порядку, и поиск прекращается на первом совпавшем регулярном выражении:
location ~ ^/api/ {
# ...
}
location ~ ^/api/admin/ {
# ...
}
Второй блок может никогда не выполниться, потому что первый тоже подходит. Но строковые prefix-location выбираются иначе — по самому длинному совпавшему префиксу, независимо от порядка.
Фаерволы. В nftables правила выполняются последовательно, а терминальные решения вроде drop прекращают дальнейшую обработку. Это похожая логика, но с собственными нюансами цепочек и хуков. В частности, drop окончателен для всего ruleset, а accept завершает текущую базовую цепочку, но пакет ещё может попасть в другую цепочку того же hook.
А вот systemd drop-in работает наоборот:
10-defaults.conf
90-security.conf
Обрабатываются лексикографически, но более поздний drop-in обычно переопределяет более раннее скалярное значение.
Поэтому в systemd привычно писать 90-override.conf, а в sshd_config.d ради first wins логично использовать раннее имя вроде 00-security.conf. Одинаковая нумерация файлов скрывает противоположную семантику.
