---
title: "Debian 12: базовая настройка и hardening для production"
date: 2026-05-22
draft: false
description: "Пошаговая настройка чистого Debian 12 (Bookworm) для production: SSH, firewall, fail2ban, swap, sysctl, автообновления, логи, бэкап конфигов."
summary: "Пошаговая настройка чистого 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
