
SSH – Тонкая настройка клиента в Linux: config файл и ssh-agent
Приветствую!
Продолжаем тему использования SSH в Linux🐧. Сегодня поговорим о тонкой настройке клиента с помощью файла конфигурации ~/.ssh/config
📄, а также про автоматический импорт ключей в ssh-agent
😎 без ввода паролей.
Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике — заглядывайте в Вороний чат @r4ven_me_chat🧐. |
Предисловие

Данная заметка является 2-й из небольшого цикла про работу с SSH в Linux. Если вы мало знакомы с данным ПО, то рекомендую ознакомиться с моей вводной частью: 🔐SSH – Безопасное подключение к удалённым хостам: введение.
Все примеры из заметки выполнялись в среде дистрибутива Linux Mint 22 Cinnamon. Но вы без труда сможете экстраполировать их и на другие популярные системы Linux😌.
И так, вводные данные: у меня есть 3 сервера со следующими имена, IP адресами и SSHD портами:
Имя сервера | IP адрес | Порт SSHD |
---|---|---|
database | 192.168.122.105 | 22 |
backup | 192.168.122.70 | 2222 |
monitoring | 192.168.122.112 | 3333 |
Аутентификация по SSH на указанных серверах осуществляется по паролю учетной записи. Такой способ подключения не всегда удобен, к тому же считается небезопасным, поэтому…
Использование ключей SSH

Рекомендуемым способом является использование SSH ключей🔑. Давайте создадим такой и добавим его на сервер database:
# сперва генерируем без шифрования, просто нажмите дважды Enter
ssh-keygen -t ed25519
# копируем новый ключ на сервер database, потребуется ввод пароля
ssh-copy-id -i ~/.ssh/id_ed25519 ivan@192.168.122.105
Теперь при подключении к серверу (⚠️если разрешён доступ по ключу) нам не придется вводить пароль от учетной записи:
ssh ivan@192.168.122.105
Обратите внимание, что доступ к удаленному серверу по паролю сохранился. После добавления ключей, парольный вход желательно отключить на стороне сервера. Как это сделать смотрите в отдельной статье.
По умолчанию ssh
пытается пройти аутентификацию на сервере перебирая ключи со стандартными имена (id_<алгоритм>
) в директории ~/.ssh
. В случае, если ключ имеет нестандартное имя или расположение, он будет проигнорирован. Но его можно указать явно с помощью параметра -i
. Например:
ssh ivan@192.168.122.105 -i /some/path/database.key
Хорошей практикой считается использование разных ключей для разных серверов или проектов☝️. При таком подходе, в случае компрометации одного из ключей, вам не придется удалять/заменять его на всех серверах. Поэтому давайте сгенерируем по ключу для оставшихся 2-х серверов и настроим подключение по ним:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_backup
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_monitoring
ssh-copy-id -i ~/.ssh/id_ed25519_backup -p 2222 ivan@192.168.122.70
ssh-copy-id -i ~/.ssh/id_ed25519_monitoring -p 3333 ivan@192.168.122.112
Проверяем подключение:
ssh ivan@192.168.122.70 -i ~/.ssh/id_ed25519_backup -p 2222
ssh ivan@192.168.122.112 -i ~/.ssh/id_ed25519_monitoring -p 3333
Данные команды выглядят громоздко, да и помнить все IP адреса серверов и имена ключей к ним — это еще большее неудобство. В решении этого вопроса нам поможет…
Файл ~/.ssh/config
Данный конфиг файл позволяет задать индивидуальные параметры SSH для каждого отдельного хоста🖥️.
Создаем новый конфиг с помощью любого редактора кода, например vim или neovim:
nvim ~/.ssh/config
И наполняем его примерно таким содержимым:
Host database
HostName 192.168.122.105
User ivan
Port 22
IdentityFile ~/.ssh/id_ed25519
Host backup
HostName 192.168.122.70
User ivan
Port 2222
IdentityFile ~/.ssh/id_ed25519_backup
Host monitoring
HostName 192.168.122.112
User ivan
Port 3333
IdentityFile ~/.ssh/id_ed25519_monitoring
Compression yes
Host github.com
HostName github.com
User git
Port 22
IdentityFile ~/.ssh/id_ed25519_github
# параметры по умолчанию для всех остальных хостов
Host *
#User root
Port 22
IdentityFile ~/.ssh/id_ed25519
ForwardAgent yes
ForwardX11 yes
ForwardX11Trusted yes
#Compression yes
#ServerAliveInterval 60
- ⚠️Символом
#
определяются комментарии, которые игнорируются командами SSH.- 💡Полный список доступных параметров клиента смотрите на man странице:
man ssh_config
или тут.
Как видно, каждый блок отвечает за настройки отдельного хоста, в которых мы явно определили: короткий псевдоним, адрес (IP или DNS), порт подключения, имя пользователя и путь к файлу ключа!
💡Обратите внимание, что в файле можно описать параметры подключения и для удаленных Git сервисов, таких как GitHub.
Отдельно стоит отметить последний блок: Host *
— параметры из него будут применены для всех хостов, которые явно не определены в конфиге. Размещать этот блок рекомендуется в конце файла🔚.
Сохраняем внесенные изменения💾 и возвращаемся в терминал🧑💻. Теперь подключаться к хостам можно просто по их псевдонимам. Все параметры подтянутся автоматически🤖.
Пример:
ssh database
Согласитесь, совсем другое дело😉.
💡Список хостов можно “протабать”. Просто введите часть имени хоста в команде подключения и нажмите клавишу
Tab
.
Конфиг распространяется на все утилиты пакета OpenSSH. Пример:
hostname > /tmp/test.txt
scp /tmp/test.txt database:/tmp
ssh database cat /tmp/test.txt
К ним же относится и sftp
:
sftp database
К слову, чаще всего другие приложения, в т.ч. и графические, также подхватывают настройки из config
файла SSH. Например, файловый менеджер Nemo из Linux Mint. Если ввести в строке пути:
sftp://database
Мы попадем в корень файловой системы удаленного сервера:
Удобно? На мой взгляд да!
У вас может быть несколько конфиг файлов клиента SSH. Чтобы явно указать, какой нужно использовать при подключении добавьте к команде параметр -F <путь_до_файла>
.
Этот параметр можно использовать, если вам вдруг нужно игнорировать текущий конфиг файл:
ssh -F /dev/null user@example.com
В этом случае будут применены только общесистемные параметры по умолчанию, которые заданы в файле /etc/ssh/ssh_config
:
cat /etc/ssh/ssh_config
Так, с конфигом вроде разобрались.
Утилита ssh-agent

Уверен, что среди читателей найдутся пользователи, предпочитающие не хранить файлы ключей в открытом виде. Т.е. использовать парольную фразу для расшифровки ключа перед его использованием. Что равносильно вводу пароля при каждом подключении к удаленным хостам — лично для меня это неудобно🤷♂️. Но безопасное и почти удобное решение есть.

Рад представить вам агента Смита SSH — ssh-agent. Данная утилита идет в составе пакета openssh-client
. При запуске, она работает в фоновом режиме и может хранить приватные ключи в памяти в расшифрованном виде. Принцип работы:
- агент запускается один раз, пример:
eval $(ssh-agent)
; - в него добавляются ключи, пример:
ssh-add ~/.ssh/id_ed25519
; - агент кэширует ключ в памяти;
- если ключ защищен парольной фразой, то перед кэшированием он запрашивается один раз;
- SSH-клиент при подключении к хостам обращается к агенту не требуя ввода пароля.
Работа с агентом происходит через сокет, используя переменные окружения SSH_AUTH_SOCK
и SSH_AGENT_PID
. Ключи остаются в памяти до явного удаления или перезагрузки ОС.
Ключевое тут “до перезагрузки ОС”, т.е. пароль от защищенных ключей все равно необходимо, хоть и единожды за сеанс, но вводить😥.
Но! Если вы пользователь Linux Desktop — выход есть. И их больше, чем один:
- 1) Механизм Keyring (связка ключей) с компонентом SSH: актуален для пользователей дистрибутивов с рабочими столами Gnome/KDE и производных (в т.ч. Cinnamon🍃);
- 2) Bash скрипт (ssh-agent + Keyring (secret-tool) + expect): универсальный вариант для любых рабочих столов😉.
⚠️Далее речь пойдет об автоматизации работы ssh-agent в GUI системах. Добиться аналогичного функционала в системах без GUI несколько сложнее, возможно сделаю отдельную заметку по этой теме.
В случае 1-го варианта вам практически ничего не нужно делать. Все уже настроено за вас😨. Я лишь расскажу подробности работы механизма. Если это не ваш метод и вы предпочитаете больше контроля, то рассмотрите мою реализацию 2-го способа.
А пока мы поговорим про…
Автоматическое добавление ключей (в т.ч. шифрованных) в ssh-agent
В современных десктопных дистрибутивах Linux таких, как Linux Mint, Ubuntu, Manjaro и т.д. из коробки работает сервис, так называемой “связки ключей”, он же Keyring.
Keyring — это механизм безопасного хранения чувствительных данных (паролей, ключей, токенов). Внутри он представляет собой зашифрованное хранилище (обычно в ~/.local/share/keyrings/
), доступ к которому открывается после аутентификации пользователя (при входе на рабочий стол). Приложения, такие как барузер, почтовый клиент, файловый менеджер и др. обращаются к нему через стандартизированный API для сохранения и извлечения секретов✍️.
Существует 2 популярные реализации механизма связки ключей🔑:
Графическое окружение | Бэкенд | Фронтенд |
---|---|---|
Gnome | gnome-keyring | Seahorse |
KDE | kwallet | KWalletManager |
Сегодня мы рассмотрим работу gnome-keyring, т.к. он используется по умолчанию в Linux Mint.
Способ №1: gnome-keyring с компонентом SSH
Как это связано с ssh-agent? Напрямую: в Linux Mint программа gnome-keyring
может выступать надстройкой над ssh-agent
и управлять им. Такая связка сохраняет SSH-ключи из ~/.ssh
в защищённом хранилище, доступ к которому открывается при входе в систему — удобно и безопасно.
Покажу, как это работает. Но для начала необходимо убедиться, что в вашей системе запущен и работает сервис Keyring:
echo $SSH_AUTH_SOCK
pgrep -af ssh-agent
Если команды выше имеют подобный вывод:
Значит у вас запущен ssh-agent, которым управляет gnome-keyring. Чаще всего сервис gnome-keyring с компонентом SSH запускает специальный .desktop
файл: /etc/xdg/autostart/gnome-keyring-ssh.desktop
при старте графического сеанса пользователя.
Ниже пример содержимого такого файла:
[Desktop Entry]
Type=Application
Name=SSH Key Agent
Comment=GNOME Keyring: SSH Agent
Exec=/usr/bin/gnome-keyring-daemon --start --components=ssh
OnlyShowIn=GNOME;Unity;MATE;
X-GNOME-Autostart-Phase=PreDisplayServer
X-GNOME-AutoRestart=false
X-GNOME-Autostart-Notify=true
X-Ubuntu-Gettext-Domain=gnome-keyring
Настройки автозапуска gnome-keyring можно также увидеть в разделе автозагрузки приложений:
Если вы храните ключи в директории ~/.ssh
и в вашей системе работает gnome-keyring, то скорее всего ваши ключи уже автоматически добавлены в ssh-agent. Как проверить? В предустановленном приложении Seahorse, оно же “Пароли и ключи”:
⚠️Согласно документации для корректной работы
gnome-keyring
важно, чтобы в каталоге~/.ssh/
публичные ключи имели то же имя, что и соответствующие приватные, но с расширением.pub
.
Наличие ключей в агенте можно проверить консольной командой:
ssh-add -l
Видим, что агент автоматически обнаруживает ключи в ~/.ssh
и добавляет к себе. Это справедливо и для ключей защищенных парольной фразой, ☝️если они были сгенерированы при работающем gnome-keyring с компонентом SSH. Довольно смелая самодеятельность🤔.
В целях демонстрации давайте защитим паролем некоторые из наших ключей:
ssh-keygen -p -f ~/.ssh/id_ed25519
ssh-keygen -p -f ~/.ssh/id_ed25519_backup
💡Этой же командой можно этот пароль поменять, предварительно указав старый.
Теперь попробуем авторизоваться с их помощью на удаленных хостах:
ssh database hostname
ssh backup hostname
Видно, что запрос парольной фразы для расшифровки пароля не происходит, хотя мы в ssh-agent ничего не добавляли🪄. Убедиться в том, что ключ защищен паролем можно так:
ssh-keygen -y -f ~/.ssh/id_ed25519
Если ключ зашифрован, вы увидите запрос парольной фразы, после чего в консоль выведется открытая часть ключа. Если пароль не защищен, открытая часть выведется сразу:
Есть один нюанс, когда ssh-agent’ом управляет демон gnome-keyring: если ключи с парольной защитой генерировался в момент, когда gnome-keyring с компонентом ssh не был запущен или генерация происходила на другом сервере, то пароль от ключа в хранилище Keyring не попадет🤷♂️.
Для наглядности вышесказанного сгенерируем новый, защищенный паролем ключ на удаленном сервере, скопируем его на наш локальный хост и настроим по нему подключение:
ssh -t database ssh-keygen -t ed25519 -f ~/id_ed25519_test
scp database:./id_ed25519_test{,.pub} ~/.ssh/
ssh database cat ./id_ed25519_test.pub \>\> ./.ssh/authorized_keys
Теперь при запущенном gnome-keyring и аутентификации по новому ключу:
ssh -i ~/.ssh/id_ed25519_test database
У вас должно появиться ⚠️графическое окно с запросом парольной фразы. Именно графическое, не консольное. В этом окне будет возможность установить чекбокс для сохранения парольной фразы в связке ключей:
Теперь даже после перезагрузки системы при использовании зашифрованных SSH ключей вы всегда будете подключаться к удаленным хостам без введения каких-либо паролей:
ssh -i ~/.ssh/id_ed25519_test database hostname
Все это управление происходит в фоновом режиме и от удобства такой автоматизации становиться немного жутковато. В связи с этим многие предпочитают держать под контролем весь процесс, в т.ч. и я.
Поэтому мы переходим к классическому и проверенному, но не самому простому способу реализации описанной выше автоматизации — с помощью сценария оболочки.
Способ №2: Bash скрипт

Я подготовил Bash скрипт ssh_agent.sh
, который автоматизирует описанные действия из первого способа, но силами консольных утилит и языка оболочки Bash. Предварительно необходимо доустановить пару пакетов📦:
sudo apt install -y curl libsecret-tools expect
curl
— утилита для взаимодействия с веб: понадобится для скачивания скрипта из GitHub;libsecret-tools
— пакет с необходимой для работы скрипта утилитой —secret-tool
expect
— утилита для автоматизации интерактивного ввода: нужна для добавления вssh-agent
защищенных SSH ключей.
⚠️Если в вашей системе нет “связки ключей”, то можно установить тот же
gnome-keyring
:sudo apt install -y gnome-keyring
Скачиваем скрипт в /usr/local/bin
и делаем его исполняемым:
sudo curl -fL https://raw.githubusercontent.com/r4ven-me/bash/main/ssh_agent.sh \
-o /usr/local/bin/ssh_agent
sudo chmod +x /usr/local/bin/ssh_agent
ssh_agent.sh
#!/usr/bin/env bash
# Set system PATH
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"
# Строгий режим выполнения скрипта
set -euo pipefail
# Общие переменные
IFS=$'\n\t'
SSH_ENV="$HOME/.ssh/environment"
UTILS=("ssh-agent" "ssh-add" "ssh-keygen" "expect" "secret-tool")
# Проверка наличия в системе необходимых утилит
for util in "${UTILS[@]}"; do
if ! which "$util" > /dev/null; then
echo "$util not found or not installed"
exit 1
fi
done
# Функция добавления защищенных SSH ключей в агент
ssh_add() {
local key="$1"
local pass="$2"
if ! expect << EOF
spawn ssh-add $key
expect "Enter passphrase for $key"
send -- "$pass\r"
expect {
"Bad passphrase" {
exit 1
}
"Identity added" {
exit 0
}
eof
}
EOF
then
return 1
fi
return 0
}
# Основная логика загрузки SSH ключей
load_keys() {
echo "Loading SSH keys..."
local pass
local keys
readarray -t keys < <(find "$HOME/.ssh" -type f \( \
-name 'id_rsa*' -o -name 'id_dsa*' -o \
-name 'id_ecdsa*' -o -name 'id_ed25519*' \
\) ! -name '*.pub')
for key in "${keys[@]}"; do
if ! ssh-keygen -y -P "" -f "$key" &> /dev/null; then
if pass=$(secret-tool lookup unique ssh-store:"${key}"); then
ssh_add "$key" "$pass" &> /dev/null && echo "Loaded: $key" || echo "Failed to load: $key"
else
echo "Failed to get passphrase for $key"
if [[ -t 0 ]]; then
echo "Let's add it to keyring:"
secret-tool store --label="$(basename "$key")" unique ssh-store:"${key}"
pass=$(secret-tool lookup unique ssh-store:"${key}")
ssh_add "$key" "$pass" &> /dev/null && echo "Loaded: $key" || echo "Failed to load: $key"
fi
fi
else
ssh-add "$key" >/dev/null 2>&1 && echo "Loaded: $key" || echo "Failed to load: $key"
fi
done
}
# Инициализация агента SSH
start_agent() {
echo "Initializing new SSH agent..."
ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
chmod 600 "$SSH_ENV"
# shellcheck disable=SC1090
source "$SSH_ENV" > /dev/null
echo "SSH agent started successfully."
load_keys
}
# Проверка процесса агента
is_agent_running() {
if [[ -n "${SSH_AGENT_PID:-}" ]]; then
ps -p "$SSH_AGENT_PID" -o comm= 2>/dev/null | grep -q '^ssh-agent$'
else
return 1
fi
}
# Основной поток исполнения
if [[ -f "$SSH_ENV" ]]; then
# shellcheck disable=SC1090
source "$SSH_ENV" > /dev/null
if ! is_agent_running; then
echo "Stale SSH agent found. Restarting..."
start_agent
fi
else
start_agent
fi
Описание логики скрипта:
- Настройка окружения:
- устанавливается переменная
PATH
для гарантированного доступа к исполняемым файлам; - включается строгий режим выполнения (
set -euo pipefail
); - определяются общие переменные:
IFS
,SSH_ENV
(путь к файлу окружения SSH),UTILS
(список необходимых утилит);
- устанавливается переменная
- Проверка зависимостей:
- проверяется наличие всех необходимых утилит (
ssh-agent
,ssh-add
,ssh-keygen
,expect
,secret-tool
); - если какая-то утилита отсутствует, скрипт завершается с ошибкой;
- проверяется наличие всех необходимых утилит (
- Функция
ssh_add
:- добавляет защищённый SSH-ключ в агент с помощью
expect
, автоматически вводя пароль, извлеченный из Keyring;
- добавляет защищённый SSH-ключ в агент с помощью
- Функция
load_keys
:- ищет все приватные SSH-ключи в
~/.ssh/
(кроме файлов с расширением.pub
); - для каждого ключа:
- проверяет, требуется ли пароль (
ssh-keygen -y
); - если пароль нужен, извлекает его из хранилища в разделе “Пароли –> Вход” с помощью утилиты
secret-tool
; - если пароль не найден, предлагает сохранить его в Keyring;
- добавляет ключ в агент (утилитой
ssh-add
или функциейssh_add
);
- проверяет, требуется ли пароль (
- ищет все приватные SSH-ключи в
- Функция
start_agent
:- запускает новый
ssh-agent
, сохраняет его переменные окружения в~/.ssh/environment
; - загружает ключи (
load_keys
);
- запускает новый
- Функция
is_agent_running
:- проверяет, работает ли текущий SSH-агент (по переменной
SSH_AGENT_PID
);
- проверяет, работает ли текущий SSH-агент (по переменной
- Основная логика:
- если файл окружения (
~/.ssh/environment
) существует, загружает его; - если процесс агента отсутствует, перезапускает его (
start_agent
); - если файла окружения нет, запускает агент впервые.
- если файл окружения (
Отдельно стоит отметить ситуацию, когда SSH ключ защищен паролем, а парольной фразы нет в связке ключей. Что в таком случае происходит во время запуске скрипта:
- В интерактивном режиме (например, при запуске в терминале):
- скрипт обнаружит защищённый ключ и запросит пароль у пользователя;
- после однократного ввода пароль будет сохранён в
gnome-keyring
с помощьюsecret-tool
; - в последующих запусках скрипт автоматически будет извлекать пароль из хранилища и передавать его
ssh-agent
с помощьюexpect
;
- В неинтерактивном режиме (например, через
cron
илиssh-команду
) скрипт просто проигнорирует данный ключ.
Перед демонстрацией стоит предупредить, что если ssh-agent
уже запущен, запуск второго экземпляра создаст новый агент с другим сокетом, и ключи из первого будут недоступны☝️.
Запускаем скачанный скрипт:
ssh_agent
Агент успешно стартовал, предварительно запросив пароль для двух защищенных ключей. Эти пароли сохранились в gnome-keyring с помощью secret-rool
.
⚠️Обратите внимание, что скрипт сохраняет пароли в gnome-keyring в разделе “Пароли –> Вход“, а не в “Ключи OpenSSH” — это два разных хранилища!
Для доступа к данным ssh-agent’а в текущей сессии терминала необходимо подгрузить переменные окружения из ~/.ssh/environment
. Также проверяем наличие процесса ssh-agent
и список загруженных ключей:
pgrep -af ssh-agent
source ~/.ssh/environment
ssh-add -l
Если вам необходимо добавить защищенный ключ в Keyring вручную, чтобы он корректно считывался скриптом, воспользуйтесь командой:
secret-tool store --label=id_ed25519 unique ssh-store:/home/ivan/.ssh/id_ed25519
Где в label
— указывается имя самого приватного ключа без пути к нему, а в параметре unique
наоборот, нужно указать абсолютный путь до файла ключа.
⚠️Если на одном из ваших SSH ключей изменилась парольная фраза, то ее необходимо обновить в связке ключей. Либо через GUI “Пароли и ключи”, либо подобной выше командой.
Остается настроить автозапуск скрипта ssh_agent
. Сделать это можно через .*rc
файл:
# для bash
echo '( { sleep 5 && ssh_agent } & ) > /dev/null 2>&1' >> ~/.bashrc
# для zsh
echo '( { sleep 5 && ssh_agent } & ) > /dev/null 2>&1' >> ~/.zshrc
Или лучше в настройках системы:
Разница в том, что при автозапуске через настройки системы скрипт запуститься один раз, в случае же .*rc
файла скрипт будет запускаться каждый раз, но при этом ssh-agent
повторно не запуститься — логика скрипта предусматривает этот момент👌.
Ах да, чтобы не выполнять source $HOME/.ssh/environment
в каждой сессии терминала, добавьте следующую команду в ваш .*rc
файл:
# для bash
echo 'if [[ -f "$HOME/.ssh/environment" ]]; then source $HOME/.ssh/environment; fi' >> ~/.bashrc
# для zsh
echo 'if [[ -f "$HOME/.ssh/environment" ]]; then source $HOME/.ssh/environment; fi' >> ~/.zshrc
Для итоговой проверки рекомендую перезайти в сеанс или перезапустить ОС:
reboot
Проеряем работу агента с авторизацией на хосте по защищенному ключу:
ssh-add -l
ssh database
Никаких паролей🔥.