SSH (Secure Shell) — это сетевой протокол и стандарт де-факто для удалённого управления Unix-подобными системами. Он обеспечивает безопасное администрирование серверов и передачу файлов через незащищённые сети, шифруя весь трафик и предоставляя надёжные механизмы аутентификации. В отличие от устаревших протоколов вроде Telnet и RSH, SSH гарантирует защиту данных и удобство работы.
В основе работы SSH ключей лежит принцип ассиметричного шифрования. Разберём на примере как оно работает.
SSH-ключи — это пара файлов, используемых для аутентификации при подключении к серверу по протоколу SSH. Эти ключи состоят из:
Приватный ключ шифрует данные, а публичный ключ их расшифровывает.
Но если организация большая, то у неё может быть много шпионов, и тут уже можно действовать двумя способами:
В первом случае нужно заботиться о безопасном хранении только одного ключа - это легко и просто. Но при этом, имея на руках только один ключ можно получить доступ ко всей информации получаемой от всех шпионов.
Во втором - знать какой ключ к какому шпиону относится и так же обеспечить безопасное хранение этой информации. Но зато, если какой-то из ключей будет скомпрометирован, то это не повлияет на данные получаемые от других шпионов.
В которой:
ssh-keygen
Основная утилита для генерации SSH-ключей. Она создаёт пары ключей (публичный и приватный), которые используются для аутентификации.
-t указывает тип ключа.
rsa — алгоритм асимметричного шифрования RSA, широко используемый для SSH.
-b задаёт длину ключа в битах.
4096 — длина ключа, обеспечивающая высокий уровень безопасности. Чем больше число, тем сложнее ключ для взлома, но больше времени требуется на его генерацию и использование.
Обычно используют 2048 или 4096 бит.
-m указывает формат ключа.
PEM — текстовый формат хранения ключей. Этот параметр полезен, если вам нужен ключ в формате, совместимом с другими инструментами, например, OpenSSL.
-f задаёт имя файла для сохранения ключа.
key1 — имя, которое назначено файлам ключей. Оно может быть произвольным.
При запуске, команда просит указать пароль для для шифрования приватного (закрытого) ключа.
Пароль добавляет дополнительный уровень защиты:
Если кто-то украдёт ваш приватный ключ, без пароля он не сможет им воспользоваться.
Приватный ключ будет зашифрован, и каждый раз при его использовании (например, при подключении к серверу) система будет запрашивать этот пароль.
Если пароль оставить пустым (нажать Enter без ввода текста), ключ не будет защищён паролем, и его можно будет использовать без дополнительных вводов.
После выполнения команды создаются:
key1 — приватный ключ (всегда файл без расширения) - ни с кем им не делимся.
key1.pub — публичный ключ в формате OpenSSH - им можно делиться.
Теоретически, это обычные текстовые файлы, которые выглядят как набор бессвязных символов.
Эти ключи готовы к использованию в защищённых соединениях, однако для простого шифрования информации, которое мы пытаемся сейчас изобразить, формат публичного ключа не подходит, и его необходимо конвертировать.
Сделаем это командой ssh-keygen -f key1.pub -e -m PKCS8 > key1.public
Любая криптография кроме OpenSSH требует использование шифрующего ключа в формате PKCS8.
Разница между форматами публичного ключа:
Приступим к шифрованию. Выполним соответствующую команду, в которой укажем использовать публичный ключ, какой файл зашифровать и как назвать зашифрованный файл на выходе: openssl pkeyutl -encrypt -pubin -inkey key1.public -in message.txt -out en_message.txt
Проверяем директорию и видим что появился новый файл
Попытка прочитать содержимое файла обречена на полный провал
Попробуем теперь расшифровать этот файл, указав
приватный ключ и новое имя для итогового файла:
openssl pkeyutl -decrypt -inkey key1 -in en_message.txt -out dec_message.txt
Проверяем и видим что появился новый файл
Итак основной принцип ясен. Переходим на следующий уровень.
SSH соединение.
Для чего в нём ключи?
Допустим необходимо периодически подключаться с одного компьютера с OS Windows на другой, под управлением Linux Debian.
Команда для этого будет выглядеть так: ssh username@target_ip_address
(команда одинакова и для запуска из ОС Линукс и из под "Винды", в которой она должна запускаться из PowerShell), где "target_ip_address" соответственно адрес той системы куда подключаемся.
В ответ на это, целевая система попросит ввести пароль того пользователя, от имени которого пытаемся зайти. И именно тут возникают проблемы, которые решают SSH ключи.
- пользователь может ошибиться при вводе пароля
- пароль может подсмотреть человек находящийся рядом во время его набора
- пароль может быть перехвачен логгером клавиатуры
- пароль может быть перехвачен при "прослушивании" сетевого трафика
- при использовании скриптов и разного рода автоматизаций, пароль должен быть сохранён где-то в открытом виде, чтобы быть подставленным в момент соединения
- пароль может быть подобран с помощью брутфорс атаки
- есть вероятность использовать пароль, который был ранее скомпрометирован.
- при поддерживании политики смены паролей, сложнее запомнить комплексный пароль.
- пароль может быть забыт или утерян.
Во время SSH соединения между клиентом и сервером происходит примерно следующий диалог:
К - Клиент (откуда соединяемся)
С - Сервер (куда соединяемся)
К: "Привет, хочу установить соединение"
С: "Ок, я готов"
К: "Отлично, начинаем"
С: "Я поддерживаю SSH-2.0-OpenSSH_8.4"
К: "Я тоже использую SSH-2.0-OpenSSH_8.9"
К: "Вот список алгоритмов, которые я поддерживаю:
- Шифрование: aes256-ctr, aes192-ctr, aes128-ctr
- MAC (проверка целостности): hmac-sha2-256, hmac-sha2-512
- Сжатие: none, zlib
- Обмен ключами: curve25519-sha256, diffie-hellman-group14-sha256"
С: "Отлично, давайте использовать:
- Шифрование: aes256-ctr
- MAC: hmac-sha2-256
- Сжатие: none
- Обмен ключами: curve25519-sha256"
К: "Вот мой открытый ключ для обмена и случайные данные"
С: "А вот мой открытый ключ и мои случайные данные. И вот моя цифровая подпись, подтверждающая, что я тот самый сервер"
Начиная с этого момента весь обмен данными идет в зашифрованном виде. С помощью только что созданного сеансового ключа, который создаётся заново при каждом подключении.
С: "Ok, теперь докажи, что ты имеешь право подключаться. Какой метод аутентификации будешь использовать?"
К: "Буду использовать publickey"
С: "Хорошо, вот тебе случайное число (challenge), зашифруй его своим закрытым ключом"
К: "Вот зашифрованное число и мой открытый ключ"
С: "Проверяю... Да, твой открытый ключ есть в authorized_keys, и ты правильно зашифровал challenge. Аутентификация успешна!"
К: "Хочу открыть канал для shell"
С: "Канал открыт, номер 0"
К: "Нужен псевдотерминал с параметрами: 80x24, xterm"
С: "Псевдотерминал выделен"
К: "Запусти shell в этом псевдотерминале"
С: "Shell запущен"
С: "username@hostname:~$ " (приглашение командной строки)
К: [может отправлять команды]
С: [возвращает результаты выполнения команд]
При закрытии соединения:
К: "Закрываю канал 0"
С: "Канал 0 закрыт"
К: "Завершаю соединение"
"Отпечатки пальцев".
Так же, при первом подключении нового клиента к серверу по SSH, начало диалога немного другое:
К: "Привет, я хочу подключиться к ip 10.10.10.50"
С: "Привет! Это наша первая встреча. Вот мой открытый ключ сервера"
К: "Стоп! Я первый раз подключаюсь к этому серверу и не знаю, действительно ли это тот сервер, к которому я хотел подключиться, или кто-то притворяется им. Вот что я вижу:
Этот "отпечаток пальца" (fingerprint) - это краткая криптографическая хеш-сумма открытого ключа сервера. Как настоящий отпечаток пальца человека, он уникален для каждого ключа сервера.
Если вы ответите "yes", произойдет:
- Клиент сохранит этот отпечаток в профиле текущего пользователя по адресу ~/.ssh/known_hosts (так же создаётся/обновляется копия этого файла перед последним изменением - known_hosts.old)
- При следующих подключениях клиент будет проверять, совпадает ли отпечаток сервера с сохраненным
Это защищает от атаки "человек посередине" (man-in-the-middle):
- Если кто-то попытается перехватить ваше соединение, подменив сервер
- У поддельного сервера будет другой ключ
- Значит и отпечаток будет другим
- Клиент покажет предупреждение:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
Поэтому при первом подключении важно:
- Либо получить правильный отпечаток от администратора сервера
- Либо быть уверенным, что вы подключаетесь к правильному серверу (например, находясь в одной локальной сети)
Это похоже на ситуацию, когда вы первый раз встречаете человека - вы можете либо поверить, что он тот, за кого себя выдает, либо попросить кого-то знакомого подтвердить его личность.
Итак, после подтверждения отпечатка сервера пишем пароль и соединяемся
из системы с названием TEST2 подключились на систему TEST
Отпечаток системы TEST сохранился в директории в файле по адресу .ssh/known_hosts пользователя root системы TEST2.
И в следующий раз при соединении уже не будет запрашивать его разрешить, только если сам отпечаток не будет удалён из файла.
Соединение с помощью SSH ключей.
Сделаем теперь подключение с помощью SSH ключей. Для этого их необходимо создать. Команда одинакова и для запуска из ОС Линукс и из под "Винды", в которой она должна запускаться из PowerShell:
Здесь так же как и в Линуксе, при указании имени файла, создаётся пара ключей в той директории, в которой находишься в данный момент.
Если не указывать имя файла, то пара ключей будет создана всё в той же директории .ssh но уже у текущего пользователя: C:\Users\Username\.ssh
Из начала статьи мы помним что делиться можно только публичным ключом!
Поэтому, копируем содержимое файла публичного ключа на ту систему, к которой будем подключаться!
НА ЦЕЛЕВОЙ системе в директории .ssh того пользователя, от имени которого в дальнейшем будем подключаться, создаём файл с названием authorized_keys и копируем в него содержимое файла публичного ключа.
После сохранения, необходимо поменять права доступа на этот файл, сделав его чтение и изменение доступными только для его владельца. Т.е. того пользователя, в директории которого он находится, и от имени которого будем к этой системе подключаться. Выполнив команду chmod 0600 authorized_keys
Теперь можно подключаться без пароля, используя только приватный ключ: ssh -i key1 username@target_ip
из системы с названием TEST подключились на систему TEST2
Как становится понятно из команды выше, параметр -i используется для указания файла ключа, с которым будет происходить соединение. Возникает резонный вопрос, а можно ли использовать эту же команду, но не указывать при этом этот параметр и имя файла? Ответ простой и короткий - можно.
Для этого, на системе ОТКУДА происходит подключение, копируем файл приватного ключа в директорию .ssh и заодно меняем ему имя - cp key1 ~/.ssh/id_rsa
И теперь можно выполнять соединение используя ssh username@target_ip
Использование конфигурационного файла SSH.
Выше было несколько раз упомянуто что ключи создаются на уровне пользователя. Однако настройки и управление ключами на самом деле разделены на две основные категории и каждая из них управляется своим файлом настроек
Для Linux систем:
глобальный файл настроек - /etc/ssh/ssh_config
пользовательский файл настроек - ~/.ssh/config
Для Windows систем:
глобальный файл настроек - C:\ProgramData\ssh\sshd_config
пользовательский файл настроек - C:\Users\username\.ssh\config
Создадим пользовательский файл настроек на уже привычной системе TEST:
и добавим в него следующую конфигурацию:
Host server_any_name
Hostname dns_name.or.ip_address
User username
port 22
Теперь, если мы захотим подключиться к выбранному серверу, то можно указать его имя, указанное в строке Host. Система будет "знать", исходя из записи в конфиг файле, что на самом деле нужно обратиться к определённому адресу с определённым именем пользователя.
А т.к. приватный ключ используемый по умолчанию уже существует по адресу ~/.ssh/id_rsa, то соединение произойдёт максимально просто и быстро.
1. Видно что находимся в текущей системе TEST.
2. В файле конфигурации задано имя myserver и айпи адрес другой системы.
3. При проверке пингом днс имени другой системы получаем тот-же айпи адрес что и в файле конфигурации.
Это подтверждает что вторая система называется так, как та, к которой мы хотим подключиться. И что её адрес совпадает с адресом "привязанным" к настройке в файле конфигурации. По сути, можно вместо айпи указать то же днс имя.
4. Соединяемся по SSH с целевой системой указав только имя из файла конфигурации. Ни имя пользователя, ни пароль, ни ключ сертификата в команде не указаны. Видно что соединение произошло и мы уже внутри TEST2.
5. Выходим из TEST2 (отключаемся).
Видно что вернулись в ту систему, из которой изначально делали соединение.
Говоря простым языком, файл настроек соединения SSH похож на ярлык на рабочем столе в среде Windows. Это просто файл, который содержит в себе информацию как, с какими параметрам и куда перейти. При этом используя удобное для пользователя, произвольное имя.
Отмена проверки отпечатка. Маска.
Чуть ранее в этой статье был упомянут механизм проверки "отпечатка пальца" сервера. При первом подключении к удалённой системе запрашивается подтверждение доверия, где пользователю надо набрать на клавиатуре ответ "yes" чтобы fingerprint удалённой машины добавился в файл ~/.ssh/known_hosts.
Иногда бывает необходимость подключаться к разным машинам в сегменте который нам знаком, но при этом адреса могут постоянно меняться или заменять друг друга, как например в домашней лаборатории. Т.е. сегодня создали машину или контейнер с именем AAA и адресом 10.10.10.10, на завтра удалили её и создали новую - BBB с тем же адресом. В итоге имеем тот же айпи, но разные днс имена и разные отпечатки. А если ещё и переименовать BBB в AAA, то произойдёт классическая подмена одного сервера другим. И именно от таких подмен и защищает механизм fingerprint.
Но т.к. мы в своей знакомой среде, т.е. в окружении которому доверяем, то использование отпечатков может показаться излишним. И чтобы уклониться от необходимости каждый раз подтверждать отпечаток, можно настроить это в том же файле конфигурации. Для этого добавим в него следующие строки:
Host *
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
В данном случае звёздочка будет означать любое количество любых символов в адресе. Использование этого символа ещё называют маской.
StrictHostKeyChecking no — отключает проверку отпечатка сервера.
UserKnownHostsFile /dev/null — не сохраняет отпечатки серверов в файл ~/.ssh/known_hosts.
Т.е. для абсолютно любого хоста (хоть в интернете хоть в локальной сети) не будет проверяться и сохраняться отпечаток. А такой подход уже не является безопасным.
Поэтому, чтобы совместить удобство и безопасность, откажемся от проверки и сохранения отпечатка только для локальной сети, указав её айпи адрес со звёздочкой вместо последнего октета.
Host 10.10.10.*
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Раздел настроек, в котором используется строка с маской, должен быть первым в файле конфигурации.
Однако, если после него добавим уже использованный ранее раздел:
Host server_any_name
Hostname dns_name.or.ip_address
User username
port 22
И попробуем подключиться к хосту из этого блока, то система всё равно предложит сохранить его отпечаток. Т.е. первый блок не сработает. Почему?
Всё просто. Это происходит потому, что выбранное для удобства имя хоста из второго блока не подходит под параметры хоста из первого блока. Например, мы использовали конфигурацию:
Host myserver
Hostname 10.10.10.53
User root
port 22
Т.е. подключаемся к myserver. Но, как уже было сказано, в раздел хост первого блока указано:
Говоря простым языком, между 10.10.10.* и myserver ну никак нельзя поставить знак равенства, т.к. это разные символы т.е. названия.
Чтобы обойти данную проблему, необходимо сделать следующее:
создаём первый раздел конфига, в строке Host которого указываем маску адреса локальной сети
создаём второй раздел, в строке Host которого указываем желаемое имя для сервера в локальной сети
в строку Host первого раздела добавляем имя из строки Host второго раздела
в итоге, файл конфига будет выглядеть примерно так:
Host myserver 10.10.10.*
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Host myserver
Hostname 10.10.10.53
User root
port 22
По аналогии можем добавить и третий раздел с ещё одним сервером, главное не забыть добавить имя из его настроек в первый раздел
Host myserver test123 10.10.10.*
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Host myserver
Hostname 10.10.10.53
User root
port 22
Host test123
Hostname 10.10.10.54
User user
port 22
Конфигурация для работы с доменами.
Познакомимся с ещё одним интересным параметром.
Допустим у нас есть домен test.local, и ко всем серверам в этом домене нужно подключаться с одним и тем же ключом. Т.е. любые адреса вида chto-to.test.local, где вместо chto-to будет указано имя сервера. Для подключения ко всем этим серверам используется одно и то же имя пользователя и один и тот же (но отдельный) ключ.
Конфигурация для такого соединения будет выглядеть следующим образом:
Host *.test.local
User engineer
IdentityFile ~/.ssh/another_private_key.pem
В данном примере отсутствует номер порта, т.к. если используется порт SSH по умолчанию, то его указывать не обязательно.
И соответственно, если хотим совместить этот конфиг с ранее описанным файлом, то получится это:
Host *.test.local myserver test123 10.10.10.*
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Host myserver
Hostname 10.10.10.53
User root
Host test123
Hostname 10.10.10.54
User user
port 22
Host *.test.local
User engineer
IdentityFile ~/.ssh/another_private_key.pem
По этой аналогии можно комбинировать способы добавления разных серверов и разных ключей для них.
На этом завершим краткое знакомство с инструментарием SSH ключей. Он пригодится и тем, кто работает с серверами умного дома. А так же станет полезной базой для следующих статей, где соединения между машинами и контейнерами будут использоваться для автоматического обмена данными.
Комментарии
Отправить комментарий