суббота, 17 сентября 2011 г.

Маршрутизатор. NAT.

В предыдущем посте мы с вами поставили Дебиан на виртуальную машину с целью превратить его в сервер, раздающий интернет в нашу локалку. Попытаемся претворить это в жизнь и начнём с самого простого - дать доступ всем ко всему.

Самое просто решение - NAT. Network address translation. Трансляция сетевых адресов. Суть этого механизма довольна проста: клиент отправляет пакеты через сервер NAT, который в свою очередь меняет в каждом пакете поле "адрес отправителя" с адреса клиента на свой адрес; в ответных пакетах сервер делает обратное преобразование - ставит на место адрес клиента. Посмотрите на картинку, станет проще:

Итак, что же на ней? Допустим у нас два компа (PC1 и PC2) и сервер между ними (NAT). У PC1 и PC2 по одному интерфейсу, у сервера NAT - 2. Адреса подписаны на картинке. PC1 хочет послать пакет на PC2 и получить от него ответный пакет. Но они в разных подсетях, то есть связи напрямую между ними нет - пакеты ходят через сервер. Пусть для PC1 шлюз по умолчанию - сервер NAT. Итак по этапам:

  1. PC1 не имеет прямой связи с PC2, так что отправляет пакет на шлюз по умолчанию - на сервер NAT. При этом в поле "адрес источника" (src на картинке) он пишет свой адрес, а в поле "адрес назначения" - адрес конечного узла, то есть адрес PC2.
  2. Сервер получает пакет, выбирает маршрут для него и меняет поле "адрес источника" с адреса PC1 на свой адрес. Это изменение запоминается во временной таблице, чтобы потом знать, кому перенаправить ответный пакет от PC2. Адрес назначения остаётся тем же. Пакет идёт на PC2.
  3. PC2 получает пакет. Он думает, что этот пакет сформирован сервером NAT и ничего не подозревает о существовании PC1. Формируется ответ, который уходит на сервер NAT.
  4. Сервер получает ответный пакет. Видит, что согласно таблице NAT, это ответный пакет для PC1. Адрес назначения меняется на PC1, и пакет пересылается на PC1.
  5. PC1 получает пакет. Ни о каких изменениях в ходе его следования PC1 не в курсе - весь механизм сработал прозрачно, то есть незаметно.

Всё, конец. Все счастливы.

Вообще-то механизм маршрутизации сработал бы тут не менее эффективно. То есть сервер бы просто переслал пакет без изменений и вернул бы ответ. Никакого НАТа. Но тут есть одно ограничение: чтобы пакет ушёл и вернулся обратно, должны существовать маршруты в обе стороны. Иногда это можно организовать, иногда нет. В случае соединения компа из локальной сети с интернетом это организовать невозможно. Почему? Потому что у компа из локалки так называемый серый айпишник - никто о нём не знает, за пределами локалки этот адрес никому неизвестен, соответственно и маршрута к нему быть не может. Однако, у сервера, раздающего инет на локальную сеть, айпишник белый (глобально маршрутизируемый) - он получен от провайдера и все знают о нём. Поэтому можно воспользоваться этим адресом - он будет своего рода пропуском в интернет для всех компов из локалки.

Ну что ж, мы знаем, что делать в теории, осталось реализовать это на практике. Наверное думаете, сейчас придётся искать подходящий софт, выбирать, качать, может даже компилировать? Нет. Всё гораздо проще. В Линуксе есть межсетевой экран iptables, который встроен в ядро и помимо прочего умеет NAT. Собственно сам межсетевой экран называется netfilter, а iptables - это консольный интерфейс к нему, но это не суть. К слову, NAT есть и в Винде (и даже не в серверных версиях):

Помните такое, да? Вот это собственно и есть NAT. Ну чтобы увидеть такую херню, надо иметь хотя бы 2 сетевых интерфейса, так что не удивляйтесь, если вдруг этого пункта где-то не будет. Но кроме этой несчастной галочки больше никаких настроек и нет. Повесить NAT на два и более интерфейсов тоже нельзя. Ну оно и понятно - икспиха всё-таки клиентская ось.

В серверной Винде возможностей заметно больше (можете добавить роль "Службы политики сети и доступа" с компонентами "Службы маршрутизации и удалённого доступа" и потыкаться там), но всё же по уровню гибкости и удобству настройки, а также по наглядности конечного результата всё это счастье уступает юниксовым системам.

Но вернёмся к нашим пингвинам. Логинимся на виртуальной машине с маршрутизатором под рутом. Сеть мы настроили в прошлой статье, сейчас только проверим её работоспособность - запустим пинг с маршрутизатора на контроллер домена и наоборот. Пинги должны ходить в обе стороны. Далее включаем ip-форвардинг на маршрутизаторе:

echo 1 > /proc/sys/net/ipv4/ip_forward

Теперь через наш сервер смогут ходить транзитные пакеты (идущие от одного удалённого хоста к другому через наш сервер).

И собственно NAT:

iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j SNAT --to-source 10.0.2.15

Вот он, великий и ужасный iptables. Мы добавили в таблицу NAT в цепочку POSTROUTING (в ней вносятся изменения в пакеты на выходе) правило, которое всем пакетам с адресом источника из подсети 192.168.100.0/24 будет подставлять в качестве обратного адреса 10.0.2.15 (это адрес внешнего интерфейса маршрутизатора, его можно посмотреть в выводе ifconfig). После этих двух команд с любой машины из нашей виртуальной локалки можно пользоваться интернетом. Однако тут есть одно но: внешний интерфейс получает адрес (в данном случае 10.0.2.15) по dhcp, а следовательно в следующий раз адрес может быть и другим. В этом случае правило айпитэйблс будет срабатывать всё так же, но результат уже будет не тем - пакетам будет подставляться неправильный адрес. Чтобы избежать этого, немного изменим приведённое выше правило. Для начала удалим старое:

iptables -t nat -D POSTROUTING -s 192.168.100.0/24 -j SNAT --to-source 10.0.2.15

И добавим новое:

iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE

Маскарадинг - это то же самое подставление адреса, только на этот раз адрес будет определяться динамически. То есть iptables сначала посмотрит адрес интерфейса, с которого уходит пакет и потом подставит его в поле "адрес отправителя" в пакете. По сравнению с первым методом маскарадинг очевидно более ресурсоёмок, но как видите в данном случае без него никак.

Сейчас можно зайти с любой из виртуальных машин проверить результат - интернет должен быть везде (если, конечно, он есть на реальной машине).

C:\Windows\System32>ping -n 1 8.8.8.8

Обмен пакетами с 8.8.8.8 по с 32 байтами данных:
Ответ от 8.8.8.8: число байт=32 время=197мс TTL=125

Статистика Ping для 8.8.8.8:
Пакетов: отправлено = 1, получено = 1, потеряно = 0
(0% потерь)
Приблизительное время приема-передачи в мс:
Минимальное = 197мсек, Максимальное = 197 мсек, Среднее = 197 мсек

Угу. Оно.

Следующее "но". Все настройки правил iptables хранятся в памяти и сбрасываются при перезагрузке. Значит нам надо поставить скрипт, забивающий правила, в автозагрузку. Сделаем это. В файле /etc/network/interfaces добавим после описания интерфейса, смотрящего в инет, строчку:

up /root/iptables-script

Этот скрипт будет выполняться каждый раз при поднятии интерфейса. Собственно надо только создать файл /root/iptables-script и прописать в нём всё, что надо:

cd /root
touch iptables-script
chmod u+x iptables-script
vim iptables-script

Создали файл, дали ему права на выполнение и открыли его в текстовом редакторе. Вот содержание скрипта:

#!/bin/bash
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE

Теперь при перезагрузке всё будет хорошо. Ну это примитив. Можно немного улучшить. Сохраним уже забитые в память правила следующей командой:

iptables-save > /root/iptables.sv

А в /etc/network/interfaces заменим

up /root/iptables-script

на

up iptables-restore /root/iptables.sv; echo 1 > /proc/sys/net/ipv4/ip_forward

iptables-save и iptables-restore - утилиты для сохранения и восстановления правил iptables. Отличие от предыдущего варианта в том, что тогда правила забивались по одному (сейчас оно у нас всего одно, но дальше будет больше) - одно обращение к ядру/одно правило. Тут же всё забьётся за одно обращение к ядру. Экономия ресурсов на лицо, да же? Ну и после точки с запятой (последовательное выполнение команд) добавили разрешение прохождения транзитных пакетов.

Ну и хватит на сегодня. Детально проработаем правила в следующей статье.

1 комментарий: