Raspberry PiでVPN環境を作ろう 〜PiVPN + Google認証で二要素認証を実現する方法をまとめてみた〜

ラズパイ

外出先から自宅のネットワークに安全にアクセスしたい……そんなとき、選択肢の1つになるのがVPNです。

今回は、

  • Raspberry Pi上にPiVPN(OpenVPN)を構築
  • Google Authenticatorを使ったワンタイムパスワード認証を追加

という形で、証明書+OTPの二要素認証によるVPN環境を作っていきます。

ポイントは、

  • 固定パスワードは使わず、毎回変わる6桁のコードで認証
  • VPN専用ユーザーはシステムへの直接ログインを禁止
  • 時刻同期が命(OTPは時刻ベースなので)

といったあたりです。

モグラ先生:

「証明書」(持っているもの)と「OTP」(知っているもの)の二要素認証で、パスワード固定のリスクを減らせるのがポイントだね 🐾


目次

  1. 1. 今回作る構成のざっくりイメージ
  2. 2. まずはRaspberry Piの準備から
  3. 時刻同期の確認(超重要)
  4. 3. PiVPNのインストール
  5. 4. VPNクライアント用の証明書を作る
  6. 5. VPN専用のLinuxユーザーを作成する
  7. 5-1. ユーザー作成
  8. 5-2. パスワードはランダムで
  9. 5-3. SSHログインを禁止する
  10. 6. Google Authenticator(OTP)を導入する
  11. 6-1. パッケージのインストール
  12. 6-2. OTPのシークレットを生成
  13. 6-3. QRコードを画像にする(オプション)
  14. 7. PAM認証の設定(固定パスワードを使わないのがポイント)
  15. 8. OpenVPNサーバーの設定を確認
  16. 9. systemdの「ProtectHome」問題を解決する
  17. 10. クライアント側の設定
  18. 11. 接続してみよう
  19. 12. うまくいかないときは
  20. 13. さいごに:スマホ紛失に備えて

1. 今回作る構成のざっくりイメージ

まず、今回作るVPN環境の全体像をまとめておきます。

項目設定内容
VPNソフトウェアOpenVPN(PiVPN経由でインストール)
認証方式クライアント証明書 + TOTP(時間ベースOTP)
固定パスワード使用しない(OTPのみで認証)
VPN用ユーザーSSHログイン禁止(VPN専用)

ポイントは、

  • クライアント証明書:これがないとそもそも接続できない(第一の壁)
  • OTP(Google Authenticatorの6桁コード):証明書があっても、これがないと認証が通らない(第二の壁)
  • 固定パスワードは使わない:万が一パスワードが漏れても、OTPがなければ突破できない

という、二段階の防御を張る構成です。

モグラ先生:

証明書だけだと、スマホやPCを落としたときに心配。
OTPだけだと、スマホ紛失で詰む。
両方を要求することで、片方が漏れても突破されにくくなるんだね ⛏️


2. まずはRaspberry Piの準備から

VPNサーバーにするRaspberry Piを、最新の状態に更新しておきましょう。

sudo apt update
sudo apt upgrade -y
sudo reboot

時刻同期の確認(超重要)

OTP認証は、サーバーとスマホの時刻がズレていると認証が通りません

そのため、時刻同期が有効になっているか必ず確認してください。

timedatectl

出力例:

System clock synchronized: yes

yes と表示されていればOKです。

モグラ先生:

OTPは「時間ベース」のワンタイムパスワードだから、
サーバーとスマホの時刻が数分ズレただけで認証が通らなくなるよ。
時刻同期の確認は 「超重要」 だから、忘れずにね 🐾

小さな穴掘りポイント 🕳️

  • Raspberry Piは、デフォルトで systemd-timesyncd による時刻同期が有効になっています。
  • もし no と表示される場合は、sudo timedatectl set-ntp true で有効化できます。
  • Wi-Fi接続の場合、再起動直後は時刻同期が完了するまで少し時間がかかることがあります。

3. PiVPNのインストール

PiVPNは、OpenVPNの設定を簡単にしてくれるツールです。
公式のインストールスクリプトを実行します。

curl -L https://install.pivpn.io | bash

インストール中に、いくつか選択肢が出てきます。

インストール時の主な選択肢

項目推奨設定補足
VPN TypeOpenVPNWireGuardもありますが、今回はOpenVPN
ProtocolUDPTCPより高速
Port1194デフォルトのまま(任意で変更可)
DNSお好みでGoogle DNSやCloudflareなど
Encryptionデフォルト特に変更不要
Static IP設定する推奨(動的IPだと接続が切れる可能性)

動作確認

インストールが終わったら、サービスが起動しているか確認します。

systemctl status [email protected]

active (running) と表示されていれば成功です。

モグラ先生:

PiVPNは、OpenVPNの設定ファイルを自動生成してくれる便利ツールだよ。
手動でやると結構面倒だから、初めての人にはありがたいね ⛏️


4. VPNクライアント用の証明書を作る

次に、VPNクライアント(PCやスマホ)用の証明書を作成します。

pivpn add nopass

名前を聞かれるので、ユーザー名(例:hoge)を入力します。

nopass をつけると、パスワードなしの証明書が作られます(OTPで認証するので、証明書にパスワードは不要)。

作成済み証明書の確認

pivpn -l

生成された .ovpn ファイルは、/home/pi/ovpns/ に保存されます。


5. VPN専用のLinuxユーザーを作成する

OTP認証用に、Linuxのユーザーアカウントを作ります。

このユーザーは、VPN認証専用として使うので、システムへの直接ログインはできないように設定します。

5-1. ユーザー作成

sudo adduser hoge

パスワードや名前などを聞かれますが、とりあえず適当に進めてOKです(後でパスワードは変更します)。

5-2. パスワードはランダムで

VPN接続では、このパスワードは使いません(OTPを使うので)。

ですが、セキュリティ上、推測困難な値を設定しておきましょう。

sudo passwd hoge

パスワードマネージャーで生成した、長いランダム文字列を設定してください。
覚える必要はありません。

5-3. SSHログインを禁止する

VPN専用ユーザーなので、システムへの直接ログインを禁止します。

sudo usermod -s /usr/sbin/nologin hoge

確認コマンド:

getent passwd hoge

末尾が /usr/sbin/nologin になっていればOKです。

モグラ先生:

このユーザーは「VPN認証のためだけ」に存在するから、
SSHで直接ログインできないようにしておくのが安全だね。
万が一パスワードが漏れても、システムには入れないよ 🐾

小さな穴掘りポイント 🕳️

  • /usr/sbin/nologin を設定すると、そのユーザーでSSHやコンソールログインしようとしても、「ログインできません」というメッセージが表示されます。
  • VPN認証には影響しません(認証だけに使われるので)。

6. Google Authenticator(OTP)を導入する

ここからが本題です。
Google Authenticatorを使って、スマホに表示される6桁の数字で認証できるようにします。

6-1. パッケージのインストール

sudo apt install -y libpam-google-authenticator qrencode

6-2. OTPのシークレットを生成

VPN用ユーザー(hoge)として、google-authenticator を実行します。

sudo -u hoge -H google-authenticator

実行すると、QRコードいくつかの質問が表示されます。

質問への回答

質問(だいたいこんな感じ)回答理由
ファイルを更新するかy設定を保存するため必須
同じトークンの再利用を禁止するかyリプレイ攻撃を防止
時刻許容範囲を拡大するかnセキュリティを維持(デフォルトで十分)
レート制限を有効にするかy総当たり攻撃を防止

重要:緊急用スクラッチコードを保存

質問に答えると、画面に5つのスクラッチコードが表示されます。

これは、スマホを紛失したときの最終手段です。
各コードは1回限り使えるので、必ず安全な場所に保存してください。

Your emergency scratch codes are:
  12345678
  87654321
  ...

モグラ先生:

スクラッチコードは、スマホを失くしたときの「緊急脱出口」だよ。
これを保存しておかないと、スマホが壊れたら詰むから、
パスワードマネージャーや紙に書いて金庫に入れるなど、必ず保管しよう 🐾

6-3. QRコードを画像にする(オプション)

端末上のQRコードが読み取りにくい場合は、画像ファイルとして生成できます。

SECRET=$(sudo head -n 1 /home/hoge/.google_authenticator)
LABEL="OpenVPN:hoge"
ISSUER="MyVPN"

qrencode -o /tmp/hoge-otp.png \
  "otpauth://totp/${LABEL}?secret=${SECRET}&issuer=${ISSUER}"

生成された /tmp/hoge-otp.png を、スマホに転送してGoogle Authenticatorで読み取ってください。

権限の確認

sudo ls -l /home/hoge/.google_authenticator

-rw------- でユーザー hoge のみが読み書きできる状態になっていることを確認します。
小さな穴掘りポイント 🕳️

  • .google_authenticator ファイルには、OTPのシークレットキーが入っています。
  • このファイルが他人に読まれると、OTPを偽造される可能性があるので、権限は必ず 600 になっていることを確認してください。

7. PAM認証の設定(固定パスワードを使わないのがポイント)

OpenVPNが、OTP認証を使うように設定します。

ここで重要なのは、固定パスワード認証(pam_unix.so)を含めないことです。

/etc/pam.d/openvpn を作成

sudo tee /etc/pam.d/openvpn <<'EOF'
#%PAM-1.0
auth    required    pam_google_authenticator.so
@include common-account
EOF

これにより、

  • OpenVPN接続時は、Google Authenticatorの6桁コードだけを要求される
  • 固定パスワード(/etc/shadow に保存されているやつ)は使われない

という状態になります。

モグラ先生:

ここがこの設定の 最大のポイント だよ。
pam_unix.so(固定パスワード認証)を入れないことで、
「パスワードが漏れても意味がない」 状態を作れるんだ ⛏️

小さな穴掘りポイント 🕳️

  • もし pam_unix.so を入れると、「固定パスワード + OTP」の入力を求められます(より厳重ですが、不便)。
  • 今回は「証明書 + OTP」で十分なので、固定パスワードは使わない設定にしています。

8. OpenVPNサーバーの設定を確認

/etc/openvpn/server.conf に、以下の設定が含まれていることを確認します。

plugin /usr/lib/aarch64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
verify-client-cert require
username-as-common-name

PiVPNでインストールした場合、通常は自動で設定されています。

プラグインのパスについて

アーキテクチャによって、プラグインのパスが異なります。

  • ARM64(64ビット): /usr/lib/aarch64-linux-gnu/openvpn/plugins/
  • ARM32(32ビット): /usr/lib/arm-linux-gnueabihf/openvpn/plugins/

自分のRaspberry Piのアーキテクチャは、uname -m で確認できます。


9. systemdの「ProtectHome」問題を解決する

ここが、この設定で一番ハマりやすいポイントです。

PiVPNのOpenVPNサービスは、デフォルトで ProtectHome=true が設定されています。

この状態だと、/home 以下のファイル(.google_authenticator)にアクセスできません

override設定の作成

sudo mkdir -p /etc/systemd/system/[email protected]

sudo tee /etc/systemd/system/[email protected]/override.conf <<'EOF'
[Service]
ProtectHome=false
EOF

設定の反映

sudo systemctl daemon-reload
sudo systemctl restart [email protected]

確認

systemctl show [email protected] -p ProtectHome

ProtectHome=no と表示されればOKです。

モグラ先生:

これを忘れると、「OTPを入力しても認証が通らない」という謎現象に見舞われるよ。
journalctl のログを見ると、
「ファイルにアクセスできません」みたいなエラーが出てるはずだから、
必ずこの設定をしようね 🐾

小さな穴掘りポイント 🕳️

  • ProtectHome=true は、systemdのセキュリティ機能の1つです。
  • サービスが /home 以下にアクセスできないようにして、ユーザーファイルを保護します。
  • しかし今回は、OTPファイルが /home/hoge/.google_authenticator にあるため、これを無効化する必要があります。
  • よりセキュアにしたい場合は、OTPファイルを /etc 以下に移動する方法もありますが、設定が複雑になります。

10. クライアント側の設定

クライアント用の .ovpn ファイルを編集します。

.ovpnファイルに1行追加

auth-user-pass

この1行を追加すると、接続時にユーザー名とパスワード(OTP)の入力を求められるようになります。


11. 接続してみよう

OpenVPN GUI(Windows)やその他のクライアントで接続する際、こんな感じで入力します。

項目入力内容
ユーザー名hoge(作成したユーザー名)
パスワードGoogle Authenticatorに表示される6桁の数字のみ

入力例:

ユーザー名: hoge
パスワード: 482913

ポイントは、

  • パスワード欄には、スマホに表示されている6桁の数字だけを入力
  • 固定パスワードは入力しない(そもそも使わない設定になっている)

という点です。

モグラ先生:

証明書(.ovpnファイル)と、OTPの6桁、両方が揃って初めて接続できるよ。
これが 二要素認証 の強みだね 🐾


12. うまくいかないときは

接続に問題がある場合は、サーバー側のログを確認します。

sudo journalctl -u [email protected] -n 50 --no-pager

成功時のログ例

Initialization Sequence Completed

こんな感じで表示されれば、接続成功です。

よくある問題

症状原因と対処
OTP認証が通らない時刻同期を確認(timedatectl)。サーバーとスマホの時刻がズレている可能性
/home にアクセスできないProtectHome対策を確認。override設定を忘れている可能性
プラグインが見つからないアーキテクチャに合ったパスを指定(ARM64なら aarch64-linux-gnu

小さな穴掘りポイント 🕳️

  • OTP認証エラーの9割は、時刻同期のズレが原因です。
  • サーバー側で timedatectl を実行し、System clock synchronized: yes になっているか必ず確認してください。
  • スマホの時刻も、自動設定になっているか確認しましょう。

13. さいごに:スマホ紛失に備えて

この構成により、以下のセキュリティを実現できました。

  • 二要素認証: クライアント証明書(持っているもの)+ OTP(知っているもの)
  • 固定パスワード不使用: 漏洩リスクを低減
  • 最小権限の原則: VPN専用ユーザーはシステムにログイン不可

最後に、スマホを紛失した場合に備えて、必ずやっておくべきことをまとめておきます。

スクラッチコードの保管

Google Authenticator設定時に表示された、5つのスクラッチコードは必ず保存してください。

  • パスワードマネージャーに保存
  • 紙に書いて金庫に保管
  • 暗号化したファイルとしてクラウドに保存

など、スマホと一緒に失われない場所に保管しましょう。

QRコードの再生成

もしスクラッチコードを使い切ってしまった場合、サーバー側で再度 google-authenticator を実行すれば、新しいQRコードとスクラッチコードが生成されます。

sudo -u hoge -H google-authenticator

ただし、これをやると古いOTPは使えなくなるので、新しいQRコードをスマホで読み取り直してください。

モグラ先生:

セキュリティを高めるほど、「スマホを失くしたときに詰む」リスクも上がるから、
スクラッチコードの保管は超重要 だよ。
これを忘れると、サーバーに直接ログインして設定をやり直す羽目になるからね 🐾


これで、証明書+OTPの二要素認証によるVPN環境が完成しました。

外出先から自宅ネットワークに安全にアクセスできる環境が整ったので、ぜひ活用してみてください ⛏️

タイトルとURLをコピーしました