Hardening ядра Linux: sysctl параметры безопасности — АйТи Фреш
Москва, Щёлковское шоссе, д. 92, корп. 7 · Пн–Пт 9:00–19:00 · +7 903 729-62-41

Hardening ядра Linux: sysctl параметры безопасности

Hardening ядра Linux: sysctl параметры безопасности

Зачем нужен kernel hardening через sysctl

Установка Ubuntu или Debian «из коробки» — это компромисс между безопасностью и совместимостью. Часть параметров ядра настроена консервативно для максимальной совместимости, а не для максимальной защиты. Linux kernel hardening через sysctl — это изменение этих параметров в сторону безопасности.

Хорошая новость: большинство параметров применяются без перезагрузки и не влияют на производительность заметным образом. На всех серверах клиентов мы раскатываем hardening-конфиг через Ansible сразу после первичной настройки — до открытия каких-либо портов наружу.

Три группы параметров:

  • Защита памяти — ASLR, запрет маппинга нулевой страницы, ограничение ptrace
  • Защита сети — SYN flood, IP spoofing, ICMP redirect, source routing
  • Ограничение информации — dmesg, /proc, kernel pointers

Параметры безопасности памяти

Защита памяти затрудняет эксплуатацию уязвимостей типа buffer overflow и privilege escalation.

По умолчанию kernel.kptr_restrict равен 0: адреса ядра при выводе хешируются. При 1 указатели, выводимые через %pK, заменяются нулями для пользователей без CAP_SYSLOG, при 2 — нулями для всех, включая root. Значение kernel.dmesg_restrict по умолчанию зависит от опции сборки CONFIG_SECURITY_DMESG_RESTRICT, поэтому на разных дистрибутивах оно разное — я задаю его явно, а не полагаюсь на сборку.

ASLR и защита адресного пространства

Параметры рандомизации памяти:

# /etc/sysctl.d/10-hardening.conf

# ASLR — рандомизация адресного пространства
# 0=выключен, 1=частичный, 2=максимальный (рекомендуется)
kernel.randomize_va_space = 2

# Запрет маппинга нулевой страницы
# Предотвращает NULL pointer dereference exploits
vm.mmap_min_addr = 65536

# Ограничение ptrace — процесс не может отлаживать другой без CAP_SYS_PTRACE
# 1=только дочерние процессы, 2=только с CAP_SYS_PTRACE, 3=полный запрет
kernel.yama.ptrace_scope = 1

# Скрыть указатели ядра из /proc
# Предотвращает утечку KASLR-адресов непривилегированным пользователям
kernel.kptr_restrict = 2

# Ограничить доступ к dmesg
kernel.dmesg_restrict = 1

# Применяем сразу (без перезагрузки)
sysctl -p /etc/sysctl.d/10-hardening.conf
Защита сетевого стека
Настройки сетевой безопасности в ядре Linux

Защита сетевого стека

Сетевые параметры — самая большая группа. Защищают от IP spoofing, SYN flood, ICMP redirect и других сетевых атак.

Основные сетевые параметры

Полный набор сетевого hardening:

# Защита от IP spoofing (reverse path filtering)
# 0=выключено, 1=strict, 2=loose
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Запрет принятия ICMP redirect
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0

# Запрет source routing (атаки через маршрутизацию)
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Запрет пересылки пакетов (если не роутер)
net.ipv4.ip_forward = 0
# Для роутера/шлюза: net.ipv4.ip_forward = 1

# SYN cookies — защита от SYN flood
net.ipv4.tcp_syncookies = 1

# Игнорировать broadcast ICMP (Smurf attack)
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Защита от ICMP bogus error responses
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Логирование martian packets (пакеты с невозможными адресами)
net.ipv4.conf.all.log_martians = 1

# TCP параметры (производительность + безопасность)
net.ipv4.tcp_rfc1337 = 1       # Защита от TIME_WAIT атак
net.ipv4.tcp_timestamps = 0    # Убираем временные метки (утечка uptime)

# IPv6 аналоги
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_source_route = 0

Параметры /proc и файловой системы

Защита информации о системе:

# Запрет жёстких ссылок на файлы других пользователей
fs.protected_hardlinks = 1

# Запрет симлинков на файлы других пользователей в sticky-каталогах
fs.protected_symlinks = 1

# Ограничение доступа к /proc
# Требует ядро 5.8+
# kernel.unprivileged_userns_clone = 0  # для Ubuntu может сломать snap

# Лимит количества открытых файлов (защита от fork bomb)
fs.file-max = 2097152

# Ограничение на pipe capacity
fs.pipe-max-size = 1048576

Параметр kernel.yama.ptrace_scope = 1 — один из самых важных для production. Он ломает некоторые отладочные инструменты типа gdb, но надёжно защищает от атак типа «залезть в память другого процесса».

Готовый файл конфигурации

Собираем всё в один файл для деплоя через Ansible или вручную:

Префикс 99 я выбираю, чтобы файл шёл после вендорских и перекрывал их. Но при ручном sysctl --system последним всё равно читается /etc/sysctl.conf, поэтому перед раскаткой убираю из него дублирующие ключи.

Полный hardening.conf

Файл /etc/sysctl.d/99-hardening.conf для production-серверов:

# Kernel hardening — АйТи Фреш
# Версия: 2026-03

# === ПАМЯТЬ ===
kernel.randomize_va_space = 2
vm.mmap_min_addr = 65536
kernel.yama.ptrace_scope = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1

# === СЕТЬ: IPv4 ===
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.ip_forward = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_timestamps = 0

# === СЕТЬ: IPv6 ===
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_redirects = 0

# === ФАЙЛОВАЯ СИСТЕМА ===
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.suid_dumpable = 0

# Применить
# sysctl -p /etc/sysctl.d/99-hardening.conf

После применения проверяем через lynis: lynis audit system --quick. Большинство hardening-проверок должны быть зелёными.

Порядок загрузки: почему параметр «не применился»

Самая частая жалоба после раскатки hardening-конфига — «в файле 1, а в ядре 0». Почти всегда причина в одном из трёх мест, и я проверяю их именно в таком порядке.

Первое — очередность файлов. При загрузке параметры выставляет systemd-sysctl.service, он читает *.conf из /etc/sysctl.d/, /run/sysctl.d/, /usr/local/lib/sysctl.d/ и /usr/lib/sysctl.d/. Файл в /etc/ с тем же именем целиком заменяет вендорский. Все файлы сортируются по имени лексикографически, независимо от каталога, и при конфликте побеждает значение из файла, который идёт позже. Документация systemd советует для своих файлов в /etc/ префиксы 60–90, для пакетов в /usr/ — 10–40. Ручная команда sysctl --system проходит те же каталоги, но /etc/sysctl.conf читает последним: старое значение, забытое в нём, перекроет любой файл из sysctl.d.

# где ещё задан параметр
grep -rn "rp_filter" /etc/sysctl.conf /etc/sysctl.d/ /run/sysctl.d/ \
    /usr/local/lib/sysctl.d/ /usr/lib/sysctl.d/ 2>/dev/null

# перечитать все файлы в том же порядке, что при загрузке
sysctl --system

# что реально стоит в ядре
sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.eth0.rp_filter

Второе — модули ядра. Часть ключей появляется только после загрузки модуля: например, net.bridge.* — после br_netfilter. Если модуль грузится позже systemd-sysctl, ключа на момент применения просто нет, а запись в отсутствующий ключ логируется на уровне debug и сервис не падает. Справка sysctl.d(5) предлагает два решения: udev-правило на появление модуля или файл в /etc/modules-load.d/, чтобы модуль загружался раньше. Параметры интерфейсов (net.ipv4.conf.*, net.ipv6.conf.*) systemd применяет отдельно для каждого интерфейса по мере его появления.

Третье — путаница all, default и имени интерфейса. Ветка default задаёт значения для интерфейсов, которые будут созданы после, а на уже поднятые не влияет. Правила объединения у каждого ключа свои: для rp_filter ядро берёт максимум из conf/all и conf/<интерфейс>; log_martians включён, если единица стоит хотя бы в одном из них; accept_redirects на хосте без форвардинга остаётся включённым, если он TRUE хотя бы в одном месте. Поэтому редиректы я выключаю и в all, и в default, а проверяю на конкретном интерфейсе, а не на all.

Отдельная ловушка — Docker. Он при старте сам включает net.ipv4.ip_forward и net.ipv6.conf.all.forwarding, если они выключены, и ставит политику DROP для пересылаемых пакетов. На хосте с контейнерами строка net.ipv4.ip_forward = 0 из общего конфига бессмысленна: я убираю её из шаблона для таких серверов, чтобы проверка не показывала ложное расхождение.

Что добавить сверх базового набора

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

  • kernel.unprivileged_bpf_disabled — значение 1 запрещает вызов bpf() без CAP_SYS_ADMIN или CAP_BPF, и снять запрет на работающем ядре уже нельзя. Значение 2 запрещает то же самое, но администратор может позже записать 0 или 1. Если ядро собрано с BPF_UNPRIV_DEFAULT_OFF, по умолчанию там уже 2.
  • net.core.bpf_jit_harden = 2 — усиление JIT-компилятора BPF для всех пользователей, защита от JIT spraying. По умолчанию 0; цена — производительность BPF-программ.
  • kernel.perf_event_paranoid — по умолчанию 2: без CAP_PERFMON запрещено профилирование ядра. Ниже 2 я не опускаю без конкретной задачи мониторинга.
  • kernel.kexec_load_disabled = 1 — отключает вызовы kexec_load и kexec_file_load, вернуть 0 нельзя. Если на сервере работает kdump, образ аварийного ядра должен загрузиться до установки этого ключа.
  • fs.protected_fifos = 2 и fs.protected_regular = 2 — запрет O_CREAT на чужие FIFO и обычные файлы в sticky-каталогах, доступных на запись всем или группе (типичный пример — /tmp).
  • kernel.yama.ptrace_scope = 3 — полный запрет ptrace, включая PTRACE_TRACEME; после установки значение не меняется. На серверах, где админы пользуются strace -p и gdb --pid, оставляю 1 или 2: при 2 подключаться может только процесс с CAP_SYS_PTRACE.
# /etc/sysctl.d/99-hardening-extra.conf
kernel.unprivileged_bpf_disabled = 2
net.core.bpf_jit_harden = 2
kernel.perf_event_paranoid = 2
fs.protected_fifos = 2
fs.protected_regular = 2
# необратимые до перезагрузки — только после теста:
# kernel.kexec_load_disabled = 1
# kernel.yama.ptrace_scope = 3

Про fs.suid_dumpable: в базовом файле стоит 0, и это значение по умолчанию. Режим 1 (debug) документация ядра прямо называет небезопасным — обычный пользователь получает дампы памяти привилегированных процессов. Поэтому после любой отладки проверяю его отдельно.

Чек-лист проверки после раскатки

После Ansible-прогона я не верю выводу «ok», а сверяю ядро с файлом. Скрипт ниже проходит конфиг построчно и печатает только расхождения:

while IFS='=' read -r k v; do
  k=$(echo "$k" | xargs); v=$(echo "$v" | xargs)
  case "$k" in ''|\#*) continue;; esac
  cur=$(sysctl -n "$k" 2>/dev/null)
  [ "$cur" = "$v" ] || echo "РАСХОЖДЕНИЕ: $k ждём '$v', в ядре '${cur:-нет ключа}'"
done < /etc/sysctl.d/99-hardening.conf
  1. sysctl --system отрабатывает без ошибок, кроме ключей, которых нет на этом ядре.
  2. Скрипт сверки не выводит ни одной строки.
  3. На шлюзах с асимметричной маршрутизацией, нескольких аплинках или VPN вместо rp_filter = 1 стоит 2 (loose): так рекомендует документация ядра со ссылкой на RFC 3704, иначе легитимные пакеты отбрасываются молча.
  4. Если в логах появились предупреждения о SYN flood, выясняю, не легитимная ли это нагрузка. SYN cookies — запасной механизм; при реальном потоке соединений документация советует настраивать tcp_max_syn_backlog, tcp_synack_retries и tcp_abort_on_overflow.
  5. Тестовый сервер перезагружен, сверка повторена — так ловлю ключи, которые при загрузке не применились из-за поздних модулей.
  6. Агенты мониторинга и профилировщики, которые подключаются к чужим процессам через ptrace или читают счётчики perf, работают после изменения ptrace_scope и perf_event_paranoid.
Часто задаваемые вопросы
Чек-лист для проверки параметров безопасности

Часто задаваемые вопросы

ASLR (Address Space Layout Randomization) — рандомизация адресного пространства. Затрудняет эксплуатацию уязвимостей buffer overflow. kernel.randomize_va_space=2 — максимальная рандомизация, стандарт для production-серверов.

Большинство параметров безопасности не влияют на производительность. Исключения: tcp_syncookies при высокой нагрузке SYN, vm.swappiness при ограниченной памяти. Сетевые параметры (rp_filter, accept_redirects) работают без заметного overhead.

sysctl -a показывает все параметры. sysctl kernel.randomize_va_space — конкретный параметр. Для сравнения с hardened baseline используйте lynis или oscap с профилем CIS.

При значении 1 — только root может читать dmesg. dmesg содержит адреса памяти ядра, которые помогают при разработке эксплойта. В Ubuntu 22.04 уже стоит 1 по умолчанию.

Да, sysctl -w параметр=значение применяет немедленно. sysctl -p /etc/sysctl.d/hardening.conf перечитывает файл. Перезагрузка не нужна.

#linux kernel hardening #sysctl безопасность #ASLR linux

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

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

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

Подпишитесь на рассылку ITfresh

Каждую неделю мы выпускаем практические гайды для руководителей IT и системных администраторов. Это не просто теория! Здесь вы найдёте всё: безопасность, 1С, миграции, резервные копии и проверенные лайфхаки из наших реальных проектов.

Письмо придёт в течение минутыНе нашли его во «Входящих» — загляните в папку «Спам» или «Промоакции» и нажмите «Не спам». Так все следующие выпуски будут приходить прямо в основную почту.
Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.