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

    Прочитал топик 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. Некоторые источники почили. Ссылки я убирать не буду, но стоит иметь ввиду.
    Поделиться публикацией

    Комментарии 14

      –1
      Закройте теги в конце статьи :)
        0
        Полезная статья!

        P.S. если не ошибаюсь, вместо перезагрузки в винде можно просто отключить адаптер и включить снова.
          0
          Возможно. Проблема возникала на моей памяти один раз, и на достаточно «замусоренном» компе. Одно из первых решений в такой ситуации, которые приходят в голову — перезагрузка ОС, так что другие даже не пробовали.
          0
          спасибо, как раз подумываю поднять dgl в домашнюю сеть
          ну и ещё спасибо что на генте :)
            0
            s{режим}{права}gi;
            *fxd ;)
              0
              Ссылка 1. битая.
                –1
                А зачем с бриджом? Не легче все разрулить с помощью iptables?
                  0
                  А вы статью-то читали? Там в самом начале написано, «зачем»… Вкратце повторю: мне нужен был виртуальный ethernet, единый сегмент.

                  Кроме того, iptables ничего не «разруливает». Это программа-интерфейс к ядерному инспектору пакетов Netfilter, который фильтрует пакеты, а не маршрутизирует их. «Разруливанием» пакетов в Linux обычно занимается RPDB.

                  Так что не путайте мелкое с мягким…
                  –1
                  сейчас нарвусь на холивар. но как в этом вашем лнупсе все сложно(по сравнению с freebsd)…
                    0
                    Linux Kernel v2.6.30-gentoo-r7,
                    Netfilter Xtables support (required for ip_tables) нашёл, а вот «physdev» как то нету… Не там/то смотрю? Или?..
                    (в остальном за статью ОГРОМНОЕ спасибо!!!)
                      0
                      А вы поиском в конфигураторе ядра не пробовали воспользоваться? ;) Нажимаете "/" (рядом с Right-Shift) и вводите «physdev», там отображается — где оно находится и какие ещё для него нужно галочки поставить.
                      0
                      www.gentoo-wiki.info/HOWTO_OpenVPN_Server_for_Ethernet_Bridging_with_Server_Certificates
                      Можно поправить первую ссылку…
                        0
                        Отличная статья! Странно, что так мало плюсовали… Вот сижу твикаю подобное на убунтячем серваке(сразу оговорюсь — я не линуксоид, просто умею пользоваться гуглом, потому и убунта)))…
                          0
                          Хочу дополнительно попросить консультации у автора.
                          Имею гипервизор на дебиане (proxmox) и kvm-виртуалки на нем.
                          Мост, у каждой виртуалки свой белый ip.
                          Периодически в сети возникает бурление транзитного трафика, пакеты валят, причем ни источник, ни назначение не имеют отношения к моим адресам.

                          Мост все это исправно транслирует на софтовые сетевые виртуалок, вследствии чего идет серьезная нагрузка на сервер.

                          Правильно ли я понимаю, что установка bridge-nf-call-iptables в /etc/sysctl.conf и sysctl -p включат предварительный прогон трафика через iptables, а далее я могу загонять неугодных в -t raw -A PREROUTING -s IP -j DROP?

                          Напомню нюанс — мне не предназначены те пакеты, которые мешают жить…

                          Кстати, -j DROP в прероутинге/рав РАБОТАЕТ, проверил экспериментально.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое