外出先から自宅へVPN接続するためにWireGuardを使っていますが、素の(バニラに)WireGuardでは接続時の認証キー以外に認証(ログイン認証など)が行われません。
自分ひとりで利用するのなら自己責任での管理で良いのですが、他人にも自宅ネットワーク機器(NASなど)を解放する場合にはログイン認証機能がないとちょっと心配です。
TailscaleやNetBirdを使えばGoogleアカウントを使ったSSO(シングルサインオン)機能が使えますが、これもちょっと心配・・・・
ということで、商用サービスである「OpenVPN Access Server」みたいな感じでOpenVPNを使ってVPN接続時にログイン認証+TOTP(6桁のランダム数字パスワード)によるVPN接続環境を構築してみました。
これも素の(バニラな)OpenVPNだとなかなか面倒なので、これを実現する便利なdockerイメージを利用して構築していきます。
構築するOpenVPNサーバー環境の概要
本記事ではOpenVPNによるリモートVPN環境を構築していきます。
ポイントは以下の点です。
- VPN接続時に「ログインアカウント名 + TOTP(6桁のランダム数字)による認証」を行う
- この環境構築に当たり、有志が公開してくれているdockerイメージを利用する
6桁のランダム数字によるパスワード認証
まず、ログイン時にアカウント名に加えてTOTPによる6桁のランダム数字をパスワードとしたログイン認証を実現します。
TOTPを利用するためにはスマホ側に「Google Authenticator」などのアプリが必要、つまり、ログイン時にはスマホアプリでランダムな6桁数字を確認しそれをOpenVPNクライアントのログインパスワードとして入力する、というセキュリティになります。
このため、まずはスマホ側にGoogle Authenticatorアプリが必要です。
ユーザー管理(認証鍵管理)
本記事では、OpenVPNサーバー機能および認証鍵管理(ユーザー管理)の環境を簡単にdockerで構築できる仕組みによって構築していきます。
「d3vilh/openvpn-ui」を導入することで、OpenVPNのサーバー機能に加えて認証鍵管理(OpenVPN-UI)が使えるようになり、さらにその機能のオプションとして「2FA(二段階認証)」を有効化することができます。
そして、この「2FA(二段階認証)」機能を有効化するだけで、TOTPによる6桁ランダム数字によるパスワード認証が実現できる、という優れもののdockerイメージです。
しかし、実際にやってみるとなかなかに試行錯誤でした。
本記事ではこの試行錯誤の内容を整理して、手順を簡単に整理していきます。
導入:OpenVPN-UIイメージの導入
本記事ではOpenVPNサーバーの認証鍵管理(ユーザー管理)をd3vilh/openvpn-uiというdockerコンテナを使って構築していきます。
そして、このdockerコンテナはサーバー機能である「d3vilh/openvpn-server」と一緒に動作します。
よって、ここからは認証管理機能である「opvnvpn-ui」とOpenVPNサーバー機能である「openvpn-server」の両機能を導入する手順となります。
STEP① 事前準備
今回はopvnvpn-server/openvpn-uiの両機能をひとつのdocker-composeで管理していきます。
このため、まずはdocker-composeのプロジェクトディレクトリを作成し、そこで準備作業を行っていきます。
本記事ではプロジェクトディレクトリとして「/etc/docker/containers」とし、そこに本プロジェクトディレクトリ「openvpn」を作成します。
## プロジェクトディレクトリの作成
cd /etc/docker/containers
mkdir -p openvpn
cd openvpn
pwd
ここからの作業はこの基準ディレクトリ(/etc/docker/containers/openvpn)で行います。
※基準ディレクトリはご自身の環境に合わせていただいて結構です。
では、最初にOpenVPN-server/OpenVPN-UIが必要とするディレクトリおよびファイルを構築していきます。
### 基準ディレクトリの確認
pwd
### 初期ディレクトリ/ファイルの作成
mkdir -p pki clients config staticclients log db
touch fw-rules.sh
上記により、基準ディレクトリに4つのサブディレクトリが作成されます。
また、基準ディレクトリに「fw-rules.sh」というファイルが作成されます。
次に、必要なファイルをgithubよりダウンロードします。
### 必要なファイルをgithubからダウンロードする
curl -fsSL \
https://raw.githubusercontent.com/d3vilh/openvpn-server/main/server.conf \
-o server.conf
curl -fsSL \
https://raw.githubusercontent.com/d3vilh/openvpn-server/main/config/easy-rsa.vars \
-o config/easy-rsa.vars
curl -fsSL \
https://raw.githubusercontent.com/d3vilh/openvpn-server/main/config/client.conf \
-o config/client.conf
上記により、「config」サブディレクトリに2つのファイルがダウンロード、および基準ディレクトリに「server.conf」がダウンロードされます。
また、ダウンロードされている基準ディレクトリの「server.conf」に以下の修正(1行追記)を行います。
## server.conf
management 0.0.0.0 2080
port 1194
proto udp
#proto tcp
dev tun
ca pki/ca.crt
cert pki/issued/server.crt
key pki/private/server.key
tls-crypt /etc/openvpn/pki/ta.key
上記のように「tls-crypt /etc/openvpn/pki/ta.key」を1行追記します。
以上で必要ファイル・ディレクトリの準備は完了です。
STEP② docker-compose.ymlの作成
本来、OpenVPN-ServerとOpenVPN-UIはそれぞれ別のdockerコンテナとして提供されています。
OpenVPN-ServerコンテナはOpenVPNのサーバー機能を実現するdockerコンテナです。
OpenVPN-UIはサーバーと連携してクライアントの認証鍵管理(ユーザー管理)をやってくれるdockerコンテナです。
本記事では「OpenVPN-Server」イメージと「OpenVPN-UI」機能をひとつのdocker-composeとして管理していきます。
作成するdocker-composeファイルは以下のようになります。プロジェクトディレクトリに「docker-compose.yml」のファイル名で作成してください。
### docker-compose.yml
services:
openvpn:
container_name: openvpn-server
image: d3vilh/openvpn-server:latest
privileged: true
cap_add:
- NET_ADMIN
ports:
- "1194:1194/udp"
environment:
TRUST_SUB: "10.0.70.0/24"
GUEST_SUB: "10.0.71.0/24"
HOME_SUB: "192.168.1.0/24"
volumes:
- ./pki:/etc/openvpn/pki
- ./clients:/etc/openvpn/clients
- ./config:/etc/openvpn/config
- ./staticclients:/etc/openvpn/staticclients
- ./log:/var/log/openvpn
- ./fw-rules.sh:/opt/app/fw-rules.sh
- ./server.conf:/etc/openvpn/server.conf
restart: unless-stopped
openvpn-ui:
container_name: openvpn-ui
image: d3vilh/openvpn-ui:latest
privileged: true
ports:
- "8080:8080/tcp"
environment:
OPENVPN_ADMIN_USERNAME: "admin"
OPENVPN_ADMIN_PASSWORD: "CHANGE_THIS_PASSWORD"
volumes:
- ./:/etc/openvpn
- ./db:/opt/openvpn-ui/db
- ./pki:/usr/share/easy-rsa/pki
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
上記のdocker-compose.ymlをひな形として、ご自身の環境に合わせて修正していきます。
ネットワーク環境の修正
まず、以下の点について必要であれば修正してください。
environment:
TRUST_SUB: "10.0.70.0/24"
GUEST_SUB: "10.0.71.0/24"
HOME_SUB: "192.168.1.0/24"
| TRUST_SUB | 10.0.70.0/24 | クライアントに割り当てられるネットワークサブネット ※こだわりがなければこのままで良い |
|---|---|---|
| GUEST_SUB | 10.0.71.0/24 | クライアントに割り当てられるネットワークサブネット ※こだわりがなければこのままで良い |
| HOME_SUB | 192.168.1.0/24 | ご自身の自宅ネットワークセグメントに修正 |
以上について必要であれば修正してください。
特に、「HOME_SUB」はご自身の自宅ネットワークサブネットに正しく修正してください。
ここを間違えるとOpenVPNクライアントは正しくVPN接続されたとしても自宅ネットワークへルーティングされることはありません(実質、通信できない)。
OpenVPN-UIの管理パネルのポート番号
OpenVPN-UIでは認証管理(ユーザー管理)をブラウザで行います。
この管理パネルへの接続ポートが初期値では「8080」となっており、「http://自宅のIPアドレス:8080」で接続されます。
もし、このポート番号を変更したい場合には以下の設定を見直してください。
ports:
- "8080:8080/tcp"
たとえばポート”8080″が既に他のアプリで使用中であり”8040″に変更したい場合は「- 8040:8080/tcp」と修正します。
OpenVPN-UIの管理パネルのユーザーID/パスワード
同様に、管理パネルにログインする場合の初期ユーザーID/初期パスワードを設定します。
以下の「OPENVPN_ADMIN_PASSWORD」についてはご自身の任意のパスワードへ変更してください。
environment:
OPENVPN_ADMIN_USERNAME: "admin"
OPENVPN_ADMIN_PASSWORD: "CHANGE_THIS_PASSWORD"
STEP③ dockerの起動
ここまでの準備が完了したらdocker composeを起動します。
## docker composeの起動
docker compose up -d
正常に起動したら、プロセス状態を確認しておきましょう。
## docker起動後の確認
docker compose ps
以下のように、openvpn-server/openvpn-uiともに「STATUS=Up」が表示されていればOKです。
root@v-scdoc01:/etc/docker/containers/openvpn# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
openvpn-server d3vilh/openvpn-server:latest "/bin/sh -c ./docker…" openvpn 3 hours ago Up 2 hours 0.0.0.0:1194->1194/udp, [::]:1194->1194/udp
openvpn-ui d3vilh/openvpn-ui:latest "/bin/sh -c /opt/sta…" openvpn-ui 3 hours ago Up 2 hours 0.0.0.0:8084->8080/tcp, [::]:8084->8080/tcp
root@v-scdoc01:/etc/docker/containers/openvpn#
もしSTATUS=Up出ない場合(Restartなど)担っている場合にはログファイルを見て確認し、ここまでの手順を見直してください。
## ログファイルの確認
docker compose logs
ここまでの手順で「STATUS=Up」となっていれば、とりあえず(?)ここまでの手順は問題なさそうなので、次の手順へ進みます。
ProxmoxのLXCで構築する場合の注意点
Proxmoxという仮想サーバーをご利用で、かつ本手順によるOpenVPN-server/UI環境をLXC環境で構築する場合には少し注意が必要です。
OpenVPNでは通信のためにTUNデバイスを使用しますが、LXCでは標準でTUNデバイスを利用することができません。
このため、本手順によりLXC上の仮想コンテナ上にOpenVPN-Server/UI環境を構築する場合には、構築前にLXCコンテナからTUNデバイスを利用可能な状態に設定しておく必要があります。
LXCコンテナでTUNデバイスを利用可能とする設定
運用(管理者):OpenVPN-UIで認証鍵(ユーザー)を管理する
ここからの作業はopenvpn-uiが提供するウェブ画面(管理パネル)を使って行っていきます。
まず、「http://dockerサーバーのIPアドレス:8080」をブラウザから開きます。
※docker-compose.ymlでポート番号を変更した場合はそのポート番号

ログイン画面が表示されるので、これも「docker-compose.yml」で指定したログインユーザー/ログインパスワードでログインします。

接続先情報(Client Config)の修正・登録
まずはVPN接続ユーザーへ提供する接続先情報(自宅のネット回線のIPアドレスとポート番号)を登録します。
メニュー「Configuration > OpenVPN Client」タブ画面を開きます。
以下のように接続先情報を設定します。
また、この時点で認証方式(Authentication Mode)で「2FA(TOTP)」を設定しておきます。

| Connection Address | 自宅回線のドメイン名(グローバルIPアドレス) ※本記事では”mydomain.com”とします。 |
|---|---|
| Connection Port | OpenVPN接続を待ち受けしているポート番号 ※OpenVPNの初期値は”1194″ ※v6プラスなどでポート転送する場合は転送元ポート番号 |
| Authentication Mode | “Two Factor Auithentication(2FA)”を一覧選択 ※これでTOTP(6桁のランダム数字パスワード)が有効になる。 |
上記を設定したら画面下部の「Save Config」ボタンを押下して設定を保存します。
サーバー情報(Server Config)の修正・登録
接続情報の設定が完了したら次にサーバー情報(Server Config)を修正します。
メニュー「Configuration > OpenVPN Server」タブ画面を開きます。

画面の下のほうにスクロールします。

| Server(Trusted VPN Subnet) | VPNクライアントへ配布されるサブネット |
|---|---|
| Route(Guest VPN Subnet) | VPNクライアントへ配布されるサブネット |
| Push Route(Your Home Subnet) | VPNサーバー側でルーティングされるサブネット ※自宅ネットワークに合わせる ※docker-compose.ymlで指定した値と同じもの |
また、サーバー設定においても「2FA(TOTP)」を有効化しておきます。
さらに少し下へスクロールします。

| Authentication Mode | “Two Factor Auithentication(2FA)”を一覧選択 ※これでTOTP(6桁のランダム数字パスワード)が有効になる。 |
|---|
上記を設定したら画面下部の「Save Config」ボタンを押下して設定を保存します。
以上でサーバー設定およびクライアント設定は完了です。

Certificates(ユーザー)の登録
では、ここからはCertificates(認証鍵=VPN接続ユーザー)の登録を行っていきます。
メニュー「Certificates」タブ画面から「Create Certificate」ボタンを押下します。

Create Certificate画面が表示されます。

| Name | Certificate(VPN接続ユーザー名に相当)の名称 ※本記事では”vpnuser01″ |
|---|---|
| 2FA Name | TOTPに表示されるユーザー名 ※”aaaa@bbbb”という書式となる ※本記事では”vpnuser01@openvpn”とする |
「2FA Name」については、スマホアプリで同期をとる場合の接続先名称となります。
スマホアプリ(Google Authenticator)にはこの名前(2FA Name)および6桁の数字が表示されます。
Certificate情報を設定したら画面下部の「Create」ボタンで保存します。
以上でCertificates(ユーザー)情報の登録は完了です。
複数のVPN接続ユーザーがいる場合にはユーザーごとにCertificatesを作成してください。
以上でサーバー上での設定は完了です。
運用(利用者):VPN利用者の準備とVPN接続
スマホアプリ「Google Authenticator」の準備
TOTP(6桁のランダム数字)での認証を実現するためにはVPN利用者のスマホに「Google Authenticator」アプリが必要です。
このアプリはOpenVPN側で30秒ごとに変化するランダムな6桁数字との同期をとるためのアプリです。
まずはご利用のスマホでGoogle Authenticatorをインストールしてください。


TOTP鍵の読み込み(QRコード)
管理パネルのメニュー「Certificates」画面を開きます。

TOTP鍵を発行する「Name」の行にある「Expiration」や「Details」などの緑色をクリックすると、以下のような画面が開きます。

下のほうにスクロールするとTOTP鍵のQRコードが表示されています。

このQRコードをスマホアプリ「Google Authenticator」で読み込みます。
これにより、OpenVPNサーバー側で発行する30秒ごとのランダムな6桁数字とスマホアプリの数字が同期されるようになります。
また、「2FA Name」も控えておく必要があります。
VPN利用者がOpenVPNクライアントアプリからVPN接続する場合のログインIDがこの「2FA Name」となります。
OpenVPN認証鍵(ovpnファイル)のインストール
メニュー「Certificates」画面からユーザー名(Name)をクリックします。

ここからOpenVPNクライアントアプリに読み込ませる認証鍵(ovpnファイル)がダウンロードできます。
この認証鍵(ovpnファイル)をVPN利用者のOpenVPNクライアントアプリから「ファイルのImport」で読み込ませてください。
以上で、VPN利用者が行うべき初期設定は完了です。
OpenVPNによるリモートVPN接続
OpenVPNクライアントアプリをインストールすると、初期状態ではタスクトレイにアプリが常駐する状態となっています。
接続時には「ユーザーID=2FA Name」「パスワード=6桁のランダムな数字(Google Authenticatorに表示される数字)」を入力することで、OpenVPNサーバーへ接続することができます。