\documentclass{hitec} \usepackage{polski} \usepackage[utf8]{inputenc} \usepackage{url} \usepackage{xcolor} \usepackage[ampersand]{easylist} \newcommand{\dir}[1] {\texttt{\color{teal} \detokenize{#1}}} \definecolor{orange}{rgb}{1,0.5,0} \newcommand{\file}[1] {\texttt{\color{orange} \detokenize{#1}}} \newcommand{\filepath}[1] {\texttt{\color{violet} \detokenize{#1}}} \author{Mateusz Bielesz, Wojciech Kosior, Marek Moryl, Kamil Szarek} \title{Użytkowanie 0TDNS} \begin{document} \maketitle \begin{sloppypar} Przykłady komend konsolowych w niniejszym dokumencie idą za konwencją, wg. której komenda wykonywana jako użytkownik root poprzedzona jest znakiem '\#', a komenda wykonywana jako inny użytkownik - znakiem '\$'. \section{Wymagania części back-endowej systermu} \subsection{System} Ze względu na użycie linuksowych przestrzeni nazw, back-end 0tdns działa obecnie wyłącznie pod systemami z rodziny GNU/Linux. Dodatkowo, konieczne jest wsparcie jądra dla przekazywanie pakietów IP (forwarding), sieciowych przestrzeni nazw (namespace'ów), iptables i urządzeń tun. \subsection{Zależności} \begin{enumerate} \item cron (w dużej części dystrybucji automatycznie zainstalowany) \item libunbound z bindingiem do pythona3; w rodzinie Debiana możliwy do zainstalowania przez \begin{verbatim} # apt install python3-unbound \end{verbatim} \item python3 (system był tworzony pod pythonem 3.5.3) \item openvpn; w rodzinie Debiana możliwy do zainstalowania przez \begin{verbatim} # apt install openvpn \end{verbatim} \item iptables; w rodzinie Debiana możliwe do zainstalowania przez \begin{verbatim} # apt install iptables \end{verbatim} \end{enumerate} \subsection{Uwagi} Skrypty systemu są uruchamiane cyklicznie przez demona crona. Aby system działał, musi być uruchomiony demon crona. Najskuteczniejszym rozwiązaniem jest uruchamianie crond przy starcie systemu (w niektórych dystrybucjach, w szczególności tych z rodziny Debiana, domyślnie włączone). \section{Instalacja części back-endowej} \subsection{Pobranie systemu} W celu zainstalowania systemu klonujemy repozytorium: \url{https://repo.or.cz/0tDNS.git} \begin{verbatim} $ git clone https://repo.or.cz/0tDNS.git && cd 0tdns \end{verbatim} \subsection{Instalacja plików wchodzących w skład systemu} Wykonujemy jako użytkownik root: \begin{verbatim} # ./install.sh \end{verbatim} Skrypt \file{install.sh} przekopiowuje do \dir{/var/lib/0tdns/} oraz do \dir{/usr/sbin/} i \dir{/usr/bin/} skrypty wchodzące w skład systemu. Oprócz tego \file{install.sh} umieszcza importowany przez inne skrypty \file{ztdnslib.py} w \dir{/usr/lib/python3/dist-packages/} oraz kopiuje \file{db_connection_config.yml} do katalogu \dir{/etc/0tdns/}. Przy wywołaniu \file{install.sh} można ustawić odpowiednią zmienną środowiskową \begin{verbatim} $ INSTALL_ROOT=/some/path/ ./install.sh \end{verbatim} lub podać argument do skryptu \begin{verbatim} $ ./install.sh /some/path/ \end{verbatim} Efekt jest w obu przypadkach taki sam - wszystkie pliki zostaną zainstalowane w \dir{/some/path/}, co może zostać wykorzystane do instalacji wewnątrz chroot'a lub do stworzenia pakietu dystrybucji. \subsection{Automatyczna część konfiguracji systemu} Po instalacji plików w systemie konieczne jest utworzenie użytkownika 0tdns i dodanie wpisów do crontaba, co wykonać można wywołaniem skryptu \begin{verbatim} # ./setup.sh \end{verbatim} Funkcjonalność \file{setup.sh} i \file{install.sh} została rozdzielona z myślą o dystrybucji systemu. \subsection{Ładowanie niezbędnych modułów jądra} Dla działania systemu potrzebne są sterowniki tun i ip\_tables. W zależności od dystrybucji mogą one być wbudowane w jądro lub dostarczone w postaci modułów. W tym drugim przypadku konieczne jest załadowanie modułów: \begin{verbatim} # modprobe tun # modprobe ip_tables \end{verbatim} Tak załadowany moduł działa do momentu ponownego uruchomienia systemu operacyjnego. Aby moduły były automatycznie ładowane przy starcie, należy wpisać je do odpowiedniego pliku konfiguracyjnego: \begin{verbatim} # echo tun >> /etc/modules # echo ip_tables >> /etc/modules \end{verbatim} \subsection{Włączenie przekierowywania pakietów} Aby 0tdns działał, jądro musi przekazywać pakiety IP z domyślnego urządzenia sieciowego na urządzenia veth, które będą tworzone (za włączenie przekazywania pakietów w drugą stronę - od urządzenia veth - odpowiadają skrypty systemu). Ta funkcjonalność jest domyślnie wyłączona w przypadku większości dustrubucji. Jeśli dane jądro ją wspiera, można ją włączyć poprzez zapis do odpowiedniego pliku w systemie plików \filepath{/proc}. \begin{verbatim} # echo 1 > /proc/sys/net/ipv4/conf/all/forwarding \end{verbatim} Powyższy przykład pokazuje, jak włączyć przekazywanie ruchu ze wszystkich urządzeń. Znając nasze główne urządzenie (tj. to łączące nas z siecią internet), możemy włączyć przekazywanie jedynie pakietów przychodzących z niego. Przykładowo, jeśli tym urządzeniem jest eth0: \begin{verbatim} # echo 1 > /proc/sys/net/ipv4/conf/eth0/forwarding \end{verbatim} Tak wprowadzona zmiana nie jest jednak persystentna ze względu na ponowne uruchomienia systemu operacyjnego. Aby tak się stało, należy zmodyfikować odpowiedni plik konfiguracyjny: \begin{verbatim} # echo 'net.ipv4.conf.eth0.forwarding=1' >> /etc/sysctl.conf \end{verbatim} \subsection{Uzupełnienie pliku konfiguracyjnego} Programy działające w ramach systemu automatycznie sukają konfiguracji systemu pod \filepath{/etc/0tdns/db_connection_config.yml}. Dostarczony plik jest domyślną konfiguracją, którą administrator systemu powinien zmodyfikować precyzując w niej adekwatny adres bazy danych i inne parametry zgodnie ze specyfikacją w rozdziale poniżej. \section{Konfiguracja części back-endowej} \label{config} Konfiguracja systemu odbywa się poprzez plik \filepath{/etc/0tdns/db_connection_config.yml}. Domyślna, wymagająca uzupełnienia konfiguracja, jest dostarczona z systemem. Wykorzystany jest tu format yaml. Możliwe jest dodawanie komentarzy zaczynających się od znaku '\#'. Poniżej wyjaśnione jest znaczenie poszczególnych zmiennych w pliku konfiguracyjnym. \subsection{Zmienna user} Zawiera nazwę używanego użytkownika (roli) w bazie danych. Nazwa może - choć jeśli nie zawiera znaków specjalnych, to nie musi - być zamknięta w cudzysłów. Przykład: \begin{verbatim} user: postgres \end{verbatim} \subsection{Zmienna password} Zawiera hasło do bazy podanego w zmiennej user użytkownika. Nazwa może - choć jeśli nie zawiera znaków specjalnych, to nie musi - być zamknięta w cudzysłów. Przykład: \begin{verbatim} password: postgres \end{verbatim} \subsection{Zmienna host} Zawiera adres bazy danch. Może to być zarówno nazwa domenowa, jak i adres IP. Przykład: \begin{verbatim} host: "127.0.0.1" \end{verbatim} \subsection{Zmienna port} Zawiera port, na którym należy łączyć się z bazą danych. Port może, choć nie musi, być wzięty w cudzysłów. Przykład: \begin{verbatim} port: "5432" \end{verbatim} \subsection{Zmienna database} Zawiera nazwę używanej bazy w systemie bazodanowym. Nazwa może - choć jeśli nie zawiera znaków specjalnych, to nie musi - być zamknięta w cudzysłów. Przykład: \begin{verbatim} database: "ztdns" \end{verbatim} \subsection{Zmienna enabled} Precyzuje, czy instancja back-endu ma pracować. Jeśli zmienna ustawiona jest na ``yes'' - system działa normalnie i cogodzinnie wykonywane są zapytania. Jeśli ustawiona jest na ``no'' - system jest wyłączony. Dzięki tej zmiennej możliwe jest czasowe wyłączenie danej instancji systemu, np. w wypadku awarii. W dostarczonym szablonie pliku konfiguracyjnego system nie jest włączony. Wartość zmiennej może - choć nie musi - buć wzięta w cudzysłów. Przykłady: \begin{verbatim} enabled: no \end{verbatim} \begin{verbatim} enabled: "yes" \end{verbatim} \subsection{Zmienna handled\_vpns} Ma ona zawierać listę bazodanowych id serwerów VPN, które ta instancja back-endu ma obsługiwać. Tym samym możliwe jest rozdzielenia pracy na kilka maszym. Ta zmienna może pozostać nieustawiona - wtedy dana instancja systemu będzie obsługiwała wszystkie połączenia VPN w bazie danych. W liście mogą się znaleźć także id, które nie występują w bazie danych. Przykład: \begin{verbatim} handled_vpns: [1, 2, 17] \end{verbatim} \subsection{Zmienna parallel\_vpns} Zmienna, która decyduje o tym, ile połączeń VPN może być zestawionych jednocześnie. System w żadnym momencie nie nawiąże więcej połączeń VPN, niż wynosi wartość tej zmiennej. Umożliwia to administratorowi kontrolowanie obciążenia maszyny i łącza sieciowego. Zmienna powinna być liczbą, nieotoczoną cudzysłowami. Przykład: \begin{verbatim} parallel_vpns: 20 \end{verbatim} \subsection{Zmienna private\_addresses} Zmienna zawiera listę zakresów adresów IP, które system może nadawać parom veth. Format pojedynczego zakresu to ``{\textless}adres\_ipv4{\textgreater} - {\textless}adres\_ipv4{\textgreater}''. Znaki białe naokoło myślnika są opcjonalne. Spośród podanych w tej zmiennej zakresów powinno dać się wybrać przynajmniej tyle podsieci z maską /30, ile wynosi wartość zmiennej parallel\_vpns (niespełnienie tego wymogu objawi się odpowiednimi wpsami w logach). Przykład: \begin{verbatim} private_addresses: ["10.25.25.0 - 10.25.25.59", "10.25.26.0 - 10.25.26.255"] \end{verbatim} \section{Deinstalacja części back-endowej} W celu odwrócenia zmian dokonanych przez \file{setup.sh} należy wywołać (jako root) \begin{verbatim} # ./uninstall.sh \end{verbatim} Jeśli z systemu mają być usunięte także pliki zainstalowane przez skrypt \file{install.sh}, można użyć następującej flagi \begin{verbatim} # uninstall.sh --delete-files \end{verbatim} \section{Działanie części back-endowej systemu} \subsection{Cogodzinne odytywanie serwerów DNS} Demon crona co godzinę uruchamia skrypt \file{hourly.py}, który czyta konfigurację \filepath{/etc/0tdns/db_connection_config.yml}, następnie łączy się z bazą danych i pobiera z niej id wszystkich serverów VPN, z którymi ma nawiązać połączenie w celu odpytywania serwerów DNS. Dla każdego serwera VPN \file{hourly.py} sprawdza, czy jego plik konfiguracyjny znajduje się w systemie. Jeżli nie, jest pobierany z bazy i zapisywany pod \dir{/var/lib/0tdns/}. Przez zestawione połączenia VPN odpytywane są odpowiednie serwery DNS, a wyniki są umieszczane w bazie danych. \subsection{Sprawdzanie zakończenia wykonania} Skrypt \file{hourly.py} na początku działania tworzy plik \filepath{/var/lib/0tdns/lockfile}, który na końcu działania usuwa. Jeśli plik istnieje w systemie, oznacza to, że jakaś instancja \file{hourly.py} pracuje. Jeśli w momencie rozpoczęcia wykonania \file{hourly.py} plik lockfile istnieje, skrypt, aby uniknąć kolizji ze swoją wcześniejszą instancją, zakończy działanie bez wykonania zapytań DNS. Ten mechanizm stanowi zabezpieczenie na wypadek sytuacji szczególnej, jak np. błąd systemu. W normalnych warunkach \file{hourly.py} powinien zakończyć działanie w czasie dużo krótszym, niż godzina. Dodatkowo, 15, 30 i 45 minut po każdej godzine uruchamiany jest przez demon crona skrypt \file{check_if_done.py}, który sprawdza, czy plik lockfile istnieje i jeśli tak - wykonuje zapis do logów oraz wysyła maila do administratora (niezaimplementowane). Na wypadek nagłego wyłączenia systemu operacyjnego, do crontaba dodawane jest polecenie usunięcia pliku lockfile w momencie ponownego uruchomienia. \subsection{Powiadamienie mailowe} O każdej równej godzinie uruchamiany jest skrypt send\_emails, który sprawdza w bazie danych, jakie błędne adresy były zwrócone w poprzedniej godzinie i wysyła alert do odpowiednich użytkowników. \subsection{Logi} Logi zapisywane są do pliku \filepath{/var/log/0tdns.log}. Znajdują się tam oznaczone godziną informacje m.in. o nawiązywanych i nieudanych połączeniach VPN, działających zbyt długo instancjach skryptu \file{hourly.py} i błędach w podanych w konfiguracji zakresach adresów IP. \subsection{Zmiany w bazie danych} Wraz z wykonaniem zapytań DNS dodawane są do tabeli user\_side\_responses oraz ew. user\_side\_response wpisy z wynikami. Możliwe wartości pola 'results' w user\_side\_responses to: \begin{itemize} \item 'successful' - od serwera DNS przyszła odpowiedź, że domena istnieje (jedyny przypadek, kiedy mogą (choć nie muszą) być do tego rekordu dowiązane rekordy w user\_side\_response) \item 'not exists' - serwer DNS twierdzi, że domena nie istnieje \item 'no reponse' - nie przyszła żadna odpowiedż od serwera DNS \item 'DNS error: \textless jaki błąd\textgreater ' - jeśli zwrócony został inny kod błędu, niż powyższe \item 'internal failure: out of memory' - wykorzystywany libunbound zwrócił wartość informującą, że zapytanie DNS się nie powiodło z powodu braku pamięci \item 'internal failure: vpn\_connection\_failure' - nie udało się nawiązać danego połączenia VPN \item 'internal failure: process\_crash' - błąd skryptu wchodzącego w skład systemu \end{itemize} Nie rozróżniamy na tym etapie, czy ip zwrócone zgadzają się z oczekiwanymi. Razem z dodaniem wpisu innego typu, niż 'internal failure:', zmniejszany jest licznik ważności odpowiedniego rekordu w tabeli user\_side\_queries lub rekord jest usuwany (jeśli licznik osiągnął 0). \end{sloppypar} \end{document}