Що записано в файлі .ssh/known_hosts


Кожен раз, коли ми підключаємося по протоколу ssh до сервера ssh клієнт перевіряє збігається публічний ключ для цього сервера з тим, який був минулого разу (принаймні так рекомендує робити стандарт ssh). У OpenSSH список відомих серверів ключів зберігається в файлі known_hosts. Під катом коротко про те, що і як конкретно там зберігається.

Всі експерименти проводилися на Linux (Debian/Mint/Ubuntu). За розташування і зміст файлів інших ОС не ручаюся.

Підключаючись перший раз до сервера ssh, ми бачимо приблизно таке повідомлення:
The authenticity of host ‘192.168.0.2 (192.168.0.2) can’t be established.
RSA key fingerprint is SHA256:kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg.
Are you sure you want to continue connecting (yes/no)?Якщо погодитися, то в файл ~/.ssh/known_hosts додасться такий рядок:
|1|CuXixZ+EWfgz40wpkMugPHPalyk=|KNoVhur7z5NAZmNndtwWq0kN1SQ= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeif4ooouhwvoyrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhhbjyt5yz3j5wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdm0ojd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcuu1ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMjzntlbcblwzvтут через пропуск записані три елементи: хеш від імені сервера, назва використовуваного асиметричного алгоритму і публічний ключ сервера. Розглянемо їх по черзі.

А якщо почитати інструкціюнасправді згідно мануалу до Убунту там можуть бути ще 2 поля, так само відокремлені пропусками:

  • на початку рядка може знаходитися відмітка “@cert-authority” або “@revoked”, що означають, відповідно, що в цьому рядку записаний публічний ключ ЦА або що цей ключ був відкликаний і не може бути використаний.
  • в кінці рядка може бути довільний коментар

Ім’я сервера

У прикладі хеш від імені сервера (хоста) виглядає так:
|1|CuXixZ+EWfgz40wpkMugPHPalyk=|KNoVhur7z5NAZmNndtwWq0kN1SQ= насправді тут може бути записано і ім’я хоста у відкритому вигляді або маска, що задає безліч допустимих імен. Але у мене за замовчуванням зберігається хэшироване ім’я. Запис розділений на 3 частини символом “|”. Перша частина — алгоритм хешування. «1» відповідає HMAC-SHA1 (інших не бачив). Друга частина — сіль (ключ для HMAC). Третя частина — власне хеш (висновок HMAC).

Читайте також  Telegram погодився передавати спецслужбам [але не російським] IP-адреси і номери деяких користувачів

Перевіряємо

from base64 import b64decode
import hmac

salt = b64decode("CuXixZ+EWfgz40wpkMugPHPalyk=")
host = b'192.168.0.2'
hash = hmac.HMAC(salt, host, 'sha1').digest()
print(b64encode(hash).decode())

> ‘KNoVhur7z5NAZmNndtwWq0kN1SQ=’

Асиметричний алгоритм

В RFC-4253 перераховано 4 асиметричних алгоритму: ssh-dss (за стандартом обов’язковий, але вважається слабким і починаючи з OpenSSH7.0 вимкнено за замовчуванням), ssh-rsa (рекомендований), pgp-sign-rsa (опціональний), pgp-sign-dss (опціональний). За замовчуванням в Linux генеруються ключі перших двох видів і для не згаданих в RFC алгоритмів на еліптичних кривих. Перевага віддається останнім, однак клієнт може вибрати алгоритм опцією HostKeyAlgorithms.

Як перевірити потрібний (не за замовчуванням) відбиток ключаЦе може бути корисно, якщо, наприклад, при першому заході на сервер ви хочете перевірити відбиток ключа, а знаєте тільки відбиток ключа ssh-rsa. Тоді можна підключитися такою командою:
ssh root@192.168.0.2 -o HostKeyAlgorithms=ssh-rsa
Якщо потрібно задати ще й алгоритм хешування ключа, то можна використовувати опцію FingerprintHash. Наприклад, якщо відомий тільки md5 від ssh-rsa можна підключитися так:
ssh root@192.168.0.2 -o HostKeyAlgorithms=ssh-rsa -o FingerprintHash=md5

Публічний ключ

Публічний ключ в known_hosts збігається з тим, який записаний у файлі /etc/ssh/ssh_host_rsa_key.pub на сервері (замість rsa підставити назву використовуваного алгоритму). Якщо зняти Base64 кодування, то всередині буде ще раз назву алгоритму і власне компоненти ключа.

А чого б і не зняти Base64

b'x00x00x00x07ssh-rsax00x00x00x03x01x00x01x00x00x01x01x00x9ex88^x0e8xe5!Zxf3x98xaex1fxdexe2xafuxfa,xfex8bxd4xb4xb3s6,xfaxbex18x08Qx96x12xadxe6xf2xb2x11Wzxc7xa9xb6xe9xc7=xf1x8fx92Ayxc2x07xd1x96yV$xf29Ex1cExe7cx86x16ybxc3xc1r!x1cx12Xxb7x9cxb3xde>Vx92x0fyxc7xe9x8fxbcxefE - dxfctvx19xb1xf9xabxa1~x85x92%.cxbar"x12x99~x13xb5xc1xb5xb3x0ex12xc2x84xc0x0exbaxe1txcdx0e%xdfx9cxe4%<x8axa0x9bsxa3xf3x9dx86xcbepxafxa8xf6SYxe4x9bLxb5x1cRxedSx8bxeexf1x1bx04xfcx0c_xad#"xc5qOxb3xc5xddx10x9fx02Hlxdbxdexc2Axecxd0xbbx0c9aU??xcdwExe2Uxxa6_xebxa0x080Yxffx8axbc5Lx07xf5!xb3x08xc0/x9ezxfex0fR<_kxe1Jxe4lNxc4x17/x93xf7xbdxffx1ex94<Ot:xcc'3mx94x10x9b-lxd5'

Видно, що йдуть 4 байта, в які записана довжина поля, потім саме поле і т. д. Перше поле — назва алгоритму, інші залежать від конкретного алгоритму. У наведеному вище ключі 3 поля:

b'c ssh-rsa' - назва 
b'x01x00x01' - публічна експонента
b'x00x9ex88^x0e8xe5!Zxf3x98xaex1fxdexe2xafuxfa,xfex8bxd4xb4xb3s6,xfaxbex18x08Qx96x12xadxe6xf2xb2x11Wzxc7xa9xb6xe9xc7=xf1x8fx92Ayxc2x07xd1x96yV$xf29Ex1cExe7cx86x16ybxc3xc1r!x1cx12Xxb7x9cxb3xde>Vx92x0fyxc7xe9x8fxbcxefE - dxfctvx19xb1xf9xabxa1~x85x92%.cxbar"x12x99~x13xb5xc1xb5xb3x0ex12xc2x84xc0x0exbaxe1txcdx0e%xdfx9cxe4%<x8axa0x9bsxa3xf3x9dx86xcbepxafxa8xf6SYxe4x9bLxb5x1cRxedSx8bxeexf1x1bx04xfcx0c_xad#"xc5qOxb3xc5xddx10x9fx02Hlxdbxdexc2Axecxd0xbbx0c9aU??xcdwExe2Uxxa6_xebxa0x080Yxffx8axbc5Lx07xf5!xb3x08xc0/x9ezxfex0fR<_kxe1Jxe4lNxc4x17/x93xf7xbdxffx1ex94<Ot:xcc'3mx94x10x9b-lxd5' - модуль N (0x101 * 8 = 2048 біт)

Відбиток ключа (Fingerprint)

Відбиток ключа, який пропонується звірити при першому підключенні — це відповідний хеш (у прикладі — SHA256) від публічного ключа з минулого пункту і з /etc/ssh/ssh_host_rsa_key.pub, закодований у base64 для хеш функцій сімейства SHA або в hex для MD5.

Читайте також  Антифрод. Закон. Стільниковий зв'язок: мої питання

Вважаємо

from hashlib import sha256
from base64 import b64decode, b64encode
pub_key_bin = b64decode("AAAAB3NzaC1yc2EAAAADAQABAAABAQCeif4ooouhwvoyrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhhbjyt5yz3j5wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdm0ojd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcuu1ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMjzntlbcblwzv")
hash = sha256(pub_key_bin).digest()
fingerprint = b64encode(hash)
print(fingerprint)

> b'c kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg='

Бачимо, що хеш і правда збігається з відбитком, показаним при першому підключенні (цитата на початку статті), з точністю до символу “=” в кінці.

Тут невелика програмка для пошуку хостів у файлі known_hosts, що з’явилася в процесі експериментів.

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *