Подключение OpenWrt к OpenConnect серверу

Подключение OpenWrt к OpenConnect серверу

Приветствую!

В этой статье настроим OpenWrt для подключения к OpenConnect серверу (ocserv) и организуем гибкую маршрутизацию: весь трафик, только нужные домены или конкретные IP-подсети.

Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике — заглядывайте в Вороний чат @r4ven_me_chat🧐.

Подготовка

Самое главное — подразумевается, что у вас уже есть:

для выполнения дальнейших действий из данной статьи.

Бэкап конфигурации роутера

Перед началом любой работы с OpenWrt необходимо сделать резервную копию текущей конфигурации роутера и знать, как из нее восстановиться. Как и множество других операций, бэкап в OpenWrt можно сделать двумя способами: с помощью консоли (SSH)🖥️ и веб интерфейса (LUCI)🌐.

Очевидно, что хранить бэкапы нужно не на самом роутере. Ниже представлена команда бэкапа конфига OpenWrt с помощью консоли:

☝️Подключение к консоли роутера по SSH выполняется под учетной записью root и паролем, с которым вы подключаетесь к Web GUI.

ssh root@openwrt.lan 'sysupgrade --create-backup -' | cat > ./backup.tar.gz

💡Данная команда с помощью sysupgrade создает архив tar и передает его содержимое через механизм перенапрвления потоков локальной команде cat, которая в свою очередь сохраняет полученный вывод в файл с именем ./backup.tar.gz в текущей директории на вашем компьютере.

Восстановление из бэкапа рекомендуется выполнять с последующим перезапуском роутера.

В случае такой необходимости выполните команды:

cat ./backup.tar.gz | ssh router 'sysupgrade --restore-backup -'

ssh root@openwrt.lan reboot

💡Тут аналогичным образом команда cat передает содержимое архива backup.tar.gz на ввод удаленной команде sysupgrade, которая выполняет восстановление конфигурации.

Также удобно делать и восстанавливать бэкап через графический интерфейс роутера. Раздел: System — Backup / FLash firmware — [Generate archive | Upload archive]

Хорошо, с бэкапом и восстановлением разобрались. Приступаем к внесению изменений в конфигурацию.

Установка пакета dnsmasq-full

Немного контекста: в прошивке OpenWrt dnsmasq (DNS и DHCP сервер) является ключевым элементом системы. В OpenWrt по умолчанию используется облегчённая версия dnsmasq для экономии пространства постоянной памяти роутера💽.

Для настройки выборочной маршрутизации по списку доменов нам понадобится полная версия: dnsmasq-full, доступная из стандартных репозиториев проекта OpenWrt.

dnsmasqdansmasq-full
DNS-serverDNS-server
DHCP-serverDHCP-server
DNSSEC
DHCPV6
Auth DNS
IPset
Nfsets
Conntrack
TFTP server
Функционал пакетов dnsmasq и dnsmasq-full

И так, открываем сессию SSH на роутере:

ssh root@openwrt.lan

💡Вместо openwrt.lan можно указать IP адрес вашего роутера, по умолчанию это 192.168.1.1.

Перед установкой dnsmasq-full необходимо удалить стандартный dnsmasq, что приведет к сбоям работы DNS на роутере. Поэтому предварительно нужно выгрузить установочный пакет dnsmasq-full в виде файла на роутер. И только потом выполнять удаление и установку:

❗Пожалуйста будьте осторожны при вводе команд. Обязательно проверяйте успешность их выполнения.

opkg update

mkdir /tmp/dnsmasq_full && cd /tmp/dnsmasq_full

opkg download dnsmasq-full

opkg remove dnsmasq && opkg install dnsmasq-full --cache /tmp/dnsmasq_full

В составе пакета dnsmasq-full также идет свой файл конфигурации DHCP. Но т.к. у нас ранее был установлен dnsmasq, который уже использовал /etc/config/dhcp, то конфиг файл dnsmasq-full сохранился по пути /etc/config/dhcp-opkg:

Collected errors:
 * resolve_conffiles: Existing conffile /etc/config/dhcp is different from the conffile in the new package. The new conffile will be placed at /etc/config/dhcp-opkg.

Если у вас были специфические настройки DHCP на роутере, то перенесите их из /etc/config/dhcp в /etc/config/dhcp-opkg, например, с помощью vim, после чего сделайте новый конфиг основным:

mv /etc/config/dhcp-opkg /etc/config/dhcp

И перезапустите модуль dhcp:

service dnsmasq restart

В случае проблем, у нас есть бэкап, который мы сделали вначале данного руководства. Ведь сделали?😬

Установка клиента openconnect

Теперь необходимо установить клиентские пакеты openconnect, а также утилиту curl, которая понадобится нам для проверки защищенных подключений. Выполняем:

opkg install openconnect luci-proto-openconnect curl

Пакет luci-proto-openconnect предоставляет возможность управлять конфигурациями OpenConnect подключений в GUI роутера:

Подключение к OpenConnect серверу

В этой статье я покажу пример конфигурации с помощью консольной утилиты uci, которая управляет настройкой модулей, одноименные файлы конфигурации которых расположены в /etc/config/.

Для универсальности в начале некоторых блоков команд необходимо будет задавать переменные со своими значениями.

На этом этапе задаём ключевую переменную OC_ID прямо в консоли, с произвольным именем виртуального сетевого интерфейса OpenConnect, например:

OC_ID="oc0"

Подключение с помощью пары логин/пароль

Ниже приведены консольные команды конфигурации модуля network для создания нового туннельного подключения. Подставьте свои значения в параметры:

  • uri — адрес сервера ocserv;
  • username — имя пользователя в ocserv;
  • password — пароль пользователя в ocserv.
uci set network.${OC_ID}=interface
uci set network.${OC_ID}.proto="openconnect"
uci set network.${OC_ID}.uri="vpn.r4ven.me:4443"
uci set network.${OC_ID}.username="openwrt"
uci set network.${OC_ID}.password="PaSSw0rD"
uci set network.${OC_ID}.vpn_protocol="anyconnect"
uci set network.${OC_ID}.defaultroute="0"

💡Если используете ocserv с функционалом camouflage, в параметр uri указывайте полный адрес (в одинарных кавычках). Например:

uci set network.${OC_ID}.uri='vpn.r4ven.me:4443/?SeCrEt_WoRd'

Применяем внесенные изменения фиксацией конфига модуля network и его перезапуска:

uci commit network

service network restart

💡Откатить внесенные изменения, выполненные до момента фиксации (commit) можно командой revert. Пример:

uci revert network

Подключение с помощью файла-сертификата

Чтобы настроить подключение по сертификату пользователя нужно создать конфиг аналогичным образом, но можно без параметра password:

uci set network.${OC_ID}=interface
uci set network.${OC_ID}.proto="openconnect"
uci set network.${OC_ID}.uri='vpn.r4ven.me:4443/?SeCrEt_WoRd'
uci set network.${OC_ID}.username="openwrt"
uci set network.${OC_ID}.vpn_protocol="anyconnect"
uci set network.${OC_ID}.defaultroute="0"

Затем нужно взять файлы ключа и сертификата пользователя и, как указано в официальном README OpenWrt, положить их на роутере по пути:

/etc/openconnect/user-cert-vpn-${OC_ID}.pem
/etc/openconnect/user-key-vpn-${OC_ID}.pem

Где OC_ID, как мы помним, это имя сетевого устройства, которое мы определили вначале раздела. Обратите внимание, что пути и имена файлов на роутере обязательно должны быть, как в примере выше. Они зашиты в скрипт подключения /lib/netifd/proto/openconnect.sh. Например файл ключа там выглядит так: /etc/openconnect/user-key-vpn-$config.pem.

Если вы настраивали сервер OpenConnect по моей инструкции, то файлы пользователя, например, с именем openwrt будут находиться по пути:

/opt/openconnect/data/certs/openwrt-cert.pem
/opt/openconnect/data/certs/openwrt-privkey.pem

Фиксируем и применяем изменения:

uci commit network

service network restart

Под спойлером ниже инструкция для параноиков😅, как задать параметр servershash.

Клик на спойлер

Вообще servershash можно не указывать (ни в конфиге, ни файлом) и все будет работать (используется хэш, полученный в момент подключения). Но если вам это принципиально, то нужно указывать хэш публичного SSL сертификата вашего домена, например, полученного от Let’s encrypt.

В таком случае имейте в виду, что данный хэш придется обновлять в конфиге при каждом обновлении доменного сертификата. В случае того же Lets encrypt хэш придется обновлять каждые 3 месяца. Это не удобно и этот процесс нужно автоматизировать или не использовать данный механизм. В любом случае расскажу, как настроить.

Получить данный хэш можно двумя путями:
1) С помощью страшной команды openssl (вместо vpn.r4ven.me:4443 подставьте адрес вашего сервера ocserv):

openssl x509 -in \
    <(openssl s_client -showcerts -connect vpn.r4ven.me:4443 < /dev/null 2> /dev/null) \
    -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | \
    openssl enc -base64

Например, хэш может выглядеть так:

71DEapj8SBSa+/TImW+2JCeuQeRkm5MNpJWZG3hsuFU=

2) При неверном указании хеша в выводе журнала подключения (команда logread) на роутере будет подсказка, которой буде туказан хеш:

--servercert pin-sha256:71DEapj8SBSa+/TImW+2JCeuQeRkm5MNpJWZG3hsuFU=

После определения хеша, задаем его в опции serverhash в соответствующем блоке модуля network добавив pin-sha256: в качестве префикса:

uci set network.${OC_ID}.serverhash='pin-sha256:71DEapj8SBSa+/TImW+2JCeuQeRkm5MNpJWZG3hsuFU='

Альтернативный способ подтверждения валидности сервера без указания параметра serverhash — использовать файл сертификата.

Получаем публичный SSL серт и закидываем на роутер по SSH, например так:

☝️Команда выполняется локальной машине (не на роутере), там где есть утилита openssl.

openssl s_client -showcerts -connect vpn.r4ven.me:4443 < /dev/null 2> /dev/null | \
    ssh root@openwrt.lan 'cat > /etc/openconnect/ca-vpn-oc0.pem'

Где oc0 имя интерфейса OpenConnect в OpenWrt.

Ну или вы можете руками скопировать файл сертификата с OpenConnect сервера (для совсем параноиков😉).

После всех изменений возвращаемся в консоль роутера и выполняем:

uci commit network

service network restart

Настройка зоны firewall

Чтобы трафик из локальной сети роутера мог ходить через интерфейс OpenConnect необходимо настроить фаервол (в последних версиях OpenWrt используется nftables).

Создаём новую зону, ассоциированную с интерфейсом OpenConnect и с использованием маскарадинга:

uci set firewall.zone_${OC_ID}="zone"
uci set firewall.zone_${OC_ID}.name="zone_${OC_ID}"
uci set firewall.zone_${OC_ID}.device="vpn-${OC_ID}"
uci set firewall.zone_${OC_ID}.input="REJECT"
uci set firewall.zone_${OC_ID}.output="ACCEPT"
uci set firewall.zone_${OC_ID}.forward="REJECT"
uci set firewall.zone_${OC_ID}.masq="1"
uci set firewall.zone_${OC_ID}.mtu_fix="1"
uci set firewall.zone_${OC_ID}.family="ipv4"

Теперь создаём правило переадресации трафика из зоны lan в зону OpenConnect:

uci set firewall.lan_to_${OC_ID}="forwarding"
uci set firewall.lan_to_${OC_ID}.src="lan"
uci set firewall.lan_to_${OC_ID}.dest="zone_${OC_ID}"
uci set firewall.lan_to_${OC_ID}.family="ipv4"

Применяем изменения:

uci commit firewall
service firewall restart

Поделючение настроено.

Проверка подключения

После настройки необходимо убедиться, что подключение появилось и работает. Выполняем следующие команды в консоли роутера:

# статус туннельного интерфейса
ip address show dev vpn-${OC_ID}

# пинг через туннель
ping -c3 -I vpn-${OC_ID} r4ven.me

# внешний IP (должен быть разный)
curl eth0.me
curl --interface vpn-${OC_ID} eth0.me

# подробный статус подключения
ifstatus $VPN_ID

💡Сайт eth0.me возвращает ваш внешний IP в ответ на обычный HTTP запрос.

Обратите внимание:

  • в ip address должен появиться интерфейс vpn-oc0 (в OpenWrt наименования туннельных устройств начинаются с префикса vpn-);
  • внешний IP через обычный интерфейс и через OC-интерфейс (--interface vpn-oc0) должны отличаться, если всё работает корректно;

В случае проблем смотрим логи:

logread -e openconnect

# или

logread -f

Настройка маршрутизации трафика через OpenConnect

Маршрутизация всего трафика

Если ваша цель отправлять весь трафик роутера через OpenConnect, то необходимо настроить 2 маршрута: 1й маршрут до OpenConnect сервера через WAN, 2й маршрут по умолчанию для остального трафика.

Перед настройкой необходимо определить IP адрес вашего OpenConnect сервера и IP адрес шлюза вашего провайдера:

OC_ID="oc0"
OC_IP=$(nslookup vpn.r4ven.me | awk '/^Address: / {print $2}')
WAN_GTW=$(ip route show default | awk '{print $3}')

echo -e "OC_ID=$OC_ID\nOC_IP=$OC_IP\nWAN_GTW=$WAN_GTW"

☝️Замените vpn.r4ven.me на адрес вашего сервера OpenConnect.

Теперь настраиваем сохранение маршрута к OC-серверу через WAN:

uci set network.route_${OC_ID}="route"
uci set network.route_${OC_ID}.name="route_${OC_ID}"
uci set network.route_${OC_ID}.interface="wan"
uci set network.route_${OC_ID}.target="$OC_IP"
uci set network.route_${OC_ID}.gateway="$WAN_GTW"

И задаем маршрут по умолчанию в настройках OpenConnect соединения:

uci set network.${OC_ID}.defaultroute="1"

Ранее мы устанавливали нулевое значение для параметра defaultroute. Значение 1 — создаст правило типа default via для всего трафика, кроме маршрута до OC сервера.

В конце настройки применяем изменения:

uci commit network

service network restart

И проверяем внешний IP:

curl eth0.me

curl --interface vpn-${OC_ID} eth0.me

IP адрес в выводе обеих команд должен быть адресом OpenConnect сераера.

Выборочная маршрутизация по списку доменов

Более востребованной на мой взгляд вариант маршрутизации — по заданному списку доменов.

Коротко о том, как это работает:

  • При запросе к домену из заранее заданного списка dnsmasq получает IP-адреса от вышестоящего DNS-сервера, возвращает их клиенту и добавляет в ipset-множество.
  • Пакеты, направленные к IP-адресам из этого ipset , маркируются с помощью правил фаервола.
  • Маркированный трафик перенаправляется в отдельную таблицу маршрутизации, в которой задан маршрут через OpenConnect-туннель. Остальной трафик идёт через основной WAN интерфейс.

Одним из преимуществ данного способа является формирование ipset по wildcard, другими словами, вы добавляете домен example.com в список и при обращении к доменам нижнего уровня, например test.example.com, трафик автоматически пойдет через OpenConnect.

И так, переменные:

OC_IF="oc0"
OC_TABLE="91"
OC_MARK="0x1"
OC_ID="${OC_IF}_${OC_MARK}"

Где:

  • OC_IF — имя соединения OpenConnect;
  • OC_TABLE — произвольный ID таблицы маршрутизации;
  • OC_MARK — Произвольная маркировка в HEX формате;
  • OC_ID — ключевой идентификатор подключения, для уникальности он теперь состоит из названия подключения и метки для трафика.

Добавляем новую таблицу маршрутизации:

grep -q "${OC_TABLE} table_${OC_ID}" /etc/iproute2/rt_tables || \
    echo "${OC_TABLE} table_${OC_ID}" >> /etc/iproute2/rt_tables

Добавляем маршрут по умолчанию (0.0.0.0/0) в указанную таблицу:

uci set network.route_${OC_ID}="route"
uci set network.route_${OC_ID}.interface="${OC_IF}"
uci set network.route_${OC_ID}.table="table_${OC_ID}"
uci set network.route_${OC_ID}.target="0.0.0.0/0"

Говорим ядру: если пакет с меткой ${OC_MARK}, использовать таблицу table_${OC_ID}:

uci set network.rule_${OC_ID}="rule"
uci set network.rule_${OC_ID}.name="rule_${OC_ID}"
uci set network.rule_${OC_ID}.mark="$OC_MARK"
uci set network.rule_${OC_ID}.priority="100"
uci set network.rule_${OC_ID}.lookup="table_${OC_ID}"

Создаём ipset в firewall, куда будут попадать IP, соответствующие нужным доменам:

uci set firewall.ipset_${OC_ID}="ipset"
uci set firewall.ipset_${OC_ID}.name="ipset_${OC_ID}"
uci set firewall.ipset_${OC_ID}.match="dst_net"

Маркируем пакеты, если их адрес есть в ipset:

uci set firewall.mark_${OC_ID}="rule"
uci set firewall.mark_${OC_ID}.name="mark_${OC_ID}"
uci set firewall.mark_${OC_ID}.src="lan"
uci set firewall.mark_${OC_ID}.dest="*"
uci set firewall.mark_${OC_ID}.proto="all"
uci set firewall.mark_${OC_ID}.ipset="ipset_${OC_ID}"
uci set firewall.mark_${OC_ID}.set_mark="$OC_MARK"
uci set firewall.mark_${OC_ID}.target="MARK"
uci set firewall.mark_${OC_ID}.family="ipv4"

Указываем, какие домены нужно резолвить в ipset через dnsmasq:

uci set dhcp.ipset_${OC_ID}="ipset"
uci set dhcp.ipset_${OC_ID}.table_family="inet"
uci add_list dhcp.ipset_${OC_ID}.name="ipset_${OC_ID}"
uci add_list dhcp.ipset_${OC_ID}.domain="eth0.me"
uci add_list dhcp.ipset_${OC_ID}.domain="example.com"
uci add_list dhcp.ipset_${OC_ID}.domain="kernel.org"

Применяем изменения:

uci commit network
uci commit firewall
uci commit dhcp

service network restart
service firewall restart
service dnsmasq restart

На предыдущем шаге мы добавили ресурс eth0.me в список доменов для выборочной маршрутизации. Выполним проверку на клиентском устройстве (подключенном к LAN роутера) с помощью curl:

# должна вернуть IP провайдера
curl ifconfig.me

# должна вернуть IP OpenConnect
curl eth0.me

# маршрут должен идти через туннель
tracepath eth0.me

Если вторая команда возвращает IP, который совпадает с адресом OpenConnect сервера — выборочная маршрутизация работает🥳.

Добавление новых доменов в список

Чтобы добавить новый домен в список обновляем ipset в модуле dhcp и перезапускаем dnsmasq:

uci add_list dhcp.ipset_${OC_ID}.domain="openwrt.org"

uci commit dhcp

service dnsmasq restart

Список Ipset можно удобно изменять и в GUI в разделе Network — DHCP and DNS — IP Sets:

В отличие от консоли, для применения изменений в GUI необходимо перезагрузить роутер🤷‍♂️ (или перезапустить модуль network по SSH).

Проверка на клиентском устройстве:

tracepath openwrt.org

Маршрут должен идти через туннель.

Выборочная маршрутизация по IP подсетям

Если вам необходимо направлять в OpenConnect отдельные IP-подсети, то необходимо:

  • создать новую таблицу маршрутизации;
  • добавить маршрут до нужной подсети;
  • добавить правило маршрутизации;
  • добавить правило фаервола для маркировки трафика.

Для расширения списка подсетей, маршрут до которых должен лежать через туннель OpenConnect достаточно будет создать новый маршрут и добавить подсеть в существующий список для маркировки в правиле фаервола.

Приступаем к настройке, начиная с переменных:

OC_IF="oc1"
OC_MARK="0x2"
OC_TABLE="92"
OC_NET="172.30.30.0/24"
OC_ID="${OC_IF}_${OC_MARK}"
ROUTE_ID="172_30_30_0"

⚠️Обратите внимание, что идентификаторы маркировки и таблицы маршрутизации другие.

Добавляем новую таблицу:

grep -q "${OC_TABLE} table_${OC_ID}" /etc/iproute2/rt_tables || \
    echo "${OC_TABLE} table_${OC_ID}" >> /etc/iproute2/rt_tables

Указываем маршрут до нужной подсети:

uci set network.route_${OC_ID}_${ROUTE_ID}="route"
uci set network.route_${OC_ID}_${ROUTE_ID}.name="route_${OC_ID}_${ROUTE_ID}"
uci set network.route_${OC_ID}_${ROUTE_ID}.interface="$OC_IF"
uci set network.route_${OC_ID}_${ROUTE_ID}.table="table_${OC_ID}"
uci set network.route_${OC_ID}_${ROUTE_ID}.target="$OC_NET"

Маркируем пакеты с помощью и направляем их через таблицу table_${OC_ID}:

uci set network.rule_${OC_ID}="rule"
uci set network.rule_${OC_ID}.mark="$OC_MARK"
uci set network.rule_${OC_ID}.priority="100"
uci set network.rule_${OC_ID}.lookup="table_${OC_ID}"

Настраиваем правило, которое будет отлавливать IP-пакеты к нужной подсети и макировать их:

uci set firewall.mark_${OC_ID}="rule"
uci set firewall.mark_${OC_ID}.name="mark_${OC_ID}"
uci set firewall.mark_${OC_ID}.src="lan"
uci set firewall.mark_${OC_ID}.dest="*"
uci set firewall.mark_${OC_ID}.dest_ip="$OC_NET"
uci set firewall.mark_${OC_ID}.proto="all"
uci set firewall.mark_${OC_ID}.set_mark="$OC_MARK"
uci set firewall.mark_${OC_ID}.target="MARK"
uci set firewall.mark_${OC_ID}.family="ipv4"

Применяем изменения:

uci commit network
uci commit firewall

service network restart
service firewall restart

Проверка на клиентском устройстве:

tracepath 172.30.30.1

Добавление новых подсетей в список

Как и сказал ранее, нужно только добавить маршрут и расширить dest_ip в firewall.mark_${OC_ID}:

OC_IF="oc1"
OC_MARK="0x2"
OC_NET="10.11.11.0/24"
OC_ID="${OC_IF}_${OC_MARK}"
ROUTE_ID="10_11_11_0"

uci set network.route_${OC_ID}_${ROUTE_ID}="route"
uci set network.route_${OC_ID}_${ROUTE_ID}.name="route_${OC_ID}_${ROUTE_ID}"
uci set network.route_${OC_ID}_${ROUTE_ID}.interface="$OC_IF"
uci set network.route_${OC_ID}_${ROUTE_ID}.table="table_${OC_ID}"
uci set network.route_${OC_ID}_${ROUTE_ID}.target="$OC_NET"

uci add_list firewall.mark_${OC_ID}.dest_ip="$OC_NET"

uci commit network
uci commit firewall

service network restart
service firewall restart

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

☝️Раздел будет дополнятся при обнаружении новых кейсов.

Изменение MTU

По умолчанию для туннеля устанавливается MTU 1500, что может аффектить на работу сети. Изменить этот параметр можно так:

OC_ID="oc0"

uci set network."${OC_ID}".mtu="1434"

uci commit network

service network restart

Заключение

Тэк.. вот мы настроили OpenWrt так, чтобы он мог направлять трафик в туннель OpenConnect максимально гибко: весь целиком, только по нужным доменам или отдельным IP-подсетям.

В своих задачах я использую вариант с выборочной маршрутизацией по подсетям для доступа к защищенной инфраструктуре напрямую из локальной сети роутера. Не нужно каждый раз подключать клиентов на каждом устройстве по отдельности.

Надеюсь, статья оказалась вам полезной😌. Спасибо, что читаете!

Не забывайте про нашу телегу📱и чат💬
Всех благ✌️

That should be it. If not, check the logs 🙂

Используемые материалы

Подписаться
Уведомить о
0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии