nat loopback, partea a ii-a

Scris de spyked la data 11 Octombrie, 2009

În prima parte am discutat diverse metode de a accesa adresa (în cele mai multe cazuri, unul sau mai multe nume de site-uri interpretate de către un server web intern) externă a unei rețele din interiorul acesteia și ne-am oprit asupra cazulului particular în care router-ul are linux cu iptables instalat și putem adăuga două reguli suplimentare tabelei NAT.

Ne confruntăm aici cu o problemă: la restartarea router-ului, configurarea adițională se va pierde. În plus, softul acestuia nu permite adăugarea unor script-uri care să se execute la bootare și nici modificarea celor existente. Vom presupune că varianta schimbării firmware-ului (variantă care presupune riscul transformării router-ului într-o bucată de plastic bună de aruncat la gunoi, în cel mai rău caz) nu ne interesează, astfel că trebuie să transmitem totuși comenzile din exterior, prin telnet. Am presupus din capul locului că avem în rețea o mașină care rulează tot timpul, identificată prin $server_ip; adresa router-ului în rețea este $router_ip.

Automatizarea transmisiei de comenzi de la server către router se va face în două etape: prima, conceperea unui script care să comunice cu router-ul și a doua, verificarea la momente de timp prestabilite dacă regulile de NAT Loopback sunt în tabelă.

1.Script-ul de introducere a regulilor în NAT-ul router-ului

Pentru a putea comunica cu router-ul, trebuie în primul rând să știm cum va arăta shell-ul în sesiunea de telnet cu utilizatorul. De exemplu, pe modem-ul subsemnatului, ZTE931, prima dată se va cere user-ul și parola prin linii de forma „Login: ” și „Password: ”. Apoi va fi afișat un shell identificat prin string-ul „> ”, iar după ce introduc comanda „sh” va fi afișat un alt shell, dat de „#”. Apoi, după fiecare comandă eu voi aștepta „#” de la modem.

Această comunicație poate fi automatizată folosind utilitarul expect, a cărui pagină de manual vă recomand să o consultați. Script-ul se va rula într-un shell special; se așteaptă răspunsuri de la o entitate folosind comanda expect, iar răspunsurile se trimit cu send. Mai concret, script-ul de update al tabelei NAT al router-ului va arăta astfel:

#!/usr/bin/expect -f
spawn telnet $router_ip #deschid sesiunea telnet
expect "Login: "
send -- "admin\r"
expect "Password: "
send -- "passgoeshere\r"
expect "> "
send -- "sh\r" #deschid shell pentru comenzi
expect "#"
send -- "iptables -t nat -A PREROUTING -d $external_ip -p tcp --dport 80 \
      -j DNAT --to $server_ip; iptables -t nat -A POSTROUTING -d $server_ip \
      -s 192.168.0.0/24 -p tcp --dport 80 -j SNAT --to $router_ip"
expect "#"
send -- "exit\r" #inchiderea sesiunii
expect "> "
send -- "exit\r"

Rularea acestui script bash/expect va substitui deschiderea unei sesiuni interactive și introducerea manuală a comenzilor de către utilizator. Script-ul poate fi executat de pe oricare mașină din rețeaua internă, după restartarea router-ului. Vom presupune că am numit acest script natloop și l-am salvat pe server-ul având adresa $server_ip, în directorul /router.

2.Verificarea automată a existenței NAT Loopback

Această problemă poate fi rezolvată în mai multe moduri. Unul din aceste moduri e chiar folosirea expect pentru verificarea tabelei NAT și update în cazul în care regulile nu există. Dar din moment ce am presupus că rezolvăm forward-ul către un server web, vom da o soluție un pic mai simplă: am spus că în cazul în care router-ul nu satisface NAT Loopback pe portul 80, vom fi direcționați către interfața web a acestuia. Un wget pe $router_ip (sau pe $external_ip, atunci când nu există NAT Loopback) va întoarce cel mai probabil un cod de eroare 401 (forbidden, deoarece nu au fost specificate user-ul și parola pentru sesiune). În schimb, dacă iau $nume_domeniu cu wget, voi primi 200 (eventual după câteva redirectări). Codul script-ului pingip (nume ales complet aleator, salvat probabil tot în /router) este următorul:

#!/bin/bash
mesaj=`wget $adr_domeniu 2>&1 | grep -i "HTTP request" | tail -n 1 | cut -d " " -f6`
if [ $mesaj != 200 ] ; then
    echo "$(date) $mesaj Failed" >> /router/nat.log
    /router/natloop
else
    echo "$(date) $mesaj Merge" >> /router/nat.log
fi

Vom diseca întâi prima linie, care are rolul de a lua efectiv codul întors de cererea HTTP și a-l salva în variabila $mesaj. Practic, generez un flux de string-uri, executând wget pe una din adresele web externe ale server-ului și redirectând stderr către stdout. Acest flux îl filtrez cu un grep (doresc să știu doar codul întors de pagină, iar acesta are în față „HTTP Request”) și iau ultima linie, care va conține cel mai probabil ori 200 ori 401. Împart linia în coloane (după spații) folosind cut și iau a șasea coloană, care reprezintă codul propriu-zis.

Apoi tot ce fac este să verific dacă am un cod diferit de 200 și să rulez natloop în acest caz. Pentru a monitoriza activitatea script-ului, îl pun să scrie timestamp-ul și un mesaj (cod plus stare) într-un fișier pentru logging. Ce mai rămâne este adăugarea unei linii în /etc/crontab, pentru a executa script-ul nostru la anumite intervale de timp (eu l-am pus să facă verificarea la fiecare cinci minute, de exemplu).

Trebuie remarcat că e posibil ca acest exercițiu de scripting să nu dea întotdeauna rezultate corecte. Problema în sine e destul de complicată, deoarece presupune interdependența mai multor noduri din rețea (chiar și așa, putând fi complicată și mai mult în funcție de diverși factori). Personal, sugerez cititorului să înțeleagă și să se documenteze bine înainte să ruleze oricare din comenzile menționate aici (e de ajuns un comment și voi oferi lămuriri suplimentare dacă e nevoie). De asemenea, dacă cineva vine cu o soluție mai bună, sunt numai ochi.

2 răspunsuri la “nat loopback, partea a ii-a”

  1. [...] because it costs too much or because other people made bad decisions. This is also the case of a problem I adressed some time ago. The post is in Romanian, but the scripts there should be fairly [...]

  2. [...] this part, I will narrow down to the actual initial problem. We previously made some nice wrappers over the python socket send() and recv() primitives and we [...]

Lasă un răspuns

Wordpress RSS Feed Output Error
Bazele SEO