Debian 12: базовая настройка и hardening для production#
Поставил чистый Debian 12 и думаешь сразу накатывать приложения? Не торопись. Час на базовую настройку сейчас - это недели сэкономленного времени на разгребание последствий взлома или падения.
Статья покрывает всё что нужно для production-сервера без контейнеров. Пройдёшь по шагам и получишь сервер с закрытым SSH, firewall, защитой от брутфорса, ограничением ресурсов ядра и автоматическими обновлениями безопасности. Docker - в следующей статье серии.
Исходные данные#
- Чистый Debian 12 (Bookworm)
- Root доступ по SSH
- Статический IP
Проверь версию:
lsb_release -aОжидаемый вывод:
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookwormВсе команды выполняются от root если не указано иное.
Обновление системы#
apt update
apt upgrade -y
apt dist-upgrade -y
apt autoremove -yПроверь нужна ли перезагрузка:
[ -f /var/run/reboot-required ] && echo "Reboot needed" || echo "No reboot needed"Если нужна - перезагрузись:
rebootБазовые утилиты#
apt install -y \
sudo curl wget git vim htop iotop iftop \
net-tools dnsutils tcpdump screen tmux rsync \
util-linux unzip ca-certificates gnupg2 lsb-release \
smartmontoolsНастройка системы#
Hostname#
hostnamectl set-hostname srv01.example.comДобавь в /etc/hosts:
nano /etc/hosts127.0.0.1 localhost
203.0.113.10 srv01.example.com srv01
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allroutersЗамени 203.0.113.10 на реальный IP сервера, srv01.example.com - на своё имя.
Проверь FQDN:
hostname -f
# srv01.example.comTimezone#
timedatectl set-timezone Europe/MoscowПроверь:
timedatectlЛокаль#
apt install -y locales
dpkg-reconfigure localesВыбери en_US.UTF-8 как основную. Добавь ru_RU.UTF-8 если нужна кириллица в системных сообщениях.
Проверь:
locale
# LANG=en_US.UTF-8Пользователь и SSH#
Создание пользователя#
Root сессия - одна ошибка без возможности откатиться. sudo даёт контроль и лог действий. Создай непривилегированного пользователя:
adduser adminДобавь в sudo:
usermod -aG sudo adminПроверь:
groups admin
# admin : admin sudoSSH ключи#
На своей рабочей машине сгенерируй ключ:
# Linux/macOS
ssh-keygen -t ed25519 -C "admin@srv01"# Windows
ssh-keygen -t ed25519Скопируй публичный ключ на сервер:
# Linux/macOS
ssh-copy-id admin@203.0.113.10# Windows
type $env:userprofile\.ssh\id_ed25519.pub | ssh admin@203.0.113.10 "mkdir -m 700 -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Проверь вход по ключу - не должен спрашивать пароль:
ssh admin@203.0.113.10SSH hardening#
Залогинься под admin и открой конфиг:
sudo nano /etc/ssh/sshd_configЗамени содержимое (или найди и измени каждый параметр):
# Нестандартный порт - отсекает 90% ботов
Port 2222
# Только ключи
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Запретить root логин
PermitRootLogin no
# Только ключи, без паролей
PasswordAuthentication no
PermitEmptyPasswords no
KbdInteractiveAuthentication no
# Ограничения сессий
MaxAuthTries 3
MaxSessions 3
LoginGraceTime 30
# Таймауты - отключать неактивные сессии
ClientAliveInterval 300
ClientAliveCountMax 2
# Отключить лишнее
X11Forwarding no
PrintMotd no
UsePAM yes
AcceptEnv LANG LC_*
# Только IPv4 (убери если используешь IPv6)
AddressFamily inetКРИТИЧНО: Перед перезапуском SSH открой вторую сессию и не закрывай её - страховка если что-то пойдёт не так.
Проверь конфиг на ошибки:
sudo sshd -tНет вывода - нет ошибок. Перезапусти:
sudo systemctl restart sshdВ новом терминале проверь подключение на новом порту:
ssh -p 2222 admin@203.0.113.10Если работает - старые сессии можно закрывать.
SSH banner (опционально)#
Создай предупреждение при входе:
sudo nano /etc/ssh/banner###############################################################################
# Доступ только для авторизованных пользователей. #
# Все действия логируются. #
# Несанкционированный доступ преследуется по закону. #
###############################################################################Добавь в sshd_config:
Banner /etc/ssh/bannerПерезапусти SSH:
sudo systemctl restart sshdFirewall (UFW)#
Важно: сначала настраиваем правила, потом включаем. Иначе заблокируешь себя.
sudo apt install -y ufwПолитика по умолчанию:
# Блокировать все входящие
sudo ufw default deny incoming
# Разрешить все исходящие
sudo ufw default allow outgoingРазрешаем SSH на новом порту:
# Замени 2222 на свой порт если менял
sudo ufw allow 2222/tcp comment 'SSH'Включаем firewall:
sudo ufw enableПроверь статус:
sudo ufw status verboseОжидаемый вывод:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
2222/tcp ALLOW IN Anywhere # SSH
2222/tcp (v6) ALLOW IN Anywhere (v6) # SSHRate limiting на SSH#
# Блокировать IP после 6 подключений за 30 секунд
sudo ufw limit 2222/tcp comment 'SSH rate limit'Открытие дополнительных портов#
Когда будешь ставить сервисы - открывай только нужные порты:
# HTTP/HTTPS
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# PostgreSQL только с конкретного IP
sudo ufw allow from 192.168.1.10 to any port 5432 comment 'PostgreSQL'Проверь что слушает на хосте:
ss -tulnpВсё что слушает - должно быть либо закрыто firewall, либо намеренно открыто.
Fail2ban#
sudo apt install -y fail2banСоздай локальный конфиг - не редактируй jail.conf, он перезаписывается при обновлении:
sudo nano /etc/fail2ban/jail.local[DEFAULT]
# Бан на 1 час
bantime = 3600
# За 10 минут
findtime = 600
# После 3 неудачных попыток
maxretry = 3
# Не банить свои IP
ignoreip = 127.0.0.1/8 ::1
# Явно указываем backend - важно для совместимости в серии Ubuntu/Rocky
backend = systemd
[sshd]
enabled = true
port = 2222
filter = sshd
maxretry = 3
bantime = 3600Замени 2222 на свой SSH порт.
Запусти:
sudo systemctl enable fail2ban
sudo systemctl start fail2banПроверь статус:
sudo fail2ban-client statusStatus
|- Number of jail: 1
`- Jail list: sshdПроверь статус конкретного jail:
sudo fail2ban-client status sshdSwap#
Swap в виде файла гибче раздела: можно увеличить или уменьшить без переразбивки диска.
Создание swap файла#
Проверь текущий swap:
free -h
sudo swapon --showСоздай файл (пример - 2GB):
# Создать файл нужного размера
sudo fallocate -l 2G /swapfileЕсли fallocate не поддерживается файловой системой:
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progressУстанови права - swap файл не должен читаться другими пользователями:
sudo chmod 600 /swapfileИнициализируй и активируй:
# Разметить как swap
sudo mkswap /swapfile
# Включить
sudo swapon /swapfileПроверь:
free -h
sudo swapon --showДобавь в /etc/fstab для автомонтирования после перезагрузки:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabНастройка swappiness#
swappiness определяет агрессивность использования swap. Для production снижаем - swap только когда RAM заполнена на 90%+:
# Проверить текущее значение (по умолчанию 60)
cat /proc/sys/vm/swappinessУстанови постоянно:
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.d/99-swap.conf
sudo sysctl -p /etc/sysctl.d/99-swap.confИзменение размера swap (когда потребуется)#
# Отключить текущий swap
sudo swapoff /swapfile
# Изменить размер (пример - увеличить до 4GB)
sudo fallocate -l 4G /swapfile
# Переинициализировать
sudo mkswap /swapfile
# Включить
sudo swapon /swapfileПроверь результат:
free -hЗапись в /etc/fstab менять не нужно - путь тот же.
Удаление swap (если потребуется)#
sudo swapoff /swapfile
sudo rm /swapfileУдали строку из /etc/fstab:
sudo sed -i '/\/swapfile/d' /etc/fstabSysctl - защита ядра#
Создай отдельный файл - чище чем писать в /etc/sysctl.conf:
sudo nano /etc/sysctl.d/99-hardening.conf# Защита от SYN flood
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Игнорировать ICMP redirects (защита от MITM)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Не отправлять ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Игнорировать source routed packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Защита от IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Логировать подозрительные пакеты
net.ipv4.conf.all.log_martians = 1
# Защита от time-wait assassination
net.ipv4.tcp_rfc1337 = 1
# Отключить IPv6 если не используешь
# net.ipv6.conf.all.disable_ipv6 = 1
# net.ipv6.conf.default.disable_ipv6 = 1Примени:
sudo sysctl -p /etc/sysctl.d/99-hardening.confПроверь что применилось:
sudo sysctl net.ipv4.tcp_syncookies
# net.ipv4.tcp_syncookies = 1Автообновления безопасности#
sudo apt install -y unattended-upgrades apt-listchangessudo nano /etc/apt/apt.conf.d/50unattended-upgradesРаскомментируй/измени:
Unattended-Upgrade::Mail "root";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";Automatic-Reboot "false" - важно для production. Перезагрузку после обновления ядра делать вручную в удобное время.
Включи:
sudo dpkg-reconfigure -plow unattended-upgradesВыбери Yes.
Настрой расписание:
sudo nano /etc/apt/apt.conf.d/10periodicAPT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";Проверь что сервис работает:
sudo systemctl status unattended-upgradesОтключение ненужных сервисов (при наличии)#
Посмотри что запущено:
systemctl list-units --type=service --state=runningСтандартные кандидаты на отключение - проверь что не используешь перед отключением:
# Bluetooth - на сервере не нужен
sudo systemctl disable --now bluetooth.service
# avahi-daemon - mDNS/DNS-SD, нужен редко
sudo systemctl disable --now avahi-daemon.service
# ModemManager - управление модемами
sudo systemctl disable --now ModemManager.serviceПроверь что слушает на сетевых портах:
ss -tulnpВсё незнакомое - выясни что это и реши нужно ли.
Логи и ротация#
Ограничение journald#
sudo nano /etc/systemd/journald.confРаскомментируй/измени:
SystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=1monthПерезапусти:
sudo systemctl restart systemd-journaldПроверь размер:
sudo journalctl --disk-usageНа чистом Debian 12 rsyslog не установлен - логи пишутся только в journald. Настройки выше достаточно. Если устанавливаешь rsyslog отдельно - создай /etc/logrotate.d/rsyslog с правилами ротации вручную.
Мониторинг диска#
На VPS имя устройства зависит от гипервизора - не всегда /dev/sda. Определи актуальное:
# Найти дисковые устройства
lsblk -d -o NAME,TYPE | grep disk
# NAME TYPE
# vda disk
#
# Результат зависит от гипервизора и провайдера:
# /dev/sda - KVM у некоторых провайдеров, Xen
# /dev/vda - KVM (наиболее распространённый вариант)
# /dev/xvda - XenПроверь состояние:
# Замени vda на своё устройство
sudo smartctl -H /dev/vdaОжидаемый вывод:
# ATA диски:
# SMART overall-health self-assessment test result: PASSED
#
# SCSI/NVMe диски (типично для VPS):
# SMART Health Status: OK
#
# Оба варианта означают одно: диск здоровЕсли FAILED - диск умирает. Срочно бэкап и замена.
Детали SMART:
sudo smartctl -a /dev/vdaОбращай внимание на Reallocated_Sector_Ct, Pending_Sector_Count, Offline_Uncorrectable - если ненулевые, диск проблемный.
Примечание:
На виртуальных дисках (VMware, KVM) SMART может быть недоступен: SMART support is: Unavailable - device lacks SMART capability.
В этом случае smartctl полезен только для физических серверов.
На VPS ориентируйся на мониторинг от провайдера.
Бэкап конфигов#
Конфиги на диске - не бэкап. Скрипт архивирует /etc/ и SSH ключи.
nano ~/backup-configs.sh#!/bin/bash
BACKUP_DIR="$HOME/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/config_backup_$DATE.tar.gz"
mkdir -p "$BACKUP_DIR"
# Сохранить список установленных пакетов
dpkg --get-selections > "$HOME/installed-packages.txt"
# Архивировать конфиги и SSH ключи
sudo tar -czf "$BACKUP_FILE" \
/etc/ \
/root/.ssh/ \
/home/*/.ssh/ \
"$HOME/installed-packages.txt" \
2>/dev/null
echo "Backup: $BACKUP_FILE"
ls -lh "$BACKUP_FILE"
# Удалить бэкапы старше 30 дней
find "$BACKUP_DIR" -name "config_backup_*.tar.gz" -mtime +30 -deleteПрава и первый запуск:
chmod +x ~/backup-configs.sh
~/backup-configs.shОжидаемый вывод:
Backup: /home/admin/backups/config_backup_20260522_030000.tar.gz
-rw-r--r-- 1 root root 1.2M May 22 03:00 /home/admin/backups/config_backup_20260522_030000.tar.gzАвтоматизация через cron (каждое воскресенье в 3:00):
crontab -e0 3 * * 0 /home/admin/backup-configs.sh >> /var/log/backup-configs.log 2>&1Восстановление#
Восстановить конфиги:
sudo tar -xzf ~/backups/config_backup_ДАТА.tar.gz -C /Восстановить список пакетов:
sudo dpkg --set-selections < ~/installed-packages.txt
sudo apt-get dselect-upgradeФинальная проверка#
# Открытые порты
ss -tulnp
# Статус firewall
sudo ufw status verbose
# Статус fail2ban
sudo fail2ban-client status sshd
# Состояние диска
sudo smartctl -H /dev/vda
# Swap
free -hЧто дальше#
Сервер готов к установке приложений. База одинакова для любой нагрузки - веб, база данных, почта.
Если следующий шаг - контейнеры, читай продолжение серии: Debian 12: настройка Docker-хоста для production.
Стек этой статьи: Debian 12 (Bookworm) · UFW · Fail2ban · OpenSSH · unattended-upgrades · systemd · smartmontools








