Сеть в Linux и SSH
IP и порты, отладка соединений, правильные SSH-ключи, туннели и базовый файрвол.
За 30 секунд
Сервер общается с миром через сетевые интерфейсы, IP-адреса и порты. Несколько инструментов показывают, что слушает порты, проверяют, работает ли URL или DNS-запись, и позволяют безопасно входить по SSH-ключам. Добавьте простой файрвол, который разрешает только необходимое, — и вы закроете большую часть повседневной сетевой работы на Linux-сервере.
Почему я это использую
- Я управляю каждым сервером по SSH с ключами, и «только SSH-ключи, без входа по паролю» — первый пункт моего базового набора безопасности.
- Моё правило файрвола на серверах простое: ufw плюс файрвол провайдера, открыты только порты 22, 80 и 443.
- Когда сайт недоступен, инструменты вроде ss, curl и dig за минуту показывают, в чём дело: в DNS, файрволе, nginx или приложении.
Как это работает
01Интерфейсы, IP-адреса и порты
Сетевой интерфейс — это точка подключения: физическая или виртуальная карта, например eth0 или ens3, плюс lo — loopback-интерфейс. У каждого интерфейса один или несколько IP-адресов. 127.0.0.1 (и ::1 в IPv6) — это localhost: он доступен только с самой машины, поэтому это правильное место для баз данных и серверов приложений, стоящих за nginx. Частные диапазоны вроде 10.0.0.0/8 и 192.168.0.0/16 используются внутри сетей; публичный IP доступен из интернета. Порт — это число от 1 до 65535, которое обозначает сервис на IP: 22 для SSH, 80 для HTTP, 443 для HTTPS, 5432 для PostgreSQL, 3306 для MySQL. Приложение, слушающее 0.0.0.0, принимает соединения на всех интерфейсах, включая публичный.
ip -br addr # interfaces and IPs, brief
ip route # routing table and default gateway
hostname -I # all IPs of this host
ping -c 3 example.com # basic reachability (ICMP may be blocked)
traceroute example.com # path to a host (apt install traceroute)
02Кто слушает порты: ss, curl и инструменты DNS
В современных дистрибутивах ss заменил netstat. ss -tlnp показывает слушающие TCP-сокеты с числовыми портами и процессом-владельцем; убедитесь, что базы данных и внутренние сервисы слушают 127.0.0.1, а не 0.0.0.0. curl — инструмент отладки HTTP: -I показывает только заголовки, -v показывает весь обмен, включая TLS, а --resolve позволяет проверить новый сервер до того, как на него укажет DNS. Для простого скачивания файлов лучше подходит wget. dig напрямую запрашивает DNS и показывает, резолвится ли домен в ожидаемый сервер. Запрос к публичному резолверу через @1.1.1.1 позволяет обойти локальный кэш.
sudo ss -tlnp # listening TCP ports + process
sudo ss -tnp state established # current connections
curl -I https://example.com # status and headers only
curl -v http://127.0.0.1:3000/health # talk to the app directly
curl --resolve example.com:443:203.0.113.10 https://example.com/
dig +short example.com A # IPv4 address
dig example.com MX # mail servers
dig @1.1.1.1 example.com # ask a specific resolver
dig +trace example.com # follow delegation from the root
03SSH-ключи: как они работают и как их настроить
SSH даёт зашифрованный shell на удалённой машине. При входе по ключу вы создаёте пару ключей на своём компьютере: приватный ключ, который никогда его не покидает, и публичный ключ, который вы копируете в ~/.ssh/authorized_keys на сервере. При подключении сервер проверяет, что приватный ключ у вас есть, причём сам ключ никуда не отправляется. Используйте тип ключа ed25519: он короткий, быстрый и является современным стандартом. Защитите приватный ключ парольной фразой и пусть ssh-agent её запоминает, тогда вводить её придётся один раз за сессию. При первом подключении SSH показывает отпечаток сервера и сохраняет его в known_hosts; если позже отпечаток изменится, SSH предупредит вас, потому что это может означать, что кто-то встал посередине.
# On your laptop
ssh-keygen -t ed25519 -C "your_user@laptop"
ssh-copy-id [email protected] # installs the public key
ssh [email protected]
# If ssh-copy-id is not available, append the .pub file manually:
# cat ~/.ssh/id_ed25519.pub | ssh [email protected] "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Permissions on the server must be strict:
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
04~/.ssh/config и отключение входа по паролю
Файл ~/.ssh/config на ноутбуке даёт каждому серверу короткое имя с пользователем, адресом, ключом и опциями, так что ssh web1 заменяет длинную команду. На сервере, как только ключ заработал, отключите вход по паролю и прямой вход под root; это убирает brute-force-атаки, которые обрушиваются на любой публичный SSH-порт в течение нескольких минут. В актуальной Ubuntu кладите свои настройки в файл в /etc/ssh/sshd_config.d/, который подключается раньше основного файла (побеждает первое заданное значение). Всегда проверяйте конфиг через sshd -t и держите текущую сессию открытой, пока тестируете новую, чтобы ошибка не отрезала вам доступ. Некоторые облачные образы кладут в этот каталог собственный файл, который снова включает пароли, так что проверьте и его.
# ~/.ssh/config on your laptop
Host web1
HostName 203.0.113.10
User your_user
IdentityFile ~/.ssh/id_ed25519
# /etc/ssh/sshd_config.d/10-hardening.conf on the server
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
# Then: sudo sshd -t && sudo systemctl reload ssh (sshd on RHEL)
05Копирование файлов: scp и rsync
scp копирует файлы по SSH с синтаксисом, похожим на cp, и подходит для одного файла. Для всего, что побольше, лучше rsync: он передаёт только различия, умеет докачивать, сохраняет права и время с -a и может зеркалировать каталог с --delete. Завершающий слэш важен: rsync src/ dest/ копирует содержимое src, а rsync src dest/ создаёт dest/src. Всегда запускайте деструктивный rsync сначала с --dry-run (-n), чтобы увидеть, что изменится. Оба инструмента используют ваш SSH-конфиг, так что алиасы хостов работают.
scp ./dump.sql web1:/tmp/ # one file up
scp web1:/var/log/nginx/error.log . # one file down
rsync -avz --progress ./dist/ web1:/srv/myapp/dist/
rsync -avzn --delete ./dist/ web1:/srv/myapp/dist/ # dry run first!
rsync -avz web1:/var/www/app/storage/app/ ./storage-backup/
06Проброс портов и туннели
SSH может передавать другой трафик через своё зашифрованное соединение. Самый полезный — локальный проброс (-L): он открывает порт на ноутбуке, ведущий к порту на сервере, так что вы можете достучаться до базы данных или админки, которые слушают только localhost сервера, ни разу не открыв их в интернет. Удалённый проброс (-R) делает обратное и выставляет порт вашего ноутбука на сервере, например чтобы показать локальное приложение. -D создаёт SOCKS-прокси. Jump-хост (ProxyJump или -J) позволяет достучаться до приватных серверов через один публичный бастион. Добавьте -N, если нужен только туннель без shell.
# Reach the server's local PostgreSQL at localhost:5433 on your laptop
ssh -N -L 5433:127.0.0.1:5432 web1
# Expose your local port 3000 as port 8080 on the server's localhost
ssh -N -R 8080:127.0.0.1:3000 web1
# Reach a private server through a bastion
ssh -J [email protected] [email protected]
# Or in ~/.ssh/config: ProxyJump bastion
07Основы файрвола с ufw
Файрвол решает, какие входящие и исходящие соединения разрешены. В Ubuntu ufw (uncomplicated firewall) — дружелюбная надстройка над правилами netfilter в ядре; в RHEL используется firewalld. Безопасная политика по умолчанию — запретить весь входящий трафик, разрешить исходящий и открыть только то, что вы обслуживаете: SSH, HTTP и HTTPS. Разрешите SSH до включения файрвола, иначе отрежете собственную сессию. Используйте облачный файрвол провайдера как второй слой: он блокирует трафик ещё до того, как тот дойдёт до сервера. Важный подвох: Docker публикует порты контейнеров, записывая собственные правила iptables, которые обходят ufw, поэтому привязывайте контейнеры к 127.0.0.1, если им действительно не нужно быть публичными.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # allow 22 BEFORE enabling
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw limit OpenSSH # rate-limit repeated SSH attempts
sudo ufw enable
sudo ufw status verbose
sudo ufw allow from 10.0.0.0/16 to any port 5432 # DB from private net only
Когда применять
- Настройка любого нового сервера: ключи, запрет входа по паролю, файрвол — ещё до деплоя чего-либо.
- Разбор жалоб «сайт лежит»: DNS, порт, TLS или приложение — слой за слоем.
- Безопасный доступ к базам данных и внутренним инструментам через SSH-туннели вместо их публикации.
- Деплой или бэкап файлов через rsync по SSH.
Когда не стоит
- Не открывайте порты базы данных в интернет только ради подключения GUI-клиента; используйте SSH-туннель.
- Не считайте смену SSH-порта защитой: она лишь уменьшает шум в логах. Настоящую работу делают ключи и файрвол.
- Для команд с большим количеством серверов голые общие ключи быстро превращаются в хаос; присмотритесь к SSH-сертификатам или инструменту управления доступом.
Альтернативы
- firewalld / nftables
firewalld используется по умолчанию в RHEL; nftables — современная система правил ядра, которую могут использовать и ufw, и firewalld.
- Облачные файрволы провайдеров
Фильтруют трафик ещё до того, как он дойдёт до VM. Используйте их вместе с файрволом на хосте.
- WireGuard / Tailscale
Приватная VPN-сеть, при которой админские порты вообще никогда не бывают публичными.
- Менеджеры сессий
Инструменты вроде AWS Systems Manager или Teleport дают доступ к shell с аудитом и без открытых SSH-портов.
Лучшие практики
- Используйте ключи ed25519 с парольной фразой и ssh-agent; по ключу на устройство, чтобы можно было отозвать один.
- Отключите PasswordAuthentication и PermitRootLogin и проверьте конфиг через sshd -t перед reload.
- Держите вторую SSH-сессию открытой, пока меняете настройки SSH или файрвола.
- Входящий трафик по умолчанию запрещён; откройте только 22, 80 и 443, а файрвол провайдера используйте как второй слой.
- Привязывайте базы данных, Redis и серверы приложений к 127.0.0.1 или частной сети, никогда к 0.0.0.0 на публичном хосте.
- Добавьте fail2ban, чтобы банить IP, которые раз за разом проваливают вход по SSH или на сайт.
- Запускайте rsync с --dry-run перед любым --delete.
Частые ошибки
- Включают ufw до того, как разрешили SSH, и запирают себя снаружи сервера.
- Отключают вход по паролю, не проверив, что ключ действительно работает.
- Публикуют порты Docker (-p 5432:5432) и считают, что их защищает ufw.
- Слишком свободные права на ~/.ssh или authorized_keys, из-за которых SSH молча игнорирует ключ.
- Игнорируют предупреждение «host key changed» и удаляют запись из known_hosts, не выяснив причину.
Глубже
08Состояния TCP-соединений и о чём они говорят
Каждое TCP-соединение проходит через состояния, и их подсчёт — быстрый health check. Серверный сокет находится в LISTEN; ядро завершает three-way handshake и ставит новые соединения в accept backlog, пока приложение их не примет. Соединения ESTABLISHED передают данные. Сторона, закрывающая соединение первой, примерно на минуту остаётся в TIME_WAIT, чтобы запоздавшие пакеты не испортили новое соединение; тысячи сокетов в TIME_WAIT на нагруженном прокси — это нормально. CLOSE_WAIT — другое дело: удалённая сторона закрыла соединение, а ваше приложение свой конец не закрыло. Растущее число сокетов в CLOSE_WAIT означает баг, обычно утечку соединения в пуле, и в итоге процессу не хватает файловых дескрипторов. Когда backlog переполняется, новые клиенты видят таймауты, хотя процесс выглядит здоровым; ss показывает размеры очередей у слушающих сокетов.
ss -s # summary of sockets by state
ss -tan state close-wait | wc -l # leaked connections?
ss -tan state time-wait | wc -l
ss -ltn # Recv-Q/Send-Q = backlog use/size
sysctl net.core.somaxconn net.ipv4.ip_local_port_range
nstat -az | grep -i -E 'ListenOverflows|ListenDrops'
09Внутри SSH-соединения и SSH-сертификаты
SSH-сессия начинается с обмена ключами: клиент и сервер согласуют алгоритмы и выводят сессионные ключи, а сервер подтверждает свою идентичность host key, который клиент сверяет с known_hosts. Начиная с OpenSSH 10.0 (апрель 2025) обменом ключей по умолчанию стал mlkem768x25519-sha256 — гибрид классического X25519 и постквантового ML-KEM, а ключи DSA полностью удалены. Только после этого происходит аутентификация пользователя — по ключу, сертификату или другими методами. Для команд обычные файлы authorized_keys плохо масштабируются: на каждом сервере свой список, который надо поддерживать, а отзыв доступа человека означает правку всех списков. SSH-сертификаты решают эту проблему. Внутренний центр сертификации подписывает публичный ключ пользователя с указанием идентичности, разрешённых логинов и срока действия, а серверы доверяют CA одной строкой — TrustedUserCAKeys. Короткоживущие сертификаты истекают сами, поэтому offboarding в основном происходит автоматически.
ssh -Q kex # key exchange algorithms supported
ssh -v web1 2>&1 | grep -i 'kex: algorithm'
# Certificate authority (keep the CA private key offline or in a vault)
ssh-keygen -t ed25519 -f user_ca -C "user CA"
ssh-keygen -s user_ca -I [email protected] -n your_user -V +8h ~/.ssh/id_ed25519.pub
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub # inspect the certificate
# On each server, in sshd_config.d:
# TrustedUserCAKeys /etc/ssh/user_ca.pub
10Netfilter: где встречаются ufw, Docker и nftables
Все файрволы Linux — это фронтенды к netfilter, фреймворку фильтрации пакетов в ядре. Пакеты проходят через хуки: PREROUTING при поступлении, затем INPUT, если они адресованы этому хосту, или FORWARD, если маршрутизируются дальше, OUTPUT для локально созданных пакетов и POSTROUTING на выходе. ufw управляет только правилами типа INPUT для самого хоста. Docker публикует порты контейнеров через DNAT в PREROUTING, что превращает трафик в транзитный, который никогда не встречается с правилами INPUT в ufw; в этом и состоит механизм известного обхода и причина существования цепочки DOCKER-USER. Connection tracking (conntrack) запоминает потоки, поэтому правила могут дёшево пропускать ответы на установленные соединения; на очень нагруженных хостах таблица conntrack может переполниться и начать отбрасывать новые соединения. В современных дистрибутивах команда iptables обычно записывает под капотом правила nftables, а nft list ruleset показывает всё в одном месте.
sudo nft list ruleset | less # every rule, all tools
sudo iptables -L DOCKER-USER -n -v # Docker's filter hook
# Allow only one network to reach published containers
sudo iptables -I DOCKER-USER -i eth0 ! -s 198.51.100.0/24 -j DROP
sudo conntrack -C # tracked connections (conntrack pkg)
sysctl net.netfilter.nf_conntrack_max
11Как хост разрешает имена
dig обращается к DNS-серверам напрямую, но приложения используют системный резолвер, и результаты могут расходиться. Библиотека C следует /etc/nsswitch.conf, где обычно сказано: сначала проверить /etc/hosts, затем DNS. В Ubuntu DNS-запросы идут в systemd-resolved — локальный кэширующий stub на 127.0.0.53, который пересылает их upstream-серверам, настроенным через netplan или DHCP. Поэтому приложение может видеть старый адрес из кэша или переопределение из /etc/hosts, а dig — уже новый. getent hosts использует ровно тот же путь, что и приложения, поэтому это правильный тест. resolvectl показывает upstream-серверы по интерфейсам и умеет сбрасывать кэш. Языки добавляют собственное кэширование: JVM кэширует результаты поиска, а пулы соединений продолжают использовать старый IP, пока не переподключатся, что важно при миграциях.
getent hosts example.com # what applications will see
resolvectl status # upstream DNS per interface (Ubuntu)
resolvectl query example.com # resolve through systemd-resolved
sudo resolvectl flush-caches
grep hosts /etc/nsswitch.conf # lookup order
cat /etc/resolv.conf # usually points to 127.0.0.53
Реальные ситуации
Перенос SSH на другой порт в Ubuntu 24.04
Проблема
Чтобы уменьшить шум в логах, вы меняете Port в файле sshd_config.d и перезапускаете ssh, но сервер продолжает слушать 22. В другой попытке вы едва не заблокировали себе доступ.
Как решить
Начиная с Ubuntu 22.10 SSH активируется через сокет: слушающий порт принадлежит ssh.socket в systemd, а сам порт генерируется из sshd_config в момент daemon-reload. Сначала откройте новый порт в ufw и файрволе провайдера, оставив 22 открытым. Добавьте Port 2222 (пока рядом с Port 22) в /etc/ssh/sshd_config.d/, выполните sudo sshd -t, затем sudo systemctl daemon-reload и sudo systemctl restart ssh.socket. Проверьте через ss -tlnp и войдите по новому порту из второго терминала. Только после этого уберите порт 22 из конфига и файрвола. Помните: смена порта лишь уменьшает шум, а настоящая защита — ключи, отказ от паролей и fail2ban.
Connection refused или connection timed out
Проблема
Новый API на порту 8080 недоступен из офиса. Одни коллеги сообщают «connection refused», у других запрос висит до таймаута.
Как решить
Эти две ошибки указывают на разные уровни. Refused означает, что хост активно ответил сбросом: пакет дошёл, но на этом адресе и порту никто не слушает — часто потому, что приложение привязано к 127.0.0.1 или упало; проверьте ss -tlnp на сервере. Таймаут означает, что пакеты молча отбрасываются, — это проблема файрвола (ufw, файрвол провайдера, офисная сеть) или маршрутизации. Проверьте с самого сервера через curl localhost:8080, затем с другого сервера в том же регионе, затем снаружи. Во время проверки запустите tcpdump -ni eth0 port 8080, чтобы увидеть, доходят ли пакеты вообще. Обычно правильное решение — не открывать 8080, а проксировать API через nginx на 443.
Приложение через несколько часов перестаёт принимать соединения
Проблема
Сервис на Node работает после каждого рестарта, но в течение нескольких часов перестаёт отвечать. CPU низкий, а в логах — «EMFILE: too many open files».
Как решить
Посчитайте сокеты по состояниям: ss -tanp | grep node | awk '{print $1}' | sort | uniq -c. Тысячи в CLOSE_WAIT означают, что приложение не закрывает соединения, которые удалённая сторона уже закрыла, — обычно это утечка соединений в HTTP-клиенте или пуле базы данных либо ответы, которые никогда не дочитываются. Каждый сокет — файловый дескриптор, поэтому процесс упирается в свой лимит, который виден в /proc/PID/limits. Поднимите LimitNOFILE в юните как буфер, но исправьте код: переиспользуйте один HTTP-агент с keep-alive и разумными таймаутами, всегда освобождайте соединения из пула в блоках finally и задайте таймауты на сервере. Добавьте метрику открытых файловых дескрипторов, чтобы следующую утечку поймать рано.
Шпаргалка
- Сводка сокетов по состояниям
ss -s- Какой процесс занимает порт 443
sudo ss -ltnp 'sport = :443'- Смотреть пакеты на порту
sudo tcpdump -ni any port 443 -c 20- Проверить TCP-порт без curl
nc -zv 203.0.113.10 5432- Проверить сертификат, который отдаёт сервер
openssl s_client -connect example.com:443 -servername example.com </dev/null- Во что приложения разрешают имя
getent hosts example.com- Показать полный набор правил файрвола
sudo nft list ruleset- Переиспользовать одно SSH-соединение для многих сессий
ControlMaster auto / ControlPath ~/.ssh/cm-%r@%h:%p / ControlPersist 10m- Применить смену порта SSH в Ubuntu 24.04
sudo sshd -t && sudo systemctl daemon-reload && sudo systemctl restart ssh.socket- Статус банов SSH-jail в fail2ban
sudo fail2ban-client status sshd
В моих реальных проектах
- Я управляю своими серверами по SSH со входом по ключам, и мой базовый набор — только SSH-ключи, вход по паролю отключён.
- Мой стандарт файрвола — ufw плюс файрвол провайдера, открыты только 22, 80 и 443, а базы данных привязаны к localhost или частной сети.
- На серверах, которые я веду, стоят fail2ban и ufw, а перед некоторыми сайтами для DNS стоит Cloudflare.
Вопросы и ответы
Q1Как работает аутентификация по SSH-ключу и чем она лучше паролей?
Вы генерируете пару ключей. Публичный ключ попадает в authorized_keys на сервере, приватный остаётся на вашей машине. При подключении сервер отправляет вызов (challenge), который может подписать только владелец приватного ключа, и проверяет подпись публичным ключом. Приватный ключ никогда не передаётся по сети. Пароли можно подобрать, использовать повторно или выманить фишингом, а каждый публичный SSH-порт постоянно получает brute-force-попытки. У ключа гораздо больше энтропии, чем у любого пароля, а под парольной фразой он ещё и бесполезен тому, кто украдёт файл. Кроме того, можно отозвать ключ одного устройства, не затрагивая остальные.
Q2Сайт не открывается. Как искать проблему со стороны сервера?
Иду слой за слоем. Сначала DNS: dig +short example.com должен вернуть IP сервера. Затем доступность: curl -I снаружи и проверка, что файрволы провайдера и ufw пропускают 80 и 443. На сервере ss -tlnp показывает, слушает ли nginx эти порты, а systemctl status nginx и nginx -t — работает ли он с валидным конфигом. Дальше я обхожу nginx и делаю curl напрямую к приложению на его локальном порту. Если это не работает, журнал или лог приложения скажут почему. Если работает, проблема в конфиге прокси или в TLS, что наглядно показывает curl -v.
Q3Что показывает ss -tlnp и почему важно, слушает ли сервис 0.0.0.0?
ss выводит сокеты: -t для TCP, -l для слушающих, -n для числовых портов вместо имён и -p для процесса-владельца. Он отвечает на вопрос «что открыто на этой машине и кто это открыл». Адрес важен: 127.0.0.1 означает, что подключаться могут только локальные процессы, 0.0.0.0 (или [::]) — все интерфейсы, включая публичный. База данных или Redis, слушающие 0.0.0.0 на хосте, смотрящем в интернет, в одной ошибке файрвола от того, чтобы оказаться открытыми, а открытые базы находят и атакуют сканеры за считаные часы. Поэтому я привязываю внутренние сервисы к localhost или к интерфейсу частной сети.
Q4Как подключиться к продакшен-базе, которая не открыта в интернет?
Через SSH-туннель. ssh -N -L 5433:127.0.0.1:5432 your_user@server открывает порт 5433 на моём ноутбуке, и всё, что к нему подключается, идёт внутри зашифрованной SSH-сессии и выходит на сервере как локальное подключение к PostgreSQL. Мой клиент БД смотрит на localhost:5433. База продолжает слушать только localhost, так что в файрволе ничего не открывается. Если база работает на отдельном приватном хосте, я прокладываю туннель через сервер приложения или бастион с ProxyJump. Для команды разовые туннели можно заменить VPN вроде WireGuard или управляемым инструментом доступа.
Q5Защищает ли ufw порты, опубликованные Docker?
По умолчанию нет, и это многих удивляет. Когда вы публикуете порт через -p 8080:80, Docker вставляет собственные правила iptables, которые перенаправляют трафик в контейнер раньше, чем проверяются правила ufw, поэтому порт доступен из интернета, даже если ufw его запрещает. Самое простое решение — публиковать порт только на localhost, например -p 127.0.0.1:8080:80, и ставить перед ним nginx. Docker также поддерживает фильтрацию в цепочке DOCKER-USER, а облачный файрвол провайдера перед сервером блокирует трафик в любом случае. Я всегда проверяю снаружи сканированием портов или через curl.
Q6Чем scp отличается от rsync?
Оба копируют файлы по SSH. scp — это простое копирование, как cp между машинами: он всегда передаёт файл целиком и почти не имеет опций. rsync сравнивает источник и назначение и отправляет только изменения, поэтому повторные синхронизации больших каталогов идут быстро. Он умеет докачивать прерванные передачи, сохранять права, владельцев и временные метки с -a, сжимать с -z, исключать по шаблонам и точно зеркалировать каталог с --delete. Поэтому он правильный инструмент для деплоя статических сборок и для бэкапов. Когда участвует --delete, я всегда сначала запускаю его с --dry-run и слежу за завершающим слэшем в пути источника.
Вопросы уровня senior
S1Как дать команде инженеров SSH-доступ к десяткам серверов?
Копирование личных публичных ключей на каждый сервер работает для двух человек и пяти серверов; дальше онбординг и особенно offboarding становятся источником ошибок. Лучшая модель — централизованная идентичность с короткоживущими учётными данными. SSH-сертификаты, подписанные внутренним CA и выдаваемые на несколько часов после single sign-on, означают, что серверы доверяют одному ключу CA и при уходе человека ничего не нужно чистить. Инструменты вроде Teleport, облачные менеджеры сессий или SSH engine в HashiCorp Vault автоматизируют выдачу и добавляют запись сессий. Поместите серверы в частную сеть, доступную через bastion или VPN-mesh вроде WireGuard или Tailscale, чтобы SSH вообще не был открыт публично. Цена — эксплуатация самого CA или инструмента и необходимость аварийного пути доступа на случай, если он недоступен.
S2Чем рискован SSH agent forwarding и что использовать вместо него?
При agent forwarding (-A) удалённый сервер получает сокет к вашему локальному ssh-agent. Приватный ключ никогда не покидает ваш ноутбук, но любой, у кого есть root на этом сервере или доступ к вашему пользователю там, может через этот сокет аутентифицироваться от вашего имени на любом другом хосте, пока вы подключены. На общем или скомпрометированном jump-хосте это реальный риск lateral movement. Для доступа к внутренним серверам более безопасная замена — ProxyJump (-J): соединение с целевым сервером туннелируется через bastion, но аутентификация идёт end to end с вашего ноутбука, так что bastion никогда не касается ваших ключей. Для pull из Git на сервере используйте read-only deploy key для конкретного репозитория, а не пробрасывайте личный агент.
S3Файрвол на хосте, облачный файрвол или оба? Как вы подходите к сетевой защите VPS?
Оба, потому что они ломаются по-разному. Облачный файрвол провайдера фильтрует трафик до того, как он дойдёт до машины, переживает неправильно настроенный или сброшенный набор правил хоста и не обходится правилами iptables от Docker. Файрвол на хосте работает независимо от провайдера, позволяет выразить более тонкие правила, например ограничения по интерфейсу или источнику, и продолжает защищать хост, если облачные правила по ошибке изменят. Ни один из них не заменяет привязку внутренних сервисов к localhost или приватному интерфейсу, которая полностью убирает экспозицию, а не фильтрует её. Сверху добавляются rate limit и fail2ban для эндпоинтов входа, а также CDN или прокси перед веб-трафиком для поглощения DDoS, при этом origin принимает веб-трафик только с диапазонов прокси. Цена — поддерживать согласованность двух наборов правил, в идеале в виде кода.
Глоссарий
- Three-way handshake
- SYN, SYN-ACK, ACK: обмен, открывающий TCP-соединение до отправки каких-либо данных.
- Accept backlog
- Очередь ядра из установленных соединений, ожидающих, пока приложение их примет. Переполнение вызывает таймауты у клиентов.
- TIME_WAIT
- Состояние, которое ненадолго сохраняет сторона, закрывшая соединение первой, чтобы запоздавшие пакеты не путали новое соединение. В больших количествах нормально.
- CLOSE_WAIT
- Удалённая сторона закрыла соединение, а локальное приложение — нет. Большое их число говорит об утечке соединений в приложении.
- Host key
- Собственная пара SSH-ключей сервера. Клиенты запоминают её отпечаток, чтобы обнаружить подмену при следующих подключениях.
- SSH-сертификат
- Ключ пользователя или хоста, подписанный доверенным CA, с именами и сроком действия; заменяет списки ключей на каждом сервере.
- Netfilter
- Фреймворк ядра Linux, который фильтрует, транслирует и отслеживает пакеты. Его настраивают iptables, nftables, ufw и firewalld.
- DNAT
- Destination NAT: подмена адреса или порта назначения пакета; Docker использует её, чтобы пробрасывать опубликованные порты в контейнеры.
- conntrack
- Таблица активных потоков в netfilter, позволяющая правилам пропускать ответный трафик и обеспечивающая работу NAT. У неё есть ограничение размера.
- Stub-резолвер
- Локальный DNS-форвардер, например systemd-resolved на 127.0.0.53, который кэширует ответы и пересылает запросы upstream.
Что изучить дальше
Есть идея продукта?Давайте создадим его.
Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.