Files
oakazanin/content/posts/debian-12-base-hardening/index.md
T

838 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Debian 12: базовая настройка и hardening для production"
date: 2026-05-22
draft: false
description: "Пошаговая настройка чистого Debian 12 (Bookworm) для production: SSH, firewall, fail2ban, swap, sysctl, автообновления, логи, бэкап конфигов."
tags: ["debian 12", "security", "hardening", "ssh", "ufw", "fail2ban", "sysctl", "production", "linux"]
categories: ["Системное администрирование"]
series: ["Linux Hardening"]
series_order: 1
---
# Debian 12: базовая настройка и hardening для production
Поставил чистый Debian 12 и думаешь сразу накатывать приложения? Не торопись. Час на базовую настройку сейчас - это недели сэкономленного времени на разгребание последствий взлома или падения.
Статья покрывает всё что нужно для production-сервера без контейнеров. Пройдёшь по шагам и получишь сервер с закрытым SSH, firewall, защитой от брутфорса, ограничением ресурсов ядра и автоматическими обновлениями безопасности. Docker - в следующей статье серии.
## Исходные данные
- Чистый Debian 12 (Bookworm)
- Root доступ по SSH
- Статический IP
Проверь версию:
```bash
lsb_release -a
```
Ожидаемый вывод:
```
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm
```
Все команды выполняются от `root` если не указано иное.
## Обновление системы
```bash
apt update
apt upgrade -y
apt dist-upgrade -y
apt autoremove -y
```
Проверь нужна ли перезагрузка:
```bash
[ -f /var/run/reboot-required ] && echo "Reboot needed" || echo "No reboot needed"
```
Если нужна - перезагрузись:
```bash
reboot
```
## Базовые утилиты
```bash
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
```bash
hostnamectl set-hostname srv01.example.com
```
Добавь в `/etc/hosts`:
```bash
nano /etc/hosts
```
```
127.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:
```bash
hostname -f
# srv01.example.com
```
### Timezone
```bash
timedatectl set-timezone Europe/Moscow
```
Проверь:
```bash
timedatectl
```
### Локаль
```bash
apt install -y locales
dpkg-reconfigure locales
```
Выбери `en_US.UTF-8` как основную. Добавь `ru_RU.UTF-8` если нужна кириллица в системных сообщениях.
Проверь:
```bash
locale
# LANG=en_US.UTF-8
```
## Пользователь и SSH
### Создание пользователя
Root сессия - одна ошибка без возможности откатиться. `sudo` даёт контроль и лог действий. Создай непривилегированного пользователя:
```bash
adduser admin
```
Добавь в sudo:
```bash
usermod -aG sudo admin
```
Проверь:
```bash
groups admin
# admin : admin sudo
```
### SSH ключи
На своей рабочей машине сгенерируй ключ:
```bash
# Linux/macOS
ssh-keygen -t ed25519 -C "admin@srv01"
```
```powershell
# Windows
ssh-keygen -t ed25519
```
Скопируй публичный ключ на сервер:
```bash
# Linux/macOS
ssh-copy-id admin@203.0.113.10
```
```powershell
# 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"
```
Проверь вход по ключу - не должен спрашивать пароль:
```bash
ssh admin@203.0.113.10
```
### SSH hardening
Залогинься под `admin` и открой конфиг:
```bash
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 открой вторую сессию и не закрывай её - страховка если что-то пойдёт не так.
Проверь конфиг на ошибки:
```bash
sudo sshd -t
```
Нет вывода - нет ошибок. Перезапусти:
```bash
sudo systemctl restart sshd
```
В новом терминале проверь подключение на новом порту:
```bash
ssh -p 2222 admin@203.0.113.10
```
Если работает - старые сессии можно закрывать.
### SSH banner (опционально)
Создай предупреждение при входе:
```bash
sudo nano /etc/ssh/banner
```
```
###############################################################################
# Доступ только для авторизованных пользователей. #
# Все действия логируются. #
# Несанкционированный доступ преследуется по закону. #
###############################################################################
```
Добавь в `sshd_config`:
```
Banner /etc/ssh/banner
```
Перезапусти SSH:
```bash
sudo systemctl restart sshd
```
## Firewall (UFW)
**Важно:** сначала настраиваем правила, потом включаем. Иначе заблокируешь себя.
```bash
sudo apt install -y ufw
```
Политика по умолчанию:
```bash
# Блокировать все входящие
sudo ufw default deny incoming
# Разрешить все исходящие
sudo ufw default allow outgoing
```
Разрешаем SSH на новом порту:
```bash
# Замени 2222 на свой порт если менял
sudo ufw allow 2222/tcp comment 'SSH'
```
Включаем firewall:
```bash
sudo ufw enable
```
Проверь статус:
```bash
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) # SSH
```
### Rate limiting на SSH
```bash
# Блокировать IP после 6 подключений за 30 секунд
sudo ufw limit 2222/tcp comment 'SSH rate limit'
```
### Открытие дополнительных портов
Когда будешь ставить сервисы - открывай только нужные порты:
```bash
# 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'
```
Проверь что слушает на хосте:
```bash
ss -tulnp
```
Всё что слушает - должно быть либо закрыто firewall, либо намеренно открыто.
## Fail2ban
```bash
sudo apt install -y fail2ban
```
Создай локальный конфиг - не редактируй `jail.conf`, он перезаписывается при обновлении:
```bash
sudo nano /etc/fail2ban/jail.local
```
```ini
[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 порт.
Запусти:
```bash
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
```
Проверь статус:
```bash
sudo fail2ban-client status
```
```
Status
|- Number of jail: 1
`- Jail list: sshd
```
Проверь статус конкретного jail:
```bash
sudo fail2ban-client status sshd
```
## Swap
Swap в виде файла гибче раздела: можно увеличить или уменьшить без переразбивки диска.
### Создание swap файла
Проверь текущий swap:
```bash
free -h
sudo swapon --show
```
Создай файл (пример - 2GB):
```bash
# Создать файл нужного размера
sudo fallocate -l 2G /swapfile
```
Если `fallocate` не поддерживается файловой системой:
```bash
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
```
Установи права - swap файл не должен читаться другими пользователями:
```bash
sudo chmod 600 /swapfile
```
Инициализируй и активируй:
```bash
# Разметить как swap
sudo mkswap /swapfile
# Включить
sudo swapon /swapfile
```
Проверь:
```bash
free -h
sudo swapon --show
```
Добавь в `/etc/fstab` для автомонтирования после перезагрузки:
```bash
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```
### Настройка swappiness
`swappiness` определяет агрессивность использования swap. Для production снижаем - swap только когда RAM заполнена на 90%+:
```bash
# Проверить текущее значение (по умолчанию 60)
cat /proc/sys/vm/swappiness
```
Установи постоянно:
```bash
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.d/99-swap.conf
sudo sysctl -p /etc/sysctl.d/99-swap.conf
```
### Изменение размера swap (когда потребуется)
```bash
# Отключить текущий swap
sudo swapoff /swapfile
# Изменить размер (пример - увеличить до 4GB)
sudo fallocate -l 4G /swapfile
# Переинициализировать
sudo mkswap /swapfile
# Включить
sudo swapon /swapfile
```
Проверь результат:
```bash
free -h
```
Запись в `/etc/fstab` менять не нужно - путь тот же.
### Удаление swap (если потребуется)
```bash
sudo swapoff /swapfile
sudo rm /swapfile
```
Удали строку из `/etc/fstab`:
```bash
sudo sed -i '/\/swapfile/d' /etc/fstab
```
## Sysctl - защита ядра
Создай отдельный файл - чище чем писать в `/etc/sysctl.conf`:
```bash
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
```
Примени:
```bash
sudo sysctl -p /etc/sysctl.d/99-hardening.conf
```
Проверь что применилось:
```bash
sudo sysctl net.ipv4.tcp_syncookies
# net.ipv4.tcp_syncookies = 1
```
## Автообновления безопасности
```bash
sudo apt install -y unattended-upgrades apt-listchanges
```
```bash
sudo 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. Перезагрузку после обновления ядра делать вручную в удобное время.
Включи:
```bash
sudo dpkg-reconfigure -plow unattended-upgrades
```
Выбери `Yes`.
Настрой расписание:
```bash
sudo nano /etc/apt/apt.conf.d/10periodic
```
```
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
```
Проверь что сервис работает:
```bash
sudo systemctl status unattended-upgrades
```
## Отключение ненужных сервисов (при наличии)
Посмотри что запущено:
```bash
systemctl list-units --type=service --state=running
```
Стандартные кандидаты на отключение - проверь что не используешь перед отключением:
```bash
# 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
```
Проверь что слушает на сетевых портах:
```bash
ss -tulnp
```
Всё незнакомое - выясни что это и реши нужно ли.
## Логи и ротация
### Ограничение journald
```bash
sudo nano /etc/systemd/journald.conf
```
Раскомментируй/измени:
```
SystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=1month
```
Перезапусти:
```bash
sudo systemctl restart systemd-journald
```
Проверь размер:
```bash
sudo journalctl --disk-usage
```
На чистом Debian 12 rsyslog не установлен - логи пишутся только в journald. Настройки выше достаточно.
Если устанавливаешь rsyslog отдельно - создай /etc/logrotate.d/rsyslog с правилами ротации вручную.
## Мониторинг диска
На VPS имя устройства зависит от гипервизора - не всегда `/dev/sda`. Определи актуальное:
```bash
# Найти дисковые устройства
lsblk -d -o NAME,TYPE | grep disk
# NAME TYPE
# vda disk
#
# Результат зависит от гипервизора и провайдера:
# /dev/sda - KVM у некоторых провайдеров, Xen
# /dev/vda - KVM (наиболее распространённый вариант)
# /dev/xvda - Xen
```
Проверь состояние:
```bash
# Замени vda на своё устройство
sudo smartctl -H /dev/vda
```
Ожидаемый вывод:
```
# ATA диски:
# SMART overall-health self-assessment test result: PASSED
#
# SCSI/NVMe диски (типично для VPS):
# SMART Health Status: OK
#
# Оба варианта означают одно: диск здоров
```
Если `FAILED` - диск умирает. Срочно бэкап и замена.
Детали SMART:
```bash
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 ключи.
```bash
nano ~/backup-configs.sh
```
```bash
#!/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
```
Права и первый запуск:
```bash
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):
```bash
crontab -e
```
```
0 3 * * 0 /home/admin/backup-configs.sh >> /var/log/backup-configs.log 2>&1
```
### Восстановление
Восстановить конфиги:
```bash
sudo tar -xzf ~/backups/config_backup_ДАТА.tar.gz -C /
```
Восстановить список пакетов:
```bash
sudo dpkg --set-selections < ~/installed-packages.txt
sudo apt-get dselect-upgrade
```
## Финальная проверка
```bash
# Открытые порты
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](/posts/debian-12-docker-hardening/).
**Стек этой статьи:** Debian 12 (Bookworm) · UFW · Fail2ban · OpenSSH · unattended-upgrades · systemd · smartmontools