Pull to refresh

VPN-мост в локальную сеть

Configuring Linux *
Прочитал топик habrahabr.ru/blogs/linux/67209 и решил выложить сюда свою статью, которая была до этого видна только в закрытой корпоративной Wiki.

Обычно, при создании VPN, используется подключение типа точка-точка к некоторому серверу, либо установка ethernet-туннеля с некоторым сервером, при котором туннелю назначается определённая подсеть. Сервер VPN при этом выполняет функции маршрутизации и фильтрования трафика для доступа к локальной сети через VPN.

Данная статья рассматривает другой подход к созданию виртуальной сети, при котором удалённые системы включаются в уже существующую локальную подсеть, а сервер VPN выполняет роль Ethernet-шлюза. При использовании такого подхода мы всё ещё имеем возможность фильтровать трафик на основании способа подключения (например, использовать для локальной сети и для удалённых пользователей разные фильтры), но исключается необходимость настройки маршрутизации, а удалённые машины включаются прямо в локальную сеть, видят ресурсы, даже способны использовать широковещательные посылки вообще без дополнительной настройки. Через такой VPN у них отображаются все компьютеры локальной сети Windows, все доступные XDMCP-серверы при XDMCP broadcast и т. д.



Структура сети и настройка сервера


Предположим, что имеется офис с локальной сетью, используется IP-подсеть 192.168.168.0/24. В эту локальную сеть мы включим домашних пользователей, то есть они будут иметь адрес из этой же самой подсети. Необходимо убедиться, что у них «дома» не встречается данная подсеть, и что никакие системы в локальной сети не имеют адресов из диапазона, который мы выделим для удалённых пользователей.

Поддержка моста в ядре


Для работы такой техники нам нужны некоторые ядерные драйвера. Это универсальный драйвер виртуальных сетевых интерфейсов tun, и драйвер ethernet-моста bridge. Можно включить их в ядро, или собрать модулями:

    -> Networking
      -> Networking support (NET [=y])
        -> Networking options
          <*> 802.1d Ethenet Bridging (BRIDGE [=y])
    -> Device Drivers
      -> Network device support (NETDEVICES [=y])
        <*> Universal TUN/TAP device driver support (TUN [=y])

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

Программное обеспечение


Для сервера потребуется OpenVPN и утилиты для обслуживания моста. В Gentoo они собираются следующим образом:

  emerge net-misc/bridge-utils net-misc/openvpn


При использовании >=sys-apps/baselayout-1.12.6 этого достаточно, для более старых версий потребуются специальные утилиты для обслуживания tun/tap-устройств:

  emerge sys-apps/usermode-utilities


Настройка сети


Положим, eth2 — интерфейс, к которому подключена локальная сеть, с назначенным адресом 192.168.168.254. Его настройка выглядела примерно так:

  config_eth2=( "192.168.168.254/24" )


Поскольку он будет участвовать в мосте, ему не нужно назначать адреса. Также, в мосте участвует вновь создаваемый виртуальный интерфейс tap0, которому тоже не назначается никакого адреса. Адрес, который использовался eth2, назначается теперь мосту br0:

  config_eth2=( "null" )
  
  tuntap_tap0="tap"
  config_tap0=( "null" )
  
  depend_br0() {
    need net.tap0 net.eth2
  }
  
  # указываем существующие интерфейсы, объединяя их в мост
  bridge_br0="eth2 tap0"
  # либо, можно динамически подключать туда вновь появляющиеся интерфейсы
  #bridge_add_eth2="br0"
  
  config_br0=( "192.168.168.254/24" )


Также нужно создать настроечные скрипты для указанных интерфейсов:

  cd /etc/init.d
  ln -s net.lo net.eth2
  ln -s net.lo net.tap0
  ln -s net.lo net.br0


Достаточно автоматически загружать только интерфейс br0. depend_br0() автоматически поднимет все остальные необходимые ему для работы:

  rc-update add net.br0 default
  /etc/init.d/net.eth2 stop
  /etc/init.d/net.br0 start


Создание ключей OpenVPN


Мы будем авторизовывать клиентов посредством RSA-ключей OpenSSL. Для упрощения процесса, для нас приготовили несколько init-скриптов:

  cd /usr/share/openvpn/easy-rsa/


Там есть файл vars, в который мы занесём общие значения:

  nano vars


Внизу этого файла мы заполняем наши переменные:

  export KEY_COUNTRY="RU"
  export KEY_PROVINCE="Voronezh oblast"
  export KEY_CITY="Boguchar"
  export KEY_ORG="OrganiZationnAme"
  export KEY_EMAIL="root@oza.ru"


Загружаем переменные из этого файла и строим CA (Certificate Authority):

  source ./vars
  ./clean-all
  ./build-ca


Ключ сервера


Для генерации ключа сервера с именем office, используем следующую команду:

  ./build-key-server office


На вопрос «Common Name» нужно ответить именем сервера (в нашем случае, office). На два вопроса в конце «Sign the certificate? [y/n]» и «1 out of 1 certificate requests certified, commit? [y/n]» отвечаем «y».

При необходимости, можно будет создать дополнительные ключи серверов. Например, это могут быть резервные серверы доступа для повышения надёжности системы. Они создаются той же командой, перед ней нужно выполнить source ./vars.

Параметры Диффи-Хеллмана

Здесь ничего дополнительно делать не придётся, но придётся подождать.

  ./build-dh


Этот файл нужен только на сервере.

Ключи клиентов


Каждому клиенту необходимо выдать свой ключ. Для клиента с именем client ключ создаётся командой

  ./build-key client


На вопрос о «Common Name» отвечаем именем клиента (в данном случае, client). На два вопроса в конце отвечаем согласием.

Сгенерированные ключи и сертификаты передаём клиентам через защищённый канал. При необходимости, можно создавать ещё ключи той же командой. Перед её запуском, необходимо загрузить окружение — выполнить source ./vars.

Настройка и запуск сервиса OpenVPN


Для запуска следует использовать следующую конфигурацию сервера (файл /etc/openvpn/openvpn.conf):

  # Этот порт рекомендован IANA для OpenVPN. Можно перевесить на другой порт, но секретность не повысится - он всё равно первым делом признаётся, что он - OpenPVN.
  port 1194
  
  # OpenVPN может использовать tcp и udp в качестве транспортного протокола, udp - предпочтительнее
  proto udp
  
  # Виртуальный интерфейс, который мы включили в мост, непременно типа tap (через tun нельзя эмулировать Ethernet)
  dev tap0
  
  # Корневой самоподписанный сертификат CA
  ca /etc/openvpn/keys/ca.crt
  
  # Сертификат и секретный ключ сервера. crt должен иметь режимы 644, key - 600
  cert /etc/openvpn/keys/office.crt
  key /etc/openvpn/keys/office.key
  
  # Файл с параметрами Диффи-Хеллмана. Если у вас другая длина ключей, исправьте имя :)
  dh /etc/openvpn/keys/dh1024.pem
  
  # Раздавать удалённым клиентам адреса в этой подсети, из этого диапазона (обратите внимание - подсеть задаётся ВСЯ, как в конфиге сетевухи, а диапазон - часть подсети)
  server-bridge 192.168.168.254 255.255.255.0 192.168.168.128 192.168.168.159
  
  # Разрешить взаимодействие клиентов друг с другом (иначе только с сервером и сегментом сети "за мостом")
  client-to-client
  
  # Это позволит выдать клиенту тот же самый адрес, какой выдавали раньше, если не занят
  ifconfig-pool-persist /etc/openvpn/ipp.txt
  
  # Если вы не хотите через DHCP передавать также и адрес DNS-сервера, можно убрать следующую строку
  push "dhcp-option DNS 192.168.168.254"
  
  # Компрессия
  comp-lzo
  # Максимальное число клиентов - имеет смысл сделать меньше или равно числу адресов в диапазоне server-bridge
  max-clients 32

  # Подробности относительно этих ключей - в документации OpenVPN
  keepalive 10 120

  # Не переинициализировать tun и не перечитывать ключ при переподключениях; если работаем не как root, а как nobody, то нам это и не позволят, поэтому либо все эти опции, либо ни одну из них
  user nobody
  group nobody
  persist-key
  persist-tun

  # Каждую минуту OpenVPN сбрасывает сюда текущее состояние (список клиентов, маршруты и т. д.)
  status /tmp/openvpn-status.log
  
  # Очень шумный лог, нормальная работа - verb 2
  verb 6
  log-append  /var/log/openvpn.log


Ключ office.key должен иметь режим 600 (доступ только владельцу). Файлы office.crt и dh1024.pem имеют режим 644.

Настройка фильтрования



Поскольку мы используем мост, есть несколько особенностей организации фильтрования пакетов. Например, не все проходящие пакеты могут вообще оказаться IPv4. Для настройки работы моста в ядре существует несколько параметров:

Переменные этой группы сохраняются в файлах директории /proc/sys/net/bridge/. Их можно также настраивать в /etc/sysctl.conf, тогда они все получат префикс «net.brigde.»
  • bridge-nf-call-arptables
    Логическая переменная bridge-nf-call-arptables управляет передачей трафика ARP в цепочку FORWARD пакетного фильтра arptables. Установленное по умолчанию значение 1 разрешает передачу пакетов фильтрам, 0 – запрещает.
  • bridge-nf-call-iptables
    Логическая переменная bridge-nf-call-iptables управляет передачей проходящего через мост трафика IPv4 в цепочки iptables. Используемое по умолчанию значение 1 разрешает передачу пакетов для фильтрации, 0 – запрещает.
  • bridge-nf-call-ip6tables
    Действие аналогично предыдущему, только оно настраивает передачу трафика IPv6 для фильтрования в цепочки ip6tables.
  • bridge-nf-filter-vlan-tagged
    Логическая переменная bridge-nf-filter-vlan-tagged определяет возможность передачи трафика IP/ARP с тегами VLAN программам фильтрации пакетов (arptables/iptables). Значение 1 (установлено по умолчанию) разрешает передачу пакетов с тегами VLAN программам фильтрации, 0 – запрещает.


Для фильтрования пакетов, проходящих через мост, используется соответствие physdev, которое различает, с какого и на какой порт моста следует пакет. Включаем его в ядре:

-> Networking
  -> Networking support (NET [=y])
    -> Networking options
      -> Network packet filtering framework (Netfilter) (NETFILTER [=y])
        -> Core Netfilter Configuration
          -> Netfilter Xtables support (required for ip_tables) (NETFILTER_XTABLES [=y])
            -> "physdev" match support (NETFILTER_XT_MATCH_PHYSDEV [=y])


Кроме этого, конфигурация ядра должна разрешать передачу пакетов на фильтрацию iptables, т.е. bridge-nf-call-iptables=1 и bridge-nf-call-ip6tables=1 (если вы используете IPv6).
После можете использовать, например, такие правила для фильтрования:

  iptables -A FORWARD -p tcp --dport 22 -m physdev --physdev-in eth1 --physdev-out eth0 -j ACCEPT

Поподробнее про настройку фильтрации между портами поста можно почитать в статье Building bridges with Linux

Если вы не хотите делать никаких различий между пользователями LAN и пользователями bridged VPN, вы можете просто выключить эти параметры в ядре (они включены по умолчанию):

  echo "net.bridge.bridge-nf-call-iptables = 0" >> /etc/sysctl.conf
  echo "net.bridge.bridge-nf-call-ip6tables = 0" >> /etc/sysctl.conf


Клиенты


На клиенте необходимо создать конфигурационный файл OpenVPN следующего содержания:

  client
  nobind
  dev tap
  proto udp
  # Куда подключаться. Можно указать несколько опций remote - будет использоваться первый доступный сервер. Если для server.example.net имеется несколько A-записей, между ними выбор производится случайно.
  remote server.example.net 1194
  # Никогда не сдаваться, пытаться подключаться бесконечно.
  resolv-retry infinite
  # Либо все опции вместе, либо ни одна из них
  persist-key
  persist-tun
  user nobody
  group nogroup
  comp-lzo
  ns-cert-type server
  ca ca.crt
  cert client.crt
  key client.key


Если сервер подключен через несколько провайдеров, можно повысить устойчивость сети к отказам. Для этого клиенту нужно прописать несколько опций remote, по одной на сервер, в порядке «сначала предпочтительные».

Имена файлов, указанные в параметрах ca, cert и key — это файлы, переданные через защищённый канал. Права доступа к файлу key должны быть установлены в 600.

Linux


Необходим universal tun/tap driver в ядре, либо модулем, но загруженный.

Gentoo

При установке net-misc/openvpn создаётся скрипт /etc/init.d/openvpn. Этот скрипт запускает openvpn с конфигурационным файлом /etc/openvpn/openvpn.conf. Мы, однако, можем поддерживать несколько конфигураций OpenVPN одновременно, если сделаем симлинки вида /etc/init.d/openvpn.network-name -> /etc/init.d/openvpn — каждый такой скрипт запускает OpenVPN с конфигурационным файлом /etc/openvpn/network-name.conf.

Соответственно, помещаем туда вышеприведённый конфиг, создаём симлинк и кладём скрипты в поддиректорию в /etc/openvpn/. В конфиге прописываем полный путь к ключу и сертификатам. Следите, чтобы имена файлов в конфиге не пересекались, во избежание неприятных эффектов!

Запуск и останов сети производятся через управление сервисом /etc/openvpn.network-name.

Windows


Конфигурационный файл помещается в директорию «C:\Program Files\OpenVPN\config\» с именем вроде «office.ovpn», туда же помещаются остальные файлы — ключи и сертификаты. Если мы их помещаем в поддиректорию (например, хотим использовать несколько виртуальных сетей и все они предоставили файлы с одинаковым именем ca.crt), указываем полные пути к файлам.

Для запуска сетей можно либо запустить сервис OpenVPN (тогда будут запущены все конфигурации *.ovpn, найденные в config\), либо по отдельности — щёлкаем по файлу .ovpn правой кнопкой и выбираем «Запустить OpenVPN с этой конфигурацией».

Возможные проблемы


Проверить доступность сервера, если он запущен на TCP, можно обычным telnetом.

Windows


Нет свободного виртуального адаптера TAP

Wed Dec 31 10:43:51 2008 TCP connection established with 88.83.201.253:1194 
Wed Dec 31 10:43:51 2008 TCPv4_CLIENT link local: [undef] 
Wed Dec 31 10:43:51 2008 TCPv4_CLIENT link remote: 88.83.201.253:1194 
Wed Dec 31 10:44:51 2008 TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity) 
Wed Dec 31 10:44:51 2008 TLS Error: TLS handshake failed 
Wed Dec 31 10:44:51 2008 Fatal TLS error (check_tls_errors_co), restarting 
Wed Dec 31 10:44:51 2008 SIGUSR1[soft,tls-error] received, process restarting 
Wed Dec 31 10:44:56 2008 IMPORTANT: OpenVPN's default port number is now 1194, based on an official port number assignment by IANA.  OpenVPN 2.0-beta16 and earlier used 5000 as the default port. 
Wed Dec 31 10:44:56 2008 Re-using SSL/TLS context 
Wed Dec 31 10:44:56 2008 LZO compression initialized 
Wed Dec 31 10:44:56 2008 Attempting to establish TCP connection with 88.83.201.253:1194 
Wed Dec 31 10:44:56 2008 TCP connection established with 88.83.201.253:1194 
Wed Dec 31 10:44:56 2008 TCPv4_CLIENT link local: [undef] 
Wed Dec 31 10:44:56 2008 TCPv4_CLIENT link remote: 88.83.201.253:1194 
Wed Dec 31 10:45:11 2008 [office] Peer Connection Initiated with 88.83.201.253:1194 
Wed Dec 31 10:45:13 2008 All TAP-Win32 adapters on this system are currently in use. 
Wed Dec 31 10:45:13 2008 Exiting 
Press any key to continue...

По логу OpenVPN видно, что клиент успешно присоединился к серверу, авторизовался, но не смог привязать виртуальную сеть к виртуальному адаптеру. Скорее всего, какие-то другие процессы уже задествовали все имеющиеся в системе адаптеры TAP-Win32. Это мог быть и сам OpenVPN, повисший и не отдавший адаптер.

Лечится перезагрузкой или выяснением, какие бы это могли быть процессы и принудительным их убиванием.

Ссылки


При написании данной статьи, использовались следующие источники:
  1. Gentoo Linux Wiki — HOWTO OpenVPN Server for Ethenet Bridging with Server Certificates (Есть копия этой страницы, по адресу: http://www.gentoo-wiki.info/HOWTO_OpenVPN_Server_for_Ethernet_Bridging_with_Server_Certificates. Спасибо hexes за ссылку!)
  2. Gentoo Linux Wiki — HOWTO OpenVPN Linux Server Windows Client
  3. OpenVPN Documentation — HOWTO
  4. Энциклопедия сетевых протоколов — параметры sysctl для стека IP
  5. Building bridges with Linux


P.S. Некоторые источники почили. Ссылки я убирать не буду, но стоит иметь ввиду.
Tags:
Hubs:
Total votes 29: ↑23 and ↓6 +17
Views 193K
Comments Comments 14