SSH в глубину: ключи, config, tunneling, multiplexing
ssh — это не просто «подключиться к серверу». Это один из самых мощных инструментов в арсенале DE: 1) безопасный shell на удалённой машине, 2) tunneling (доступ к закрытым сервисам через jump host), 3) port forwarding, 4) file transfer (scp/rsync поверх ssh), 5) git remote (git под капотом — ssh+protocol).
В этом уроке — за пределы базового ssh user@host: ключи, конфиг, tunneling для доступа к internal databases, multiplexing для скорости, jump hosts для secure access.
Базовое подключение
# Простое подключение
ssh [email protected]
# Нестандартный порт
ssh -p 2222 [email protected]
# С приватным ключом (если не дефолтный)
ssh -i ~/.ssh/work_key [email protected]
# Запуск команды на удалённой машине
ssh [email protected] "uptime; df -h"
# Интерактивный + команда (сначала вход, потом выполнить)
ssh -t [email protected] "tmux attach -t work"
-t — force TTY allocation, нужно для интерактивных команд (vim, tmux, htop) при удалённом запуске.
TLS handshake: как устанавливается защищённое соединение SSH-ключи для Git: настройка и несколько аккаунтов
SSH ключи: ed25519 — современный выбор
В 2026 году rsa-4096 — это устаревшее. Современный стандарт — ed25519:
Современный выбор vs legacy
# Сгенерировать ed25519 ключ
ssh-keygen -t ed25519 -C "[email protected]"
# С нестандартным именем
ssh-keygen -t ed25519 -f ~/.ssh/work_key -C "[email protected]"
# Без passphrase (для скриптов — но безопаснее с passphrase + ssh-agent)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/automation_key
Файлы:
~/.ssh/id_ed25519— приватный ключ (никогда не shared).~/.ssh/id_ed25519.pub— публичный ключ (его можно показывать).
# Скопировать public key на сервер
ssh-copy-id [email protected]
# Или вручную
cat ~/.ssh/id_ed25519.pub | ssh [email protected] "cat >> ~/.ssh/authorized_keys"
После этого ssh [email protected] будет логиниться без пароля.
ssh-agent — управление ключами
Если у ключа passphrase — каждый раз спрашивает. Скучно. ssh-agent хранит расшифрованные ключи в памяти:
# Запуск ssh-agent (обычно делается автоматически)
eval $(ssh-agent -s)
# Добавить ключ
ssh-add ~/.ssh/id_ed25519
# Спросит passphrase один раз
# Список ключей в агенте
ssh-add -l
# Удалить все
ssh-add -D
В macOS ssh-agent встроен в Keychain — ssh-add --apple-use-keychain ~/.ssh/id_ed25519 запомнит passphrase в Keychain. На Linux — обычно agent запускается из систем-сервиса или из .zshrc/.bashrc.
~/.ssh/config — алиасы и опции
Самая полезная и недооценённая возможность. Вместо запоминания длинных команд:
# Без config — каждый раз вводить
ssh -i ~/.ssh/work_key -p 2222 -o StrictHostKeyChecking=no [email protected]
# С config
ssh prod-airflow
Файл ~/.ssh/config:
# Глобальные defaults
Host *
ServerAliveInterval 60
ServerAliveCountMax 5
AddKeysToAgent yes
# Production Airflow
Host prod-airflow
HostName 10.0.5.42
Port 2222
User admin
IdentityFile ~/.ssh/work_key
# Staging
Host stage
HostName stage.company.com
User levo
IdentityFile ~/.ssh/work_key
# All hosts in our cloud (wildcards work)
Host *.internal.company.com
User ec2-user
IdentityFile ~/.ssh/aws_key
ProxyJump bastion.company.com
Часто встречающиеся в DE-конфигах
Tunneling: -L и -R
Это волшебная фишка SSH, незаменимая для DE.
Local Forward: -L (доступ к remote сервису локально)
Сценарий: на сервере production бежит PostgreSQL на порту 5432, но он недоступен извне. Хочется подключиться к нему с локального laptop.
# Локальный порт 5432 -> server.com:5432
ssh -L 5432:localhost:5432 [email protected]
Теперь psql -h localhost -p 5432 на ноуте подключается к Postgres на сервере через SSH-туннель.
# Можно проксировать дальше — на third party host через jump host
ssh -L 5432:internal-db.local:5432 [email protected]
# Что значит:
# Local port 5432 -> bastion -> внутри сети -> internal-db.local:5432
Трафик идёт через SSH туннель — даже если remote сервис недоступен напрямую
Remote Forward: -R (доступ к local сервису удалённо)
Реже используется. Сценарий: на ноутбуке локально работает webhook receiver на порту 8080, но нужно показать его коллеге, у которого только доступ к удалённому серверу.
# Remote port 9000 на server -> localhost:8080 на вашем ноуте
ssh -R 9000:localhost:8080 [email protected]
# Теперь на server.com:9000 проксируется в ваш ноутбук
Это используется реже, чем -L, но иногда полезно (например, для ngrok-like сценариев без ngrok).
Dynamic Forward: -D (SOCKS proxy)
# SOCKS5 proxy на локальном порту 1080, трафик через ssh
ssh -D 1080 [email protected]
# Теперь можно настроить браузер на SOCKS5 localhost:1080
# Весь трафик идёт через ssh-туннель
Бывает полезно когда нужно «выйти в интернет» через remote сервер (доступ к internal resources, обход GeoIP блокировок).
ProxyJump — bastion access pattern
Production DE-команды редко выставляют все серверы в интернет. Стандартный паттерн: bastion host (jump server) — единственный сервер с открытым SSH портом, через который идёт доступ ко всему остальному.
# Старый стиль — два прыжка вручную
ssh [email protected]
# затем
ssh internal-server.local
# Современный стиль — ProxyJump
ssh -J bastion.company.com internal-server.local
# Через config — ещё лучше
~/.ssh/config:
Host bastion
HostName bastion.company.com
User admin
IdentityFile ~/.ssh/work_key
Host *.internal.company.com
User ec2-user
IdentityFile ~/.ssh/work_key
ProxyJump bastion
Теперь:
ssh airflow.internal.company.com
# Прозрачно через bastion
Также работает с scp и rsync — они используют тот же config.
Multiplexing — ускорение повторных подключений
Если часто переподключаетесь к одному серверу (например, в скрипте делаете 100 ssh-вызовов) — каждое подключение делает full TLS handshake (~1 секунда). Multiplexing — один канал переиспользуется:
Host prod-*
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h:%p
ControlPersist 10m
Первое подключение создаёт socket в ~/.ssh/sockets/. Последующие переиспользуют — moiстантое подключение.
# Создать директорию для sockets
mkdir -p ~/.ssh/sockets
# Теперь повторные ssh — мгновенные
ssh prod-airflow # ~1 секунда (создание мастера)
ssh prod-airflow # 0.1 секунды (через мастера)
DE-польза: bash-скрипты с многими ssh-вызовами на один хост (например, проверка кластера Hadoop из 20 нод) — резкое ускорение.
scp и sftp (превью, подробно в уроке 03)
# Скопировать файл на remote
scp local.txt [email protected]:/path/
# С remote на local
scp [email protected]:/path/file.csv ./
# Папку рекурсивно
scp -r /local/dir [email protected]:/remote/dir/
scp устарел и медленный. Для production — используйте rsync (следующий урок). Здесь упомянут как «он существует и есть в любом дистре».
sshfs — монтировать remote директорию
Иногда хочется работать с remote директорией как локальной (вьюинг через свой Finder/VS Code):
# Установить (Linux)
sudo apt install sshfs
# На macOS — нужен macFUSE, может быть проблематично
# Смонтировать
mkdir ~/remote-data
sshfs [email protected]:/var/data ~/remote-data
# Работать как с локальной директорией
ls ~/remote-data
cat ~/remote-data/log.txt
vim ~/remote-data/file.py # редактируется на сервере!
# Размонтировать
fusermount -u ~/remote-data
# или (macOS)
umount ~/remote-data
Удобно для разовых задач. Минусы: медленнее чем нативная FS, может «зависнуть» при потере связи.
DE-сценарии
1. Конфиг для production stack
# ~/.ssh/config
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
UseKeychain yes # macOS only
Host bastion
HostName bastion.company.com
User admin
IdentityFile ~/.ssh/work_key
Host airflow
HostName airflow.internal.company.com
User ec2-user
IdentityFile ~/.ssh/work_key
ProxyJump bastion
# Tunnel Airflow UI и Postgres
LocalForward 8080 localhost:8080
LocalForward 5432 localhost:5432
Host spark-master
HostName spark.internal.company.com
User hadoop
IdentityFile ~/.ssh/work_key
ProxyJump bastion
LocalForward 4040 localhost:4040 # Spark UI
LocalForward 8088 localhost:8088 # YARN
Теперь:
ssh airflow
# Подключение + автоматический tunnel для UI и Postgres
# В браузере: http://localhost:8080 -> Airflow UI
# В psql: psql -h localhost -p 5432
2. Безопасное подключение к Postgres production
# Через tunnel, без открытого порта на сервере
ssh -L 5432:localhost:5432 -N [email protected]
# -N = no remote command (просто tunnel)
# В другом терминале
psql -h localhost -p 5432 -U analyst
3. Запуск bash-скрипта на 20 нодах
# Список нод
NODES=(node-{01..20}.cluster.local)
# Запуск с multiplexing — намного быстрее
for n in "${NODES[@]}"; do
ssh -o ControlMaster=auto -o ControlPath=~/.ssh/sockets/%r@%h:%p "$n" "df -h /data" &
done
wait
4. SSH config для кучи AWS EC2
Host i-*
User ec2-user
IdentityFile ~/.ssh/aws_key
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
Подключение через AWS Systems Manager Session Manager, без публичных SSH портов вообще.
Подводные камни
”Permission denied (publickey)”
Самая частая ошибка. Diagnose:
ssh -v [email protected]
# Verbose покажет:
# - какой ключ пробуется
# - принят/отклонён
# - что говорит сервер
Чек-лист:
- Правильный ли ключ указан (
IdentityFileв config или-i). - Правильные ли права на ключе:
chmod 600 ~/.ssh/id_ed25519. - Публичный ключ в
~/.ssh/authorized_keysна сервере. - Права на
~/.ssh/на сервере:chmod 700 ~/.ssh,chmod 600 authorized_keys.
Подвисает соединение
ServerAliveInterval 60 в config — keepalive раз в минуту. Сеть не успевает разорвать через NAT timeout.
Host key changed warning
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Сервер пересоздали, или MITM-атака. Если знаете что сервер перенастроили:
ssh-keygen -R server.com
# Удаляет старый ключ из known_hosts
# Подключитесь заново — будет новый prompt
В production-окружении этот warning не игнорировать — может быть атака.
Попробуй сам
# 1. Сгенерируй ключ
ssh-keygen -t ed25519 -f ~/.ssh/test_key -N ""
# 2. Посмотри
cat ~/.ssh/test_key.pub
# 3. Создай простой config
cat > ~/.ssh/test_config <<'EOF'
Host test-localhost
HostName localhost
User $USER
IdentityFile ~/.ssh/test_key
EOF
# 4. Tunneling — поставь временно nginx локально (или uvicorn)
# В одном терминале:
python3 -m http.server 8000
# В другом терминале:
ssh -L 9000:localhost:8000 $USER@localhost
# Если ssh-доступ к самому себе настроен
# В третьем терминале:
curl http://localhost:9000
# Через тунель идёт к python http.server
# 5. Multiplexing
mkdir -p ~/.ssh/sockets
echo 'Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h:%p
ControlPersist 10m' >> ~/.ssh/config
# Первое подключение
time ssh $USER@localhost "uptime"
# Второе — должно быть быстрее
time ssh $USER@localhost "uptime"
Cross-link: предыдущий урок 01 — curl/wget. Следующий урок 03 — scp/rsync для копирования файлов через ssh. Модуль 17 — production-bash может использовать ssh для деплоя.