Użyj dwóch różnych adresów IP na host w SSH

23

Mam serwer o nazwie gamma, stale działający i działający w pracy. Czasami łączę się z nim z domu, w którym to przypadku korzystam z publicznego adresu IP 55.22.33.99. Zdarza się, że mogę podłączyć do niego, kiedy jestem w pracy, a zamiast odbić moje pakiety wokół niepotrzebnie podłączyć poprzez lokalny adres IP 192.168.1.100.

W tej chwili mam je podzielone na dwa różne wpisy w ~/.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

Więc jeśli jestem w pracy, wszystko co muszę wpisać to ssh gamma-locali jestem w; jeśli jestem w domu (lub gdziekolwiek indziej na świecie), biegam ssh gamma-remote.

Podczas łączenia się z serwerem wolałbym nie wpisywać innej nazwy w zależności od tego, gdzie jestem, wolałbym, aby ta część była wykonywana automatycznie; na przykład w niektórych przypadkach mam zautomatyzowane skrypty, które łączą się, którzy nie wiedzą, gdzie jestem.

Istnieje pytanie, które rozwiązuje ten problem za pomocą skryptu Bash, aby „spróbować” najpierw połączyć się z lokalnym, a jeśli się nie łączy, spróbuj połączyć się ze zdalnym adresem IP. Jest to miłe, ale (1) wydaje się nieefektywne (szczególnie, że czasami trzeba „poczekać” na przekroczenie limitu czasu połączeń, ponieważ nie zawsze natychmiast wysyłają błąd z powrotem) i (2) wymaga Basha i ciągnięcia się za skryptem.

Czy istnieje alternatywny sposób osiągnięcia tego celu, który nie polega na użyciu skryptów Bash ani „testowaniu”, aby sprawdzić, czy połączenie działa najpierw?

IQAndreas
źródło
Próbuję się dowiedzieć, czy można to zrobić przy /etc/hostspomocy pliku konfiguracyjnego SSH? A może jakiś sposób na „wykrycie”, do której sieci LAN jesteś aktualnie podłączony?
IQAndreas,
Czy masz oddzielny zestaw serwerów nazw używanych przez sieć biurową?
Sree,
@ Sree Ach, widzę, dokąd zmierzasz; sprytny! W tej chwili nie obsługujemy serwera nazw, ale zdecydowanie mogę przekonwertować jedną z maszyn na jedną.
IQAndreas,
@ Sree Zapraszam do opracowania i dodania tego jako odpowiedzi, która zaczyna się od wiersza „Jeśli masz serwer nazw w sieci biurowej ...”
IQAndreas
Zrobił to. Zachęcamy do głosowania w górę / w dół :)
Sree

Odpowiedzi:

28

Jeśli potrafisz rozpoznać, w której sieci jesteś, możesz użyć Matchsłowa kluczowego w, ~/.ssh/configaby zrobić to, co chcesz. Wymaga to OpenSSH ≥6,5.

Używam czegoś podobnego do

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

Używam więc identyfikatora używanej sieci Wi-Fi, aby zdecydować, czy jestem w domu na potrzeby połączenia SSH, ale można również użyć adresu IP przypisanego do komputera lub czegokolwiek innego, co odróżnia obie sieci. .

Michał Politowski
źródło
+1 sprytne użycie Match, dzięki! Dobrym pomysłem może być umieszczenie wykrycia w skrypcie wychodzącym z zerowym lub niezerowym statusem wyjścia, ponieważ dzięki temu będzie można go ponownie wykorzystać.
peterph
@peterph z pewnością można go streścić w zewnętrznym skrypcie, ale do tej pory potrzebowałem tylko w jednym miejscu, więc reguła trzech jeszcze się nie uruchomiła.
Michał Politowski,
Co by się stało, gdybyś był w pracy, ale tak naprawdę chcesz połączyć się z inną maszyną? Czy instrukcja exec nie byłaby zgodna, ponieważ nie używasz Wi-Fi z identyfikatorem HomeESSID, a zatem ustawiłeś nazwę hosta na 192.168.1.100?
odszedł
@ gone Nie rozumiem pytania. Mecz odbywa się również na hoście docelowym.
Michał Politowski
1
@typelogic Nie tak łatwo, o ile wiem. Możesz spróbować użyć ProxyCommand nc $address ssh, ale potem np. czy wszystkie urządzenia mają ten sam klucz hosta? Reklama, jeśli i tak musisz ustawić zmienną środowiskową, czy nie będzie łatwiej po prostu podać adres, z którym chcesz się bezpośrednio połączyć jako argument ssh?
Michał Politowski
5

Jeśli zdarzy ci się mieć prywatne serwery nazw w pracy i jeśli używasz tego samego laptopa w biurze i domu, możesz to wykorzystać, aby to osiągnąć:

  1. Zmodyfikuj swój nsswitch.confkomputer, aby najpierw sprawdzić DNS
  2. Utwórz wpis DNS, gammaaby rozwiązać do 192.168.1.100 w prywatnym DNS w biurze.
  3. Utwórz wpis w pliku / etc / hosts na komputerze, aby rozwiązać gammado 55.22.33.99.

W ten sposób, gdy będziesz w ssh gammabiurze, zostanie rozpoznane z biurowego DNS na 192.168.1.100, a kiedy połączysz się z domu, rozwiąże się do 55.22.33.99 z pliku hosts.

PS : Ta odpowiedź zakłada, że ​​nie chcesz, aby gamma miała publiczny wpis DNS. Ponadto, jeśli korzystasz z SSH do serwera z komputera z systemem Windows, myślę, że powinno być miejsce równoważne plikowi nssswitch.conf, aby zastąpić wpisy pliku hosts.

Sree
źródło
Jest - jest to plik hosts. I właśnie tak to robię - kiedy w domu moja domena publiczna rozwiązuje się na mój lokalny adres IP. Doskonała odpowiedź - to była pierwsza rzecz, o której myślałem. Cóż ... dzisiaj, kiedy to założyłem kilka lat temu, musiałem dużo wyszukiwać w Google, aby znaleźć rozwiązanie.
mikeserv
2

Nie wiem, czy jest to możliwe, ~/.ssh/configale innym podejściem byłoby połączenie się z jednym lub drugim w oparciu o twój zewnętrzny adres IP. Ponieważ, prawdopodobnie, kiedy jesteś w pracy, twoje IP będzie 55.22.33.NNN, możesz uruchomić coś takiego:

[[ $(wget -qO - http://wtfismyip.com/text) =~ ^'55.22.33.' ]] && 
    ssh 192.168.1.100 ||
    ssh -p 12345 55.22.33.99

Jeszcze prostszym rozwiązaniem jest użycie wewnętrznego adresu IP. Nie wiem, jak skonfigurowane są dwie sieci, ale jeśli łatwo jest ustalić, czy jesteś w pracy, czy nie poprzez swój adres IP (na przykład, jeśli masz konkretny adres IP w pracy, np. 192.168.1.12), Możesz to zrobić ( zmień eth0na dowolną nazwę twojej karty sieciowej):

[[ $(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

Cokolwiek zdecydujesz się użyć, możesz dodać go jako alias do powłoki (dodaj ten wiersz do pliku inicjalizacji powłoki, ~/.bashrcjeśli używasz bash):

alias gamma="[[ $(wget -qO - http://wtfismyip.com/text) =~ ^'55.22.33.' ]] && ssh 192.168.1.100 || ssh -p 12345 55.22.33.99

Możesz także zrobić to ze skryptu, jeśli chcesz, aby inne skrypty miały do ​​niego dostęp (aliasy z .bashrcnie są odczytywane podczas uruchamiania skryptu).

terdon
źródło
2

Nie można tego osiągnąć, ~/.ssh/configużywając adresów IP jako nazw hostów. Dodatkową komplikację wprowadza fakt, że łączysz się nie tylko z różnymi adresami IP, ale także z różnymi portami, ponieważ w zasadzie wyklucza to jakiekolwiek modyfikacje twojego resolvera DNS.

Stoję poprawiony - możesz użyć Match originalhost ... exec ...kombinacji w ~/.ssh/config- patrz odpowiedź @ MichałPolitowski . Jednak chociaż będzie działać idealnie w przypadku OpenSSH, niekoniecznie możesz znaleźć podobną funkcjonalność w innych klientach SSH.

Możesz obejść ten problem za pomocą prostego opakowania (funkcji powłoki lub skryptu, jeśli musisz użyć go z różnych powłok) ssh, który sprawdzi, w której sieci jesteś, i użyje odpowiedniego Hostwpisu. Najważniejsze pytanie brzmi: jak niezawodnie wykryć sieć, w której się znajdujesz. Lokalny adres IP przychodzi mi na myśl, ale nie jest niezawodny, ponieważ równie dobrze możesz łączyć się z sieci LAN korzystającej z tej samej podsieci co sieć robocza.

Jeśli możesz mieć ten sam port zarówno dla sieci lokalnych, jak i zdalnych, możesz edytować swój w /etc/resolv.confzależności od sieci, w której się znajdujesz - oczywiście musiałoby to być zrobione automatycznie (najprawdopodobniej ze skryptu przechwytującego klienta DHCP). Lub - lepiej - uruchomić lokalny serwer nazw (jak na przykład dnsmasq) i dostarczyć mu odpowiednią konfigurację. Wykracza to jednak poza zakres tego pytania.

Inną opcją - jeśli potrzebujesz się tylko połączyć interaktywnie - jest użycie uzupełniania poleceń, które by skanowało ~/.ssh/config. Pozwoliłoby to zaoszczędzić trochę pisania (szczególnie, jeśli masz wystarczająco różnych Hostwpisów). Coś w tym stylu (do bashinicjalizacji):

# complete session names for 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

Pierwsza funkcja tworzy listę, z której hosty są ukończone (zwykle wystarczy uruchomić to raz w każdej powłoce), druga wykonuje rzeczywiste zakończenie, choć w nieco niezdarny sposób - uzupełnia nazwę hosta tylko wtedy, gdy jest ostatni token w linii poleceń.

To powiedziawszy, właściwym sposobem na rozwiązanie tego problemu jest połączenie się z siecią roboczą przez VPN, a tym samym dostęp do lokalnego roboczego adresu IP, tak jakbyś był w biurze. Następnie można twardego drutu adres do cokolwiek cokolwiek warstwa wolisz: ~/.ssh/config, /etc/resolv.conflub (imho najlepszym rozwiązaniem) Serwer nazw biurowych.

Peter
źródło
Porty nie są osadzone w kamieniu. Mógłbym zdecydowanie zmienić lokalny port SSH, tak gammaaby pasował do portu zdalnego, gdyby to ułatwiło.
IQAndreas,
2

Kilka lat temu napisałem program do podobnego celu. Może pasować do twoich potrzeb. Za pomocą tego programu konfiguracja ssh może wyglądać następująco:

Host gamma
    ProxyCommand ssh-multipath-proxy 192.168.1.100:22 55.22.33.99:12345
    User andreas
kasperd
źródło
1

Jeszcze innym rozwiązaniem jest użycie dwóch różnych plików konfiguracyjnych dla SSH. Możesz uznać to za nieco mniej eleganckie niż posiadanie wszystkiego w jednym pliku konfiguracyjnym, ale łatwiej go utrzymać.

Wybierz plik konfiguracyjny, którego chcesz użyć -F <configfile>.

Marcel Loose
źródło
Dzięki. Podoba mi się ta -Fopcja.
typelogic
0

Częściowa odpowiedź:

Wiele odpowiedzi powyżej zaczyna się od „jeśli możesz wykryć, w której sieci jesteś”. W tym celu używam skryptu, który jest uruchamiany, gdy interfejsy są połączone, aby uruchamiać różne rzeczy (zwykle VPN), gdy łączę się z jedną ze swoich zwykłych sieci. Technika polega na użyciu ARP w celu uzyskania adresu MAC bramy:

function identifyConnection {
    gatewayIP=$(route -n | grep -e '^0\.0\.0\.0' | tr -s ' ' | cut -d ' ' -f 2)

    if [[ ! -z "$gatewayIP" ]]
    then
        # Identify the gateway by its MAC (uniqueness...)
        log "Gateway IP address=$gatewayIP"
        log "Obtaining corresponding gateway MAC address"
        gatewayData=($(arp -n $gatewayIP | grep -e $gatewayIP | tr -s ' '))
        if [[ "${gatewayData[1]}" == "(incomplete)" ]]
        then
            log "Status of gateway $gatewayIP "incomplete""
            echo ""
        elif [[ "${gatewayData[2]}" == "--" ]]
        then 
            log "No MAC address found for $gatewayIP"
            echo ""
        else
            log "Gateway MAC address=[${gatewayData[2]}]"
            echo "${gatewayData[2]}"
        fi
    fi
}

A potem mam tabelę odnośników do powiązania sieci z bramą MAC. Oczywiście ta tabela może pomieścić kilka adresów MAC dla tej samej sieci (typowym przypadkiem są różne hotspoty Wi-Fi na dużej stronie korporacyjnej). Inna tabela odnośników służy do określenia skryptu, który ma zostać uruchomiony dla tej sieci.

W moim przypadku skrypt jest uruchamiany za pomocą powiadomień na pulpicie Plazmy, więc wszystko jest w obszarze użytkownika.

ksenoid
źródło