Linux и инструменты разработчика
Основы Bash для работы в безопасности
Пайпы, перенаправление, переменные, циклы, коды завершения и set -euo pipefail, а затем подсчёт неудачных входов и хеширование папки на этой основе.
Как работают ключи хоста и пользователя в SSH, почему по умолчанию стоит брать Ed25519 с парольной фразой и как настроить и защитить вход по ключу на своей лабораторной VM.
SSH (Secure Shell) — протокол, через который большинство людей заходит на серверы Linux, лабораторные виртуальные машины и Git-хостинги. Он открывает shell по зашифрованному каналу, и по тому же каналу можно копировать файлы или пробрасывать порты. Задач у него три. Он шифрует трафик, чтобы никто на сетевом пути не мог его прочитать. Он доказывает серверу, кто вы. Он доказывает вам, кто этот сервер.
Вторую задачу ключи решают без отправки пароля по сети. Пароль можно подобрать или выманить фишингом. Пара ключей работает иначе. Сервер хранит только публичную половину. При входе клиент подписывает запрос (challenge) приватной половиной, а сервер проверяет подпись. Приватный ключ никогда не покидает вашу машину.
В некоторых случаях SSH вас не защищает, и знать их нужно не меньше, чем сам протокол. Если скомпрометирован ваш компьютер, вредоносное ПО, запущенное от вашего пользователя, может прочитать файл приватного ключа или попросить агент что-нибудь подписать. Если приватный ключ украден, а парольная фраза слабая, злоумышленник может подбирать её офлайн. Если вы принимаете личность сервера без проверки, вы, возможно, разговариваете вовсе не с сервером.
В SSH есть два разных вида ключей, и их часто путают.
Ключи хоста идентифицируют сервер. У каждого SSH-сервера их один или несколько, обычно они лежат в /etc/ssh/ssh_host_*_key. Приватная часть остаётся на сервере. Публичная предъявляется каждому подключающемуся клиенту. Увидев её впервые, клиент сохраняет её в known_hosts. При последующих подключениях изменившийся ключ хоста вызывает предупреждение.
Пользовательские ключи идентифицируют вас. Пару вы создаёте на своей машине. Приватный ключ остаётся у вас. Публичный отправляется на сервер, в ~/.ssh/authorized_keys нужного аккаунта.
У этого различия есть практические последствия. Скопировав приватный ключ на сервер, вы даёте этому серверу и всем, кто может его прочитать, всё необходимое, чтобы действовать от вашего имени везде, где ключу доверяют. Игнорируя предупреждение о ключе хоста, вы рискуете отправить пароль или команды самозванцу.
OpenSSH поддерживает несколько типов ключей. RSA всё ещё работает, но новые ключи RSA должны быть длиной не меньше 3072 бит, и строки публичных ключей получаются длинными. Ключи Ed25519 короткие, быстро проверяются и стандартизованы для SSH в RFC 8709. Начиная с OpenSSH 9.5, Ed25519 используется в ssh-keygen по умолчанию. Для лабораторного ключа Ed25519 подходит по умолчанию, если только нужная вам система не принимает лишь более старый тип.
Парольная фраза шифрует файл приватного ключа на диске. Без неё файлом сможет воспользоваться любой, кто его скопировал. С ней понадобятся и файл, и фраза. Это защищает от самых частых случайностей: резервная копия ушла не туда, ключ остался в общей папке, файл по ошибке синхронизировался в облачное хранилище. Когда ключ загружен в агент и от вашего имени работает вредоносное ПО, парольная фраза уже не поможет. Делайте её длинной, храните в менеджере паролей и держите резервную копию файла ключа в надёжном месте.
Транспортный уровень описан в RFC 4253. В общих чертах подключение проходит такие шаги:
known_hosts. Если хост новый, клиент показывает отпечаток и просит подтвердить его.authorized_keys, он отправляет запрос. Клиент подписывает запрос приватным ключом, а сервер проверяет подпись.Обратите внимание, откуда берётся доверие. Шаг 3 касается сервера. Шаг 4 касается вас. Если на шаге 3 пропустить проверку отпечатка, остаток рукопожатия будет работать на того, кто ответил на соединение.
Отпечаток — это короткий хеш публичного ключа. OpenSSH по умолчанию показывает отпечатки SHA256. Два человека могут сравнить отпечаток и убедиться, что у них один и тот же публичный ключ, не зачитывая ключ целиком.
Команды ниже запускались на машине с Windows 11 и OpenSSH 9.9p2 (ssh -V), в Git Bash. Ключ был одноразовой копией, созданной для этого наблюдения, с пустой парольной фразой, чтобы вывод можно было здесь показать. У вашего настоящего ключа будет другой отпечаток.
ssh-keygen -t ed25519 -C 'lab-key' -f ./lab_ed25519 -N ''
ssh-keygen -lf ./lab_ed25519.pubНаблюдаемый вывод:
256 SHA256:2BAyZTrHADdiG1FNwfCwb/fC5vSVZPwfGnZjRaaExc4 lab-key (ED25519)ssh-keygen -lvf печатает тот же отпечаток с картинкой random-art. На картинке проще заметить изменения, но сравнивать нужно текст отпечатка.
Понадобится виртуальная машина или облачный инстанс, которым вы владеете и который контролируете, с запущенным сервером OpenSSH и аккаунтом, которому доступен sudo. Подойдёт локальная VM в VirtualBox или Hyper-V. Подойдёт и небольшой облачный инстанс, за который вы платите и который можете удалить. Не выполняйте эти шаги на чужой машине.
ssh-keygen -t ed25519 -C 'lab-key' -f ~/.ssh/lab_ed25519Когда появится запрос, введите длинную парольную фразу. Команда создаёт два файла: lab_ed25519 (приватный) и lab_ed25519.pub (публичный). Покидать вашу машину должен только файл .pub.
ssh-keygen -lf ~/.ssh/lab_ed25519.pubЗапишите этот отпечаток или хотя бы первые несколько символов. Позже вы сравните его ещё раз.
chmod 600 ~/.ssh/lab_ed25519В Linux и macOS ssh отказывается работать с приватным ключом, который могут читать другие пользователи. В Windows OpenSSH проверяет ACL файла, биты режима Unix ему безразличны. На этой машине с Windows chmod 600 отработал без ошибки, но ls -l после этого всё ещё показывал -rw-r--r--. Не рассчитывайте, что биты режима там изменились. Если ssh в Windows жалуется на права, проверьте файл через icacls и уберите доступ для других аккаунтов.
ssh-copy-id -i ~/.ssh/lab_ed25519.pub youruser@your-lab-vmЭто единственный раз, когда вы намеренно вводите пароль аккаунта. Команда дописывает публичный ключ в ~/.ssh/authorized_keys на VM.
При первом подключении к VM вы также увидите отпечаток ключа хоста. Сравните его с настоящим. На VM выполните:
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pubЕсли отпечатки совпадают, введите yes. Если нет, остановитесь и выясните причину, прежде чем продолжать. Несовпадение может означать, что вы переустановили VM или что между вами и ней кто-то сидит.
Этот шаг здесь не выполнялся, потому что VM на этой машине не было. По той же причине команды на шагах с 4 по 6 показаны без вывода. Каждую из них можно проверить на своей VM.
ssh -i ~/.ssh/lab_ed25519 youruser@your-lab-vmКлиент спросит парольную фразу ключа, пароль аккаунта ему не нужен. Если вы получили shell, ключ работает. Оставьте эту сессию открытой для следующего шага.
На VM:
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart sshСтрока sed заменяет строку PasswordAuthentication независимо от того, закомментирована она или нет. sshd -t проверяет синтаксис конфигурации и прерывает цепочку при ошибке. systemctl restart ssh применяет изменение. В Fedora и других системах семейства RHEL служба называется sshd, используйте это имя.
Здесь часто случаются три проблемы:
/etc/ssh/sshd_config включает файлы из /etc/ssh/sshd_config.d/, и действует первое прочитанное значение ключевого слова. Проверьте значение, которое sshd реально использует:sudo sshd -T | grep -i '^passwordauthentication'После изменения вывод должен быть passwordauthentication no. Если там всё ещё yes, найдите файл, который это задаёт, и исправьте его.
sed редактирует только существующую строку. Если строки PasswordAuthentication нет, действует значение по умолчанию, и по man-странице sshd_config(5) это yes. Добавьте строку сами:echo 'PasswordAuthentication no' | sudo tee -a /etc/ssh/sshd_configЗатем проверьте со своей машины, что вход по паролю отклоняется:
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password youruser@your-lab-vmПодключение должно завершиться сообщением об отказе в доступе. Выполните эту проверку сами на своей VM. На этой машине она не выполнялась.
Вводить парольную фразу при каждом подключении утомительно. ssh-agent держит расшифрованный ключ в памяти на время сессии, так что фразу вы вводите один раз.
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/lab_ed25519
ssh-add -lssh-add -l показывает отпечатки, которые хранит агент. На этой машине с Windows ssh-add -l запустился без работающего агента, напечатал Could not open a connection to your authentication agent. и завершился с кодом 2. В Windows для этого есть служба OpenSSH Authentication Agent. Запустите её из консоли Services или из PowerShell с правами администратора. Это изменение вашей системы, поэтому делайте его сами и только если агент вам нужен.
Агент удобен, но он ещё и цель. Любой, кто может обратиться к сокету агента, пока вы в системе, способен попросить его что-нибудь подписать. Поэтому не используйте проброс агента (ssh -A) на хосты, которым не доверяете полностью. Пользователь root на удалённом хосте может воспользоваться вашим проброшенным агентом. Если нужно попасть на закрытый хост через бастион, используйте jump-хост:
ssh -J youruser@bastion youruser@private-vmКлиент хранит ключи хостов в ~/.ssh/known_hosts, по одной строке на хост и тип ключа. Если задать HashKnownHosts yes, строки хешируются, и тот, кто прочитает файл, не увидит имён хостов.
Чтобы найти хост:
ssh-keygen -F your-lab-vmЧтобы удалить запись после пересборки VM и получения нового ключа хоста:
ssh-keygen -R your-lab-vmДелайте это, только когда знаете, почему ключ изменился. Изменившийся ключ хоста — это предупреждение, и отмахиваться от него нельзя. Если удалить старую запись без проверки нового отпечатка, вы приучите себя игнорировать следующее предупреждение, а оно может оказаться настоящим.
.pub.authorized_keys..pub в authorized_keys.~/.ssh должен быть режим 700, а у authorized_keys режим 600. При более свободных правах sshd может проигнорировать ключ. Если вход по ключу не работает, смотрите журналы sshd.lab_ed25519 для лаборатории и другой ключ для GitHub.yes.ssh-copy-id безвреден, но если вручную дописывать ключ несколько раз, файл становится сложнее проверять. Удаляйте ключи, которыми больше не пользуетесь.Когда лаборатория заработает, несколько шагов сделают повседневную работу безопаснее и аккуратнее.
~/.ssh/config. Задайте каждому хосту алиас, укажите IdentityFile и добавьте IdentitiesOnly yes, чтобы клиент предлагал только выбранный ключ. Тогда он не будет пробовать на сервере все ключи из агента.ssh-keygen -t ed25519-sk. Приватную часть тогда нельзя скопировать с устройства. Прежде чем на это полагаться, проверьте версию OpenSSH и поддержку на своём ключе.ssh-keygen и sshd_config описывают все опции, которые вам встретятся. RFC 4253 объясняет транспортный уровень, а RFC 8709 посвящён Ed25519 в SSH.Если режимы файлов и владельцы для вас пока в новинку, сначала прочитайте предыдущую статью о файлах и правах в Linux. Следующая статья рассказывает об изолированных сетях Docker для лабораторий, с их помощью трафик лаборатории можно держать подальше от повседневной сети.
ssh-keygen(1): https://man.openbsd.org/ssh-keygensshd_config(5): https://man.openbsd.org/sshd_configLinux и инструменты разработчика
Пайпы, перенаправление, переменные, циклы, коды завершения и set -euo pipefail, а затем подсчёт неудачных входов и хеширование папки на этой основе.
Linux и инструменты разработчика
Владельцы и группы в Linux, биты rwx, chmod, umask и setuid, а также лабораторная, где доступ проверяется от имени второго пользователя и ищутся опасные режимы файлов.
Linux и инструменты разработчика
GitHub для начинающих: что делают Git и GitHub, какие термины нужны, локальная практика с Git и как проверить проект перед запуском.