Веб-страничка Дмитрия Кашина

Настройка сети при помощи VPNС и interfaces

Русский / English

Суть проблемы

Работа программиста может происходить как на рабочем месте, так и удалённо, посредством подключения к рабочему месту через сеть. Вот и на моей работе в Jet Infosystems передо мной встала задача подключения ко внутренней сети.

По нашим порядкам подключение к рабочей сети у нас настраивается через vpnc. Собственно, настроить временное подключение к сети при помощи данной утилиты не составляет труда. Достаточно выполнить:

% sudo vpnc

И программа сама запросит все необходимые для подключения параметры, создаст туннельное устройство, получит по dhcp адрес, пропишет маршруты и модифицирует файл resolv.conf. Который, к слову, успешно будет затёрт, когда придёт время обновить аренду адреса у локального роутера. Последнее меня категорически не устраивало.

Несколько слов о тонкой настройке

Я придерживаюсь мнения, что наиболее тонкой настройки сети можно добиться лишь при помощи механизма /network/interfaces, предоставляемого большинством современных дистрибутивов.

Вполне возможно, что это не так, но в любом случае, это - механизм управления сетевыми соединениями, который в том или ином виде всегда присутствует, в то время как горячо любимые десктопными дистрибутивами NetworkManager или Wicd могут запросто отсутствовать в системе, с которой по воле случая вам приведётся работать.

Все примеры, которые встретятся далее, были проверены в системе GNU/Linux Debian.

/etc/network

Все настройки сетевых интерфейсов лежат в директории /etc/network. Настройки интерфейсов хранятся либо в файле /etc/network/interfaces, либо каждый отдельно в своём файле в каталоге /etc/network/interfaces.d/. Описываются интерфейсы так:

iface <iface-name> <address-family> <method>
	<option1>	<value1>
	<option2>	<value2>
	...

Когда пользователь запускает команду ifup eth0 среди описаний сетевых скрипт ifupdown ищет в описани интерефейсов тот, у которого <iface-name> является eth0, формирует в соответствии с описанием некоторые переменные окружения, а затем по очереди вызывает скрипты сначала из каталога /etc/network/if-pre-up.d, а затем из /etc/network/if-up.d/. Точно таким же образом ситуация обстоит при вызове ifdown eth0, только скрипты вызываются из других каталогов (названных соответственно со словом down).

При вызове этих скриптов им передаются переменные окружения, содержащие имя интерфейса, метода, и даже режима; а также все опции. Задача скриптов заключается в том, чтобы по данным переменным окружения правильно произвести настройку сетевого интерфейса.

Чтобы понимать, как именно формируются имена переменных окружения, я очень рекомендую прочитать interfaces(8). Понимание этих подробностей позволит Вам при желании писать собственные скрипты для настройки сетевых соединений.

Ради единообразия управления сетью, мне бы очень хотелось, чтобы для подключения к vpn, можно было бы дать команду вида ifup vpn.

Собственно, сделать это довольно просто. В if-up.d и if-down.d я просто добавил скрипт следующего содержания:

#!/bin/sh

# This script allows to set up only 1 vpnc iface.
# If you need more, improve vpnc-disconnect script.

VPNC=/usr/sbin/vpnc
VPNC_DISCONNECT=/usr/sbin/vpnc-disconnect

[ "$METHOD" = "manual" ] && [ "$IF_VPNC_IFACE_P" = "true" ] || exit 0
[ -x $VPNC ] || exit 1
[ -x $VPNC_DISCONNECT ] || exit 2

if [ "$MODE" = "start" ]
then
    echo "Setting up iface '$IFACE' with VPNC"

    $VPNC - <<EOF

Interface name $IFACE
IPSec gateway $IF_IPSEC_GATEWAY
IPSec ID $IF_IPSEC_ID
IPSec secret $IF_IPSEC_SECRET
Xauth username $IF_VPN_LOGIN
Xauth password $IF_VPN_PASSWORD

EOF
    exit 0
fi

if [ "$MODE" = "stop" ]
then
   echo "Stopping iface '$IFACE'"
   $VPNC_DISCONNECT
   exit 0
fi

echo "Unknown mode"
exit 3

Теперь мы можем определить интерфейс vpn-соединения. Запишем файл /etc/network/interfaces.d/vpn:

iface vpn inet manual
      vpnc-iface-p	true
      ipsec-gateway	<...ip-addr...>
      ipsec-id		<...secret...>
      ipsec-secret	<...secret...>
      vpn-login		<...secret...>
      vpn-password	<...secret...>
      dns-search	room1.jet.msk.su room2.jet.msk.su

Теперь при вызове ifup vpn, у нас будет создано устройство /dev/vpn, которое будет использовано для соединения с рабочей сетью.

resolvconf

Однако этого не достаточно для того, чтобы пользоваться сетью. Давайте сразу обратим внимание на опцию dns-search, присутствующую в конце конфигурации интерфейса. Она задаёт домены, которыми будет дополнено имя машины при поиске. Таким образом при вызове host pc-freehck будет выполнен сначала поиск имени pc-freehck.room1.jet.msk.su, а потом pc-freehck.room2.jet.msk.su. Эти опции не являются частью скрипта, который мы создали, и предназначены для скрипта, сопровождающего программу resolvconv, которая отвечает за то, чтобы файл с настройками DNS /etc/resolv.conf всегда содержал актуальные параметры.

Без этой программы файл /etc/resolv.conf будет содержать информацию для резолвинга имён, которую ему предоставит последний, процесс, который конфигурировал интерфейс. Какалось бы, тут нет ничего страшного, но надо иметь в виду, что работать с виртуальной сетью скорее всего придётся довольно долго, а за это время срок аренды адреса Вашего домашнего роутера скорее всего истечёт. dhclient его обновит, а вместе с тем обновит и настройки DNS: просто перезаписав файл resolv.conf. Чтобы этого не произошло, необходимо иметь в системе одноимённый с программой пакет resolvconv.

Когда скрипт конфигурации сетевого интерфейса поднимает сеть, он формирует новый resolv.conf. Но в случае, если есть программа resolvconv, он не перезаписывает /etc/resolv.conf, а передаёт его на вход resolvconv примерно следующим образом:

echo "$NEW_RESOLVCONF" | /sbin/resolvconf -a $TUNDEV

Программа resolvconv запоминает настройки, и однозначно связывает их с новым интерфейсом. Когда же она вызывается с флагом -u, она создаёт новый /etc/resolv.conf, являющийся объединением настроек DNS всех поднятых на данный момент интерфейсов.

Раскрытие имён

Есть ещё один подводный камень. В моей виртуальной сети существуют доменные имена, связанные со внутренними адресами сети. И конечно же они не доступны, если запрашивать публичный DNS-сервер снаружи: они исключительно для внутреннего пользования. Назовём их room1 и room2.

Проблема в том, что алгоритм запроса резолвинга имени в DNS-сервера в GNU/Linux по умолчанию следующий: из файла /etc/resolv.conf последовательно выбираются все строчки, начинающиеся с nameserver, после чего в порядке их появления начинают опрашиваться соответствующие серверы. И следующий сервер будет опрошен только в том случае, если предыдущий не ответит по таймауту.

Это, в частности, означает, что раз уж у вас первым DNS-сервером окажется ваш локальный роутер, то запросы ко внутренним серверам компании уже не пойдут, и при попытке обартиться к домену room1.jet.msk.su вы получите ответ, что такого ip-адреса ваш сервер не знает.

Решение, чтобы справиться с этой проблемой, напрашивается само собой: необходимо запрашивать одни внутренние домены надо запрашивать у внутренних DNS-серверов. Это легко делается при помощи локального DNS-сервера dnsmasq. Всё, что нужно, это установить его и прописать в его конфиге /etc/dnsmasq.conf строки наподобие таких:

% grep server /etc/dnsmasq.conf | grep -v ^#
server=/room1.jet.msk.su/<...gw-ip-addr...>
server=/room2.jet.msk.su/<...gw-ip-addr...>
Последние изменения: 31 Январь 2015