OpenSSH Tricks.

OpenSSH & SSHFP How-To.

Когда вы впервые пытаетесь пройти по ssh на новый хост, задавались
ли вы вопросом, а на тот ли хост логинитесь? Ведь в наше неспокойное
время злоумышленники могут каким-либо образом прикинуться тем самым
сервером, на который вы собираетесь залогиниться, и перехватить
ваши ценные пароли. Да-да, мы неоднократно видели в кино как легко
это делается!

В идеальном мире наш системный администратор ведет глобальную базу
хостовых ключей в /etc/ssh/ssh_known_hosts, которая содержит ключи
всех хостов, на которым нам когда либо понадобится доступ. Эти
ключи получены из надежных источников и их достоверность не подлежит
сомнению. Стоп, кому нужен ssh в идеальном мире?

В реальности же при первом логине на хост вы не знаете, на самом
деле этот хост тот, за кого он себя выдает. Ssh(1) пытается помочь
вам в решении этой задачи и предлагает вам сравнить отпечаток хоста,
подразумевая, что вы знаете эталонный отпечаток. Но спросите себя
– как много хостовых отпечатков вы знаете? Подозреваю, что ни
одного. В подавляющем количестве случаев на запрос ssh клиента о
совпадении отпечатков пользователь просто отвечает “да-да, все
верно”. При этом хостовый ключ записывается в вашу локальную базу
ключей, которая по умолчанию хранится в файле ~/.ssh/known_hosts.
Кстати, этa база может стать дополнительным источником информации
о вас. Кстати #2 – опция HashKnownHosts испортит праздник излишне
любопытным (см. Фокус#2 ниже).

RFC4255[1] предлагает новый способ хранения хостовых отпечатков при
помощи DNS. Фактически, вводится новый вид записи (resource record,
RR) в зонном файле. Формат этой записи подробно описан в [1] и
выглядит следующим образом:

IN SSHFP

OpenSSH[2] имеет поддержку данного RFC, так почему бы ею не
воспользоваться?

Решено, пробуем. Сначала сформируем записи в DNS. В этом нам
поможет утилита sshfp[3]. Во FreeBSD это ports/dns/sshfp/.

$ sshfp -ts ahost.example.com
no hostkey alg

ahost.example.com. IN SSHFP 2 1 c8f89de1164557de51e8207b29c8b4bd06acc634

Приведенную выше запись мы добавляем в зону example.com, инкрементируем
ее serial numbler и заставляем named перечитать зонный файл.

Проверяем, что наша запись появилась в DNS:

$ host -t sshfp ahost.example.com
ahost.example.com has SSHFP record 2 1 C8F89DE1164557DE51E8207B29C8B4BD06ACC634

Похоже на правду.

Теперь наша задача научить ssh проверять отпечаток в DNS. Для этого
на хосте, с которого вы будете запускать ssh, редактируем файл
~/.ssh/config (или system wide config file /etc/ssh/ssh_config) и
добавляем туда следующие строки:

Host ahost.example.com
VerifyHostKeyDNS yes
#StrictHostKeyChecking yes
#HashKnownHosts yes

Вместо “ahost.example.com” вы можете поставить “*”, тогда
конфигурационные опции ниже будут относиться ко всем хостам.

Убеждаемся, что ssh начал сверять отпечаток с информацией в DNS:

$ ssh -vvv ahost.example.com 2>&1| grep -i dns
debug3: verify_host_key_dns
debug1: found 1 insecure fingerprints in DNS
debug1: matching host key fingerprint found in DNS
The authenticity of host ‘ahost.example.com (10.0.0.1)’ can’t be established.
DSA key fingerprint is ab:35:6b:40:5d:74:08:91:71:dc:eb:93:8e:98:7e:4d.
Matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)?

Как видим, ssh нашел отпечаток и он совпал с отпечатком ключа хоста
ahost.example.com.

Как будет выглядеть этот же процесс, если на хосте ahost.example.com
по каким либо причинам изменится публичный ключ? Для приданию
сюжету остроты раскомментируем StrictHostKeyChecking в ~/.ssh/config.
Тогда картина будет выглядеть примерно так:

$ ssh ahost.example.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the DSA host key has just been changed.
The fingerprint for the DSA key sent by the remote host is
ab:35:6b:40:5d:74:08:91:71:dc:eb:93:8e:98:7e:4d.
Please contact your system administrator.
Update the SSHFP RR in DNS with the new host key to get rid of this message.
No DSA host key is known for ahost.example.com and you have requested strict checking.
Host key verification failed.

Собственно, этого мы и добивались: злоумышленник пытается провести
MiM атаку, но у него ничего не получается и посрамленный он
возвращается в свой темный угол.

Замечание #1: Думаю, очевидно умолчание, что мы доверяем нашим DNS
серверам.

Замечание #2: Если есть логин на ahost.example.com, то возможно
сгенерировать записи оттуда при помощи ssh-keygen(1):

ahost$ ssh-keygen -r ahost.example.com.
ahost.example.com. IN SSHFP 1 1 4806f8f777ff6e7575e99cbcf8c1325f20304628
ahost.example.com. IN SSHFP 2 1 c8f89de1164557de51e8207b29c8b4bd06acc634

Фокус #2: Лирическое отступление по поводу HashKnownHosts.
———————————————————-

При использовании опции HashKnownHosts хостовый ключ в файле
known_hosts выглядит так:

|1|dUGlfqr+3lXuyAC1gW7gotFUPII=|/W03FP/KkPGogjnYLQL1jd3ylEw= 1024 33 12828<...>

Первое поле – это захешированный hostname.

Если же эту опцию не использовать, то ключ будет выглядеть так:

ahost.example.com 1024 33 12828<...>

В данном случае хостнейм виден невооруженным взглядом. Так как
~/.ssh/known_hosts довольно часто бывает world readable, то любой
пользователь в системе может подсмотреть вашу базу хостов. Сама
по себе эта база несекретная, но в качестве дополнительно инструмента
социальной инженерии вполне сгодится.

Еще одно замечание про HashKnownHosts, из ssh_config(5):

Note that existing names and addresses in known hosts files will not
be converted automatically, but may be manually hashed using
ssh-keygen(1).

Кстати, теперь OpenSSH умеет писать в know_hosts не только имя
хоста, но и порт, для которого получен хостовый ключ. Это очень
удобно, когда применяются различные трюки с форвардингом портов.
Например, на ahost.example.com на порту 22/tcp слушает sshd, а на
порту 52222 работает проброс на anotherhost.example.com на порт
22/tcp. Теперь для хоста ahost.example.com в моем локальном
know_hosts две записи по числу портов, на которые я осуществлял
логин и ssh клиент не ругается страшными словами о несовпадении
хостовых ключей.

Фокус #3: Control Master.
————————-

OpenSSH теперь имеет забавную функциональность, которую проще
продемонстрировать на примере. Запускаем команду:

$ ssh -o ‘ControlPath=/tmp/%h’ -o ‘ControlMaster=yes’ ahost.example.com

Данный процесс будет выступать мастером. Он создаст в /tmp служебный
сокет, %h в спецификации пути заменит на имя хоста. Т.е. в результате
получим UNIX domain socket /tmp/ahost.example.com.

Вторая команда в соседнем терминале:

$ ssh -o ‘ControlPath=/tmp/%h’ -o ‘ControlMaster=no’ ahost.example.com

пропустит вас на хост практически мгновенно и безо всякой авторизации.
Как уже, наверное, понятно второй ssh будет работать с ahost.example.com
не через обычное TCP соединение, а через служебный сокет и через
уже установленный канал в первой сессии.

Я долго думал о практическом смысле данного функционала, пока
подсказка не пришла от Руслана Ермилова – вторая и последующие
сессии работают через уже установленное соединение и процесс логина
на сервер проходит существенно быстрее. В ряде случаев, когда
выполняется batch jobs на удаленном сервере, это существенно экономит
время.

Ну и не надо забывать о существенной экономии TCP соединений! 🙂

SSH escape sequences.
———————

Регулярно встречаю вопрос, связанные с SSH escape sequences.
Оказывается, не так уж много людей на планете знаю как, например,
закрыть “зависшее” ssh соединение. Приятно ощущать себя гуру, что
и говорить. Раскрою очередной секрет шаолиня. Итак, если в ssh
сессии последовательно нажать “~?” (без двойных кавычек),
то получим такое сообщение от OpenSSH клиента:

%Supported escape sequences:
%~. – terminate connection
%~B – send a BREAK to the remote system
%~C – open a command line
%~R – Request rekey (SSH protocol 2 only)
%~^Z – suspend ssh
%~# – list forwarded connections
%~& – this message
%~? – this message
%~~ – send the escape character by typing it twice
%(Note that escapes are only recognized immediately after newline.)

Смешно – когда я делал paste этого сообщения, у меня закрылась ssh
сессия.

Как видим, ответ на вопрос выше – нажать “~.”. Полезно
помнить, что при последовательном заходе по ssh с хоста на хост,
фактически мы имеем дело с несколькими ssh сессиями. Если вы хотите
передать escape seq конкретной сессии, например второй или третьей,
то вам необходимо расcчитать количество тильд, которые придется
ввести. Например, для того, чтобы засаспендить третью сессию, вам
нужна следующая комбинация: “~~~^Z”.

Отдельно стоит упомянуть последовательность ~C. Посмотрим, что это
такое на самом деле. После ввода “~C” попадаем в приглашение
ssh клиента:

%ssh> ?
%Commands:
% -L[bind_address:]port:host:hostport Request local forward
% -R[bind_address:]port:host:hostport Request remote forward
% -KR[bind_address:]port Cancel remote forward
% !args Execute local command

Хорошо видно, что в данной командной строке мы можем интерактивно
управлять локальным и удаленным форвардингами (ключи командной
строки -L и -R) и выполнять команды в локальном шелле. Полезно.

Do you know about ForceCommand?
——————————-

В OpenSSH появилась директива sshd(8) ForceCommand. Эта опция
позволяет принудительно задать команду, которая будет выполнена при
заходе пользователя на хост. Все команды, указанные пользователем
в командной строке, будут проигнорированы. Более подробно, как
обычно, написано в sshd_config(5).

Экспериментируйте с этой опцией аккуратно, так как если вы укажете
ее глобально для всех пользователей, то может получиться так, что
больше вы на этот хост не залогинитесь 🙂

Фокус #4: Match.
—————-

Да, это опять новая и полезная директива. Покажем простой пример.
Добавляем в /etc/ssh/sshd_config следующие записи:

Match User ma?im
ForceCommand /usr/local/bin/figlet bye babe

Теперь для все пользователей, чей login name совпадает с указанной
маской, принудительно будет выполняться указанная команда. В
качестве критерия можно указывать User, Group, Host, и Address.
Какие опции можно переопределять – прочтите самостоятельно в
sshd_config(5).

Еще несколько забавных примеров[4]:

Match Address 192.168.32.*,127.0.0.1
AllowTcpForwarding yes
GatewayPorts no

Match User bar,baz
AllowTcpForwarding yes

Match Host t*
AllowTcpForwarding yes

Думаю, эти примеры достаточно прозрачны, чтобы нуждаться в объяснении.

В [4] наглядно показано, какие новые директивы будет поддерживать
Match в будущих версиях OpenSSH.

OpenSSH Certkey.
—————-

http://lists.freebsd.org/pipermail/freebsd-current/2006-November/067427.html
http://lists.freebsd.org/pipermail/freebsd-current/2006-November/067428.html

– — Примечания.

Все рассуждения велись для OpenSSH 4.4p1, bind 9.x.

Постоянный URL статьи: http://maxim.int.ru/tricks/sshfp.txt
Замечания, комментарии, ценные призы: Maxim Konovalov

1. RFC4255, Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints.
http://www.ietf.org/rfc/rfc4255.txt

2. OpenSSH Project, http://openssh.org/

3. http://freshmeat.net/projects/sshfp/

4. http://bugzilla.mindrot.org/show_bug.cgi?id=match

Scroll to top