勝手に切れるVyOSのIPsec接続を何とかしたお話

VyOS の事をネタにしてもあまりヒット数も伸びないのですが、
こういうニッチ系はいつか誰かの役に立つと思うので、
定期的に更新しています。

下のシリーズで紹介した設定を実運用にて導入しています。
VyOSをNGN網に接続してIXルーターとVPN繋いでみたお話(その壱)
VyOSをNGN網に接続してIXルーターとVPN繋いでみたお話(その弍)
VyOSをNGN網に接続してIXルーターとVPN繋いでみたお話(その参)

しかし早速課題が
前回も少し触れましたけど、どうやら
通信が全く発生しない時間が続くと IPsec が勝手に切れてしまいついでに
vti インターフェースも down してしまうようです。
セキュアな状態を保つという点では納得は出来ますし、
手動で reset すれば復旧するのですが、正直言ってまぁまぁ面倒。
そんな時、自分の記事を読み返すと…

「 VPN = 正常、且つ vti = A/D の時にリンクアップコマンドを叩く」
的なスクリプトを組んで、cron 的に実行させるとかそういう対応が
ちょっと面倒ですが必要になります。

VyOSをNGN網に接続してIXルーターとVPN繋いでみたお話(その参)より

だそうな。
自分でも無意識に書いていたので完全に忘れてましたが(おいっ!)
上記のようなスクリプトの実装が今回のネタになります。
ちょっと条件等は変わりましたが最後までお付き合いください。


VPN が成立していると判断する条件とは

筆者がまず考えたのは以下の通りとなります。

  1.  vti インターフェースが up していること

  2.  [ show vpn ipsec sa ] コマンドで [ State ] が up であること

  3.  対向の vti インターフェースに疎通すること

つまりこの3つの条件のいずれかが当てはまらない時に
IPsec は down していると筆者は判断します。
ただ今回は、複雑に考えることなく
「 ping 飛ばして OK だったら何もせず、
 ping が NG だったら reset コマンドとリンクアップコマンドを
 叩きましょうね」

程度の粒度に留めることにします。
スクリプト書く際に複数分岐という時点で面倒ですし、
ping 飛ばして疎通 NG だったら他の条件に合致しようが何しようが、
結局なんだかんだ IPsec を reset もリンクアップコマンドも
実施する形にはなりますし

こんな感じのゆるふわな条件です。
ちなみにですが、たとえ State が[ u/u ] の状態のインターフェースに
リンクアップコマンドを実施してもカーネル誤作動とか
そういった類の問題は発生しなかったハズ。
昔、ふと気になって source を調べたことがあるのですが当然ながら
結果しか覚えてないので(おいっ!)
そう言うもんだと思っておいて頂ければ。

bash ? vbash ??

VyOS でスクリプトを仕込む際、まず考慮しなければならないのは
bash と vbash 、どちらを使用するか」問題です。
 ※ csh や zsh 等は実装されていなかったはずなので割愛します。
正直似た様なモノなのでどちらでも良いし各々の好みで!
と言ってしまえばそれまでなのですが、今回は普通に bash で作りました。
どうしても VyOS のコマンドを多数実装しなくてはいけない、
その方が効率が良い場合であれば vbash で実装したと思うのですが、
今回は別にそんなでもないですし。

早速作ってみよう。

まずは第一の分岐条件、
対向の vti インターフェースに疎通すること
を確認するスクリプトを仕込みます。
(※: ping の宛先は、仮の値を採用しています)

#!/bin/bash

PING=`/bin/vbash -ic "ping 1.1.1.2 count 1 | grep transmitted | awk '{ print $4}'"`

と | (パイプ)使ってワンライナーでごちゃごちゃ書いていますが、
シンプルに言うと…
ping 1.1.1.2 count 1
→「pingを対向に一発打って…」

(※:以下、実際に素でコマンド実行したログ)

vyos@vyos:~$ ping 1.1.1.2 count 1 PING 1.1.1.2 (1.1.1.2) 56(84) bytes of data. 
64 bytes from 1.1.1.2: icmp_seq=1 ttl=64 time=4.69 ms

--- 1.1.1.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 4.685/4.685/4.685/0.000 ms
vyos@vyos:~$

grep transmitted
→「その実行結果から" transmitted "という文言が存在する行を抽出して…」

(※:以下、実際に…)

vyos@vyos:~$ ping 1.1.1.2 count 1 | grep transmitted
1 packets transmitted, 1 received, 0% packet loss, time 0ms
vyos@vyos:~$

awk '{ print $4}'
→「その行の 4 フィールド目を切り出す。」

(※:以下…)

vyos@vyos:~$ ping 1.1.1.2 count 1 | grep transmitted | awk '{ print $4}'
1
vyos@vyos:~$

でその値を変数 [ PING ] に格納する、ただそれだけの事だったりします。

ping を1発しか実行していないのでここの値は [ 0 ] もしくは [ 1 ] にしか
ならない想定です。
[ PING ] という変数に [ 0 ] が格納されたら疎通 NG となるので、
その場合に reset する様な if 文をこしらえます。
ついでに vti インターフェースもリンクアップさせましょう。

if [ $PING = 0 ]; then
        /bin/vbash -ic "reset vpn ipsec site-to-site peer IPsecPEER"
        sudo ip link set vti0 up
fi

うーん、我ながらシンプル。
chmod で実行権限も付けて早速ですがテストしてみましょう。
だけれども…

vyos@vyos:/config/scripts$ sudo bash checkvpn.sh
checkvpn.sh: line 6: [: too many arguments
vyos@vyos:/config/scripts$

こけてもた。

[ too many arguments ] 、引数が多すぎるって怒られてしまいました。
原因がよく分からないので [ -x ] オプションを付与して
デバッグモードで実行してみましょう。

vyos@vyos:/config/scripts$ sudo bash -x checkvpn.sh
++ /bin/vbash -ic 'ping 1.1.1.2 count 1 | grep transmitted | awk '''{ print }''''
+ PING='1 packets transmitted, 1 received, 0% packet loss, time 0ms'
+ '[' 1 packets transmitted, 1 received, 0% packet loss, time 0ms = 0 ']'
checkvpn.sh: line 6: [: too many arguments
vyos@vyos:/config/scripts$

あれ?  awk が実行されてない!?

…なんでや???…

あ!
ここのコマンドは /bin/vbash で実行させているから
[ $4 ] のところを読み込んでくれないのか…
というわけで ping コマンド結果を変数に格納する箇所を
VyOS コマンドから Linux コマンドに変更します。

-PING=`/bin/vbash -ic "ping 1.1.1.2 count 1 | grep transmitted | awk '{ print $4}'"`
+PING=`sudo ping -c 1 1.1.1.2 | grep transmitted | awk '{ print $4}'`

あんまり変わっていない様に見えますが、
変更後改めてデバッグモードで実施

vyos@vyos:/config/scripts$ bash -x checkvpn.sh
++ awk '{ print $4}'
++ grep transmitted
++ sudo ping -c 1 1.1.1.2
+ PING=1
+ '[' 1 = 0 ']'
vyos@vyos:/config/scripts$

うん、正常時の動作は問題ないですね。
VPN が切れている時もやってみましょう。

vyos@vyos:/config/scripts$ bash -x checkvpn.sh
++ awk '{ print $4}'
++ grep transmitted
++ sudo ping -c 1 1.1.1.2
+ PING=0
+ '[' 0 = 0 ']'
+ /bin/vbash -ic 'reset vpn ipsec site-to-site peer IPsecPEER'
Peer IPsecPEER reset result: success
+ sudo ip link set vti0 up
vyos@vyos:/config/scripts$

この後、実際に ping を飛ばしてみましたが疎通したので
こちらも問題なさそうです。

忘れてはならぬ正常時処理。

冒頭から引用すると、「 ping 飛ばして OK だったら何もせず」
の部分になります。
そもそも最初の ping で疎通 OK なら VPN の reset も vti のリンクアップも
当の然ですが全く以てやらなくて良い処理になります。
というわけで、ping が通った場合は何もせず処理終了、
という処理を追加します。

if [ $PING = 0 ]; then
        /bin/vbash -ic "reset vpn ipsec site-to-site peer IPsecPEER"
        sudo ip link set vti0 up
else
        exit 0
fi

実際にはログ出力させる処理を足してはいますが、要はそのまま exit 0
正常終了させてその後の処理をスキップさせています。
何も問題なかったら何もしない。
スクリプトを書いていると意外と忘れがちなので気を付けましょう。
ちなみに忘れていた状態でスクリプトを走らせた場合、
ping 疎通の結果に関わらず無駄にプロセス再起動が発生します。
わざわざ if 文書いた意味そのものがなくなります。

というわけで最終形態

/config/scripts/checkvpn.sh
#!/bin/bash

PING=`sudo ping -c 1 1.1.1.2 | grep transmitted | awk '{ print $4}'`
if [ $PING = 0 ]; then
        /bin/vbash -ic "reset vpn ipsec site-to-site peer IPsecPEER"
        sudo ip link set vti0 up
else
        exit 0
fi

exit 0

IPsec の対向が複数ある中で Down している対向のみに実行させたい、
その際に vti インターフェースのリンクアップも連動させたい、
とかなると幾分か複雑なスクリプトになるでしょうが、
今回はそのキモとなる処理のみ紹介させて頂きました。

ログを吐き出させたい場合は、
echo して tee して出力させても良いかと思います。
前述の通り実環境では実装してますけど長くなるので割愛させて頂きます。

定期実行。


VyOSにはtask scheduller機能が実装されています。
何度も言うように元はと言えば Debian なので
crontab に書き込んでも良い気もしますが、
せっかく楽に実装できて、
せっかくshow configuration でサクッと確認できる様にできるのであれば
使わない手はないです。
というわけで早速設定を紹介します。

set system task-scheduler task checkvpn executable path '/config/scripts/checkvpn.sh'
set system task-scheduler task checkvpn interval '10m'

5カラム目 ( checkvpn ) はタスク名ですので任意の名前をご指定下さい。
[ executable path ] の後に指定する値が実行されるスクリプトです。
[ interval ] の後に指定する値にて実行間隔を指定する形になります。
実行間隔については他にも
set system task-scheduler task checkvpn crontab-spec '*/10 * * * *'
と言う様に、通常の crontab の形式で指定することもできます。
この辺は以前から大きくは変わっていない(ハズ)ですし、
紹介されている文献はそこそこあるので詳細を知りたい方は
他の参考文献を頼りにして下さい。

まとめ

他の大半の NW 系 OS と比較し VyOS の強みとしてよく挙げられるのが、
大抵の Linux 系 OS のコマンドが使える事ですし、
今回はその強みを充分活かせる試みだったかと思います。
ちょっとしたトラップがあるものの、
がっつり Linux 系の理解があるわけでもない筆者程度の知見であっても
出来ることは色々広がりそうですね。
あと最後の定期実行の箇所を読んで頂ければお解り頂けるかと思いますが、
作ったスクリプトを 10 分おきに実施しています。
すると「通信が全く発生しない時間が続くと IPsec が勝手に切れてしまう
という状況にならないので割と安定するようになりました。
ということは、
敢えてプロセス再起動とか仕込まなくても
定期的にping飛ばすだけで良かったのでは!?

とか
ダイナミックルーティング等を喋らせていれば、
定期的に Hello パケットなり Keepalive パケットなりを投げてくれるので
わざわざこんな事しなくても良いのでは、
と思わなくもなかったり…
ま、ネタになったし勉強になったから良しとしましょう…

最後まで読んでくれてありがとうございます。
よかったら「スキ❤️」も押してくれると嬉しいです。

この記事が気に入ったらサポートをしてみませんか?