Learning Platform
Глоссарий Troubleshooting
Урок 13.02 · 28 мин
Средний
sshssh-keygenssh-configtunnelingjump hosted25519multiplexing

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:

SSH key types — что использовать

Современный выбор vs legacy

ed25519Современный (с 2014). Маленький размер ключа (~80 байт), быстрый, безопасный по дизайну. Поддерживается всеми современными SSH (>= OpenSSH 6.5, 2014)
rsa-4096Legacy, ещё используется. Большой ключ, медленнее. Используйте только для совместимости со старыми системами
ecdsaКриптографически OK, но скептически воспринимается из-за привязки к NIST-кривым (которые могут быть backdoored)
rsa-2048Устаревший, недостаточная стойкость. Не используйте
# Сгенерировать 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
Полезные опции в ~/.ssh/config

Часто встречающиеся в DE-конфигах

HostNameРеальный IP или DNS. Может отличаться от Host (alias)
UserИмя пользователя на удалённой стороне
PortНестандартный порт SSH
IdentityFileКакой ключ использовать
ProxyJumpПодключиться через jump host (bastion)
LocalForwardLocal port forwarding из config (вместо -L)
ServerAliveIntervalСлать keepalive каждые N секунд — соединение не отвалится по таймауту NAT
ControlMaster autoMultiplexing — повторные подключения через тот же канал (быстро)
StrictHostKeyCheckingПроверять host key. По умолчанию ask (запрос). no — не безопасно но удобно для dev

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 -L Local Forward

Трафик идёт через SSH туннель — даже если remote сервис недоступен напрямую

Ваш ноутpsql -h localhost -p 5432
SSH tunnel
bastionBastion server, виден из internet
internal network
internal-db.local:5432БД, не видна снаружи

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 покажет:
# - какой ключ пробуется
# - принят/отклонён
# - что говорит сервер

Чек-лист:

  1. Правильный ли ключ указан (IdentityFile в config или -i).
  2. Правильные ли права на ключе: chmod 600 ~/.ssh/id_ed25519.
  3. Публичный ключ в ~/.ssh/authorized_keys на сервере.
  4. Права на ~/.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 для деплоя.


Проверка знанийKnowledge check
У вас на ноутбуке нужно подключаться к Airflow UI и Postgres, которые работают на production сервере airflow.internal.company.com. Прямого SSH-доступа нет — только через bastion.company.com. Опишите конфиг ~/.ssh/config, который позволит одной командой ssh airflow получить shell + автоматически проброс портов 8080 (Airflow UI) и 5432 (Postgres).
ОтветAnswer
Конфиг ~/.ssh/config:\n\nHost bastion\n HostName bastion.company.com\n User admin\n IdentityFile ~/.ssh/work_key\n\nHost airflow\n HostName airflow.internal.company.com\n User ec2-user\n IdentityFile ~/.ssh/work_key\n ProxyJump bastion\n LocalForward 8080 localhost:8080\n LocalForward 5432 localhost:5432\n\nКак работает: 1) ssh airflow — алиас, SSH использует airflow.internal.company.com для подключения. 2) ProxyJump bastion — SSH сначала подключится к bastion (используя его config с HostName, User, IdentityFile), потом через bastion подключится к airflow. 3) LocalForward создаёт SSH tunnel: локальный порт 8080 -> localhost:8080 на сервере airflow (т.е. там где работает Airflow webserver). 4) То же для 5432 (Postgres). После ssh airflow: у вас shell на airflow-сервере, ПЛЮС в браузере http://localhost:8080 откроет Airflow UI, ПЛЮС psql -h localhost -p 5432 подключится к Postgres. Можно добавить ServerAliveInterval 60 чтобы соединение не отваливалось по NAT timeout. Это базовый паттерн для работы с production-инфраструктурой через bastion.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Какой тип SSH-ключа рекомендуется в 2026 году и почему?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5