それマグで!

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

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

fortigate ssl-vpn を linux/macOS Xから接続する

ssl vpn の機器が百花繚乱

ssl-vpn が手軽らしく、アプライアンスでベンダから提供される機器によくついていて、VPNSSL_VPNみたいになってる。機器に付属しているので多くのネットワークで使う場面が多いですよね。

個人的にはあまり好きじゃないんですね。機器ごとに設定に癖がある。というか仕様がオープンじゃないので使いにくい。エンドポイントが違ったり、ユーザー名をXHRで投げたり、GETで投げたり・・・

つまり、互換性や接続方式に違いが合って共通クライアントがなかなか無いんですよね。

今回は fortigate です。

fortigateのsslvpn は iOS / Mac OS / Windows などが提供されてるのですが。クライアントソフトにアンチウイルスソフトなど不要なものが付属してきて、どうも面倒くさい。

vpn だけ張れたらいいという目的を達するようなものはないのかと、調べたら見つけた。

openfotigateです。

github.com

openfotigate で Fotigate の ssl-vpn とのcompatibleなクライアントで、コレを使えば、コマンドから接続できて代替クライアントとして利用することが出来ました。

ぱぱっとインストール

手軽にインストールして使いたいと思います。

git clone して

git clone https://github.com/adrienverge/openfortivpn

ビルドします。

cd openfortivpn
aclocal && autoconf && automake --add-missing
./configure 
make

必須ライブラリは openssl でした。

接続します。

sudo ./openfortivpn 10.23.100.1:443   -u takuya

カンタンに接続できました。

証明書エラー

証明書がオレオレ(よくないとおもうけど)のときは、--trusted-cert に、証明書のcert を追加します。

--trusted-cert=91677ab6e45e84 

マニュアルを見る限りでは、--insecure-ssl でもいけそう

trusted-cert に入れるハッシュは、接続エラーメッセージに含まれていました。

MacOS X でも接続したい

macOSのバージョンに依って のopenssl が少し古い場合があるので。homebrewでインストールしたものを使う。

brew install openssl

clone and build

OSX 対応版を作ってfork している人がいるので fork 先から取得する。

git clone https://github.com/fretn/openfortivpn

mac 対応版の fortigate パッチは本家にPRがマージされないので、マージされるまではfork先から取ったほうが良さそう

build する

cd openfortivpn
aclocal && autoconf && automake --add-missing
export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CPPFLAGS="-I/usr/local/opt/openssl/include"
./configure 
make

参照するべきopenssl は homebrew の opt の中にあるはず。今の私のhombew は少し古いのでsierra バージョンになってない。

DNS とルートの設定

Linux で接続した場合は /etc/resolv.conf が書き換わってしまったので、注意が必要。切断後も戻らなかった。

DNSを書き換えないオプションをつけたほうが良さそう

openfortigate --no-dns

ルートの確認

ルーティング・テーブルが書き換わったことを確認

netstat -nr

コマンドは嫌だGUIでやりたい!

そんなときは GNOME のNetwork Manager 専用のプラグインが作られています。

Fortigate SSL VPN support added to NetworkManager – Lubomir Rintel's web log

ソースについてはGNOMEのレポジトリに入ってる。

https://git.gnome.org/browse/network-manager-fortisslvpn

内部的には openfotigate を使ってるらしい。

GNOMEなので、Fedora / CentOSubuntudebian からも使えそうですね。

私はNetworkMangerを使ってないので今回は試さなかった。

参考資料

Mac OS X port by fretn · Pull Request #48 · adrienverge/openfortivpn · GitHub

GitHub - adrienverge/openfortivpn: Client for PPP+SSL VPN tunnel services

GitHub - fretn/openfortivpn: Client for PPP+SSL VPN tunnel services

Fortigate SSL VPN support added to NetworkManager – Lubomir Rintel's web log

bashのパラメータのブレース展開での拡張子・ディレクトリ名・ファイル名の取得・拡張子の置換

bash のbrace 展開で文字列を置換する

文字の置換は、bashはとても楽ちんである。

NAME=www.example.com
echo ${NAME//./_} # www_example.com
echo ${NAME//./_} # www_example_com

これをもう少し掘り下げる。

bash でファイル名から拡張子を取出す方法

ファイル名から拡張子を取出すのはカンタンでした。

path=/etc/apache2/httpd.conf
extension=${path##*.}  #=> conf

魔法の記述方法 ${varname##*.} を使うと 手軽に拡張子を取得できる。

拡張子を書き換える方法

拡張子を書き換えるには、変数の書き換えを使えば直ぐ出来る。

f_name=httpd.conf
txt_name=${f_name/.*/.txt}   #=> httpd.txt

basename / dirname の取得

basename や dirname も文字列マッチで行うことが出来る。

path=/etc/apache2/httpd.conf
basename=${path##*/} #=> httpd.conf
dirname=${path%/*} #=>/etc/apache2

拡張子だけを取り除く

path=/etc/apache2/httpd.conf
basename=${path%.*} # /etc/apache2/httpd

仕組みについて

何でこんな不思議な事が出来るのか ${varname##*.} のような記述方法は何のか。

http://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html によると パラメータの展開 と呼ばれる機能だとわかった。

パラメータ展開について

bash には 変数の展開で、アレコレできるような機能が備わってる

記述方法 意味
${parameter%word} 後方マッチ削除
${parameter%%word} 後方マッチ削除(最長マッチ)
${parameter#word} 前方マッチ削除
${parameter##word} 前方マッチ削除(最長マッチ)
${parameter/search/replacement} マッチ置換

わかりにくいので、正規表現と対表作ってみる(bashはglobマッチなので厳密には一致しないがイメージとして覚えておくために)

記述方法 意味
${parameter%%word} parameter.replace( /(word)(.*)/ , '$1' )
${parameter##word} parameter.replace( /(word)(.*)/ , '$2' )
${parameter/search/replacement} parameter.replace( /word/ , replacement )

マッチの順番と考え方

パターンに依るパラメータ展開に目を通したので、冒頭の記号だらけの置換処理を見直してみる。

拡張子にマッチさせたときの bash の展開の流れ

最初の例の拡張子にマッチさせたときのbash展開の考え方について

path=/etc/apache2/httpd.conf
extension=${path##*.} #=> conf
  1. /etc/apache2/httpd.conf場合
  2. /etc/apache2/httpd.conf は *.とマッチさせる
  3. [ '/etc/apache2/httpd.' , 'conf'] に分割される
  4. マッチした前方は消される
  5. conf が残る

basenameと同等の文字列マッチの場合の考え方

冒頭の basename を擬似的に文字列マッチで行った場合の例は

path=/etc/apache2/httpd.conf
basename=${path##*/} #=> httpd.conf
  1. /etc/apache2/httpd.conf場合
  2. /etc/apache2/httpd.conf*/と先頭から最長マッチさせる
  3. [ '/etc/apache2/' , 'httpd.conf'] に分割される
  4. マッチした前方は消される
  5. httpd.conf が残る

次と同等になる。

'/etc/apache2/httpd.conf'.replace(/(^.*\/)(.*)/,'$2')

dirnameと同等の文字列マッチの場合の考え方

冒頭のdirnameと同等の文字列マッチの考え方。

path=/etc/apache2/httpd.conf

dirname=${path%/*}
echo $dirname #=>/etc/apache2
  1. /etc/apache2/httpd.conf場合
  2. /etc/apache2/httpd.conf/*と末尾からマッチさせる
  3. /httpd.confがマッチする
  4. [ '/etc/apache2' , '/httpd.conf'] に分割される
  5. マッチした後方は消される
  6. /etc/apache2 が残る

次と同等になる。

'/etc/apache2/httpd.conf'.replace(/(^.*\/)(.*)/,'$1')

変数展開で文字列を分割する

これらは、パスやファイル名の扱いというよりは、bash変数の文字列から部分文字列を取出す基本セットです。

man によると他にも

記述方法 意味
${parameter:offset} 部分文字列 ( 開始位置 )
${parameter:offset:length} 部分文字列(開始位置から指定長)
${#parameter} 文字列長取得

他にどんなときに使えば便利?

今回はパスだったけど、起動オプションの引数や、PATH変数の操作などにも使えて便利ですね

sed 使わずに文字列置換できるのは本当に助かる

参考資料

bashスクリプトだけで、ファイル名、拡張子を取得する - 作業ノート

http://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html