OpenVPNサーバーの構築

Debian 11 でOpenVPNサーバーを構築する

今回はQNAPのContainer StationにあるDebian 11 (LXD)にOpenVPNサーバーを構築した。

コンテナではなく通常のDebianを使っている人は、コンテナの初期設定を無視して構いません(入っていないものは入れてください)。以下全てrootで実行した場合のコードになっています。

コンテナの初期設定(入っていなものがあればインストール)

# コンテナ作成時、ネットワークはbridgeにする。
# 端末で「/bin/sh」でターミナルを開く
$ passwd  #rootのパスワードを設定
$ ip a  #IPアドレスが振られている対象インタフェースを確認する(当方はeth0)
$ vi /etc/network/interfaces  #IPアドレスを固定する。以下は例である。
auto lo
iface lo inet loopback

iface eth0 inet static
	address 192.168.0.2
	netmask 255.255.255.0
	gateway 192.168.0.1
	dns-nameserver 8.8.8.8

変更したら「:wq」で保存して閉じる。

$ vi /etc/sysctl.conf

設定ファイルを開き、最終行に以下を追記し、IPv6を禁止する。

net.ipv6.conf.all.disable_ipv6 = 1

追記したら「:wq」で保存して閉じる。

$ reboot  #再起動する
$ ip a  #IPアドレスが固定されていることを確認する
$ timedatectl set-timezone Asia/Tokyo  #タイムゾーンを東京に変更
$ date  #現在時刻が正しいか確認する
$ apt update  #パッケージ一覧を更新
$ apt -y install openssh-server  #sshサーバーをインストール
$ adduser user1  #user1にssh用のユーザー名を入れる
  #パスワードを聞かれたらuser1のパスワードを入力
$ gpasswd -a user1 sudo  #user1にsudo権限を付与する
$ systemctl restart sshd  #sshdを再起動

これ以降、QTSのコンソール画面だけでなく、sshから入れる。

$ ssh user1@192.168.***.***  #IPアドレスは先ほど確認したもの
$ sudo su  #user1のパスワードを入力し、rootとなる。

# sudo: unable to resolve host **** : Name or service not known
# が表示される場合は、
$ sh -c 'echo 127.0.1.1 $(hostname) >> /etc/hosts'
#でhostsにホスト名を追加する

$ apt -y install cron  #cronをインストール
$ crontab -e  #cronファイルを開く

cronファイルを開き、最終行に以下を追記する。

@reboot mkdir -p /dev/net && mknod /dev/net/tun c 10 200 && chmod 600 /dev/net/tun

追記したら「:wq」で保存して閉じる。

$ apt -y install gpg wget  #gpgとwgetをインストール

OpenVPNのインストール

$ wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add -  #公開GPGキーをインポート
$ echo "deb http://build.openvpn.net/debian/openvpn/release/2.6 bullseye main"> /etc/apt/sources.list.d/openvpn-aptrepo.list  #レポジトリを追加(最新版を入れるため)

2.6は自分のインストールしたいバージョンを入力、bullseyeは自分のOSを入力する(実際に存在するかは上記URLにアクセスして確認可能)。

$ apt update && apt install -y openvpn  #OpenVPNをインストール
$ openvpn --version  #正しいバージョンかを確認
$ apt -y install easy-rsa iptables-persistent  #認証ファイル生成をするEasy RSAと、iptablesの設定を恒久化するためにiptables-persistentをインストール

OpenVPNの認証鍵の作成

CAの構築

$ cp -r /usr/share/easy-rsa /etc/openvpn/  #Easy RSAのディレクトリをコピー
$ cd /etc/openvpn/easy-rsa/  #ディレクトリ移動
$ cp vars.example vars  #varsのサンプルをコピー
$ vim vars  #95~100行目あたりのコメントアウトを外して、自分の環境に合わせて編集する。

95~100行目あたりのコメントアウトを外して、自分の環境に合わせて編集する。

set_var EASYRSA_REQ_COUNTRY "JP"
set_var EASYRSA_REQ_PROVINCE "Kanagawa"
set_var EASYRSA_REQ_CITY "Yokohama"
set_var EASYRSA_REQ_ORG "QNAP"
set_var EASYRSA_REQ_EMAIL "admin@example.com"
set_var EASYRSA_REQ_OU "OpenVPN"
set_var EASYRSA_CA_EXPIRE 3650  #CA証明書の有効期限(10年)
set_var EASYRSA_CERT_EXPIRE 825  #クライアント証明書の有効期限(825日)
set_var EASYRSA_CERT_RENEW 30 #有効期限よりクライアント証明書の更新が可能な日数(30日)

:wq」で保存して閉じる。

$ ./easyrsa init-pki  #PIKを作成
$ ./easyrsa build-ca  #認証局を作成
  Enter New CA Key Passphrase:  #パスフレーズを入力
  #このパスフレーズは後ほど使うので忘れないようにする。
  Re-Enter New CA Key Passphrase:  #もう一度パスフレーズを入力
  Common Name [Easy-RSA CA]:  #適当な名前を入れる。例「OpenVPN」

$ cp pki/ca.crt /etc/openvpn/  #生成されたCAの証明書ファイルを移動

サーバー秘密鍵・証明書の生成

$ ./easyrsa build-server-full server nopass  #サーバーの秘密鍵と証明書ファイルを生成
  #途中でパスフレーズを聞かれるので、先ほどのパスフレーズを入力
  #サーバーの秘密鍵にパスワードを付けるとサービス起動時に面倒くさいので、nopassオプションにより秘密鍵はパスワード無しで生成する

$ cp pki/private/server.key /etc/openvpn/  #秘密鍵をコピー
$ cp pki/issued/server.crt /etc/openvpn/  #証明書ファイルをコピー

DHパラメータの生成

$ ./easyrsa gen-dh  #DHパラメータを生成
$ cp pki/dh.pem /etc/openvpn/  #DHパラメータをコピー

TLS認証鍵の生成

$ openvpn --genkey secret ta.key  #TLS認証鍵を生成
$ cp ta.key /etc/openvpn/  #TLS認証鍵をコピー

クライアント秘密鍵・証明書の生成

$ ./easyrsa build-client-full client01 nopass  #クライアントの秘密鍵・証明書を生成
  #パスフレーズを聞かれるので先ほど同様に入力
  #「client01」はクライアントとなるユーザー名に置き換えること。

$ cp pki/private/client01.key /etc/openvpn/client/  #クライアント秘密鍵をコピー
$ cp pki/issued/client01.crt /etc/openvpn/client/  #クライアント証明書をコピー

CA証明書とクライアント証書の有効期限の確認

$ openssl x509 -text -noout -in pki/ca.crt
  #Validityに記載されている日付が有効期限

$ view pki/index.txt
  #2列目に記載されているのが各ユーザごとの有効期限

クライアント証書の有効期限が切れそうな場合、以下の手順で有効期限の更新

$ ./easyrsa renew client01 nopass
 #client01.key、client01.crtが更新されるので、「クライアント秘密鍵・証明書の生成」の手順でコピーする。

OpenVPNサーバーの設定

$ cd /etc/openvpn  #ディレクトリ移動
$ cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf ./  #server.confのサンプルコードをコピー
$ cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ./client/client01.conf  #client.confのサンプルコードをコピー
$ vim server.conf  #サーバーの設定ファイルを編集

サーバーの設定ファイルを各自の環境に合わせて変更する。当方の変更箇所のみ以下に記述する。

dh dh.pem  #ファイル名の修正
push "redirect-gateway def1 bypass-dhcp"  #コメントアウト(;)を外す
push "dhcp-option DNS 8.8.8.8"  #DNSの通知
push "dhcp-option DNS 8.8.4.4"
duplicate-cn  #同じユーザーの複数接続を許可
max-clients 10  #最大接続数:10
user nobody  #ユーザー属性の制約なし
group nogroup    #グループ属性の制約なし
status /var/log/openvpn/openvpn-status.log  #ログの記録
log /var/log/openvpn/openvpn.log  #ログの記録
log-append /var/log/openvpn/openvpn.log
explicit-exit-notify 1  #UDPの場合
 #TCPの場合はコメントアウト(;)すること。
reneg-sec 43200  #末尾に追記し、再認証するまでの時間を指定
 #当方は43200秒(12時間)とした。長すぎるとセキュリティ上好ましくないが、短すぎるとその都度接続が切れて認証が必要となるので、1回の使用時間を目安に設定する。

OpenVPNサーバーの起動

$ service openvpn@server start  #VPNサーバーを起動
$ service openvpn@server status #VPNサーバーのステータスを確認
  #activeになっていれば成功
$ journalctl -xe  #VPNのエラーが出ていないことを確認
$ ip a  #インターフェイスに「tun0」が追加されていることを確認

※QNAPで起動すると4行目で以下のメッセージが出力されることがある。

$ journalctl -xe
  systemd[1]: openvpn@server.service: Main process exited, code=killed, status=9/KILL

これはQTSがOpenVPNを見つけると強制終了させることが原因と思われる。以下の手順で強制終了させないようにする。

  1. GUIのQTSにadminでログインする。
  2. App CenterよりQVPNをインストールする。
  3. QVPNを開いてOpenVPNを選択する。
  4. 「OpenVPN Serverを有効にする」にチェックを入れ、ポートに使用していない適当な数値を入力し「適用」を押下する。(外向きに空いているポートを指定するとセキュリティホールとなるので、空いていないポートを指定すること)
  5. これにより、QTSでOpenVPNが動いているため、独自にOpenVPNを動かしてもkillされなくなる。

その他設定

パケット転送の有効化

$ vim /etc/sysctl.conf  #設定ファイルを編集
net.ipv4.ip_forward=1  #コメントアウトを外して保存
$ sysctl -p  #設定を反映

IPマスカレードの設定

$ iptables-legacy -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE  #IPマスカレードの設定をする
  #eth0は「ip a」でVPNサーバーのIPアドレスが割り振られていたインタフェース名を入力すること

$ iptables-legacy-save > /etc/iptables/rules.v4  #設定を恒久的に保存
$ update-alternatives --config iptables #デフォルトを変更
  #「iptables-legacy」の番号を入力

ここまで出来たら再起動し、設定が保持されている確認する。

$ reboot  #再起動

$ iptables -L -t nat  #再行動後も設定が残っているか確認
$ service openvpn@server status #OpenVPNが正常に作動しているか確認

クライアント用ファイルの設定

$ cd /etc/openvpn  #ディレクトリ移動
$ vim client/client01.conf  #クライアントの設定ファイルを編集

クライアントの設定ファイルを各自の環境に合わせて変更する。当方の変更箇所のみ以下に記述する。

remote my-server 1194  #my-serverをVPNサーバのIPアドレスにする
 #外から入る場合はグローバルIPアドレスを設定する。
 #その場合、ルータのポートフォワーディング(方法は各自で確認)で1194を開放し、VPNサーバのIPアドレスに転送する。(固定のグローバルIPアドレスがない場合はDDNSを設定する)
user nobody  #ユーザー属性の制約なし
group nogroup  #グループ属性の制約なし
reneg-sec 0  #末尾に追記し、再認証するまでの時間を無効(0)にする。
 #サーバーとクライアントで設定できるが、短い方の時間で再認証されるため、今回はクライアントでの指定を無効としサーバーの時間で再認証する。特定のクライアントにサーバーより短い時間を設定したい場合は0を消して秒数を指定する。

:wq」で保存して閉じる。

$ cp client/client01.conf client/client01.ovpn  #ovpnファイルを作成
$ vim client/client01.ovpn  #ovpnファイルを編集

ovpnファイルの88〜90行目にある以下の3行を削除する。

ca ca.crt
cert client.crt
key client.key

削除した箇所に以下を挿入する。「:vnew ca.crt」など並行で開きながらコピペすると作業しやすい。

<ca>
-----BEGIN CERTIFICATE-----
	#・・・ ca.crt の CERTIFICATE の中身 ・・・
-----END CERTIFICATE-----
</ca>

<cert>
-----BEGIN CERTIFICATE-----
	#・・・ client/client.crt の CERTIFICATE の中身 ・・・
-----END CERTIFICATE-----
</cert>

<key>
-----BEGIN PRIVATE KEY-----
	#・・・ client/client.key の PRIVATE KEY の中身 ・・・
-----END PRIVATE KEY-----
</key>

ovpnファイルの108行目(上記追記後の181行目あたり)にある以下の行を削除する。

tls-auth ta.key 1

削除した箇所に以下を挿入する。

key-direction 1
<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
	#・・・ ta.key の OpenVPN Static key V1 の中身 ・・・
-----END OpenVPN Static key V1-----
</tls-auth>

:wq」で保存して閉じる。

クライアント(GUI)から接続する

$ scp /etc/openvpn/client/client01.ovpn user@192.168.***.***:Downloads/

SCPコマンドなどで先ほど作成したovpnファイルをクライアントPCに転送する。userは受け取るPCのユーザー名、192.168.***.***は受け取るPCのIPアドレスを指定する。Downlodsは例なので、好きなフォルダを指定する。

  1. OpenVPN公式サイトから「OpenVPN Connect」を自分の使用環境(OS)に合わせてダウンロードする。
  2. ダウンロードしたものをインストールする。
  3. 先ほど転送した「client.ovpn」をOpenVPN Connectのアプリにドラッグ&ドロップ、またはダブルクリックしてアプリに読み込ませる。
  4. addを押下するとプロファイルが追加される。
  5. ボタンを押下して接続できるか確認する。

PAM認証によるID/パスワード認証の設定

ここまでは鍵認証方式によるOpenVPN接続について設定したが、セキュリティを強化するためにID/パスワード認証を追加で導入する。

ファイルのディレクトリを確認

$ find / | grep openvpn-plugin-auth-pam
  #openvpn-plugin-auth-pam のディレクトリを確認
  #当方は「/usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so」であった。

$ ls /etc/pam.d/login  #loginが存在するか確認

VPNサーバーの設定

$ vim /etc/openvpn/server.conf  #サーバの設定ファイルを編集
  #末尾に以下を追記し保存する。
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so login
 #ディレクトリは先ほど確認した場所を指定する。
$ service openvpn@server restart  #VPNサーバーを再起動

クライアントファイルの修正

$ vim /etc/openvpn/client/client01.ovpn  #クライアントファイルを編集
  #末尾に以下を追記し保存する。
auth-user-pass
 #これにより、ID/パスワードを入力できようになる。
  1. 「client01.ovpn」をクライアントPCに移動させる。
  2. ファイルをインポートし、IDとパスワードを入力する。
  3. 「send」ボタンを押下して接続できるか確認する。rootではログインできないため、管理権限のないユーザーで認証すること。

Google Authenticatorを用いた認証

セキュリティをさらに強化するために、鍵認証方式+パスワード認証方式+OTPの3つを合わせた強固な認証を行う。そのためにGoogle Authenticatorを用いた認証を導入する。

Google認証システムの導入

$ apt install -y libpam-google-authenticator  #Google Auth PAMモジュールをインストール
$ addgroup gauth  #gauthグループを作成
$ useradd -g gauth gauth  #gauthグループに属するgauthというユーザーを作成
$ mkdir /etc/openvpn/google-authenticator  #Google Auth用のディレクトリを作成
$ chown gauth:gauth /etc/openvpn/google-authenticator  #gauthにディレクトリの所有者を変更
$ chmod 0700 /etc/openvpn/google-authenticator  #gauthにのみ権限を付与

シークレットキーの生成

$ su -c "google-authenticator -t -d -r3 -R30 -f -l \"My VPN\" -s /etc/openvpn/google-authenticator/client01" - gauth  #Google Authトークンを作成
  #「client01」はVPNでログインするクライアントのユーザー名に変えること。

  Your new secret key is:*****  #シークレットキーをコピーしておく。
  Enter code from app (-1 to skip):-1
  Do you want to do so? (y/n):n

$ apt install -y qrencode  #QRコードを出力するためのツールをインストール
$ qrencode -o QR.png otpauth://totp/label?secret=*****  #*****に先程のシークレットキーを貼り付ける。
$ scp QR.png user@192.168.***.***:Downloads/ #クライアントPCにQRコードを転送
  #IPアドレスとディレクトリは各自指定

$ rm QR.png  #QRコードの転送が確認できたら、セキュリティの観点から削除する。

スマートフォンでGoogle認証システムの認証コードを表示

  • iPhone(iOS)の場合(iOSが最新にアップデートされていること)
    1. 「設定」->「パスワード」-> 右上の「+」
      • Webサイト:VPNサーバーのIPアドレス(192.168.***.***)
      • ユーザ名:VPNサーバーのユーザ名
      • パスワード:VPNサーバーのパスワード
    2. 入力後、「完了」を押下
    3. 保存したパスワードを開き、「アカウントオプション」->「確認コードを設定…」->「QRコードをスキャン」
    4. 先ほど生成したQRコードをスキャンさせると、認証コードが表示される。
    5. キーチェーンをiCloudに同期させると、他のデバイスやMacからでも確認可能
  • Androidの場合
    1. Google 認証システム アプリをインストール
    2. アプリを起動し右下の「+」ボタンを押下して「QRコードをスキャン」
    3. アップのトップに認証コードが表示される。

PAMの設定

$ vim /etc/pam.d/openvpn  #PAMの設定ファイルを編集
  #末尾に以下を追記し保存する。
auth required pam_google_authenticator.so authtok_prompt=pin secret=/etc/openvpn/google-authenticator/${USER} user=gauth

VPNサーバーの設定

$ vim /etc/openvpn/server.conf  #サーバー設定ファイルを編集
 #ID/パスワード認証で最終行に追記した「plugin」の1行を「yy」「p」でコピー&ペーストし、「login」を以下のように変更する。
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so login
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so "openvpn login USERNAME password PASSWORD pin OTP"
reneg-sec 43200  #再認証までの時間が問題ないか確認し、保存する。
  #短すぎると再認証の度にGoogle Authのコード入力が必要となるので注意すること。
$ service openvpn@server restart  #VPNサーバーを再起動

クライアントファイルの設定

$ vim /etc/openvpn/client/client01.ovpn  #クライアントファイルを編集
  #末尾に以下を追記し保存する。
static-challenge "Enter Google Authenticator Code:" 1
  1. 保存したら「client.ovpn」をクライアントPC(またはスマホ)に移動させる。
  2. ファイルをインポートし、IDとパスワードを入力する。
  3. 接続ボタンを押下すとコードを聞かれるので、認証コード6桁の数字を入力し、接続できるか確認する。

まとめ

以上の設定でOpenVPNサーバーを構築することができる。

鍵認証方式+パスワード認証方式+OTPの3つを合わせた認証により、外向きにポートを空けても安全性を一定程度、確保することができる。

Linux

次の記事

DHCPサーバーの構築