понедельник, 6 мая 2019 г.

SSH - коротко о главном


 Для лучшего понимания материалов этой статьи убедитесь, что вы хорошо представляете, как работает криптосистема с открытым ключом (Public-key cryptography). Так же необходим опыт использования SSH-клиента типа putty или kitty.

Для подключения к Linux мы используем Secure Shell или, как его называют, SSH. SSH - это криптографический сетевой протокол для защищённой передачи данных через не защищённые сети.  

По сути SSH - это протокол, который реализует клиент-серверную модель и используется для подключения к удалённым компьютерам и запуска команд на них. Но он так же поддерживает туннелирование, что ставит его в один ряд с протоколами туннелирования (о протоколах туннелирования я писал ранее, в статье о VPN [http://www.danshin.ms/2019/04/vpn.html]). А также реализует протоколы SSH file transfer (SFTP) и secure copy (SCP). Для обеспечения защиты канала, SSH использует криптографическую систему с открытым ключом (Public-key cryptography).

Принцип действия Public-key cryptography заключается в использовании ассиметричной пары ключей. Один ключ называют открытым, другой закрытым. Открытый ключ может быть известен всем. При помощи открытого ключа данные шифруются. Расшифровываются они закрытым ключом. Который известен только тому, кому предназначаются данные. Это очень упрощённые и поверхностные сведения о работе Public-key cryptography.

Есть два пути использования защищённого SSH соединения.

Один - это автоматическая генерация пары (открытый/закрытый) ключей, для защиты сети, а затем использования аутентификации пользователя (логин/пароль) для входа в систему.

Другой путь - использование собственной пары ключей. При этом открытый ключ помещается на все системы, куда требуется доступ. Закрытый ключ остаётся у пользователя. Такой подход позволяет авторизовать пользователя без ввода пароля. Но с точки зрения безопасности — это не очень правильный способ. В следующей статье я расскажу, как настроить  SSH аутентификацию при помощи открытого ключа и как сделать всё правильно.

Важно отметить, что закрытый ключ никогда не передаётся по сети. Он всегда остаётся в защищённом хранилище пользователя. SSH только проверяет, владеет ли тот же человек, который предлагает открытый ключ, соответствующим закрытым ключом или нет.

Существует несколько реализаций протокола SSH. Я знаком только с openssh (https://www.openssh.com/). И чаще всего использовал его совместно с CentOS. Поэтому все примеры будут строятся на этой базе. Уверен, что на том базовом уровне, который описан в данной статье, все реализации протокола SSH, на всех OS одинаковы.

CentOS, даже в самой минимальной версии, устанавливает SSH по умолчанию. Сразу после установки мы имеем запущенный демон sshd, готовый принимать наши клиентские подключения на порту 22. Для подключения из Windows мы чаще всего используем putty/kitty. Хотя я недавно обнаружил, что Windows 10 имеет openssh-клиента в установке по умолчанию (https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_overview).


Я ещё не до конца разобрался с работой ssh-клиента под Windows. Где он хранит свои настройки, как их менят, подстраивая работу клиента под себя, поэтому для примера буду использовать kitty (http://www.9bis.net/kitty/). Kitty - это форк putty. На мой взгляд там всё тоже самое, что в putty, но с дополнительными возможностями. Но вы можете использовать и putty. В рассматриваемом контексте их работа будет очень схожа.

Открываем kitty/putty и устанавливаем соединение с сервером. При первом подключении к серверу мы увидим Security Allert как показано на скриншоте. Я поясню важность этого предупреждения и почему не стоит автоматически наживать yes и продолжать работу.


Предупреждение гласит, что server's host key не закеширован в реестре (registry). Имеется ввиду, на стороне клиента. И у Вас нет гарантий в том, что вы подключаетесь именно к тому компьютеру, к которому вы думаете. Давайте разберёмся, что такое "server's host key" и как нам убедится в том, что мы подключаемся именно к тому самому серверу, к которому хотим. Как упоминалось выше, для шифрации трафика, SSH использует пару ключей (открытый/закрытый). При первом подключении клиент запрашивает у сервера открытый ключ, при помощи которого будет шифроваться трафик. А расшифровываться он будет закрытым ключом сервера.

ПРИМЕЧАНИЕ:
На самом деле всё гораздо сложнее. Но мы же тут говорим о базовых вещах. Поэтому я не буду рассказывать о том, что для шифрования, на самом деле, SSH использует симметричный ключ. А ассиметричную пару ключей использует только для того, чтобы безопасно передать симметричный ключ шифрования. Так же называемый secret key или session key. Потому, что в данном контексте это не важно. Всё это происходит автоматически, без нашего ведома и участия. Если перед нами не стоит задача по расшифровке трафика, то единственное, с чем нам приходится иметь дело - это ассиметричная пара ключей - открытый/закрытый ключ. Поэтому ограничимся только ими. Так же я не буду рассказывать о том, что на текущий момент есть две версии протокола SSH1 и SSH2. И не буду рассказывать о различиях между ними. Скажу только, что лучше использовать SSH2.

Так вот в данном случае server's host key - это открытый ключ сервера. MD5-хеш открытого ключа сервера мы видим в сообщении. В данном случае он имеет вид 

d2:d6:d3:7c:cb:eb:c9:ab:6a:4f:22:78:c1:23:ac:86.

СПРАВКА:
"ed25519" - схема подписи EdDSA использующая SHA-512 и Curve25519 (https://ru.wikipedia.org/wiki/EdDSA).

Q/A:
Q: Откуда берётся server's host key?
A: Его сообщает сервер. Он генерит его в момент установки и первого запуска ssh-сервера.

Q: Где сервер его хранит?
A: В /etc/ssh/.

Q: Как убедиться в том, что я подключаюсь именно к тому серверу, к которому хочу.
A: Сверить MD5 открытого ключа из сообщения и с тем, что хранится на сервере.

Q: Как это сделать?
A: Взять открытый ключ и вычислить MD5 функцию. Например, на сервере, к оторому мы подключаемся,  выполнить команду "ssh-keygen -E md5 -lf /etc/ssh/ssh_host_ed25519_key.pub". На выходе мы получим MD5-хеш публичного ключа.



Можно попросить администратора сервера сделать это за нас. Или предоставить нам открытый ключ сервера и вычислить MD5 сервера самостоятельно, например, используя Powershell.

Открытый ключ на сервере - это строка вида

"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDKEmQW2NUcy4waXWiKjI3hDa7YCvE49Eoekjdk2BLcl"

Здесь нас интересует только цифробуквенный код "AAAAC3NzaC1lZDI1NTE5AAAAIDKEmQW2NUcy4waXWiKjI3hDa7YCvE49Eoekjdk2BLcl" - это закодированный открытый ключ в кодировке Base64. Для начала декодируем его.

$serverPublicKey = [System.Convert]::FromBase64String("AAAAC3NzaC1lZDI1NTE5AAAAIDKEmQW2NUcy4waXWiKjI3hDa7YCvE49Eoekjdk2BLcl")

Затем вычислим MD5-хеш полученной строки

$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$hash = [System.BitConverter]::ToString($md5.ComputeHash($serverPublicKey ))



В результате, посмотрев значение переменной $hash, мы увидим строку D2-D6-D3-7C-CB-EB-C9-AB-6A-4F-22-78-C1-23-AC-86. Что полностью совпадает с тем хешем, который мы видим в предупреждении.

ЗАЧЕМ ТАКИЕ СЛОЖНОСТИ? ЧТО НАМ ВСЁ ЭТО ДАЁТ?

Проверив таким образом ключ сервера, и убедившись в том, что мы подключаемся именно туда, куда нужно, мы можем смело нажимать Yes, в диалоговом окне Security Alert, которое нам выдаёт putty/kitty при подключении. Сделав это, наш ssh-клиент закеширует fingerprint. В следующий раз, при подключении к тому же серверу, SSH клиент сравнит полученный от сервера fingerprint с теми, что находятся у него в кеше. И если найдёт совпадение, то подключение пройдёт без предупреждения. До тех пор, пока ключ на сервере не поменяется. Не очистится локалный кеш доверенных ключей или… нас не перенаправят на другой сервер. Вот ради последнего всё это и нужно. Если злоумышленники, каким-то образом, перенаправят нас на другой сервер или подменят его, то мы опять получим предупреждение, но несколько иного вида.



И тогда нам придётся разобраться в ситуации и решить - устанавливать соединение с сервером или нет. Без этого механизма мы были бы в неведении. И могли бы передать на сервер злоумышленников конфиденциальную информацию или получить с него зловредные данные.

В следующей статье я расскажу, как настроить  SSH аутентификацию при помощи открытого ключа. Как правильно сгенерировать собственную пару открытый/закрытый ключ. Как правильно их хранить, что передавать на сервер. Какие настройки требуется выполнить на стороне клиента, а какие на стороне сервера.