nginx jako alternatywa ifconfig.me

07 październik 2014

Będąc leniwym człowiekiem dosyć często zdarza mi się korzystać z serwisów typu ifconfig.me w celu ustalenia jaki adres IP powinienem podać znajomym przed grą lub podczas testowania jakiejś usługi sieciowej. Główną zaletą wcześniej wspomnianej strony jest banalność użycia: wystarczy potraktować ją wget -qO- lub curl z terminala, całkowicie pomijając przeglądarkę internetową. Niestety od kilku miesięcy serwis bardzo opuścił się z czasem działania, wydłużając niegdyś około 20 sekundową operację do nawet 3 minut.

Oczywiście stworzenie alternatywnego rozwiązania w dowolnym języku programowania nie jest skomplikowane, zdecydowałem się jednak zminimalizować narzut związany z uruchomianiem skryptów przez CGI i użyć wyłącznie funkcji oferowanych przez nginx z małą pomocą dwóch zewnętrznych modułów.

Chociaż nginx wywiązuje się ze swoich standardowych zadań doskonale, wszystkie zewnętrzne moduły muszą być obecne na dysku podczas kompilacji, a ich lokacje przekazane do skryptu configure. Wyklucza to niestety dynamiczne zarządzanie modułami – moduł jest albo nie. W związku z tym niżej opisane moduły muszą zostać wkompilowane w serwer za pomocą flag --add-module=ścieżka/do/modułu.

Pierwszym modułem, którego zadaniem jest wyplucie wskazanych danych zamiast serwowania pliku, jest ngx_echo. Sam w sobie idealnie nadaje się do debugowania konfiguracji, pokazywania danych przekazanych w żądaniu czy zmierzenia czasu jego obsłużenia; pełna dokumentacja znajduje się na Githubie. Ja potrzebowałem tylko jednej funkcji o jakże oczywistej nazwie echo. Jej argumentem będzie $remote_addr, jedna z wielu zmiennych ustawianych przez ngx_http_core_module.

server {
    server_name localhost;
    autoindex   off;

    location / {
        echo "$remote_addr";
    }
}

Po przeładowaniu serwera można przekonać się czy ngx_echo działa jak powinien:

% curl localhost
127.0.0.1

Tak naprawdę w tym momencie można by zakończyć pracę, ale po odwiedzeniu strony okazuje się, że przeglądarka zamiast wyświetlić IP, próbuje pobrać plik binarny. Powód jest bardzo prosty – błędny nagłówek Content-Type. Tutaj na scenę wkracza ngx_headers_more. Interesującą mnie funkcją jest more_set_headers, za której pomocą zmieniam wspomniany wcześniej nagłówek przed wywołaniem echo. Ostateczny rezultat po zmianie konfiguracji i przeładowaniu nginksa:

server {
    server_name localhost;
    autoindex   off;

    location / {
        more_set_headers 'Content-Type: text/plain';
        echo "$remote_addr";
    }
}
% curl -I localhost
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 16 Sep 2014 16:17:21 GMT
Content-Type: text/plain; charset=utf-8
Connection: keep-alive
Vary: Accept-Encoding

W naturze efekt można zobaczyć pod adresem myip.bpiotrowski.pl, który wyświetla adres IP poniżej sekundy. To oczywiście wierzchołek góry lodowej tego co można osiągnąć nginksem i zewnętrznymi modułami – ciekawskich odsyłam do dystrybucji nginksa o nazwie OpenResty i opartego na nim web frameworka Lapis.