Все уроки
Linux и серверыСредний14 мин чтенияВ моём стеке

Сеть в Linux и SSH

IP и порты, отладка соединений, правильные SSH-ключи, туннели и базовый файрвол.

  • IP & ports
  • SSH keys
  • ufw
  • ss / curl
  • DNS tools
Шаг 1/6
Сервер с открытым SSH. Приходит хакер со списком паролей. fail2ban на посту (слегка сонный).

За 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, принимает соединения на всех интерфейсах, включая публичный.

bash
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 позволяет обойти локальный кэш.

bash
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 предупредит вас, потому что это может означать, что кто-то встал посередине.

bash
# 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 и держите текущую сессию открытой, пока тестируете новую, чтобы ошибка не отрезала вам доступ. Некоторые облачные образы кладут в этот каталог собственный файл, который снова включает пароли, так что проверьте и его.

bash
# ~/.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-конфиг, так что алиасы хостов работают.

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

bash
# 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, если им действительно не нужно быть публичными.

bash
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-портов.

Лучшие практики

  1. Используйте ключи ed25519 с парольной фразой и ssh-agent; по ключу на устройство, чтобы можно было отозвать один.
  2. Отключите PasswordAuthentication и PermitRootLogin и проверьте конфиг через sshd -t перед reload.
  3. Держите вторую SSH-сессию открытой, пока меняете настройки SSH или файрвола.
  4. Входящий трафик по умолчанию запрещён; откройте только 22, 80 и 443, а файрвол провайдера используйте как второй слой.
  5. Привязывайте базы данных, Redis и серверы приложений к 127.0.0.1 или частной сети, никогда к 0.0.0.0 на публичном хосте.
  6. Добавьте fail2ban, чтобы банить IP, которые раз за разом проваливают вход по SSH или на сайт.
  7. Запускайте 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 показывает размеры очередей у слушающих сокетов.

bash
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 в основном происходит автоматически.

bash
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 показывает всё в одном месте.

bash
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, пока не переподключатся, что важно при миграциях.

bash
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

Реальные ситуации

01

Перенос 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.

02

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.

03

Приложение через несколько часов перестаёт принимать соединения

Проблема

Сервис на 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.

Есть идея продукта?Давайте создадим его.

Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.