Базовая настройка сервера: от выдачи машины до готовности к Docker
Инструкция доводит только что выданную машину до состояния, с которого начинается установка Docker: система обновлена, работа идёт под отдельным пользователем, вход — только по ключу, межсетевой экран включён, время синхронизировано, журналы ограничены по размеру. Каждый шаг заканчивается проверкой с однозначным ожидаемым результатом: если результат другой — переходите к разделу «Типичные отказы», не выполняя следующий шаг.
20 минутИнструкция доводит только что выданную машину до состояния, с которого начинается установка Docker: система обновлена, работа идёт под отдельным пользователем, вход — только по ключу, межсетевой экран включён, время синхронизировано, журналы ограничены по размеру. Каждый шаг заканчивается проверкой с однозначным ожидаемым результатом: если результат другой — переходите к разделу «Типичные отказы», не выполняя следующий шаг.
Что нужно до начала:
- машина с Ubuntu 22.04/24.04 LTS, выданная провайдером (следующий документ цепочки допускает также Debian 12; отличия отмечены по месту);
- доступ по SSH под пользователем с правами администратора —
rootили пользователь в группеsudo; - аварийная консоль провайдера (VNC/KVM в панели управления) и известный пароль администратора;
- ключевая пара на рабочей машине, с которой вы подключаетесь.
Проверки предусловий — на сервере, в текущем сеансе:
. /etc/os-release && echo "$ID $VERSION_ID" # ожидается: ubuntu 22.04 или ubuntu 24.04
sudo -v # ожидается: команда завершается без сообщенийНа рабочей машине:
ls -l ~/.ssh/id_ed25519.pub # ожидается: файл существует
ssh-keygen -t ed25519 # только если файла нетЗакрытый ключ (id_ed25519, без .pub) остаётся на рабочей машине и никуда не копируется. На сервер
уходит только открытая часть.
Аварийную консоль проверьте до изменений: откройте её в панели провайдера и убедитесь, что видно
приглашение login:. Консоль работает в обход SSH и межсетевого экрана — это единственный путь назад,
если доступ по сети потерян. Консоль без известного пароля бесполезна: вход в неё идёт по паролю, а не
по ключу.
Место в цепочке
| Откуда пришли | Этот документ | Куда ведёт |
|---|---|---|
| провайдер выдал машину с образом Ubuntu LTS и доступом администратора | обновления, рабочий пользователь, вход по ключу, межсетевой экран, время, автообновления безопасности, ограничение журналов | установка Docker и дальше по цепочке разворачивания |
Что предыдущее звено обязано обеспечить: доступ администратора по SSH и работающая аварийная консоль. Без консоли шаги 3 и 4 выполнять нельзя — при ошибке в них доступ по сети теряется, и восстановить его будет нечем.
Что этот документ оставляет следующему звену:
- пользователя с
sudo— именно его документ о Docker добавляет в группуdocker; - включённый межсетевой экран с разрешённым SSH. Порты 80 и 443 открывает документ о HTTPS — до появления домена они не нужны;
- синхронизированное время: без него проверка сертификата и сопоставление журналов разных машин дают неверный результат;
- ограниченные по размеру системные журналы. Логи контейнеров ограничиваются отдельно, в документе о Docker, — они пишутся драйвером Docker, а не journald.
Отдельно: включённый межсетевой экран не защищает порты, опубликованные контейнерами. Docker
добавляет свои правила в iptables в цепочку, которая обрабатывается раньше правил ufw, поэтому порт,
опубликованный без адреса, доступен снаружи вопреки запрету. Публикацией на loopback занимается
следующий документ; здесь важно не считать ufw достаточной мерой для контейнеров.
Порядок принципиален в двух местах:
- Вход по ключу проверяется до запрета парольного входа. Обратный порядок оставляет машину без единого работающего способа подключиться.
- Правило, разрешающее SSH, добавляется до
ufw enable. По умолчанию экран запрещает входящие соединения, поэтому включение без такого правила отрезает новые подключения.
Шаг 1. Обновление системы и базовый набор инструментов
Свежий образ провайдера отстаёт от репозитория на срок с момента его сборки, включая исправления безопасности.
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release ufw unattended-upgrades rsync gitНабор минимальный и подобран под следующие шаги: ca-certificates, curl, gnupg, lsb-release
нужны для подключения репозитория Docker; ufw — шаг 4; unattended-upgrades — шаг 6; rsync
используется при резервном копировании.
apt list --upgradable # ожидается: только строка "Listing... Done"Если обновилось ядро или системные библиотеки, нужна перезагрузка:
[ -f /var/run/reboot-required ] && cat /var/run/reboot-required
sudo rebootОжидается: файл /var/run/reboot-required отсутствует (команда ничего не печатает) либо машина
перезагружена и файл исчез.
Шаг 2. Отдельный пользователь для работы
Вход администратором не именной: под root действия всех людей выглядят одинаково, а любая ошибка
выполняется с полными правами без промежуточного подтверждения. Отдельный пользователь с sudo даёт
именные записи в журнале и явную границу между обычными действиями и действиями с правами root.
Дальше пользователь называется deploy; имя может быть любым, но оно должно совпадать во всех
командах ниже.
sudo adduser --gecos "" deploy # спросит пароль: задайте и сохраните в менеджере паролей
sudo usermod -aG sudo deployПароль нужен для sudo и для входа через аварийную консоль провайдера. По сети он работать не будет:
парольный вход отключается на шаге 3. Пользователь, созданный с --disabled-password, не сможет
выполнить sudo — команде нечего будет проверить.
Скопируйте открытый ключ. Содержимое ~/.ssh/id_ed25519.pub с рабочей машины вставляется одной
строкой:
sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
sudo tee /home/deploy/.ssh/authorized_keys >/dev/null <<'EOF'
ssh-ed25519 AAAA…сюда содержимое id_ed25519.pub целиком, одной строкой…
EOF
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
sudo chmod 600 /home/deploy/.ssh/authorized_keysТот же результат с рабочей машины, пока парольный вход ещё разрешён:
ssh-copy-id -i ~/.ssh/id_ed25519.pub -o User=deploy example.comЗдесь и далее example.com — адрес вашего сервера. Форма ssh -l имя адрес равнозначна привычной
записи с @.
id deploy # ожидается: в списке групп есть sudo
stat -c '%a %U %n' /home/deploy /home/deploy/.ssh /home/deploy/.ssh/authorized_keysОжидается: каталог .ssh — 700 deploy, файл authorized_keys — 600 deploy, домашний каталог —
750 или 755 и владелец deploy. Права шире (запись для группы или для всех) — sshd откажется
использовать ключ и потребует пароль.
Проверка входа выполняется вторым сеансом, не закрывая текущий:
ssh -l deploy example.com # ожидается: вход без запроса пароля
sudo whoami # в новом сеансе; ожидается: root (спросит пароль пользователя)Шаг 3. Вход только по ключу, без администратора
Настройка кладётся в отдельный файл, а не в /etc/ssh/sshd_config: основной файл принадлежит пакету и
может быть заменён при обновлении. В Ubuntu 22.04 и новее первой строкой основного файла идёт
Include /etc/ssh/sshd_config.d/*.conf.
grep -n '^Include' /etc/ssh/sshd_config # ожидается: строка Include ... sshd_config.d/*.confИмя файла начинается с малого числа не случайно. Файлы читаются в алфавитном порядке, а для каждого
параметра sshd применяет первое встреченное значение. Образы провайдеров часто содержат
50-cloud-init.conf с PasswordAuthentication yes; файл с префиксом 10- читается раньше и
определяет итоговое значение.
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf >/dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOF
sudo sshd -t # проверка синтаксиса; молчание означает, что ошибок нет
sudo systemctl reload ssh # служба может называться sshd — это её псевдонимsshd -t обязателен перед перезагрузкой: с ошибочным файлом служба не поднимется, а обнаружится это
только при следующем подключении.
В системах, где sshd запускается по сокету (Ubuntu 22.10 и новее), служба может быть неактивна, и
reload сообщит, что перезагружать нечего. Конфигурацию в этом случае читает каждое новое соединение
при старте, отдельного действия не требуется.
sudo sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication)'permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
pubkeyauthentication yessshd -T показывает конфигурацию после разбора всех включённых файлов. Это единственный способ
отличить «параметр записан» от «параметр действует»: значение из 50-cloud-init.conf в самом файле не
видно.
Приём с двумя сеансами
Изменения sshd применяются только к новым соединениям — уже открытый сеанс продолжает работать, даже если новая конфигурация запрещает вход. На этом строится защита от блокировки самого себя:
- Первый сеанс (тот, в котором вы правили конфигурацию) не закрывать до конца проверки.
- В другом окне терминала подключиться заново:
ssh -l deploy example.com. - Если второй вход прошёл и
sudo whoamiвернулroot— закрыть первый сеанс. - Если второй вход не прошёл — чинить в первом, который ещё открыт. Причину показывает
sudo journalctl -u ssh -e --no-pagerна сервере и запуск клиента с-v.
Закрытый первый сеанс до успешной проверки — самая частая причина потери доступа к серверу. Восстановление в этом случае идёт через аварийную консоль (см. «Откат»).
ssh -l deploy -o PubkeyAuthentication=no -o PreferredAuthentications=password example.com
ssh -l root example.comОжидается: обе команды завершаются с Permission denied (publickey). Запрос пароля вместо этого
означает, что парольный вход остался разрешён.
Шаг 4. Межсетевой экран
Правила задаются до включения. Политика по умолчанию — запрет входящих: разрешено только то, что перечислено явно.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # профиль соответствует порту 22/tcpПрофиль OpenSSH открывает порт 22. Если sshd слушает другой порт, разрешать нужно его — иначе
включение экрана обрежет доступ:
sudo sshd -T | grep -i '^port' # фактический порт sshdТолько после того как правило для SSH добавлено:
sudo ufw enable # спросит подтверждение; ответ yКоманда предупреждает о возможном разрыве соединений. Уже установленный сеанс, как правило,
переживает включение: ufw пропускает соединения в состоянии ESTABLISHED. Новые подключения без
правила для SSH будут отброшены — доступ потеряется при первом же переподключении.
sudo ufw status verboseStatus: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)Status: inactive означает, что экран не включён. Отсутствие строки с портом SSH при активном
статусе — состояние, в котором следующее подключение не пройдёт; добавьте правило немедленно, не
закрывая текущий сеанс.
Вторая проверка — новым соединением, при открытом текущем: ssh -l deploy example.com должен
подключиться.
Ограничение частоты подключений (необязательно) отбрасывает адрес, открывший более шести соединений за 30 секунд:
sudo ufw limit OpenSSHПравило снижает объём записей о переборе паролей в журнале. При работе, где сеансы открываются пачками (например, параллельные команды по SSH), оно может отбросить и ваши собственные подключения.
Если доступ к серверу идёт через частную сеть, SSH можно ограничить её диапазоном:
sudo ufw allow from 10.0.0.0/8 to any port 22 proto tcpТакое правило отрезает вход из любой другой сети, включая ту, из которой вы работаете сейчас. Добавляйте его, только когда подключение через частную сеть уже проверено вторым сеансом.
Облачный межсетевой экран провайдера — отдельный слой поверх ufw. Если он включён, те же порты
открываются и в панели управления: правило на сервере не отменяет запрет у провайдера.
Шаг 5. Часы и часовой пояс
Расхождение часов ломает то, что опирается на время, а не на порядок событий: проверку срока действия
сертификата (not yet valid при верном сертификате), сроки жизни токенов, одноразовые коды, а также
сопоставление журналов нескольких машин при разборе отказа.
timedatectlОжидается: System clock synchronized: yes и NTP service: active.
Часовой пояс на сервере — UTC: журналы разных машин сравниваются без пересчёта, а переход на летнее время не создаёт в отметках времени пропусков и повторов.
sudo timedatectl set-timezone UTCЕсли синхронизация выключена:
sudo timedatectl set-ntp trueЕсли служба синхронизации отсутствует или нужна более устойчивая, ставится chrony:
sudo apt install -y chrony
chronyc trackingОжидается: Leap status : Normal и малое значение в строке System time (доли секунды).
chrony заменяет systemd-timesyncd: обе службы синхронизируют часы, и одновременно работать они не
должны — пакет отключает встроенную службу при установке. Держать нужно одну.
timedatectl | grep -E 'Time zone|System clock|NTP service'
date -uОжидается: пояс UTC, System clock synchronized: yes, дата и время совпадают с фактическими.
Шаг 6. Автоматические обновления безопасности
Пакет unattended-upgrades установлен на шаге 1. Периодический запуск включается отдельным файлом:
sudo tee /etc/apt/apt.conf.d/20auto-upgrades >/dev/null <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOFЧто именно ставится, задано в /etc/apt/apt.conf.d/50unattended-upgrades. По умолчанию включён только
источник ${distro_id}:${distro_codename}-security — обновления безопасности. Добавление -updates
расширяет набор до обычных обновлений; на сервере это увеличивает и объём изменений, происходящих без
участия человека.
sudo unattended-upgrade --dry-run --debug | tail -n 20
systemctl list-timers 'apt-daily*' --no-pagerОжидается: в выводе первой команды — список пакетов к обновлению либо строка о том, что
обновляемых пакетов нет; во второй — таймеры apt-daily.timer и apt-daily-upgrade.timer с
назначенным временем следующего запуска.
Журнал работы: /var/log/unattended-upgrades/unattended-upgrades.log.
Перезагрузка после обновления ядра не выполняется сама. Её можно включить, дописав в
/etc/apt/apt.conf.d/50unattended-upgrades:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";Перезагрузка остановит и приложения. Включать её имеет смысл, когда сервисы поднимаются сами — для
контейнеров это политика restart: unless-stopped из документа о Docker. Иначе
перезагрузку делают вручную, ориентируясь на /var/run/reboot-required.
Шаг 7. Подкачка, если памяти мало
Подкачка нужна, когда памяти 1–2 ГБ или когда процессы завершаются ядром при нехватке памяти. Она не ускоряет работу: страницы на диске читаются в разы медленнее, чем из памяти. Подкачка меняет аварийное завершение процесса на замедление — при постоянной нехватке памяти правильное решение другое, увеличить память.
free -h # сколько памяти и есть ли подкачка
swapon --show # пустой вывод — подкачки нетЕсли провайдер уже выделил раздел подкачки (swapon --show не пуст), шаг пропускается.
sudo fallocate -l 2G /swapfile # если ФС не поддерживает: sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabРаспространённый ориентир объёма при памяти до 2 ГБ — столько же, сколько памяти. Точное значение зависит от нагрузки; больший файл занимает место на диске, но не ускоряет работу.
Параметры ядра:
sudo tee /etc/sysctl.d/99-swap.conf >/dev/null <<'EOF'
vm.swappiness=10
vm.vfs_cache_pressure=50
EOF
sudo sysctl --systemvm.swappiness задаёт, насколько охотно ядро вытесняет страницы на диск: значение по умолчанию 60,
значение 10 откладывает вытеснение до реальной нехватки. vm.vfs_cache_pressure ниже 100 сохраняет
кэш метаданных файловой системы дольше.
swapon --show # ожидается строка /swapfile с указанным размером
free -h # ожидается ненулевая строка Swap
cat /proc/sys/vm/swappiness # ожидается: 10Запись в /etc/fstab проверяется без перезагрузки — иначе ошибка обнаружится только при следующем
старте:
sudo swapoff /swapfile && sudo swapon -a && swapon --showОжидается: /swapfile снова в списке. Пустой вывод означает ошибку в строке /etc/fstab.
Шаг 8. Ограничение размера журналов
Журнал systemd по умолчанию занимает до 10% файловой системы. На небольшом диске это заметная доля, которая расходуется незаметно и обнаруживается как «нет места на устройстве» при следующей сборке.
journalctl --disk-usage # сколько занято сейчасsudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/10-size.conf >/dev/null <<'EOF'
[Journal]
Storage=persistent
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=1month
EOF
sudo systemctl restart systemd-journaldStorage=persistent сохраняет журнал между перезагрузками: без этого записи о причинах отказа
пропадают ровно тогда, когда нужны — после перезапуска машины.
journalctl --disk-usageОжидается: объём не превышает заданный предел. Уже накопленное сверх лимита удаляется по мере ротации; освободить место сразу:
sudo journalctl --vacuum-size=200M
sudo journalctl --vacuum-time=14dФайлы в /var/log, которые пишут не через journald, обслуживает logrotate — пакеты приносят свои
правила при установке:
systemctl status logrotate.timer --no-pager # ожидается: active
sudo du -xh --max-depth=1 /var/log | sort -h | tail -n 5Вторая команда показывает крупнейших потребителей места, когда диск всё же заполнился.
Логи контейнеров сюда не входят: их пишет драйвер Docker, и ограничиваются они в документе о Docker.
Итоговая проверка состояния
Один прогон перед переходом к установке Docker:
. /etc/os-release && echo "$PRETTY_NAME"
id deploy
sudo sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication)'
sudo ufw status verbose | head -n 4
timedatectl | grep -E 'Time zone|System clock'
systemctl list-timers 'apt-daily*' --no-pager | head -n 3
swapon --show
journalctl --disk-usage
df -h /Ожидается: Ubuntu LTS; пользователь deploy в группе sudo; permitrootlogin no и
passwordauthentication no; Status: active с разрешённым SSH; пояс UTC и синхронизированные часы;
назначенные таймеры обновлений; подкачка (если настраивалась) и объём журнала в пределах лимита;
свободное место на корневом разделе.
Дальше — установка Docker.
Типичные отказы
| Признак | Причина | Что делать |
|---|---|---|
Permission denied (publickey) под рабочим пользователем | ключ не установлен либо права на ~/.ssh шире положенных | stat -c '%a %U' /home/deploy/.ssh /home/deploy/.ssh/authorized_keys — ожидается 700 и 600; в журнале sudo journalctl -u ssh -e — Authentication refused: bad ownership or modes |
| второй сеанс не подключается, первый ещё открыт | ошибка в конфигурации sshd | не закрывая первый: sudo sshd -T, sudo journalctl -u ssh -e --no-pager; при неясности — удалить 10-hardening.conf и повторить настройку |
после ufw enable новые подключения отбрасываются | правило для SSH не добавлено до включения | войти через консоль провайдера, sudo ufw disable, добавить sudo ufw allow OpenSSH, включить снова |
| SSH на нестандартном порту, экран включён — доступа нет | разрешён профиль OpenSSH (порт 22), а sshd слушает другой порт | через консоль: sudo sshd -T | grep -i '^port', затем sudo ufw allow <порт>/tcp |
порт контейнера доступен снаружи, хотя ufw его запрещает | правила Docker в iptables обрабатываются раньше правил ufw | публиковать порт с адресом 127.0.0.1 — документ о Docker, публикация портов |
sudo отказывает: пароль не подходит | пользователь создан с --disabled-password | sudo passwd deploy от администратора |
| доступ потерян полностью, консоль пускает | сеть закрыта экраном или sshd | sudo ufw disable, sudo rm /etc/ssh/sshd_config.d/10-hardening.conf, sudo systemctl restart ssh — далее по разделу «Откат» |
выпуск или проверка сертификата падает с not yet valid либо expired при верном сроке | часы разошлись | timedatectl, sudo timedatectl set-ntp true, при необходимости chrony (шаг 5) |
| записи журналов разных машин не сходятся по времени | разные часовые пояса или нет синхронизации | привести все машины к UTC и включить синхронизацию (шаг 5) |
| «нет места на устройстве», приложения занимают мало | журналы заняли диск | journalctl --disk-usage, sudo journalctl --vacuum-size=200M, sudo du -xh --max-depth=1 /var/log | sort -h | tail |
| обновления безопасности не ставятся | не создан 20auto-upgrades либо таймеры выключены | шаг 6, проверить systemctl list-timers 'apt-daily*' |
| после включения подкачки сервер стал заметно медленнее | памяти не хватает постоянно, работа идёт с диска | free -h под нагрузкой; подкачка не заменяет память — увеличить память или снизить нагрузку |
| подкачка пропала после перезагрузки | нет записи в /etc/fstab или в ней опечатка | проверить sudo swapoff /swapfile && sudo swapon -a (шаг 7) |
Откат
Настройки снимаются по отдельности — возвращать всё сразу не требуется.
Вернуть парольный вход и вход администратором. Достаточно удалить файл настройки; параметры вернутся к значениям из основного конфигурационного файла и файлов образа:
sudo rm /etc/ssh/sshd_config.d/10-hardening.conf
sudo sshd -t && sudo systemctl reload ssh
sudo sshd -T | grep -i '^passwordauthentication' # ожидается: yesЧтобы вернуть только парольный вход, оставив запрет для администратора, замените содержимое файла на
PermitRootLogin no и перезагрузите службу.
Отключить межсетевой экран. Правила при этом сохраняются и вернутся при следующем включении:
sudo ufw disable
sudo ufw status # ожидается: Status: inactiveПолный сброс правил — sudo ufw reset; прежние наборы сохраняются рядом в /etc/ufw с отметкой
времени.
Если доступ по сети потерян. Порядок восстановления:
- Открыть консоль в панели провайдера и войти под администратором или под
deployпо паролю. Консоль работает в обход SSH иufw. - Снять то, что закрыло доступ:
sudo ufw disable, при необходимостиsudo rm /etc/ssh/sshd_config.d/10-hardening.confиsudo systemctl restart ssh. - Подключиться по SSH и повторить настройку, не закрывая сеанс до проверки вторым сеансом.
Вставка из буфера обмена в консоли провайдера работает не всегда — длинные строки приходится набирать вручную. Это ещё одна причина проверять ключ до запрета пароля, а не после.
Если пароль администратора неизвестен и консоль не пускает, остаётся режим восстановления
провайдера: машина загружается со служебного образа, корневой раздел монтируется как обычный каталог.
В смонтированном разделе удаляется etc/ssh/sshd_config.d/10-hardening.conf, а в etc/ufw/ufw.conf
значение ENABLED меняется на no. После обычной загрузки доступ по паролю и без экрана
восстанавливается.
sudo swapoff /swapfile
sudo sed -i '\#^/swapfile #d' /etc/fstab
sudo rm /swapfile
swapon --show # ожидается: пустой выводОтключить автоматические обновления: заменить в /etc/apt/apt.conf.d/20auto-upgrades значения на
"0". Уже установленные обновления не откатываются.
Что при откате не удаляется: созданный пользователь и его домашний каталог с ключом, установленные
пакеты, ограничения журналов и настройки времени. Пользователя удаляют отдельно —
sudo deluser --remove-home deploy, и только после того, как проверен другой рабочий способ входа.
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.