SSH (или secure shell) – наиболее распространенный способ подключения к хостам Linux для удаленного управления. Основы подключения к одному хосту довольно просты и очевидны, но задача станет гораздо сложнее, когда вы начнете работать с большим количеством удаленных систем.
К счастью, OpenSSH позволяет вам настраивать параметры подключения на стороне клиента. Их можно сохранить в файле конфигурации, который будет использоваться для определения значений каждого хоста. Это поможет сохранить индивидуальные параметры подключения, которые вы используете для каждого хоста, и сократит количество данных, которые нужно указать в командной строке, когда вам нужно подключиться.
Требования
Для работы вам понадобятся базовые знания SSH и некоторых параметров подключения. Вы также можете настроить SSH-аутентификацию на основе ключей для некоторых ваших пользователей или хостов (по крайней мере, для тестирования).
Структура и алгоритм интерпретации конфигурационного файла SSH
Каждый пользователь локальной системы может поддерживать конфигурационный файл SSH на стороне клиента. Этот файл может содержать любые параметры подключения, которые нужно использовать в командной строке, что позволяет автоматически обрабатывать их при подключении. Значения, определенные в конфигурации, всегда можно переопределить во время соединения с помощью обычных флагов ssh.
Расположение конфигурационного файла клиента SSH
Клиентский конфигурационный файл называется config, он находится в домашнем каталоге пользователя, в подкаталоге конфигурации .ssh. Часто этот файл не создается по умолчанию, поэтому вам может понадобиться создать его самостоятельно:
touch ~/.ssh/config
Структура конфигурационного файла
Конфигурационный файл организован по хостам. Каждое определение хоста может содержать параметры подключения для конкретного совпадающего хоста. Также можно использовать подстановочные знаки для параметров, которые должны иметь более широкий охват.
Каждый из разделов начинается с заголовка, определяющего хосты, которые должны соответствовать параметрам конфигурации в этом разделе. Конкретные элементы конфигурации для этого хоста определяются ниже. Здесь необходимо указать только те элементы, которые отличаются от значений по умолчанию, поскольку хост наследует эти значения для любых неопределенных элементов. Раздел определяется от одного заголовка Host до следующего заголовка Host.
Как правило, для организационных целей и удобочитаемости параметры, заданные для каждого хоста, имеют отступы. Это позволяет упростить интерпретацию файла.
Общий формат будет выглядеть примерно так:
Host firsthost
SSH_OPTION_1 custom_value
SSH_OPTION_2 custom_value
SSH_OPTION_3 custom_value
Host secondhost
ANOTHER_OPTION custom_value
Host *host
ANOTHER_OPTION custom_value
Host *
CHANGE_DEFAULT custom_value
Здесь представлены четыре раздела, которые будут применяться к каждой попытке подключения в зависимости от того, какому разделу отвечает тот или иной хост.
Алгоритм интерпретации
Очень важно понять, как SSH интерпретирует файл для применения значений, определенных внутри. Это имеет большие последствия при использовании подстановочных знаков и определения Host *.
SSH будет искать совпадения по имени хоста, указанному в командной строке, с каждым из заголовков Host, которые определяют разделы конфигурации. Он будет начинать анализ с верхней части страницы, поэтому порядок разделов невероятно важен.
Шаблоны в определении хоста не должны совпадать с фактическим хостом, с помощью которого вы будете подключаться. Вы можете использовать эти определения для настройки псевдонимов хостов, которые могут использоваться вместо фактического имени хоста.
Например:
Host devel
HostName devel.example.com
User tom
Этот хост позволяет подключиться как tom@devel.example.com, введя в командной строке:
ssh devel
Имея это в виду, мы можем обсудить, как SSH применяет каждый параметр конфигурации, когда он перемещается вниз по файлу. Он начинает анализ с начала файла и проверяет каждое определение хоста, чтобы узнать, соответствует ли оно значению, указанному в командной строке.
Когда SSH находит первое совпадение с определением хоста, каждый из связанных с ним параметров применяется к новому соединению. Однако интерпретация на этом не заканчивается.
Затем SSH перемещается вниз по файлу, проверяя, совпадает ли хост с другими определениями хоста. Если найдено другое определение, которое отвечает текущему имени хоста в командной строке, SSH рассмотрит параметры, связанные с новым разделом. Затем он будет применять все параметры SSH, определенные в новом разделе, которых не было в предыдущих разделах.
Этот последний момент чрезвычайно важен. SSH будет по порядку интерпретировать каждый из разделов Host, который отвечает имени хоста в командной строке. Во время этого процесса он всегда будет использовать первое значение, указанное для каждой опции. Переопределить значение, которое уже было предоставлено в каком-либо предыдущем разделе, невозможно.
Это означает, что ваш конфигурационный файл должен следовать простому правилу: наиболее конкретные конфигурации должны находиться вверху файла. Более общие определения могут появиться позже (это позволяет применить опции, которые не были определены предыдущими разделами).
Давайте снова посмотрим на конфигурационный файл config, который мы использовали в последнем разделе:
Host firsthost
SSH_OPTION_1 custom_value
SSH_OPTION_2 custom_value
SSH_OPTION_3 custom_value
Host secondhost
ANOTHER_OPTION custom_value
Host *host
ANOTHER_OPTION custom_value
Host *
CHANGE_DEFAULT custom_value
Здесь первые два раздела определяются буквальными именами хостов (или псевдонимами), что означает, что они не используют никаких подстановочных знаков. Если создать подключение с помощью ssh firsthost, то сначала будет применен первый раздел. Для этого соединения будут установлены опции SSH_OPTION_1, SSH_OPTION_2 и SSH_OPTION_3.
SSH проверит второй раздел и обнаружит, что он не соответствует, после чего продолжит читать файл. Затем он найдет третий раздел и увидит в нем соответствия. Он проверит ANOTHER_OPTION, чтобы узнать, было ли это значение установлено в предыдущих разделах. Выяснив, что это не так, он применит это значение. Последний раздел будет отвечать любому хосту, поскольку определение * соответствует каждому соединению. Так как у SSH нет значения для переопределения опции CHANGE_DEFAULT из других разделов, он возьмет значение из этого раздела. Затем с опциями, собранными в этом процессе, выполняется соединение.
Давайте попробуем теперь вызвать ssh secondhost из командной строки.
Опять же, интерпретация начнется в первом разделе. Поскольку этот раздел отвечает только соединению firsthost, SSH пропустит этот раздел. Он перейдет во второй раздел. Узнав, что этот раздел соответствует запросу, он соберет значение ANOTHER_OPTION для этого соединения.
Затем SSH просмотрит третье определение и обнаружит, что подстановочный знак также соответствует текущему соединению. Затем он проверит, есть ли уже значение ANOTHER_OPTION. Поскольку этот параметр был определен во втором разделе, который уже был прочитан, SSH сбросит значение из третьего раздела.
Затем SSH проверит четвертый раздел и применит параметры, которые не были определены в предыдущих соответствующих разделах. Затем он попытается создать подключение, используя полученные значения.
Базовые опции подключений
Теперь, когда у вас есть представление об общем формате конфигурационного файла, давайте обсудим некоторые общие параметры и формат, который будет использоваться для указания их в командной строке.
Основная информация, необходимая для подключения к удаленному хосту – это имя хоста, имя пользователя и порт, на котором запущен демон SSH.
Например, нам нужно подключиться из командной строки как пользователь apollo к хосту example.com, SSH-демон которого работает по порту 4567. Предоставить переменные можно различными способами. Наиболее распространенным, вероятно, будет формат:
ssh -p 4567 apollo@example.com
Но также можно использовать полные опции с флагом –о:
ssh -o «User=apollo» -o «Port=4567» -o «HostName=example.com» anything
Здесь все параметры передаются с помощью флага -o. Тут также можно указать псевдоним anything для хоста, как в конфигурационном файле. Фактическое имя хоста берется из параметра HostName.
Имена опций, написанные с большой буквы, во втором варианте – то же самое, что нужно использовать в файле конфигурации. Вы можете найти полный список доступных параметров с помощью команды:
man ssh_config
Чтобы установить их в конфигурационном файле, сначала нужно решить, какие хосты будут их использовать.
Также у вас есть возможность назначить псевдоним для этого соединения. Благодаря этому вам не придется вводить все имя хоста каждый раз. Например, вы можете использовать псевдоним «home» для ссылки на это соединение и связанные с ним параметры:
Host home
Теперь можно определить детали соединения для этого хоста. Вы можете использовать второй формат, который мы рассмотрели выше.
Host home
HostName example.com
User apollo
Port 4567
Мы определяем параметры с помощью системы «ключ-значение». Каждая пара должна быть в отдельной строке. Ключи могут быть отделены от значений либо пробелом, либо знаком равенства с дополнительным пробелом. Все эти строки клиент SSH интерпретирует одинаково:
Port 4567
Port=4567
Port = 4567
Единственное различие заключается в том, что (в зависимости от параметра и значения) используя знак равенства без пробелов, вы можете указать опцию в командной строке без кавычек. Это зависит только от ваших предпочтений.
Настройка общих параметров
Пока конфигурация, которую мы разработали, невероятно проста. В целом она выглядит так:
Host home
HostName example.com
User apollo
Port 4567
Что делать, если вы используете одно и то же имя пользователя на рабочем и на домашнем компьютере? Вы могли бы добавить дополнительные опции для рабочей машины следующим образом:
Host home
HostName example.com
User apollo
Port 4567
Host work
HostName company.com
User apollo
Это работает, но так вам придется повторять значения. Это всего лишь одна опция, поэтому это не очень важно, но иногда нужно повторять большое количество опций. Лучший способ сделать это – вынести общие параметры в отдельные разделы.
Если вы используете имя пользователя «apollo» на всех машинах, к которым подключаетесь, вы можете поместить это имя в общее определение «Host», обозначенное одним символом *, которое отвечает каждому соединению. Помните: чем глобальнее конфигурация, тем дальше она должна находиться в файле.
Host home
HostName example.com
Port 4567
Host work
HostName company.com
Host *
User apollo
Это устраняет проблему повторения в конфигурации и будет работать, если «apollo» является именем вашего пользователя по умолчанию в большинстве систем, к которым вы подключаетесь.
Но что, если некоторые системы не используют это имя? Существует несколько различных способов настройки в зависимости от того, насколько широко используется имя этого пользователя.
Если имя пользователя «apollo» используется почти на всех ваших хостах, вероятно, лучше оставить его в общем разделе Host *. Этот параметр применяется ко всем хостам, которые не получили имя пользователя из вышеперечисленных разделов конфигурации. Для машин, которые используют другое имя пользователя, можно переопределить значение по умолчанию. Это значение будет иметь приоритет, если оно определено перед общим разделом:
Host home
HostName example.com
Port 4567
Host work
HostName company.com
Host oddity
HostName weird.com
User zeus
Host *
User apollo
К хосту oddity SSH будет подключаться с помощью пользователя «zeus». Все другие соединения не получат свое имя пользователя до тех пор, пока не прочитается общее определение Host *.
Что произойдет, если имя «apollo» будет использоваться несколькими соединениями, не являясь значением по умолчанию? Чтобы переименовать псевдонимы, можно использовать подстановочный символ (для применения дополнительных параметров только к этим двум хостам).
Вы можете изменить псевдоним home на что-то вроде hapollo, а рабочее соединение заменить чем-то вроде wapollo. Таким образом, оба хоста совместно используют часть apollo, что позволяет сослаться на них в других разделах с помощью подстановочных знаков:
Host hapollo
HostName example.com
Port 4567
Host wapollo
HostName company.com
Host *apollo
User apollo
Host *
User diffdefault
Здесь общее определение User перемещено в раздел хоста, который отвечает соединениям SSH к хостам, которые заканчиваются на apollo. Любое соединение, не заканчивающееся на apollo (и без отдельного Host, определяющего User), получит имя diffdefault.
Обратите внимание, что мы учли порядок конфигураций: индивидуальные идут в начале файла, а глобальные – в конце.
Базовые опции SSH
На данный момент мы обсудили некоторые основные параметры, необходимые для установления соединения:
HostName: фактическое имя хоста, которое должно использоваться для соединения. Оно заменяет любой псевдоним, определенный в заголовке Host. Этот параметр не требуется, если определение Host указывает валидное имя хоста для подключения.
User: имя пользователя, с помощью которого нужно создать подключение.
Port: порт, по которому работает удаленный демон SSH (эта опция нужна, только если удаленный экземпляр не работает по стандартному порту 22).
SSH поддерживает еще много полезных опций, которые нужно рассмотреть.
Общие опции подключений
Вот несколько широко применяемых опций (чаще всего они используются в разделе Host *).
ServerAliveInterval: Этот параметр указывает SSH, когда отправлять пакет для проверки ответа от сервера. Это полезно, если ваше соединение ненадежно, и вы хотите узнать, доступен ли сервер.
LogLevel: Настраивает уровень детализации, с которым SSH будет регистрироваться на стороне клиента. Это позволяет отключить регистрацию в определенных ситуациях или увеличить уровень детализации при отладке. Уровни по возрастанию детализации: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG1, DEBUG2 и DEBUG3.
StrictHostKeyChecking: Этот параметр настраивает автоматическое добавление хостов в файл ~/.ssh/known_hosts. По умолчанию для этого параметра будет задано значение «ask», что означает, что он предупредит вас, если Host Key, полученный с удаленного сервера, не соответствует найденному в файле known_hostsfile. Если вы постоянно подключаетесь к большому числу эфемерных хостов, вы можете переключить его на «no». Тогда SSH автоматически добавит в файл все хосты. Это может иметь последствия для безопасности, поэтому тщательно подумайте, прежде чем включать этот параметр.
UserKnownHostsFile: Этот параметр указывает расположение, в котором SSH будет хранить информацию о том, к каким хостам он подключился. Обычно этот параметр не используется, но вы можете установить его в /dev/null, если вы отключили проверку хоста.
VisualHostKey: Этот параметр позволяет SSH отображать ASCII-представление ключа удаленного хоста при подключении. Включив его, вы получите простой способ узнать ключ вашего хоста, что позволит вам легко распознать его, если вам понадобится подключиться с другого компьютера в будущем.
Compression: Этот параметр полезен при создании очень медленных соединений. Он используется очень редко.
Учитывая перечисленные выше элементы, можно внести в конфигурации несколько полезных настроек.
Например, если вы очень быстро создаете и удаляете хосты у провайдера облачных вычислений, вам может пригодиться такой раздел:
Host home
VisualHostKey yes
Host cloud*
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
LogLevel QUIET
Host *
StrictHostKeyChecking ask
UserKnownHostsFile ~/.ssh/known_hosts
LogLevel INFO
ServerAliveInterval 120
Это включит визуальный ключ хоста для домашнего подключения, что позволит вам ознакомиться с ним, чтобы вы могли узнать, меняется ли он или при подключении с другого компьютера. Также любой хост, который начинается с cloud*, не будет проверять хосты и регистрировать ошибки в логах.
Переадресация соединений
Одним из распространенных способов использования SSH является переадресация соединений, позволяющая локальному подключению создавать туннель через удаленный хост (или удаленной машине получать доступ к туннелю через локальную машину). SSH также может выполнять динамическую переадресацию с помощью протоколов, таких как SOCKS5, которые включают информацию пересылки для удаленного хоста.
Важные опции:
LocalForward: Этот параметр используется для определения соединения, которое будет перенаправлять трафик локального порта на удаленную машину, туннелируя его в удаленную сеть. Первым аргументом должен быть локальный порт, на который вы хотите направить трафик, а вторым – адрес и порт, на которые вы хотите направить этот трафик.
RemoteForward: Этот параметр используется для определения удаленного порта, на который можно направить трафик (чтобы туннель выходил из локальной машины). Первым аргументом должен быть удаленный порт, по которому трафик будет направляться на удаленную систему. Второй аргумент – адрес и порт, на который нужно направить трафик, когда он прибывает в локальную систему.
DynamicForward: Позволяет настроить локальный порт, который может использоваться с протоколом динамической пересылки, таким как SOCKS5. Трафик с использованием протокола динамической пересылки может быть затем направлен на этот порт на локальном компьютере и на удаленной машине (он будет маршрутизироваться в соответствии с включенными значениями).
Эти опции можно использовать для перенаправления портов в обе стороны:
# This will allow us to use port 8080 on the local machine
# in order to access example.com at port 80 from the remote machine
Host local_to_remote
LocalForward 8080 example.com:80
# This will allow us to offer access to internal.com at port 443
# to the remote machine through port 7777 on the other side
Host remote_to_local
RemoteForward 7777 internal.com:443
Другая переадресация
Наряду с переадресацией соединений SSH также поддерживает другие виды переадресации.
Вы можете пересылать любые ключи SSH, хранящиеся в агенте на локальной машине, что позволяет подключаться из удаленной системы, используя учетные данные локальной системы. Также можно запускать приложения в удаленной системе и пересылать графический дисплей в локальную систему с помощью пересылки X11.
ForwardAgent: Эта опция позволяет передавать ключи аутентификации, хранящиеся на локальном компьютере, в систему, к которой вы подключаетесь. Она позволяет переходить от хоста к хосту с помощью домашних ключей.
ForwardX11: Если вы хотите перенаправить графический экран приложения, работающего в удаленной системе, вы можете включить этот параметр.
Эти опции используют значения yes и no.
Определение ключей
Если вы используете SSH-ключи, эти параметры помогут вам управлять ключами для каждого хоста.
IdentityFile: Эта опция может использоваться для указания расположения ключа каждого хоста. Если ваши ключи находятся в местах по умолчанию, каждый из них будет проверен автоматически. Если у вас есть несколько ключей, и все они используются для разных целей, эта опция укажет точный путь, где можно найти правильный ключ.
IdentitiesOnly: Благодаря этой опции SSH будет полагаться только на тождества, предоставленные в файле конфигурации. Она может быть необходима, если у агента SSH в памяти есть альтернативные ключи, которые недействительны для данного хоста.
Эти параметры особенно полезны, если вам нужно отслеживать большое количество ключей разных хостов и использовать один или несколько агентов SSH.
Мультиплексирование SSH через одно TCP-соединение
SSH имеет возможность использовать одно TCP-соединение для нескольких SSH-соединений с одним и тем же хост-компьютером. Это может быть полезно, если для установления рукопожатия TCP с удаленной машиной требуется некоторое время (служебные данные будут удалены из дополнительных SSH-соединений).
Для этого можно использовать опции:
ControlMaster: Этот параметр определяет, включать ли мультиплексирование, когда это возможно. Как правило, если вы хотите использовать эту опцию, вы должны установить здесь auto в разделе хостов, которые медленно подключаются, или в общем разделе Host *.
ControlPath: Эта опция определяет файл сокета, который используется для управления соединениями. Она должна указывать место в файловой системе. Как правило, она позволяет легко маркировать сокеты по хостам. Чтобы назвать сокет на основе имени пользователя, удаленного хоста и порта, вы можете использовать /path/to/socket/%r@%h:%p.
ControlPersist: Эта опция устанавливает время в секундах, в течение которого соединение TCP должно оставаться открытым после закрытия последнего соединения SSH. Установив здесь большое значение, вы сможете открывать новые соединения после закрытия первого, но чаще здесь устанавливается значение 1 (чтобы не открывать неиспользуемое TCP-соединение).
Для настройки такого поведения можно использовать этот раздел:
Host *
ControlMaster auto
ControlPath ~/.ssh/multiplex/%r@%h:%p
ControlPersist 1
Убедитесь, что каталог был создан.
mkdir -p ~/.ssh/multiplex
Если вы не хотите использовать мультиплексирование для отдельных соединений, вы можете отключить его с помощью команды:
ssh -S none user@host