Задача:

  • Linux-машина (назовем ее router) подключается к strongswan-серверу (IPSec IKEv2)
  • Некоторые другие машины прописывают ip router как default route и работают через VPN (прозрачно для них)

Как сервер настроить – ничего особенного, например по этой статье: . Как соединить 2 сети тоже понятно: https://selectel.ru/blog/tutorials/how-to-set-up-vpn-ipsec/ (но нам не нужно, чтобы с vpn-сервера была видна вся внутренняя сеть, нам бы nat и правила доступа).

Если использовать обычный клиент (требуется доступ с локального компьютера, а не предоставляется доступ с локальной сети), то можно включить https://docs.strongswan.org/docs/5.9/plugins/bypass-lan.html плагин, чтобы при включенном VPN эта машинка продолжала видеть локальную сеть.

Есть еще один вариант – на router запустить proxy-сервер (squid) и через это раздавать VPN-соединение, но не хочется нагружать слабую машинку.

В заметке описывается как настроить XFRM-клиент (Route-based VPN) и все что нужно вокруг. Изначально планировалось VTI-клиент, но там больше магии, чем в XFRM (вроде как XFRM предпочтительнее).

Настраиваем для SUSE MicroOS. Для других дистрибьютивов в деталях может быть по другому. Например, здесь включен ip forward по умолчанию, не нужно менять эту настройку.

Отключаем SELinux для начала (в принципе, можно еще помучаться и включить, но не сейчас):

cat >/etc/selinux/config <<EOF
SELINUX=permissive
SELINUXTYPE=targeted
EOF

# да, оно просто так не отключается, нужно еще grub править
vim /etc/default/grub
# изменить: enforcing=1 -> enforcing=0
transactional-update grub.cfg
reboot

Ставим пакеты:

transactional-update pkg in strongswan nftables
reboot

Кладем сертификат с сервера в /etc/ipsec.d/cacerts/ca-cert.pem.

/etc/ipsec.conf устарел и не содержит новые параметры (if_id_in). Поэтому используем swan-файлы настройки. 192.168.3.0/24 – внутренняя сеть в примерах. 10.10.10.0/24 – сеть для vpn, настроенная на vpn-сервере (rightsourceip=10.10.10.0/24).

Настраиваем vpn:

vim /etc/strongswan.conf
# add 2 params inside charon to disable automatic routes (our script will do job):
# install_routes = no
# install_virtual_ip = no
# (может быть можно через какие-то параметры настроить, чтобы оно само правильно формировалось, но мне удалось это решить ручными скриптами)

cat >/etc/swanctl/conf.d/ikev2-do.conf <<EOF
connections {
    ikev2-myconn {
        include /etc/swanctl/conf.d/ike_sa_default.conf
        version = 2
        if_id_in = %unique
        if_id_out = %unique
        # it can be any IP, sever can provide another but it's required to have some IP here
        vips = 10.10.10.4
        remote_addrs = myremote.example.com
        children {
            ikev2-do {
                include /etc/swanctl/conf.d/child_sa_default.conf
                start_action = start
                local_ts = dynamic
                remote_ts = 0.0.0.0/0
                updown = /usr/local/bin/rw-updown.sh
            }
        }
        local-0 {
            auth = eap-mschapv2
            id = mi-vpn-client
        }
        remote-0 {
            auth = pubkey
            id = @myremote.example.com
        }
    }
}
secrets {
    eap-mi-vpn-client {
        secret = "myPassword!"
        id-0 = myUser1
    }
}
EOF

cat >/usr/local/bin/rw-updown.sh <<EOF
#!/bin/bash
set -eEuo pipefail

XFRM_IF="xfrm${PLUTO_UNIQUEID}"

case "${PLUTO_VERB}" in
    up-client)
        echo "vpn rw-updown" ${PLUTO_VERB} ${PLUTO_UNIQUEID} ${XFRM_IF} ${PLUTO_ME} ${PLUTO_PEER} ${PLUTO_IF_ID_IN} ${PLUTO_IF_ID_OUT}
        ip link add ${XFRM_IF} type xfrm dev lo if_id ${PLUTO_IF_ID_IN}
        ip link set dev ${XFRM_IF} mtu 1418
        ip addr add ${PLUTO_MY_CLIENT} dev ${XFRM_IF}
        ip link set ${XFRM_IF} up
        ip route add default dev ${XFRM_IF}
        nft add rule nat postrouting ip saddr 192.168.3.0/24 oifname "${XFRM_IF}" masquerade
        ;;
    down-client)
	echo down
	    # NOTE: folling command is planned but not supported yet by nft
	    # nft delete rule nat postrouting ip saddr 192.168.3.0/24 oifname "xfrm1" masquerade
	    nft flush chain nat postrouting
	    	    
	    ip route delete default
        ip link del ${XFRM_IF}
        ;;
esac
EOF
chmod +x /usr/local/bin/rw-updown.sh

И роутинг (отключаем дефолтный роут – он будет от vpn и настраиваем прямой доступ к vpn-серверу):

nmcli connection modify ens18 ipv4.never-default true
nmcli connection modify ens18 +ipv4.routes "<ip address of myremote.example.com> 192.168.3.1"

Немного firewall (для vpn только nat (пустая цепочка), остальное можно не настраивать):

cat >/etc/nftables.conf <<EOF
#!/usr/sbin/nft -f

flush ruleset

# ipv4 + ipv6
table inet filter {
    set wanted_tcp_ports {
        type inet_service
        flags interval
        elements = { 22 }
    }

    chain base_checks {
        ## another set, this time for connection tracking states.
        # allow established/related connections
        ct state {established, related} accept;

        # early drop of invalid connections
        ct state invalid drop;
    }

    chain input {
        type filter hook input priority 0; policy drop;

        # allow from loopback
        iif "lo" accept;

        jump base_checks;

        # allow icmp and igmp
        ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, packet-too-big, time-exceeded, parameter-problem, destination-unreachable, packet-too-big, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept;
        ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept;
        ip protocol igmp accept;
        tcp dport @wanted_tcp_ports accept

        # for testing reject with logging
        counter log prefix "[nftables] input reject " reject;
    }
    chain forward {
        type filter hook forward priority 0; policy accept;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}
table ip nat {
	chain postrouting {
		type nat hook postrouting priority srcnat; policy accept;
	}
}
EOF
chmod +x /etc/nftables.conf

Стартуем (да, встроенного стартера nftables нет):

cat >/etc/systemd/system/nftables.service <<EOF
[Unit]
Description=Initialises nftab rules

[Service]
Type=oneshot
ExecStart=/etc/nftables.conf

[Install]
WantedBy=default.target
EOF

systemctl daemon-reload
systemctl enable --now nftables.service
systemctl enable --now strongswan
reboot

И разные команды, чтобы проверить как все настроилось:

swanctl -l
swanctl -L
ip a l
ip -s link show xfrm1
ip -d link show xfrm1
ip rule ls
ip r l table 0
ip route show table lan
ip route show table vpn
nft list ruleset
nmcli connection show ens18
curl https://myip.ru/index_small.php

Как-то слишком много настроек и знаний для этих настроек нужно, но проще не получилось.