それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

openWrt でipsecしてトンネルを経由する。(nft/nftablesに注意する。)

openWrt は nft / nftables になっているので、注意する。

最初にパケットを定期的に送る。(watch を使うのがコツ / ping コマンドで送ると継続パケットになり、パケットがマークされたままになり、経路が変わっても以前の経路を通る)

ssh s0  -- watch -n 2 'ping -c 1  1.0.0.1'

LAN(br-lan)からのForwardをAcceptするには

## src net を書いたり
iptables -I FORWARD -o "${VTI_IF}" -s 172.16.9.0/24 -j ACCEPT
## または
iptables -I FORWARD -o "${VTI_IF}" -i br-lan -j ACCEPT

iptablesでルールを書いて先頭に入れてAcceptしたら良い。ただし。OpenWrtはNFT/nfstable であるので、これを書き換える必要がある。

たとえば次のようになる。

nft insert rule inet fw4 forward iifname $IIF oifname $VTI_IF accept;

ただ、上記の場合だと不要になったあとに削除が面倒なので、ユニークなコメントを入れておく。

nft insert rule inet fw4 forward iifname $IIF oifname $VTI_IF accept comment "\"${UNIQUE_COMMENT}\"";

ただ、これだとルーティングで広めにとると全部転送されてしまうので、もう少し限定する。

nft insert rule inet fw4 forward iifname $IIF ip daddr 1.1.1.1 accept comment "\"${UNIQUE_COMMENT}\"";

これで、削除するときに、コメントをマーク代わり使って削除できる。

HANDLES=$(nft -a list table inet fw4 | grep "${UNIQUE_COMMENT}" | grep -oP '(?<=handle )+\d+')
for h in $HANDLES; do
  nft delete rule inet fw4 forward handle $h
done

マスカレードを入れる。そのままでは応答パケット先が不明になるため、OpenWrtでマスカレードして解決する

nft add table ${TABLE}
nft add chain ${TABLE} prerouting  '{ type nat hook prerouting priority 0; }'
nft add chain ${TABLE} postrouting '{ type nat hook postrouting priority 0; }'
nft add rule  ${TABLE} postrouting oifname "${VTI_IF}" masquerade

これで、通信できた。

あとは、これらを設定にまとめてup-down スクリプトにまとめてvti をつかうようにする。(後述)

最初にパッケージのインストール

opkg install \
strongswan-default  \
strongswan-swanctl  \
strongswan-mod-eap-mschapv2  \
strongswan-mod-eap-identity  \
strongswan-default  \
strongswan-ipsec  \

ipsec の設定を書く

/etc/ipsec.conf

include /etc/config/custom/strongswan/ipsec.conf

/etc/config/custom/strongswan/ipsec.conf

include ./*/ipsec.conf

/etc/config/custom/strongswan/ipsec.secrets

include ./*/ipsec.secrets

/etc/strongswan.conf

## (中略)
## 末尾に追加する。最後の記述で上書きされる。
include /etc/config/custom/strongswan/strongswan.d/vti.conf

/etc/config/custom/strongswan/strongswan.d/vti.conf

# vim: set ft=css et :
## 上書きする設定。
charon { plugins { resolve {
    file = /etc/resolve.ipsec.conf
    load = yes
  }
}}
charon {
   install_routes = no
   install_virtual_ip = no
}

接続設定を書く

/etc/config/custom/strongswan/vpnx.example.com/ipsec.conf

conn myConn

  keyexchange=ikev2

  right=vpnx.example.com
  rightid=vpnx.example.com
  rightauth=pubkey
  rightsubnet=0.0.0.0/0
  type=tunnel

  leftauth=eap
  leftsourceip=%config4
  leftid=takuya001
  eap_identity=takuya001

  auto=add

  leftupdown=/etc/config/custom/strongswan/vpnx.example.com/link-updown.sh
  mark=218

/etc/config/custom/strongswan/vpnx.example.com/ipsec.secrets

# 認証(パスワード)を書く 
# for ikev2 or ms-chap-v2
takuya001       vpnx.example.com : EAP "xxxxxxxxxxxx"

up-down スクリプトを作る

mkdir -p /etc/config/custom/strongswan/vpnx.example.com/
touch /etc/config/custom/strongswan/vpnx.example.com/link-updown.sh
chmod +x /etc/config/custom/strongswan/vpnx.example.com/link-updown.sh
vi /etc/config/custom/strongswan/vpnx.example.com/link-updown.sh

中身

#!/usr/bin/env bash



set -o nounset
#set -o errexit


UNIQUE_NAME=$( basename $( dirname $0 ) )



VTI_IF="vti${PLUTO_UNIQUEID}"
TABLE="inet ${UNIQUE_NAME}"
UNIQUE_COMMENT="takuya:${UNIQUE_NAME}_accept"
SSH_SERVER=xxx.xxx.xxx.xxx
SAMPLE_IP=1.1.1.1

## vpn 先にネットワークがある場合
VPN_GW=${PLUTO_MY_SOURCEIP%.*}.1
VPN_NET=${PLUTO_MY_SOURCEIP%.*}.0/24


add_nft(){
  IIF=br-lan
  ## clear nft
  remove_nft
  ## MASQUERADE
  nft add table ${TABLE}
  nft add chain ${TABLE} prerouting  '{ type nat hook prerouting priority 0; }'
  nft add chain ${TABLE} postrouting '{ type nat hook postrouting priority 0; }'
  nft add rule  ${TABLE} postrouting oifname "${VTI_IF}" masquerade
  ## accept
  nft insert rule inet fw4 forward iifname $IIF ip daddr { ${SSH_SERVER} } counter accept comment "\"${UNIQUE_COMMENT}\""
  nft insert rule inet fw4 forward iifname $IIF ip daddr { ${SAMPLE_IP} } counter accept comment "\"${UNIQUE_COMMENT}\""
}
remove_nft(){
  ## nft
  nft delete table ${TABLE}  2>/dev/null
  HANDLES=$(nft -a list table inet fw4 | grep "${UNIQUE_COMMENT}" | grep -oP '(?<=handle )+\d+')
  for h in $HANDLES; do
    nft delete rule inet fw4 forward handle $h
  done

}
vpn_routing(){
  ## add vpn gw
  if [[ ! -z $VPN_NET ]]; then
    ip route add $VPN_NET dev "${VTI_IF}"
  fi

  ## static routing
  ip route add ${SAMPLE_IP} dev "${VTI_IF}"
  ip route add ${SSH_SERVER} dev "${VTI_IF}"
}

function show_env(){

  env | grep PLUTO
  echo SHELL=$SHELL
}
function link_up_down(){
  case "${PLUTO_VERB}" in
    up-client)
      ## vti tunnel device
      ip tunnel add "${VTI_IF}" mode vti \
        local "${PLUTO_ME}" remote "${PLUTO_PEER}" \
        okey "${PLUTO_MARK_OUT%%/*}" ikey "${PLUTO_MARK_IN%%/*}"
      ip link set "${VTI_IF}" up
      ip addr add ${PLUTO_MY_SOURCEIP} dev "${VTI_IF}"
      ## routing / ルーティングはVIF削除で一緒に消える
      vpn_routing
      # firewall
      add_nft
      ;;
    down-client)
      remove_nft
      ip tunnel del "${VTI_IF}"
      ;;
  esac
}


function main(){
  show_env
  link_up_down $@
}




if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi

起動と終了

ipsec restart
ipsec up myconn
ipsec status
ipsec down myconn
ipsec status

接続の確認

ping 1.1.1.1
tcpdupm -i vtiX host 1.1.1.1
tcpdupm -i pppoe host vpn.example.com

vtiXのパケットは中身が見えていて、pppoe 経由だとUDP-encap: ESP になっていれば暗号化するトンネルと経由している。