По ходу настройки keyring
для Nextcloud-клиента (см. Linux: Nextcloud клиент, qtkeychain и ошибка «The name org.freedesktop.secrets was not provided by any .service files») – решил навести порядок в своих SSH-ключах, которых много, и аутентификация иногда преврашается в достаточно геморройный процесс.
В целом, для упрощения работы можно использовать системное хранилище секретов – gnome-keyring
, либо KeeyPassXC, про них в другой раз.
В этом посте посмотрим примеры работы с ssh-agent
и то, как можно хранить и управлять запароленными RSA-ключами без таких бекендов.
Примеры выполняются на Arch Linux (и, местами, для проверки – на Manjaro Linux с Budgie DE).
Содержание
ssh-agent
ssh-agent предназначен для управления SSH-ключами пользователя и их паролями, что бы не вводить пароль к ключу каждый раз при использовании.
Запуск агента
Выполняем:
[simterm]
$ ssh-agent SSH_AUTH_SOCK=/tmp/ssh-dMDE5mED77tM/agent.436347; export SSH_AUTH_SOCK; SSH_AGENT_PID=436348; export SSH_AGENT_PID; echo Agent pid 436348;
[/simterm]
Для работы клиентов важны переменные, которые задаются агентом:
SSH_AGENT_PID
: с PID процесса сssh-agent
, которая будет использоваться при, например,ssh-agent -k
для остановки агентаSSH_AUTH_SOCK
: указывает на путь к unix-сокету, который будут использовать другие процессы для коммуникации сssh-agent
Что бы запустить агента без вывода всей этой информации – используем:
[simterm]
$ eval $(ssh-agent) > /dev/null
[/simterm]
Вариантов запуска много, рассмотрим их в конце, в Запуск ssh-agent и несколько консолей.
Примеры
Рассмотрим базовые примеры использования ssh-agent
.
Создание ключа
Генерируем ключ:
[simterm]
$ ssh-keygen -t rsa -b 2048 -f /home/setevoy/.ssh/test-key -C "Testing key" -P pass Generating public/private rsa key pair. Your identification has been saved in /home/setevoy/.ssh/test-key. Your public key has been saved in /home/setevoy/.ssh/test-key.pub. The key fingerprint is: SHA256:pTyrGtk1hnNHB6b8ilp5jRe1+K4KrLHg50yUGilApLY Testing key The key's randomart image is: +---[RSA 2048]----+ |.o o | |o . o . | |o. o o o | |o .. . o = + . | |.Eo o o S = . | | . + + B O o | | o = B = o . | | . +.B + . . | | .oB.. ..... | +----[SHA256]-----+
[/simterm]
Опции тут:
-t
: тип RSA-b
: длина ключа в битах (по-умолчанию 3072 для RSA)-f
: путь к файлу ключа (по-умолчанию будет~/.ssh/id_rsa
)-C
: комментарий к ключу (по-умолчанию будет username@hostname)-P
: пароль для доступа к ключу
Проверка пароля
Что бы проверить пароль ключа – используем -f
для указания ключа, и -y
для вывода информации о ключе, поэтому запросит пароль:
[simterm]
$ ssh-keygen -y -f /home/setevoy/.ssh/test-key Enter passphrase: ssh-rsa AAAAB***gud2vedL/V Testing key
[/simterm]
Смена пароля
Что бы изменить пароль, заня старый:
[simterm]
$ ssh-keygen -p -f ~/.ssh/test-key Enter old passphrase: Key has comment 'Testing Key' Enter new passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved with the new passphrase.
[/simterm]
ssh-copy-id
– копирование ключа на сервер
Скопировать ключ можно вручную, получив его публичную часть:
[simterm]
$ cat .ssh/test-key.pub ssh-rsa AAAAB***gud2vedL/V Testing key
[/simterm]
И скопировав содержимое в файл ~/.ssh/authorized_keys
на удалённом хосте.
Другой вариант – используя утилиту ssh-copy-id
, которая, по сути, выполнит всё тоже самое, но при этом ещё и проверит права доступа на каталоги и файлы (самая частая проблема при использовании SSH-ключей для аутентификации):
[simterm]
$ ssh-copy-id -i /home/setevoy/.ssh/test-key [email protected] /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/setevoy/.ssh/test-key.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys [email protected]'s password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh '[email protected]'" and check to make sure that only the key(s) you wanted were added.
[/simterm]
И пробуем подключиться, используя этот ключ:
[simterm]
$ ssh [email protected] -i .ssh/test-key Enter passphrase for key '.ssh/test-key': Linux rtfm-do-production 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64 ... setevoy@rtfm-do-production:~$
[/simterm]
ssh-add
Окей, теперь у нас ключ для аутентификации на сервере, и ключ закрыт паролем.
При каждом обращении к серверу – нам придётся вводить пароль заново – Enter passphrase for key ‘/home/setevoy/.ssh/test-key’.
Что бы избежать этого – добавим ключ в ssh-agent
, используя ssh-add
.
Проверим, что агент запущен:
[simterm]
$ ps aux | grep ssh-agent setevoy 1322 0.0 0.0 5796 456 ? Ss Nov30 0:00 ssh-agent -s setevoy 1324 0.0 0.0 5796 2160 ? Ss Nov30 0:00 ssh-agent -s ...
[/simterm]
Could not open a connection to your authentication agent
Самая частая ошибка при использовании агента – когда к нему невозможно подключиться, и ssh-add
сообщает:
[simterm]
$ ssh-add Could not open a connection to your authentication agent.
[/simterm]
Первым делом проверяем SSH_AGENT_PID
(или $SSH_AUTH_SOCK
, т.к. запросы от ssh-add
выполняются через сокет, заданный в этой переменной):
[simterm]
$ test -z $SSH_AGENT_PID; echo $? 0
[/simterm]
Переменная пустая.
ssh-agent
был запущен в другом терминале (об этом тоже поговорим ниже), поэтому перезапустим его.
Для “чистоты эксперимента” – убиваем запущеные инстансы агента:
[simterm]
$ killall ssh-agent
[/simterm]
И запускаем заново:
[simterm]
$ eval $(ssh-agent -s) Agent pid 452333
[/simterm]
-s
используем для создания переменных, т.к. не все будут запускать из bash
, а eval
– что бы сразу выполнить експорт переменных (export SSH_AUTH_SOCK
) из output самого ssh-agent
.
Проверяем ещё раз:
[simterm]
$ test -z $SSH_AGENT_PID; echo $? 1
[/simterm]
И ssh-add
:
[simterm]
$ ssh-add -l The agent has no identities.
[/simterm]
Тут всё готово.
Добавление ключа
Выполняем:
[simterm]
$ ssh-add /home/setevoy/.ssh/test-key Enter passphrase for /home/setevoy/.ssh/test-key: Identity added: /home/setevoy/.ssh/test-key (Testing key)
[/simterm]
Проверка ключей в агенте
Проверяем загруженные в агент ключи, используя -l
:
[simterm]
$ ssh-add -l 2048 SHA256:pTyrGtk1hnNHB6b8ilp5jRe1+K4KrLHg50yUGilApLY Testing key (RSA)
[/simterm]
Удаление ключа
Используем -d
для удаления одного ключа:
[simterm]
$ ssh-add -d .ssh/test-key Identity removed: .ssh/test-key (Testing key)
[/simterm]
Или -D
для удаления всех ключей:
[simterm]
$ ssh-add -D All identities removed.
[/simterm]
Автоматическое добавление в ssh-agent
Что бы ssh
(и git
, например) всегда добавляли ключ в ssh-agent
без явного ручного вызова ssh-add
– добавьте AddKeysToAgent
в ~/.ssh/config
, указав yes, confirm или ask (см. SSH_ASKPASS
):
[simterm]
$ head -1 .ssh/config AddKeysToAgent yes
[/simterm]
Проверяем – сейчас ключей в агенте нет:
[simterm]
$ ssh-add -l The agent has no identities.
[/simterm]
Выполняем подключение, вводим пароль ключа:
[simterm]
$ ssh -i .ssh/test-key [email protected] Enter passphrase for key '.ssh/test-key': ... setevoy@rtfm-do-production:~$
[/simterm]
Отключаемся, проверяем ключи в агенте:
[simterm]
setevoy@rtfm-do-production:~$ logout Connection to rtfm.co.ua closed. $ ssh-add -l 2048 SHA256:pTyrGtk1hnNHB6b8ilp5jRe1+K4KrLHg50yUGilApLY Testing key (RSA)
[/simterm]
И теперь при повторном подключении – ключ уже будет взят из агента, и пароль вводить не потребуется:
[simterm]
$ ssh -i .ssh/test-key [email protected] ... setevoy@rtfm-do-production:~$
[/simterm]
Запуск ssh-agent
и несколько консолей
Одна из основных проблем, это то, что при запуске новой вкладки в Konsole и инициализации новой bash-сесии – там не будет переменной $SSH_AUTH_SOCK
, и ssh-клиент не сможет получить доступ к ключу.
Например, при вызове ssh-add
в новом терминале получим уже упомянутую ошибку “Could not open a connection to your authentication agent“:
[simterm]
$ ssh-add -l Could not open a connection to your authentication agent.
[/simterm]
~/.bashrc
Вариантов много, например самый простой – добавить в ~/.bashrc
:
if [ -z "$SSH_AUTH_SOCK" ] ; then eval `ssh-agent -s` ssh-add /home/setevoy/.ssh/test-key fi
Но тогда для каждой сессии bash
будет запускаться новый агент.
Ещё одним вариантом запуска через .bashrc
может быть такой:
ssh-add -l &>/dev/null if [ "$?" == 2 ]; then test -r ~/.ssh-agent-env && \ eval "$(<~/.ssh-agent-env)" >/dev/null ssh-add -l &>/dev/null if [ "$?" == 2 ]; then (umask 066; ssh-agent > ~/.ssh-agent-env) eval "$(<~/.ssh-agent-env)" >/dev/null ssh-add /home/setevoy/.ssh/test-key fi fi
Тут выполняется (см. коды ответа ssh-agent
в документации):
- пытается выполнить
ssh-add -l
, перенаправляет весь вывод в/dev/null
- проверяет код выхода предыдущей комнады
- если код == 2 (ошибка подключения к агенту)
- проверяет доступен ли на чтение
~/.ssh-agent-env
, считывает его содержимое, и передаёт его вbash
- повторяет
ssh-add -l
- если код по-прежнему 2:
- создаёт файл
~/.ssh-agent-env
с правами 660 (чтение и запись только владельцу) - запускает
ssh-agent
, и перенаправляет его вывод в файл.ssh-agent-env
- считывает содержимое
.ssh-agent-env
, и передаёт его вbash
- выполняет
ssh-add /home/setevoy/.ssh/test-key
- создаёт файл
- проверяет доступен ли на чтение
- если код == 2 (ошибка подключения к агенту)
Более-менее вариант, т.к. во всех сессиях у нас будет использоваться один и тот же агент (хотя некоторые рекомендуют использовать различных агентов для разных пулов ключей, например для личных ключей – один агент, для рабочих – другой).
systemd
Ещё один вариант – создать systemd
unit-файл и запускать ssh-agent
как сервис, см. Arch Wiki.
Добавляем каталог, если не создан:
[simterm]
$ mkdir -p .config/systemd/user/
[/simterm]
И создаём файл ~/.config/systemd/user/ssh-agent.service
:
[Unit] Description=SSH key agent [Service] Type=simple Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket ExecStart=/usr/bin/ssh-agent -D -a $SSH_AUTH_SOCK [Install] WantedBy=default.target
Далее, Wiki говорит про файл ~/.pam_environment
, но у меня Openbox и я переменные обычно задаю в .config/openbox/autostart
:
[simterm]
$ head -2 .config/openbox/autostart # ssh-agent.service SSH_AUTH_SOCK="${XDG_RUNTIME_DIR}/ssh-agent.socket"
[/simterm]
Кстати, вспомнил про такую вещь, как BASH: переменные — передача значений по-умолчанию ${var:-defaultvalue}, замена значений — ${var:+alternatevalue} и сообщений — ${var:?message}
Останавливаем запущенные инстансы агента:
[simterm]
$ killall ssh-agent
[/simterm]
Проверяем значение XDG_RUNTIME_DIR
:
[simterm]
$ echo $XDG_RUNTIME_DIR /run/user/1000
[/simterm]
Сейчас задаём переменную $SSH_AUTH_SOCK
вручную:
[simterm]
$ SSH_AUTH_SOCK="${XDG_RUNTIME_DIR}/ssh-agent.socket"
[/simterm]
И запускам агент через systemctl --user
:
[simterm]
$ systemctl --user start ssh-agent
[/simterm]
Проверяем:
[simterm]
$ systemctl --user status ssh-agent ● ssh-agent.service - SSH key agent Loaded: loaded (/home/setevoy/.config/systemd/user/ssh-agent.service; disabled; vendor preset: enabled) Active: active (running) since Sun 2019-12-01 09:15:18 EET; 2s ago Main PID: 497687 (ssh-agent) CGroup: /user.slice/user-1000.slice/[email protected]/ssh-agent.service └─497687 /usr/bin/ssh-agent -D -a /run/user/1000/ssh-agent.socket Dec 01 09:15:18 setevoy-arch-pc systemd[670]: Started SSH key agent. Dec 01 09:15:19 setevoy-arch-pc ssh-agent[497687]: SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket; export SSH_AUTH_SOCK; Dec 01 09:15:19 setevoy-arch-pc ssh-agent[497687]: echo Agent pid 497687;
[/simterm]
Переменную сокета:
[simterm]
$ echo $SSH_AUTH_SOCK /run/user/1000/ssh-agent.socket
[/simterm]
И пробуем ssh-add
:
[simterm]
$ ssh-add -l The agent has no identities.
[/simterm]
Работает.
Можно добавить в автозапуск:
[simterm]
$ systemctl --user enable ssh-agent Created symlink /home/setevoy/.config/systemd/user/default.target.wants/ssh-agent.service → /home/setevoy/.config/systemd/user/ssh-agent.service.
[/simterm]
~/.xinitrc
Ещё один вариант – запускать из ~/.xinitrc
.
В таком случае, при вызове startx
(как у меня, когда login manager нет, и X.Org запускается вручную, через вызов startx
в консоли) будет запущен агент, а затем – Openbox, см. документацию:
[simterm]
$ cat ~/.xinitrc eval $(ssh-agent) & exec openbox-session
[/simterm]
Кроме того, как уже говорилось в начале – вместо самого ssh-agent
можно использовать либо “обёртки”, которые будут “проксировать” запросы от ssh-add
и других клиентов к ssh-agent
, либо полностью заменят ssh-agent
, но об этом – в следующий раз.