Вопрос или проблема
У меня есть сервер, названный gamma
, который постоянно работает на работе. Иногда я подключаюсь к нему из дома, в этом случае я использую публичный IP-адрес 55.22.33.99
. Иногда я подключаюсь к нему, когда нахожусь на работе, и вместо того, чтобы ненужно переправлять свои пакеты, я подключаюсь через локальный IP-адрес 192.168.1.100
.
На данный момент я разделил их на две разные записи в ~/.ssh/conf
Host gamma-local
HostName 192.168.1.100
Port 22
User andreas
Host gamma-remote
HostName 55.22.33.99
Port 12345
User andreas
Таким образом, если я на работе, все, что мне нужно ввести, это ssh gamma-local
, и я внутри; если я дома (или где-то еще в мире), я ввожу ssh gamma-remote
.
Когда я подключаюсь к серверу, я бы предпочел не вводить другое имя в зависимости от того, где я нахожусь, я бы предпочел, чтобы эта часть выполнялась автоматически; например, в некоторых случаях у меня есть автоматизированные скрипты для подключения, которые не знают, где я нахожусь.
Существует вопрос, который решает эту проблему, используя Bash-скрипт, чтобы “попробовать” сначала подключиться к локальному, и если он не подключается, попробовать подключиться к удаленному IP-адресу. Это хорошо, но (1) кажется неэффективным (особенно поскольку иногда нужно “ждать”, пока соединения не истекут, так как они не всегда сразу отправляют ошибку) и (2) требует Bash и переноски скрипта.
Есть ли альтернативный способ достижения этого, который не полагается на использование Bash-скриптов или “тестирование”, работает ли соединение сначала?
Если у вас есть способ распознать, в какой сети вы находитесь, то вы можете использовать ключевое слово Match
в ~/.ssh/config
, чтобы сделать то, что вам нужно. Это требует OpenSSH ≥6.5.
Я использую что-то подобное
Match originalhost gamma exec "[ x$(/sbin/iwgetid --scheme) != xMyHomeESSID ]"
HostName 192.168.1.100
Port 22
Host gamma
User andreas
Port 12345
HostName 55.22.33.99
Таким образом, я использую идентификатор используемой сети Wi-Fi, чтобы определить, нахожусь ли я дома для целей подключения по SSH, но также можно использовать проверку IP-адреса, назначенного вашему компьютеру, или что-то еще, что позволяет различать две сети.
Если у вас есть собственные DNS-серверы на работе и если вы используете один и тот же ноутбук из офиса и дома, вы можете воспользоваться этим для достижения цели:
- Измените свой
nsswitch.conf
на своем компьютере, чтобы сначала проверять в DNS - Создайте запись DNS для
gamma
, чтобы она разрешалась в 192.168.1.100 в вашем частном DNS в офисе. - Создайте запись в файле /etc/hosts вашего компьютера для разрешения
gamma
в 55.22.33.99.
Таким образом, когда вы будете использовать ssh gamma
из офиса, он будет разрешаться из офисного DNS в 192.168.1.100, а когда вы подключитесь из дома, он будет разрешаться в 55.22.33.99 из вашего файла hosts.
P.S: Этот ответ предполагает, что вы не хотите, чтобы gamma имела публичную запись DNS. Кроме того, если вы подключаетесь к своему серверу с устройства Windows, я думаю, что должно быть какое-то место, эквивалентное файлу nsswitch.conf, чтобы переопределить записи файла hosts.
Несколько лет назад я написал программу для аналогичной цели. Она может удовлетворить ваши потребности. С этой программой конфигурация ssh может выглядеть так:
Host gamma
ProxyCommand ssh-multipath-proxy 192.168.1.100:22 55.22.33.99:12345
User andreas
Я не знаю, возможно ли сделать это через ~/.ssh/config
, но другой подход заключается в том, чтобы подключаться к одному или другому в зависимости от вашего внешнего IP-адреса. Поскольку, предположительно, когда вы находитесь на работе, ваш IP будет 55.22.33.NNN
, вы можете запустить что-то вроде:
[[ $(wget -qO - http://wtfismyip.com/text) =~ ^'55.22.33.' ]] &&
ssh 192.168.1.100 ||
ssh -p 12345 55.22.33.99
Еще проще использовать ваш внутренний IP. Я не знаю, как у вас настроены две сети, но если легко определить, находитесь ли вы на работе или нет по вашему IP (например, если у вас есть конкретный IP на работе, такой как 192.168.1.12
), вы можете сделать (замените eth0
на имя вашего сетевого адаптера):
[[ $(ip address show dev eth0 | grep -Po 'inet \K[\d.]+') = '192.168.1.12' ]] &&
ssh 192.168.1.100 ||
ssh -p 12345 55.22.33.99
Что бы вы ни решили использовать, вы можете добавить это как псевдоним в вашу оболочку (добавьте эту строку в файл инициализации вашей оболочки, ~/.bashrc
, если вы используете bash
):
alias gamma="[[ $(wget -qO - http://wtfismyip.com/text) =~ ^'55.22.33.' ]] && ssh 192.168.1.100 || ssh -p 12345 55.22.33.99
Вы также можете сделать это скриптом, если хотите, чтобы другие скрипты имели доступ к нему (псевдонимы из .bashrc
не читаются при запуске скрипта).
Вы не можете достичь этого в ~/.ssh/config
, используя IP-адреса в качестве имен хостов. Дополнительные осложнения связаны с тем, что вы не только подключаетесь к разным IP-адресам, но и к разным портам, так как это делает невозможным какое-либо изменение вашего DNS-решателя.
Я был неправ – вы можете использовать комбинацию Match originalhost ... exec ...
в ~/.ssh/config
– смотрите @ответ Михаła Политовского. Однако, хотя это будет работать прекрасно для OpenSSH, вы не обязательно найдете аналогичную функциональность в других SSH-клиентах.
Вы можете обойти проблему, используя простой обертку (либо функцию оболочки, либо скрипт, если вам нужно использовать его из различных оболочек) для ssh
, которой будет проверять, в какой сети вы находитесь, и использовать соответствующую запись Host
. Большой вопрос заключается в том, как надежно определить, в какой сети вы находитесь. Локальный IP-адрес приходит на ум, но не является надежным, так как вы также можете подключаться из локальной сети, которая использует ту же подсеть, что и ваша рабочая сеть.
Если у вас могут быть одинаковые порты для локальных и удаленных сетей, вы можете редактировать ваш /etc/resolv.conf
в зависимости от вашей сети – очевидно, это должно быть сделано автоматически (скорее всего, из скрипта хука вашего DHCP-клиента). Или – лучше – запустите локальный сервер имен (например, dnsmasq
) и предоставьте ему соответствующую конфигурацию. Но это выходит за рамки этого вопроса.
Еще один вариант – если вам нужно только взаимодействующее соединение – это использовать автозаполнение команд, которое будет сканировать ~/.ssh/config
. Это сэкономит вам немного времени на ввод (особенно если у вас достаточно различные записи Host
). Что-то вроде этого (для инициализации bash
):
# Дополните имена сессий для ssh
declare -g _ssh_complete_hostlist 2> /dev/null
function _ssh_complete_init () {
_ssh_complete_hostlist=$( \
sed -nr '/^\s*Host\s*=/{s/^[^=]+= *//;s/ /\n/g;p}' ~/.ssh/config \
| sort )
}
_ssh_complete_init
function _ssh_complete () {
local match=${COMP_WORDS[${COMP_CWORD}]}
local hosts=
local default=
for h in $_ssh_complete_hostlist; do
if [[ $h =~ ^$match ]]; then
hosts="$hosts $h"
fi
done
if ! (( ${COMP_CWORD} == ${#COMP_WORDS[@]}-1 )); then
default=$( compgen -f ${COMP_WORDS[${COMP_CWORD}]} )
fi
COMPREPLY=($hosts $default)
}
complete -F _ssh_complete ssh
Первая функция создает список, из которого будут завершаться хосты (обычно достаточно запустить это один раз в каждой оболочке), вторая функция выполняет само завершение, хотя несколько неуклюжим образом – она завершается только тогда, когда имя хоста является последним токеном в командной строке.
Сказав это, правильный способ подойти к этой проблеме – подключиться к рабочей сети через VPN и тем самым сделать локальный рабочий IP-адрес доступным, как если бы вы находились в офисе. Таким образом, вы можете жестко зарезервировать адрес в любом нужном слое: ~/.ssh/config
, /etc/resolv.conf
или (по моему мнению, лучший вариант) сервере имен в офисе.
Еще одно решение – использовать два разных конфигурационных файла для SSH. Вы можете считать это немного менее элегантным, чем иметь все в одном конфигурационном файле, но это проще в обслуживании.
Вы выбираете конфигурационный файл, который хотите использовать, с помощью -F <configfile>
.
После того как я не смог добиться чего-то, что будет работать в различных средах (например, Hostname -I
не работает в git bash), я в конечном итоге выбрал что-то немного более вручную, немного более надежное и немного менее навороченное:
Match originalhost gamma exec "[ -e ~/.ssh/gamma.local ]"
HostName 192.168.1.100
Port 22
Host gamma
User andreas
Port 12345
HostName 55.22.33.99
Затем touch ~/.ssh/gamma.local
на машинах в одной сети.
Частичный ответ:
Многие ответы выше начинаются с “если вы можете определить, в какой сети вы находитесь”. Для этого я использую скрипт, который запускается, когда интерфейсы подключены, чтобы запускать различные вещи (обычно VPN), когда я подключаюсь к одной из своих обычных сетей. Техника заключается в использовании ARP для получения MAC-адреса шлюза:
function identifyConnection {
gatewayIP=$(route -n | grep -e '^0\.0\.0\.0' | tr -s ' ' | cut -d ' ' -f 2)
if [[ ! -z "$gatewayIP" ]]
then
# Определите шлюз по его MAC (уникальность...)
log "IP-адрес шлюза=$gatewayIP"
log "Получение соответствующего MAC-адреса шлюза"
gatewayData=($(arp -n $gatewayIP | grep -e $gatewayIP | tr -s ' '))
if [[ "${gatewayData[1]}" == "(incomplete)" ]]
then
log "Статус шлюза $gatewayIP "incomplete""
echo ""
elif [[ "${gatewayData[2]}" == "--" ]]
then
log "MAC-адрес для $gatewayIP не найден"
echo ""
else
log "MAC-адрес шлюза=[${gatewayData[2]}]"
echo "${gatewayData[2]}"
fi
fi
}
И тогда у меня есть таблица поиска, чтобы связать сеть с MAC шлюза. Конечно, эта таблица может содержать несколько MAC для одной и той же сети (типичный случай – различные точки доступа Wi-Fi на большом корпоративном сайте). В другой таблице поиска используется для определения скрипта, который будет запущен для этой сети.
В моем случае скрипт выполняется с помощью уведомлений рабочего стола Plasma, поэтому все находится в пространстве пользователя.
В духе https://unix.stackexchange.com/a/175395/319687 и https://stackoverflow.com/a/34358304/2850770 я использую следующее, чтобы проверить доступность и затем подключиться к первому доступному.
# любое количество таких возможно
Match originalhost my_server exec "nc -z <один адрес сервера> 22 2>/dev/null"
Port 22
User root
HostName <один адрес сервера>
# запасной вариант
Host my_server
Port 22
User root
HostName <другой адрес сервера>
Поскольку каждую из этих проверок нужно немного времени, вы можете сначала поставить ту, которая, вероятнее всего, сработает.
Мне нужно было нечто очень похожее, и я придумал этот простой bash-скрипт, который, исключая комментарии и информационный вывод, кажется одним из самых коротких решений здесь.
Он предполагает, что когда вы находитесь на работе, ваш публичный IP совпадает с IP целевого сервера, и использует публичный API для определения вашего публичного IP.
Настройте его под свои IP-адреса, значения или даже домен:
#!/bin/bash
echo "Определение сети..."
# Используйте публичный API для определения текущего публичного IP
LOCAL_PUBLIC_IP=$(curl -s https://ipinfo.io/ip)
# IP для целевого сервера
TARGET_PUBLIC_IP=your.target.remote.ip
# Полезный совет: если вы хотите использовать домен вместо IP, используйте это вместо:
# TARGET_PUBLIC_IP=$(dig +short target.domain.example.com)
# Если уже нацелена сеть
if [ "$LOCAL_PUBLIC_IP" = "$TARGET_PUBLIC_IP" ]
then
echo "Уже в целевой сети. Подключение локально."
ssh [email protected]
else
echo "На удаленной сети. Подключение к публичному домену."
ssh [email protected]
fi
Ответ или решение
Использование двух различных IP-адресов для одного хоста в SSH
В данной статье мы рассмотрим, как обеспечить автоматическое переключение между двумя IP-адресами для подключения к одноимённому серверу через SSH в зависимости от местоположения. Это может быть полезно для оптимизации подключения и минимизации времени ожидания при попытке установить соединение.
Проблема
У вас есть сервер с именем gamma
, доступный по локальному адресу 192.168.1.100
на работе и по публичному адресу 55.22.33.99
из любого другого места, например, дома. В текущей конфигурации SSH вы используете два отдельных имени хоста в конфигурационном файле ~/.ssh/config
, чтобы подключаться из разных сетей:
Host gamma-local
HostName 192.168.1.100
Port 22
User andreas
Host gamma-remote
HostName 55.22.33.99
Port 12345
User andreas
При этом, если вы хотите подключаться только по команде ssh gamma
, необходимо реализовать более элегантное решение.
Решение
Способ 1: Использование директивы Match в ~/.ssh/config
С помощью директивы Match
в конфигурации SSH можно определить параметры подключения в зависимости от исходного хоста. Это работает, начиная с OpenSSH версии 6.5.
Пример конфигурации может выглядеть так:
Match originalhost gamma exec "[ x$(/sbin/iwgetid --scheme) != xMyHomeESSID ]"
HostName 192.168.1.100
Port 22
Host gamma
User andreas
HostName 55.22.33.99
Port 12345
В этом примере скрипт проверяет, подключены ли вы к заданной Wi-Fi сети. Если вы находитесь в домашней сети, используется локальный адрес; в противном случае, подключение происходит через публичный IP.
Способ 2: DNS-решение
Если у вас есть возможность использовать частный DNS-сервер на работе, вы можете создать разные DNS-записи для хоста gamma
:
- Измените файл
/etc/nsswitch.conf
, чтобы DNS проверялся в первую очередь. - Создайте DNS-запись, которая будет разрешать
gamma
в192.168.1.100
на вашем рабочем DNS. - Добавьте запись в файл
/etc/hosts
, чтобы разрешитьgamma
в55.22.33.99
дома.
Таким образом, когда вы выполняете ssh gamma
, сервер будет автоматически разрешаться в зависимости от вашей текущей сети.
Способ 3: Использование скриптов
Скрипты также являются хорошим вариантом, чтобы убедиться в подключении к правильному адресу. Например, можно создать простое скриптовое решение:
#!/bin/bash
echo "Определение сети..."
PUBLIC_IP=$(curl -s https://ipinfo.io/ip)
TARGET_PUBLIC_IP="55.22.33.99"
if [ "$PUBLIC_IP" == "$TARGET_PUBLIC_IP" ]; then
echo "Вы находитесь в сети офиса. Подключение к локальному IP."
ssh andreas@192.168.1.100
else
echo "Вы находитесь вне сети офиса. Подключение к публичному IP."
ssh -p 12345 andreas@55.22.33.99
fi
Данный скрипт определяет текущее публичное IP-адрес и выполняет подключение к локальному или удаленному серверу в зависимости от вашего местоположения.
Заключение
Чтобы избежать ручного выбора IP-адреса при подключении к SSH, существуют различные подходы: использование директивы Match
в конфигурационном файле SSH, настройка DNS-записей на частном сервере или использование готового скрипта. Каждый из этих методов имеет свои преимущества и недостатки, и выбор оптимального варианта зависит от конкретных условий и требований рабочего процесса.
Эти решения обеспечат автоматизацию подключения и упростят процесс работы с серверами.