gitaspen docs

Базовая настройка сервера: от выдачи машины до готовности к Docker

Инструкция доводит только что выданную машину до состояния, с которого начинается установка Docker: система обновлена, работа идёт под отдельным пользователем, вход — только по ключу, межсетевой экран включён, время синхронизировано, журналы ограничены по размеру. Каждый шаг заканчивается проверкой с однозначным ожидаемым результатом: если результат другой — переходите к разделу «Типичные отказы», не выполняя следующий шаг.

20 минут

Инструкция доводит только что выданную машину до состояния, с которого начинается установка Docker: система обновлена, работа идёт под отдельным пользователем, вход — только по ключу, межсетевой экран включён, время синхронизировано, журналы ограничены по размеру. Каждый шаг заканчивается проверкой с однозначным ожидаемым результатом: если результат другой — переходите к разделу «Типичные отказы», не выполняя следующий шаг.

Что нужно до начала:

  • машина с Ubuntu 22.04/24.04 LTS, выданная провайдером (следующий документ цепочки допускает также Debian 12; отличия отмечены по месту);
  • доступ по SSH под пользователем с правами администратора — root или пользователь в группе sudo;
  • аварийная консоль провайдера (VNC/KVM в панели управления) и известный пароль администратора;
  • ключевая пара на рабочей машине, с которой вы подключаетесь.

Проверки предусловий — на сервере, в текущем сеансе:

bash
. /etc/os-release && echo "$ID $VERSION_ID"     # ожидается: ubuntu 22.04 или ubuntu 24.04
sudo -v                                          # ожидается: команда завершается без сообщений

На рабочей машине:

bash
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 достаточной мерой для контейнеров.

Порядок принципиален в двух местах:

  1. Вход по ключу проверяется до запрета парольного входа. Обратный порядок оставляет машину без единого работающего способа подключиться.
  2. Правило, разрешающее SSH, добавляется до ufw enable. По умолчанию экран запрещает входящие соединения, поэтому включение без такого правила отрезает новые подключения.

Шаг 1. Обновление системы и базовый набор инструментов

Свежий образ провайдера отстаёт от репозитория на срок с момента его сборки, включая исправления безопасности.

bash
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"

Если обновилось ядро или системные библиотеки, нужна перезагрузка:

bash
[ -f /var/run/reboot-required ] && cat /var/run/reboot-required
sudo reboot

Ожидается: файл /var/run/reboot-required отсутствует (команда ничего не печатает) либо машина перезагружена и файл исчез.


Шаг 2. Отдельный пользователь для работы

Вход администратором не именной: под root действия всех людей выглядят одинаково, а любая ошибка выполняется с полными правами без промежуточного подтверждения. Отдельный пользователь с sudo даёт именные записи в журнале и явную границу между обычными действиями и действиями с правами root.

Дальше пользователь называется deploy; имя может быть любым, но оно должно совпадать во всех командах ниже.

bash
sudo adduser --gecos "" deploy      # спросит пароль: задайте и сохраните в менеджере паролей
sudo usermod -aG sudo deploy

Пароль нужен для sudo и для входа через аварийную консоль провайдера. По сети он работать не будет: парольный вход отключается на шаге 3. Пользователь, созданный с --disabled-password, не сможет выполнить sudo — команде нечего будет проверить.

Скопируйте открытый ключ. Содержимое ~/.ssh/id_ed25519.pub с рабочей машины вставляется одной строкой:

bash
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

Тот же результат с рабочей машины, пока парольный вход ещё разрешён:

bash
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

Ожидается: каталог .ssh700 deploy, файл authorized_keys600 deploy, домашний каталог — 750 или 755 и владелец deploy. Права шире (запись для группы или для всех) — sshd откажется использовать ключ и потребует пароль.

Проверка входа выполняется вторым сеансом, не закрывая текущий:

bash
ssh -l deploy example.com     # ожидается: вход без запроса пароля
sudo whoami                   # в новом сеансе; ожидается: root (спросит пароль пользователя)

Шаг 3. Вход только по ключу, без администратора

Настройка кладётся в отдельный файл, а не в /etc/ssh/sshd_config: основной файл принадлежит пакету и может быть заменён при обновлении. В Ubuntu 22.04 и новее первой строкой основного файла идёт Include /etc/ssh/sshd_config.d/*.conf.

bash
grep -n '^Include' /etc/ssh/sshd_config     # ожидается: строка Include ... sshd_config.d/*.conf

Имя файла начинается с малого числа не случайно. Файлы читаются в алфавитном порядке, а для каждого параметра sshd применяет первое встреченное значение. Образы провайдеров часто содержат 50-cloud-init.conf с PasswordAuthentication yes; файл с префиксом 10- читается раньше и определяет итоговое значение.

bash
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 yes

sshd -T показывает конфигурацию после разбора всех включённых файлов. Это единственный способ отличить «параметр записан» от «параметр действует»: значение из 50-cloud-init.conf в самом файле не видно.

Приём с двумя сеансами

Изменения sshd применяются только к новым соединениям — уже открытый сеанс продолжает работать, даже если новая конфигурация запрещает вход. На этом строится защита от блокировки самого себя:

  1. Первый сеанс (тот, в котором вы правили конфигурацию) не закрывать до конца проверки.
  2. В другом окне терминала подключиться заново: ssh -l deploy example.com.
  3. Если второй вход прошёл и sudo whoami вернул root — закрыть первый сеанс.
  4. Если второй вход не прошёл — чинить в первом, который ещё открыт. Причину показывает 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. Межсетевой экран

Правила задаются до включения. Политика по умолчанию — запрет входящих: разрешено только то, что перечислено явно.

bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH            # профиль соответствует порту 22/tcp

Профиль OpenSSH открывает порт 22. Если sshd слушает другой порт, разрешать нужно его — иначе включение экрана обрежет доступ:

bash
sudo sshd -T | grep -i '^port'    # фактический порт sshd

Только после того как правило для SSH добавлено:

bash
sudo ufw enable                   # спросит подтверждение; ответ y

Команда предупреждает о возможном разрыве соединений. Уже установленный сеанс, как правило, переживает включение: ufw пропускает соединения в состоянии ESTABLISHED. Новые подключения без правила для SSH будут отброшены — доступ потеряется при первом же переподключении.

Проверка:
sudo ufw status verbose
Ожидается:
Status: 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 секунд:

bash
sudo ufw limit OpenSSH

Правило снижает объём записей о переборе паролей в журнале. При работе, где сеансы открываются пачками (например, параллельные команды по SSH), оно может отбросить и ваши собственные подключения.

Если доступ к серверу идёт через частную сеть, SSH можно ограничить её диапазоном:

bash
sudo ufw allow from 10.0.0.0/8 to any port 22 proto tcp

Такое правило отрезает вход из любой другой сети, включая ту, из которой вы работаете сейчас. Добавляйте его, только когда подключение через частную сеть уже проверено вторым сеансом.

Облачный межсетевой экран провайдера — отдельный слой поверх ufw. Если он включён, те же порты открываются и в панели управления: правило на сервере не отменяет запрет у провайдера.


Шаг 5. Часы и часовой пояс

Расхождение часов ломает то, что опирается на время, а не на порядок событий: проверку срока действия сертификата (not yet valid при верном сертификате), сроки жизни токенов, одноразовые коды, а также сопоставление журналов нескольких машин при разборе отказа.

bash
timedatectl

Ожидается: System clock synchronized: yes и NTP service: active.

Часовой пояс на сервере — UTC: журналы разных машин сравниваются без пересчёта, а переход на летнее время не создаёт в отметках времени пропусков и повторов.

bash
sudo timedatectl set-timezone UTC

Если синхронизация выключена:

bash
sudo timedatectl set-ntp true

Если служба синхронизации отсутствует или нужна более устойчивая, ставится chrony:

bash
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. Периодический запуск включается отдельным файлом:

bash
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 ГБ или когда процессы завершаются ядром при нехватке памяти. Она не ускоряет работу: страницы на диске читаются в разы медленнее, чем из памяти. Подкачка меняет аварийное завершение процесса на замедление — при постоянной нехватке памяти правильное решение другое, увеличить память.

bash
free -h                # сколько памяти и есть ли подкачка
swapon --show          # пустой вывод — подкачки нет

Если провайдер уже выделил раздел подкачки (swapon --show не пуст), шаг пропускается.

bash
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 ГБ — столько же, сколько памяти. Точное значение зависит от нагрузки; больший файл занимает место на диске, но не ускоряет работу.

Параметры ядра:

bash
sudo tee /etc/sysctl.d/99-swap.conf >/dev/null <<'EOF'
vm.swappiness=10
vm.vfs_cache_pressure=50
EOF

sudo sysctl --system

vm.swappiness задаёт, насколько охотно ядро вытесняет страницы на диск: значение по умолчанию 60, значение 10 откладывает вытеснение до реальной нехватки. vm.vfs_cache_pressure ниже 100 сохраняет кэш метаданных файловой системы дольше.

Проверка:
swapon --show                       # ожидается строка /swapfile с указанным размером
free -h                             # ожидается ненулевая строка Swap
cat /proc/sys/vm/swappiness         # ожидается: 10

Запись в /etc/fstab проверяется без перезагрузки — иначе ошибка обнаружится только при следующем старте:

bash
sudo swapoff /swapfile && sudo swapon -a && swapon --show

Ожидается: /swapfile снова в списке. Пустой вывод означает ошибку в строке /etc/fstab.


Шаг 8. Ограничение размера журналов

Журнал systemd по умолчанию занимает до 10% файловой системы. На небольшом диске это заметная доля, которая расходуется незаметно и обнаруживается как «нет места на устройстве» при следующей сборке.

bash
journalctl --disk-usage         # сколько занято сейчас
bash
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-journald

Storage=persistent сохраняет журнал между перезагрузками: без этого записи о причинах отказа пропадают ровно тогда, когда нужны — после перезапуска машины.

Проверка:
journalctl --disk-usage

Ожидается: объём не превышает заданный предел. Уже накопленное сверх лимита удаляется по мере ротации; освободить место сразу:

bash
sudo journalctl --vacuum-size=200M
sudo journalctl --vacuum-time=14d

Файлы в /var/log, которые пишут не через journald, обслуживает logrotate — пакеты приносят свои правила при установке:

bash
systemctl status logrotate.timer --no-pager           # ожидается: active
sudo du -xh --max-depth=1 /var/log | sort -h | tail -n 5

Вторая команда показывает крупнейших потребителей места, когда диск всё же заполнился.

Логи контейнеров сюда не входят: их пишет драйвер Docker, и ограничиваются они в документе о Docker.


Итоговая проверка состояния

Один прогон перед переходом к установке Docker:

bash
. /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 -eAuthentication 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-passwordsudo passwd deploy от администратора
доступ потерян полностью, консоль пускаетсеть закрыта экраном или sshdsudo 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)

Откат

Настройки снимаются по отдельности — возвращать всё сразу не требуется.

Вернуть парольный вход и вход администратором. Достаточно удалить файл настройки; параметры вернутся к значениям из основного конфигурационного файла и файлов образа:

bash
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 и перезагрузите службу.

Отключить межсетевой экран. Правила при этом сохраняются и вернутся при следующем включении:

bash
sudo ufw disable
sudo ufw status         # ожидается: Status: inactive

Полный сброс правил — sudo ufw reset; прежние наборы сохраняются рядом в /etc/ufw с отметкой времени.

Если доступ по сети потерян. Порядок восстановления:

  1. Открыть консоль в панели провайдера и войти под администратором или под deploy по паролю. Консоль работает в обход SSH и ufw.
  2. Снять то, что закрыло доступ: sudo ufw disable, при необходимости sudo rm /etc/ssh/sshd_config.d/10-hardening.conf и sudo systemctl restart ssh.
  3. Подключиться по 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, и только после того, как проверен другой рабочий способ входа.

Инструкция не помогла?

Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.