書類
openvpnを初めから 〜OTP(google autenticator)認証へ〜 #Ubuntu22.04

openvpnを初めから 〜OTP(google autenticator)認証へ〜 #Ubuntu22.04

2024-11-22 OpenVPNをgoogle authenticator認証で 検証環境の整備の関係でvpnでリモートから入ってこれるようにする必要が出てきました。 「VPN」というキーワードはなかなか刺激的な脆弱性も多いことから、監査の人たちにかなり注意して見られることもあり、認証系を現実的な範囲でガッチガチ

Related articles

【2024年】VPNの人気製品をランキング順に比較!おすすめ製品の特徴・機能を紹介|ITトレンド How to change location on Google Chrome VPN(Virtual Private Network)接続とは?意味や目的や仕組みを監視回線を基に解説 YouTubeの地域ブロックを解除しよう!トップ級のYouTube VPN七選 【Manga Plus by Shueisha】集英社のジャンプ漫画が無料で読める海外向けサービス Tor(トーア)ブラウザの使い方|ダークウェブにアクセスできる安全な匿名ネットワーク 日本からNFL(アメフト)の全試合を見る方法!VPNとカナダ版DAZNで見れる 余ったラズベリーパイを活用してNASとVPNサーバー環境を導入してみました

OpenVPNをgoogle authenticator認証で

検証環境の整備の関係でvpnでリモートから入ってこれるようにする必要が出てきました。
「VPN」というキーワードはなかなか刺激的な脆弱性も多いことから、監査の人たちにかなり注意して見られることもあり、認証系を現実的な範囲でガッチガチに固める、接続元制限などの他の対策や、無理なく続けられる運用ルールなどなど、かなり気を使って準備をしてあげないといけません。

VPN で ググる て 、 取る 合える す 色々 でき そう で 、 かつ 歴史 も あっ て 信頼 でき そう 、 と いう ミーハー な スタンス で openvpn を 使う こと に し まし た 。
ちなみ に 、 国産 品 を と いう 意味 だ と softether を 使う たい ところ です が 、 、 、 今回 の 用途 上 は そもそも l 3 VPN に し たかっ た の と 、 MFA は やる よう は ある けど 手間 が 増える ( 別途 radius を 作る など ) 、 と いう こと で 、 今回 は openvpn で 整理 し て みる と 思う ます 。

で、OpenVPNを使うとして、認証はOTP(google authenticator)までは実装する前提で準備しないといけません。
素直にopenvpn、OTP、MFAなどでググると情報は沢山見つかるのですが、openvpnの設定は一通り理解している人向けの内容だったり、openvpn “AccessServer”(有償)の前提だったり、数年前の情報(対象としているOSがubuntu 18.04など古い、同じ方法を22.04で適用できない(pkgの依存で引っ張ってくるlibsslの名称が微妙に違う))などなど。
単独でゴールまでたどり着ける情報はなく、結構苦労しました。
そこら編のTipsをまとめておこうと思います。

OpenVPN の 種類とバージョン

openvpn関連は情報自体の「量」は充実してるのですが、古い情報もかなり多いです。
そこら編の目利きができるようにまず頭の整理しないといけません。。

OpenVPN の 種類

公式 サイト の 情報 を ざっ と まとめる と 、 、
https://openvpn.net/

  • OpenVPN Cloud: クラウドサービス、有償
  • OpenVPN Access Server: WebUIがついているOpenVPN、有償(同時接続2件までなら無償)
  • OpenVPN Comunity Edition: 無償、Linuxディストリからも配布されているもの。
  • OpenVPN Connect: クライアント 側に導入するソフト。vpnuxなど、これ以外の選択肢もある。

こんな感じです。
webui 操作 に なる AS ( ACCESS Server ) と 、 ガリガリ CUI 操作 に なる Community Edition の 2 種類 ある 、 と いう こと を 念頭 に おい て ググる 、 と いう の が 注意 でしょう か 。
今回 は 価格 面 を 鑑みる て Community Edition で 行く こと に し た の です が 、 設定 方法 調べる て ググ って 、 それ っぽい の が 見つかる た な と 思う たら AS の 手順 でし た 、 と いう こと is あっ が 結構 あっ た の で 。

OpenVPN Community EditionのバージョンとEOL

https://community.openvpn.net/openvpn/wiki/SupportedVersions
https://openvpn.net/community-downloads/

上記だとまだ記載されてないようなのですが、DLサイトを見るに、現時点の最新は2.5.8のようです。
Ubuntu22.04のディストリ配布版だと、2.5.5。

現時点で2.5系のEOLは提示されていませんが、過去のバージョンを見るに数年は使えそうです。
まぁ 、 Ubuntu の ディストリ 配布 版 を つかう ます の で 、 バックポート 含み Ubuntu 頼り で いい です か ね 。

あと、version3というものもあるらしく、数年前から情報が出ているそうですが、、以前開発中らしく。
そのうち3系への切り替えが出てくるかもしれない、、ということで、一応記憶の片隅に置いときます。
https : / / github . com / OpenVPN / openvpn 3

OpenVPNを取り合えす理解する

なん だ かん だ 言う て も 、 公式 サイト の how to ページ に 従う て 、 一通り 流す の が 一番 な 近道 な 気 is し が し ます 。

「ルーティング」と「ブリッジ」

どちらもモードを使うかによって、コンフィグが結構変わります。

ルーティング は 、 左 の よう に 、 リモート アクセス し たい セグメント ( 青 ) 、 サーバ と クライアント 間 の トンネル に 使う セグメント ( 緑 ) の 2 つ が あり 、 openvpn サーバ で 2 つ の セグメント を ルーティング する こと に なり ます 。
下の図に入れてませんが、今回の構成ではこの青相当が複数あるので、OpenVPNサーバでルーティングする構成がわかりやすいので、こっちを採用します。

右 is 構成 は ブリッジ 構成 。
こちら は リモート アクセス し たい セグメント ( 青 ) を クライアント 側 に 張る 出す 感じ に なり ます 。
サーバ側がちょっとややこしく、OpenVPNのトンネルで作るIFとサーバの持っているIFをブリッジで束ねて使う方法になります。
青相当が複数ある場合も、青→別青のルーティングをOpenVPNサーバにやらせればいいっちゃいいのですが、 IPtables制御がややこしくなるので、今回は使うの避けます。

openvpnを初めから 〜OTP(google autenticator)認証へ〜 #Ubuntu22.04

認証方式

以下に、そのものズバリの記載があります。
https://www.openvpn.jp/document/authentication-methods/

引用するとこんな。

  1. 静的 鍵 ( static Key )
  2. 証明 書 認証
  3. ID / パスワード 認証 ( プラグイン 認証 )
  4. 二要素認証(PKCS#12)

最初に引用したHowtoだと2と3の方法が紹介されてます。
1の方法も、Howtoの最初の方にリンクが入っていて、以下で紹介されてます。
https : / / WWW . openvpn . JP / Document / statickey – mini – howto /

ちなみに3ですが、(私の理解が誤ってたら恥ずかしいのですが)Howtoで紹介されているのは2の証明 書 認証をした上で、更にID/パスワード認証をする方法になるようです。
上記のURLを見ると、2と3は別物(3では証明書を使わない)というように読めるのですが、Howtoのサイトを見ると「あえてclient-cert-not-requirdedを設定しない限りは、鍵+ID/パスワード認証」と読める記載になっています。
。。。バージョンアップに伴ってHowtoの仕様になっていったけど、上記のページは更新されてない、、とかでしょうか。
OpenVPN の 情報 発信 is あり は 結構 そう いう の が 多い 感じ が あっ て 、 眉 に ツバ つけ て 見る 必要 is あり が あり ます 。

で、今回のゴールであるOTP認証は上記のどこになるかというと、4ではなく3になります。
ID/パスワード認証はpam連携での実装となるのですが、そのpam側でOTP認証の方法を取る、という動きです。

ということで、2FA、、ではあるんですけど、OpenVPN用の証明 書 認証+パスワード認証+OTP認証という、3段階・2要素認証となるわけですね。
ただし 、 それ だけ やっ てりゃ 認証 強度 は 十分 か と いう と そう で も ない です 。
そもそも、クライアント 側には実質鍵と証明書をまとめたovpnファイル(後述)を配って使わせる運用になりますし、そんなファイルはかんたんにコピーできるわけですから、証明 書 認証の認証強度は実質無いに等しいでしょう。
で、OTPも後述しますが、シードの情報がサーバ側のユーザディレクトリ配下に置きっぱになるので、What you haveと言いつつ複製はかんたんにできる、と。
そもそも 2 FA も 突破 さ れる 事例 増える て ます し 、 「 2 FA に し た から 安心 」 と 言う わけ に も いく ん わけ で 。
まぁ、他の方法との併用は別途ちゃんと考えましょう。

構築

今回は「ルーティング」+「証明 書 認証+ID/パスワード認証+OTP」の構成で作っていってみます。

インストール

サイト で は Debian など の 場合 は APT を 使える 、 と 書く て ある でけ です が 、 以下 で OK 。

$ sudo APT install openvpn 

で、起動(+常駐化)は以下の通り。

$ sudo systemctl start openvpn-server@<設定ファイル名>

と 簡単 に 書く まし た が 、 頭 の 整理 is 必要 が 超 必要 。
使い方 が openvpn サイト の 情報 と 異なる の で 、 要 注意 です 。

systemctl化するにあたって、openvpn-serverのサービス情報は/lib/systemd/system/openvpn-server@.serviceに定義されてます。
systemctlについては既習済みの前提で恐縮ですが、WorkingDirectory=/etc/openvpn/serverも設定されているのでコンフィグファイルは/etc/openvpn/server配下を探しに行く、そのコンフィグファイルの中でファイル名のみ指定で鍵や証明書の名称を書くと、それらも上記のディレクトリ直下を探しに行く、という動きをします。

ざっくりですが、こんな感じのコンフィグ書く場合は、こんな感じの配置になります。

$ cat /etc/openvpn/server/server.conf
...
ca ca.crt
cert server.crt                                                                      key server.key
dh dh.pem                                                                            tls-auth ta.key 0
...
$ tree /etc/openvpn/server
/etc/openvpn/server
├── ca.crt
├── dh.pem
├── server.conf
├── server.crt
├── server.key
└── ta.key

この状態でOpenVPNサーバとして起動する場合は、

$ sudo systemctl start openvpn-server@server.service

と なり ます 。

ググ って 出る て くる 情報 だ と / etc / openvpn 直下 に ファイル を おい たり 、 / etc / openvpn / KEYS の 下 に 鍵 を おい たり する の です が 、 Ubuntu で openvpn 入れる て 、 systemctl で 常駐 化 し て 、 て する と 上記 の フォルダ 配置 に なる の で 注意 です 。

逆に、クライアント 側は、

$ sudo openvpn-client@<設定ファイル名>

として、設定ファイルや証明書等々は/etc/openvpn/client/に配置します。

ただし、これはクライアントもUbuntuだった場合の話。
普通クライアントはWindowsPCやMAC PCになるでしょうから、別途ovpnファイルを作成して、PCに配置したOpenVPN Connectで読み込む方法を取ることになるとは思いますけど。

証明書の作成

先 に ディレクトリ の 配置 を 紹介 し て しまう まし た が 、 改めて 証明 書 配置 。

方針として「証明 書 認証」を使いますので、証明書の発行を行う認証局を作って、それからその認証局でサーバとクライアントの証明書を発行する、ということをしていかないといけません。
。。。やることはそんな難しくないのですが、一気にめんどくさい気がしてしまうのはなんともですね。。

認証局の作成

easy-rsaを使います。
ただし 、 howto サイト に 記載 さ れ て いる 方法 は 古い よう で 、 Ubuntu 22.04 で ディストリ 配布 の easy – RSA を 使う 場合 は うつ コマンド is 変わり が 変わる ます 。
なんともなぁ、、、

$ sudo apt install easy-rsa
$ make-cadir ./myca
$ cd myca
$ ./easyrsa init-pki
$ ./easyrsa build-ca nopass

/usr/share/doc/easy-rsa/README.Debianに使い方が書いてあるので、それに従うことになります。
上記のコマンドで、認証局が使う鍵ファイルと証明書(./myca/pki/private/ca.key, ./mydc/pki/ca.crt)が生成されます。

で、サーバ用の鍵・証明書のペアと、クライアント用の鍵・証明書の生成はこんな。
mycaディレクトリの中で。

$ ./easyrsa build-server-full server
$ ./easyrsa build-client-full client

上記で、CommonNameが”server”になっている証明書・鍵のペアと、”client”になっている証明書・鍵のペアが生成されます。
証明書は./pki/issued、鍵は./pki/private/に生成されます。
Client側のCommonNameは重要で、OpenVNPサーバではクライアントが使ってきたクライアント証明書のCommonNameでクライアントを識別して、付与するIPを変えたり教える経路を変えたりします。

サーバ 用 の 鍵 で ある . / PKI / priveate / Server . Key 、 証明 書 で ある . / PKI / issued / Server . CRT と 、 証明 書 を 署名 し た Ca の 証明 書 で ある . / PKI / Ca . crt is コピー は / etc / openvpn / Server に コピー 。
クライアント用の./pki/private/client.key、./pki/issued/client.crt、あと./pki/ca.crtはクライアトへ。ubuntuをクライアントにしている場合は、クライアントマシンの/etc/openvpn/clientへ。WindowsやMACの場合はovpnファイルに加工して配布(後述)。

通常 、 1 ユーザ / 1 PC ごと に 1 枚 証明 書 を 払う 出す こと に なり ます 。
ここ ら の 処理 は 別途 スクリプト に し て おい た ほう is 良さ が 良い げ です ね 。

ちなみ に 、 Howto で も サーバ 側 で クライアント の 鍵 / 証明 書 を 作る て しまっ て 、 その ペア を クライアント に コピー する 、 と いう 手順 に なっ て ます 。
本来の鍵/証明 書 認証のTOBEを考えると、クライアント 側で鍵を作って、証明書の作成依頼をサーバ側に出して、サーバで署名して証明書を作ってクライアントに返す、となるべきです。
。 。 べき です が 、 それ やる と クライアント の 利用 者 向け の 手順 を あれこれ 作る ない と いける なく なる の で 、 かなり 面倒 さ が ます ん です よ ね 、 、 後日 考える 。

DH . PEN , TA . Key の 作成

証明書を生成したら完了、、というわけでもないです。
サンプルコンフィグ を 使う 場合 、 更に TA . Key と 、 DH . pem を 作る 必要 is あり が あり ます 。

dh.pemはサーバとクライアントの間の暗号通信を行う際のDiffie–Hellman鍵交換のパラメタ設定で、サーバ側だけにおいておけばOK。
dhの鍵交換の詳細は追っかけてませんが、、パラメタはサーバ側に置いとけば、都度クライアント 側にそのパラメタを渡してくれるようです。
https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%83%95%E3%82%A3%E3%83%BC%E3%83%BB%E3%83%98%E3%83%AB%E3%83%9E%E3%83%B3%E9%8D%B5%E5%85%B1%E6%9C%89
ta.keyは、サーバとクライアントで両方が同じものもっとかないといけない鍵で、HMAC認証(メッセージが改ざんされてないかチェック)で使うのだそうです。

生成方法は以下の通り。

$ cd myca # 上の手順のmake-cadirで作ったディレクトリ
$ ./easyrsa --keysize=4096 gen-dh # keysizeは鍵長。指定しないと、2048bitで生成。
$ cp ./pki/dh.pem /etc/openvpn/server

$ cd /etc/openvpn/server
$ openvpn --genkey secret ta.key

TA . Key の 方 is コピり は クライアント に も コピる ます 。
Ubuntu で クライアント も 実装 し て いる 場合 は / etc / openvpn / client に 配置 。

設定

設定ファイルのそれぞれの行の意味の理解については、腹をくくってHowtoを読んでください。
で、ルーティング/証明 書 認証のモードで取り合えす動く設定については、サンプルファイルを使えばOKです。

サーバ側

# cat /usr/share/doc/openvpn/examples/sample-config-files/server.conf | egrep -v '^[#;]' | sed '/^$/d' > /etc/openvpn/server/server.conf

サンプルファイルの中の設定に簡単な説明をつけるとこんな感じ。

port 1194 # 待受ポートの指定。
proto udp # L4プロトコルでudpを使うか、tcpを使うかの指定。
dev tun # ルーティングモード or ブリッジモードの指定、tunはルーティング。
ca ca.crt # 証明 書 認証を使うときの、「認証局の」証明書。
cert server.crt # 証明 書 認証使うときの、サーバの証明書(ca.crtと対になる鍵で署名してもらってるもの)。
key server.key  # 証明 書 認証使うときの、サーバ証明書の鍵。
dh dh.pem # dhパラメタ。サーバ側にだけおけばOK。
server 10.8.0.0 255.255.255.0 # ヘルパー(簡単に書くけど、複数の設定に内部で変換されるやつ)で、サーバモードで起動する、10.8.0.0のレンジでトンネルのIPを付与する、自分は10.8.0.1を取る、クライアントには10.8.0.4〜251のどれかを付与する、等々。詳細はman openvpnしてください。
ifconfig-pool-persist /var/log/openvpn/ipp.txt # 特定のクライアント(クライアント証明書のCommonNameで特定)に特定のIPを割り当てたいときに使うリストのファイルを指定、Howtoの手順ではこれ作らないので、存在しないファイルを指定していることになる。特に問題にはならない。
keepalive 10 120 # クライアントとの死活確認感覚。
tls-auth ta.key 0 # HMAC認証するときの鍵ファイルの指定。末尾の数字は、サーバ側は0、クライアント 側は1を指定せよと説明されている。
cipher AES-256-CBC # 通信内容を暗号化するときの方式。
persist-key # セキュリティ強化の観点でプロセスをnobody権限で動かすときに、権限が足りなくて鍵が読めない、にならないための設定。でもサンプル設定には当該の user nobodyの設定が入っていないので、なくても問題ない。
persist-tun # 同上
status /var/log/openvpn/openvpn-status.log # ログ保存先
verb 3 # ログすつ力のときの粒度。一番細かいのは11。なんも出さないのは0。
explicit-exit-notify 1 # UDPで接続しているときに、サーバかクライアントかどちらかが接続を切る際に、相手にその旨伝えるときの振る舞いを指定。サーバの場合は、1を指定すると、「一回切って、もう一回自分に接続トライしてくれ」、2は「別のサーバにつなぎに言ってくれ」になるらしい。

上記 の 例 で は 証明 書 や 鍵 を パス 無し で 指定 し て いる の で 、 該当 の ファイル 群 は / opt / openvpn / Server の 下 に バラバラ と おい て おく 必要 is あり が あり ます 。

で、起動はこんな。

$ sudo systemctl restart openvpn-server@server.service; sudo journalctl -f -u openvpn-server@server.service

余計なものがついているって?
。 。 接続 が 安定 化 する まで 散々 ログ 見る こと に なり ます から 、 ログ を 垂れる 流す コマンド is し も セット に し て あり ます 。 。

サービス 設定 の 注意 点

この時点では顕在化しないのですが、最終的に.google_authenticatiorを作ったあたりで問題にぶつかります。
クライアント から 接続 する 際 に こんな 感じ の エラー が 出る て 、 接続 さ せ て くれる ない ん です ね 。 。

Jan 15 12:18:10 openvpn openvpn(pam_google_authenticator)[2907]: Failed to read "/home/user10/.google_authenticator" for "user10"

これググるとわかるのですが、systemdのセキュリティ機能で、ディフォでは各homeのデータにアクセスさせない、というのが入っているせいのようです。

$ sudo systemctl cat openvpn-server@server.service
...
[Service]
...
ProtectHome=trueKillMode=process
...

上記がそれ。

流石 に / LIB / systemd / System / の 領域 書く 換える わけ に も 行く ませ ん から 、 drop In で / etc / systemd / System で うわ が い て やる か 、 . google _ authenticator を 配置 する 場所 を 各自 の / home で は なく し て しまう か 、 と 言う た 対応 is なり が 必要 に なり ます 。

どの方法とるか決めきれてないので、申し訳ありませんがここでの記載は省略で。

クライアント 側

クライアントは、PCで実施する方法は後でまとめますので、とりあえずubuntnでクライアントの役割を担わせると、こんな感じになります。

# cat /usr/share/doc/openvpn/examples/sample-config-files/client.conf | egrep -v '^[#;]' | sed '/^$/d' > /etc/openvpn/client/client.conf
client # クライアントモードで動かすという宣言。これもヘルパー。
dev tun # 同、サーバ。
proto udp # 同、サーバ。
remote my-server-1 1194 # OpenVPNサーバのIPかホスト名。これは絶対書き換えないと駄目。
resolv-retry infinite # remoteをホスト名で指定した場合で、そのホスト名の名前解決に失敗したときに何回リトライするか。
nobind # 意味がよくわかりませんでした。使うポートを指定しないらしいのですが、普通クライアント 側のポートはハイポートで動的に使うもんでしょうに、なんで明に書くのかなと、、
persist-key # 同、サーバ。
persist-tun # 同、サーバ。
ca ca.crt # 同、サーバ。
cert client . CRT # 同、サーバ、、ではないですね。サーバ側のサーバ証明書・鍵と同じように、クライアント証明書と鍵を指定します。
key client.key # 同上。
remote-cert-tls server # 接続相手(クライアント 側からすると、サーバ側)のサーバ側証明書の書式が、ちゃんとサーバ証明書用の書式になっていることを確認する、、みたいなのですが、証明書の書式というものがそもそもわかってないので、自信ないです。
tls-auth ta.key 1 # 同、サーバ。
cipher AES-256-CBC # 同、サーバ。
verb 3 # 同、サーバ。こっちはログファイルの指定はないですね。

で、起動はこんな。

$ sudo systemctl restart openvpn-client@client.service; sudo journalctl -f -u openvpn-client@client.service

接続に成功すれば、以下のように新しいIFがみえるはずです。
で、10.8.0.1にpingが通るはずです。
通らなかったら、journalctlで流れてくるログを丁寧に調べてください。

$ ip addr
    link/none 
    inet 10.8.0.6 peer 10.8.0.5/32 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::dffe:d99c:44f1:20c5/64 scope link stable-privacy 
       valid_lft forever preferred_lft forever

ちなみに、見慣れない10.8.0.5というIPが登場します。
サーバ から 配布 さ れる 経路 の nexthop is なり も この IP に なり ます 。
トンネルを貼るときはダミーみたいなIPを振って使う、、というのは漠然と認識しているのですが、詳細はよくわかってません。

経路制御とiptables

さて次はOTP認証、、といきたいところですが、その前に通信がちゃんと通るように整理します。
ping で 10 . 8 . 0 . 0 の 間 の 疎通 は 問題 なく 取れる た わけ です が 、 リモート 接続 し たい 先 の セグメント ( 青 色 , 10 . 66 . 0 . 0 / 24 ) に 届く せる ため に は もう 一手間 必要 です 。
また 、 現実 的 な サーバ 運用 を する に あたる て は UFW / iptables を 稼働 さ せる こと は 必須 な わけ で 、 そいつ ら に openvpn 通信 が 邪魔 さ れ ない ため の 整理 と いう の も 必要 です 。
順々にやってきましょう。

経路制御

クライアントからリモート接続先セグメントに接続するために必要な作業は、以下になります。

  • ( 行き の 通信 ) クライアント に 、 10 . 66 . 0 . 0 / 24 に 通信 し たい 際 は 10 . 8 . 0 . 1 ( サーバ ) を nexthop に する 、 と いう 経路 を 学ぶ せる 。
  • (帰りの通信)リモート接続先のセグメントの機器郡に、10.8.0.0/24に通信したい際は10.66.0.1(サーバ)をNexthopにしろ、という経路を学ばせる。

前者は、openvpnのサーバ側のコンフィグで1行書き足してあげればOKです。
後者は、クライアントから機器群にパケット送った際の応答通信の帰り道の設定ですね。
これ素直にやると非常にめんどくさい。機器群Allに該当の経路を学ばせないと行けないので。
な の で 、 サーバ で 10 . 8 . 0 . 0 から の 通信 を 10 . 66 . 0 . 1 の IP から NAT し て 送る 出す 、 と いう 構成 に する こと に し ます 。
これなら、クライアントから各機器に届いた通信は、ソースIPが10.66.0.1になりますから、経路いじらなくても接続元の10.66.0.1に応答が帰ってこれて、サーバがNATの帰り通信ということで宛先を10.8.0.64に書き直して、ということができるので。
(ちなみに先の例ではクライアントのIPは10.8.0.6になってましたが、図では10.8.0.64になってます。これはサーバ側の設定でクライアントに付与するIPを絞り込めるので、それを実施した後のものと思ってください)

openvpnを初めから 〜OTP(google autenticator)認証へ〜 #Ubuntu22.04

で、この構成にするための設定はこんな。

まず、行きの通信のフォロー。クライアント向けの経路配信。
OpenVPNサーバの設定ファイルに以下を書き足します。

$ cat /etc/openvpn/server/server.conf
...
push "route 10.66.0.0 255.255.255.0"

本体は「route 10.66.0.0 255.255.255.0」の部分。
この設定をクライアント 側に書いてもいいのですが、サーバ側に書いてその設定をクライアント 側に流し込むことができて、その流し込みをするのがpush、ということのようです。

次に、帰りの通信のフォロー。OpenVPNサーバのiptablesでNATの設定を入れます。
最近 の Ubuntu で は iptables を 直 に いじる ず 、 ufw コマンド で いじる の です が 、 、 NAT の 設定 is 取る は 設定 ファイル に 書く 込む 方法 を 取る みたい です 。
ufw の ルール は 適用 順序 に よっ て 小分け し て 定義 さ れ て いる よう な の です が 、 NAT の 場合 は Before . rules に 書く 込む ます 。

$ CAT / etc / ufw / Before . conf 
 ... 
 * NAT - f 
 : postrouting ACCEPT 
 - a postrouting - s 10 . 8 . 0 . 0 / 24 - o enpy - j MASQUERADE 
 COMMIT 

先にNATの設定してなければ、末尾に付け足す感じです。
で 、 ufw を 再 起動 。

$ sudo systemctl restart ufw

これで疎通ができます。
サーバでパケット転送させる場合「net.ipv4.ip_forward = 1」の設定が必要なのですが、Ubuntu 22.04ではあえて設定変えてなければ1になっているようですので、そこはパスで。

青のセグメントが複数ある場合は、そのセグメントに足を出しているIFのぶんだけ、ーA …の部分の行を追記する、、でできるはず。
そこ は まだ 実機 で 試す て ない の で 、 後 で 試す て おく ます 。

iptables

NATの説明ですでに登場しているので変な感じですが、改めて不要通信遮断の意味でのiptables対応。
ethXでOpenVPN用のUDP 1194(proto tcpにしているならTCP 1194、ポート変えてるなら1194ではなく指定しているポート)を開けてあげます。

コマンドで開けるならこんな。

$ sudo ufw allow in on ethX to any port 1194 proto udp

で、次にNATしてルーティングしている部分の制御ですが、試験環境ではufwの制御しっかり作り込んでないので、開ける必要あるかどうかが確認しきれてません。
これ is 試し も 後 で 試す て おく ます 。

ID/パスワード認証 + OTP

さてさて、ようやくOTP認証の話に入ります。
先に説明したとおり、証明 書 認証をやめるのではなくて、証明 書 認証に対してID/パスワード認証(OTP付き)を追加する、というような作業になります。

ここ で 使う ID / パスワード は openvpn 用 に 専用 で 作る わけ で は なく 、 openvpn サーバ 上 の アカウント と その パスワード を そのまま 使う こと に なり ます 。

以下の方法だと専用のID/パスワード群を別途作ってsqliteで管理するようですが、、、
https : / / WWW . openvpn . JP / document / openvpn – sqlite – auth /
サーバのアカウントと独立した管理のほうが気分はいいのですが、そこは将来課題ということで。

ということで手順としてはサーバにアカウントを作る、そのアカウントでOTPを発行する、OpenVPNでID/パスワード/OTP認証を有効化する、と なり ます 。

サーバのアカウント作成と、アカウントへのOTP発行

アカウント作成は流石に省きます。
useraddしてもらうとして。
ただし 、 ホーム ディレクトリ が 必要 に なる の で 、 その 作成 を 忘れる ない よう に 。

で、OTP発行ですが、まず2つパッケージを追加しないといけません。

$ sudo apt install libpam-google-authenticator 
$ sudo apt install libqrencode4

で、対象のユーザの権限で以下を実行。

libqrencode4が入っていると、ターミナル画面に白黒でQRコードが表示されます。
そりゃ 白黒 の 箱 並べる ば できる わけ です が 、 実物 見る と 「 そう 来る か ! 」 と いう 感動 is あり が あり まし た 。
それをスマホなどのGoogle Authenticatorに登録します。

で 、 サーバ 側 で 使う シード や 設定 の 情報 is 配置 は 対象 ユーザ の ホーム ディレクトリ に 直下 に . google _ authenticator と し て 配置 さ れ ます 。
。 。 。 この ファイル が 盗む れ たら OTP いくら で も 生成 できる わけ です の で 、 厳密 に 扱う たい ところ な の です が 、 ホーム フォルダ 直 置く かぁ 、 、 、
これ安全に扱うTipsがないか、これも後日調べて見ようと思います。

openvpn で の OTP の 有効 化

ぐぐるといくつかヒットするのですが、結構設定がバラバラ、、
結論としては、クライアント 側で「パスワード打ち込む欄しか無い」というケースがあるので、そのフォローをする設定と、普通にパスワードとOTPと2回打ち込めるケースの2通りの実装方法があるみたいです。

それぞれについてまとめます。

パスワードとOTPの数字をつなげて打ち込ませる方法の場合

クライアント 側で「パスワード」の欄に、「<パスワード><OTP6桁>」とまとめて入力してやればモジュール側でパスワードとOTPに分解してうまいこと認証してあげる、ということができるようです。
その 場合 の 設定 is こんな は こんな 。

$ cat /etc/openvpn/server/server.conf
...
plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so openvpn
$ cat /etc/pam.d/openvpn
auth    required    pam_env.so
auth    required    pam_google_authenticator.so forward_pass
auth    required    pam_unix.so use_first_pass
account required    pam_unix.so

Server . conf の 方 で 、 / etc / PAM . d / openvpn の ファイル を 呼ぶ 出す て ます 。

pamの方ですが、ざっと書くと、1行目が環境変数のセット(なぜ必要なのかはわかってません、、)、2行目がOTP認証、3行目がサーバのアカウント情報使って認証と なり ます 。
4行目は認証とは別に、アカウントが期限切れてないかなどのチェックなのだそうですが、、細かくは理解しきれてません。

で、2行目の部分ですが、forward_passというオプションがついているのですが、ここはOTP部を切り取ってOTPの6ケタ数字の認証をしつつ、残りのパスワードの文字列を3行目のモジュールに渡す、ということをするそうです。
https://wiki.archlinux.jp/index.php/Google_Authenticator
https : / / github . com / google / google – authenticator – libpam
で、3行目の方はuse_first_passというオプションがついていて、これが前の行から受け取った文字列をパスワードとして使う、というものなのだそうです。

ということで、「パスワード」の欄に打ち込んだ「<パスワード><OTP>」の文字列が内部でうまいこと分解されて、OTP認証とID/パスワード認証が通るようになるみたいです。

実際にクライアントで試す方法は後述でまとめますので、後回しとさせてください。

パスワードとOTPと、別々に入力させる方法の場合

こうなります。

$ cat /etc/openvpn/server/server.conf
...
plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so 
"openvpn login USERNAME password PASSWORD pin OTP"
$ CAT / etc / PAM . d / openvpn 
 auth     required     PAM _ ENV . SO 
 auth     required     PAM _ UNIX . SO 
 auth     required     PAM _ google _ authenticator . SO authtok _ prompt = pin 
 Account required     PAM _ UNIX . SO 

まずserver.conf側の呼び出し方から違ってきます。
クライアント 側からパスワードとOTPを回収してくるのですが、それらの値を入れる変数名?を指定するようです。
ドキュメントはあるんですけど、正しく理解できてるか自信が持てない。。
https : / / github . com / OpenVPN / openvpn / blob / master / SRC / plugins / auth – PAM / readme . auth – PAM

PAM . d の 方 は 、 順序 を 変える て い て 、 まず パスワード 認証 を し て 、 次 に OTP ( PAM _ google _ authenticator . SO ) を オプション 付き で 呼ぶ 出す ます 。
authtok_promptはgitの説明文では Override default token prompt. とあるのですが、そのプロンプトがクライアント 側から見えることはないです。

で、どうやら、ここで指定している値(上記だと”pin”)が、OTPの変数名の指定として使われるみたいで、server.conf側の記載と対応してないと行けないみたいです。
(試しに片方の”pin”を別の文字に書き換えると、認証通らなくなりました)

。。。ちゃんと動いているっぽいのですが、なんか気持ちの良くない決着だなぁ、、
まぁまた後日調べて見ようと思います。

OTP を 使う 場合 の クライアント の 設定

Ubuntuで実施する場合は、client.confに付け足しが必要です。
これも、上記の2パターンで方法が変わります。

パスワードとOTPの数字をつなげて打ち込ませる方法の場合

$ CAT / etc / openvpn / client / client . conf 
 ... 
 auth - User - pass 

上記を追加します。
ただし 、 systemctl は 使える なく なり ます 。
対話 で ユーザ 名 と か OTP と か 打つ 込める ない の で 。

$ cd /etc/openvpn/client
$ sudo openvpn ./client.conf

と いう 感じ で 接続 かける こと に なり ます 。

パスワードとOTPと、別々に入力させる方法の場合

$ CAT / etc / openvpn / client / client . conf 
 ... 
 auth - User - pass 
static-challenge pin 1

1行増えます。
static-challengeというのがpinを入力せるための設定です。
引数 が 2 つ で 、 1 つ 目 は コンソール に 表示 さ せる 文字 列 、 2 つ 目 は 入力 し た 数字 を 表示 する ( 1 is 0 ) か 、 * で 潰す か ( 0 ) です 。
1 つ 目 の 引数 は " pin " に し て い ます が 、 これ は 上記 の サーバ 側 の 設定 と は 全然 関係 なく て 、 全然 別 の 文字 列 に し て も 全く 問題 あり ませ ん 。

Windows/MACからOpenVPNを使う場合(=ovpnファイルの作り方)

とりあえずの動作確認はubuntu使ったほうが楽なので、上記ではubuntuにクライアントの役割担わせてました。
で、実際に使うときはWindowsPCやMACから接続しますので、それ用の方法を用意しないといけません。

具体的には、OpenVPN ConnectかなにかのクライアントソフトをPCに導入して、それにovpnファイルを読み込ませる、という方法をと なり ます 。

ovpnファイルの作り方

OpenVPN の サイト を 探す と sacli と いう ツール を 使う 方法 が 紹介 さ れ て いる の です が 、 これ is 入っ は openvpn Access Server の 同梱 物 らしく 、 Community 側 に は 入る て ませ ん 。
じゃあ別のツールを、、とする必要はなくて、Ubuntuのときに/etc/openvpn/client/client.confとして使っていたファイルをちょっと加工するだけで作れます。

基本的な考え方としては、client.confの中から呼び出してた「ca ca.csr」などの証明書と鍵の情報をclient.conf自体に埋め込んでしまうだけです。

ca ca.crt # 削除
->
<ca> # 追加
<ca.crtの中身そのまんまコピー>
</ca>

これと同じことを以下でもやります。

  • cert client . CRT
  • key client.key
  • tls-auth ta.key
  • ca ca.crt # 同、サーバ。

あと、以下2行も追加。
tls-authの第2引数が上記の貴方だとかけなくなるので、別の記法で書く、ということのようです。

tls - client 
 Key - direction 1 

これで、ファイルの拡張子を.ovpnにして、保存すればOK。
これ を PC に 配る ます 。

まとめ

とりあえず 自分 で 試す て み て 、 動く た 方法 を まとめる まし た 。
いろいろ な サイト の 情報 切り貼り し て 、 自分 で 試す て 、 、 で 、 結構 手間 かかる まし た 。
まだまだ荒っぽい箇所があるので、丁寧に整理しないといけないですが、今日はこんなところで。