SSH po adresie sprzętowym

https://xpil.eu/0UjNA

Każda karta sieciowa w standardzie Ethernet ma unikalny[1] kod wbudowany na stałe[2] na etapie produkcji. Kod ów, zwany adresem sprzętowym lub MAC-adresem, to 48 bitów, z których pierwsze 24 oznaczają producenta, a ostatnie 24 - numer seryjny w ramach danego producenta. Zwyczajowo zapisuje się go jako sześć dubletów heksadecymalnych, na przykład: AA:BB:CC:DD:11:22. Albo aa-bb-cc-dd-11-22 (wielkość liter oraz separator między dubletami nie mają znaczenia).

Skrót MAC oznacza Media Access Control.

Unikalność tego kodu (a przynajmniej względna unikalność - szanse, że dwa urządzenia w tej samej sieci będą miały ten sam adres MAC są przyzerowe - chyba, że się ktoś specjalnie postara) jest podstawą działania na przykład serwera DHCP, który widząc urządzenie ze znanym MAC-adresem przydzieli mu ten sam adres IP co poprzednio. Dzięki temu ten sam komputer (czy drukarka, kamera czy smartfon) dostanie w tej sieci za każdym razem ten sam adres IP.

No dobra, nie do końca tak to działa. Adresy IP przyznawane przez serwer DHCP charakteryzują się czasem życia ("dzierżawy"), który administrator serwera może ustawić na pięć minut albo pięć lat; po upływie tego czasu, jeżeli urządzenie zniknie z sieci a następnie pojawi się z powrotem (na przykład w wyniku restartu, albo wyjścia z zasięgu sieci bezprzewodowej), nie ma już gwarancji, że dostanie ten sam adres IP. Ale na ogół czas dzierżawy w sieci lokalnej mierzy się w tygodniach, miesiącach, czasem nawet latach.

Mam w domowej sieci kompa z zainstalowanym nań Linuksem. Bez podłączonego monitora, klawiatury ani myszy (czyli w trybie headless). Łączę się do niego wyłącznie przez ssh z sieci lokalnej. Komp ów ma nazwę sieciową, ale zainteresowało mnie ostatnio, czy dałbym radę napisać skrypt, który otworzy mi sesję ssh do tego kompa wyłącznie na podstawie znajomości adresu MAC, bo na przykład nazwa sieciowa może się zmienić, a adres MAC - nigdy[2][3].

Wymaganie nieco z dupy wzięte, ale spróbujmy. Ponieważ maszyna źródłowa ma na pokładzie Windows, użyjemy Powershell:

$macAddress = "90-1B-0E-68-E0-B1"
$arpEntry = arp -a | Select-String -Pattern $macAddress
if ($arpEntry) {
    $ipAddress = $arpEntry -replace ".*\s(\d{1,3}(\.\d{1,3}){3})\s.*", '$1'
    ssh user@$ipAddress
} else {
    Write-Host "MAC address $macAddress not found."
}

Jak widać zagadnienie jest całkiem proste; skrypt ma bez specjalnych wysiłków ledwie 8 linii.

Najpierw zapisujemy sobie MAC-adres docelowego kompa w zmiennej, następnie wołamy polecenie arp -a, które wypluwa kupę różnych informacji, w tym również linię z mapowaniem adresu MAC na adres IP. Poleceniem Select-String wybieramy tę jedną linię i jeżeli się udało, wyciągamy z niej (za pomocą wyrażenia regularnego) adres IP i łączymy się zeń komendą ssh, a jeżeli nie, wyrzucamy informację o niepowodzeniu.

Samo wyrażenie regularne jest obsługiwane operatorem -replace i działa tak:

  • Najpierw mamy .* czyli cokolwiek
  • Potem mamy \s czyli spację (albo tabulację, albo inny "pusty" znak)
  • Potem jest otwarty nawias zewnętrzny ( - w parze z nawiasem zamykającym niedaleko końca oznacza on tzw. "capturing group" czyli ciąg (grupę) znaków, który staramy się "złapać", znaleźć. Wewnątrz tego nawiasu będziemy próbowali "złapać" adres IP.
  • Następnie jest \d czyli dowolna cyfra 0-9
  • Potem mamy {1,3} czyli powtórzenie poprzedniego elementu między 1 a 3 razy. Czyli \d{1,3} to nic innego jak jedna, dwie lub trzy cyfry, np. 115 albo 192
  • Znów otwarty nawias (wewnętrzny) ( - tym razem oznacza on po prostu jakieś wyrażenie w nawiasie.
  • Wewnątrz tego wewnętrznego nawiasu mamy najpierw \. (kropka, poprzedzona ukośnikiem, bo kropka sama w sobie jest częścią składni RegEx, a my chcemy dopasować po prostu kropkę) i zaraz potem \d czyli dowolna cyfra, powtórzona nie więcej niż trzy razy (czyli {1,3}).
  • Zamykamy wewnętrzny nawias ) i dodajemy {3} czyli dokładnie trzy powtórzenia tego, co jest w wewnętrznym nawiasie (czyli kropka po której następują 1, 2 lub 3 cyfry).
  • Potem upewniamy się, że po ostatniej części adresu IP pojawia się spacja (albo tabulacja itd) czyli znów \s i na koniec dowolne inne śmieci czyli .*

Ciekawe, że kod, który interpretuje to wyrażenie, jest w stanie odróżnić logikę nawiasów zewnętrznych od wewnętrznych i potraktować je tak, jak trzeba. Sądzę, że po prostu capturing groups nie mogą pojawiać się wewnątrz innych nawiasów, a może chodzi o {1,3} po zamykającym nawiasie wewnętrznym? No nie wiem, w każdym razie działa.

Proste? Proste, choć sprawdzi się tylko w podstawowym scenariuszu, kiedy adres MAC pojawia się na wyjściu komendy arp tylko raz. W praktyce może być tak, że dzierżawa adresu IP jest krótka i po restarcie maszyna z Linuksem dostanie nowy adres, ale ponieważ nie restartowaliśmy komputera klienckiego (tego z Windowsem), jego pamięć arp zachowa obydwa adresy IP maszyny linuksowej. Da się to również ogarnąć, ale to już temat na kiedy indziej.

[1] Unikalny w teorii, w praktyce patrz [2].

[2] W rzeczywistości można go zmienić - nie w samej karcie, gdzie faktycznie jest zakodowany na twardo (i unikalny globalnie, przynajmniej w teorii, bo faktycznie bywa z tym różnie), ale na poziomie sterownika.

[3] Zaoszczędziłbym tu jeden odnośnik, gdyby nie [3].

https://xpil.eu/0UjNA

14 komentarzy

  1. Bait w tytule bo to jest nie jest nadal logowanie się do ssh po MACu; tylko ustalanie adresu IP na podstawie MACa.
    Wiem, wiem czepiam się. Sam kiedyś czegoś takiego używałem.
    Jednakże, jednakże: to wkręcenia wkręta nożem; poprawnie to zeroconf

  2. Tylko to będzie działać jeśli w ciągu ostatnich 240 min była komunikacja z w/w urządzeniem. Po tym czasie urządzenie wypadnie z lokalnej tablicy ARP.

    Pod tym względem, lepiej byłoby korzystać z tablicy ARP routera. Bo będzie bogadsza o dane. Przykładowo w tej chwili moja lokalna tablica ARP zawiera, tylko adres gateway i drukarki. Ale router zawiera jeszcze np. AP i wszystkie urządzenia podpięte do niego.

  3. Serwer DHCP pozwala na tworzenie statycznych wpisów, czy też mapowań MAC-IP. W ten sposób dany MAC dostanie zawsze ten sam adres IP. Raczej wszędzie dostępne pod taką czy inną nazwą, polecam poszukać.

      1. U mnie nie ma routera. Tzn. jest, gdzieś tam, ale ta konkretna maszyna jest podpięta skrętką pod inną maszynę (z Windows), która z kolei łączy się z netem przez lokalne wifi. Czyli po naszemu Windows udostępnia wifi tej maszynie z linuksem. Musi tu gdzieś być jakaś namiastka serwera dhcp, ale jeszcze się nie wgryzłem. Wystarczy mi znajomość adresu mac i lokalny cache arp żeby wydłubać ip maszynki linuksowej.

        1. OMG trzeba było to zrobić odwrotnie – Linux udostępnia net Windowsowi. Wtedy wszystko byłoby proste…

          Natomiast w tym układzie Windows jest routerem. Ale czy i jak ustawić statyczny wpis – nie wiem. Pewnie grzebanie w rejestrze. 😉

          1. Z tego co pamiętam, w takim przypadku nie ma DHCP (dopiero Win Serwer go ma) i jedynym wyjściem jest ustawianie statycznych wpisów dla obu interfejsów (obu kart sieciowych) ręcznie w obu urządzeniach.

            Inaczej z tego co pamiętam, są tam jakieś rozwiązania, które pozwalają im chyba “automatycznie” wynegocjować coś. Ale nad tym nie ma w ogóle kontroli.

            Trzeba po prostu w ustawieniach karty, zmienić z auto na ręczne wpisy. Wpisać np. 10.0.0.11 i maska 255.255.255.0 i z drugiej strony zrobić 10.0.0.12 – tak wiem, maskę można by przyciąć do mniejszej.

  4. Dla mnie w Twoim wpisie najciekawszy jest ów snippet Powershella, ponieważ w nowej pracy dali mi peceta, na którym muszę mieć Windę. Więc moja znajomość konsoli linuksowej będzie powoli odchodziła w zapomnienie, natomiast w ciągu najbliższych miesięcy wypadałoby się poduczyć PS-a. Na razie „pełnozdaniowa” składnia komend, po magicznych skrótach basha, które poznałem całkiem nieźle, wydaje mi się mało atrakcyjna…

    1. To prawda, Powershell jest bardziej “rozgadany” od Bash-a. Niektórym to nie odpowiada. Ja mam na to prawdę mówiąc wyrąbane; grunt, żeby działało. Podoba mi się natomiast, że PS podpowiada w terminalu nie tylko polecenia, ale też ich parametry. Czyli np. wpisujesz: Test-Connection -Co (naciskasz tab) i się samo dokańcza. A jak jest więcej pasujących wariantów, tab będzie wyświetlał je w cyklu.

    2. Jeśli szukasz materiałów o PS, to polecam Pawła Maziarza. Są i płatne kursy, i nagrania z konferencji. Samego PS się nie uczyłem, ale kojarzę fragmenty innych szkoleń i to co mówił o PS było interesujące.

      1. @xpil: Tak, autokompletowanie (autodokańczanie? samowykańczanie?) poleceń jest bardzo przyjemne, chociaż w ciągu ostatniego roku doceniłem i nauczyłem się używać (albo najpierw nauczyłem się używać, a potem doceniłem) linuksowych man page, gdzie w gruncie rzeczy szybko daje się znaleźć potrzebny parametr – a przy okazji człowiek się czegoś nauczy.

        @rozie: Szukam, więc dzięki za Maziarza!

        @tacitgreg: Przy pierwszym podejściu brakuje mi w PS prostego aliasowania. Bo tam, o ile zdążyłem się zorientować, aliasować można tylko podstawowe komendy bez parametrów. Żeby zapisać parametry, należy stworzyć funkcje – niby to samo, ale już odrobinę bardziej i niepotrzebnie skomplikowane.

Leave a Comment

Komentarze mile widziane.

Jeżeli chcesz do komentarza wstawić kod, użyj składni:
[code]
tutaj wstaw swój kod
[/code]

Jeżeli zrobisz literówkę lub zmienisz zdanie, możesz edytować komentarz po jego zatwierdzeniu.