Используйте два разных IP-адреса на хост для SSH.

Вопрос или проблема

У меня есть сервер, названный 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-серверы на работе и если вы используете один и тот же ноутбук из офиса и дома, вы можете воспользоваться этим для достижения цели:

  1. Измените свой nsswitch.conf на своем компьютере, чтобы сначала проверять в DNS
  2. Создайте запись DNS для gamma, чтобы она разрешалась в 192.168.1.100 в вашем частном DNS в офисе.
  3. Создайте запись в файле /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:

  1. Измените файл /etc/nsswitch.conf, чтобы DNS проверялся в первую очередь.
  2. Создайте DNS-запись, которая будет разрешать gamma в 192.168.1.100 на вашем рабочем DNS.
  3. Добавьте запись в файл /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-записей на частном сервере или использование готового скрипта. Каждый из этих методов имеет свои преимущества и недостатки, и выбор оптимального варианта зависит от конкретных условий и требований рабочего процесса.

Эти решения обеспечат автоматизацию подключения и упростят процесс работы с серверами.

Оцените материал
Добавить комментарий

Капча загружается...