🔖

OpenVPNで低コストにVPNサーバを自作構築して、固定IPでアクセス制限する

2024/02/13に公開

はじめに

こんにちは、Acompanyの田中です。この記事は、Acompanyのバレンタインアドカレ25日目の記事です。
https://recruit.acompany.tech/264dc776835849328cdbdb52b369c6f9
今回は、OpenVPNで低コストにVPNサーバを自作構築して、固定IPでアクセス制限できるようにする手順を紹介します。

やりたいこと

  • 固定IPでサーバへのアクセス制限がしたい
  • 複数人で同じ固定IPを使えるようにしたい
  • MacのPC環境でもUbuntuのサーバ環境でも同じ固定IPが使いたい
  • できるだけ低コストで構築・管理したい

実現方法

StaticIP代の$3.6と合わせて大体月$20(3000円)くらいで運用できる。50クライアント分くらいは問題なく使えそう。
月間予測$16.99

動作確認済み条件

  • VPNクライアント動作環境
    • OS: Ubuntu 22.04(GCE/n2-standard-2)
      • OpenVPN: 2.5.9 x86_64-pc-linux-gnu
    • OS: M1 MacBookPro(2021) Ventura 13.3.1
      • OpenVPN Connect: 3.4.8
  • VPNサーバ動作環境
    • OS: Ubuntu 22.04(GCE/e2-small)
      • OpenVPN: 2.5.9 x86_64-pc-linux-gnu

1. VPNサーバの構築

VPNサーバとして利用するVMはEC2でもAzure VMでも何でも良いが、今回はGCE(Google Compute Engine)を使う。

1-1. VPCネットワークを作成

名前を適当に設定する。ここではvpc-openvpn-serverとした。
名前を設定

新しいサブネットsubnet-openvpn-serverを作成する。
リージョンはどこでも良いが、物理的に近い方が何かと良いのでasia-northeast-1にしておく。
IPv4範囲は特にこだわりがなければ10.0.0.0/24で良い。
新しいサブネットを作成

ファイアウォールを設定する。
vpc-openvpn-server-allow-customフィルタ0.0.0.0/0プロトコル/ポートudp:1194とし、vpc-openvpn-server-allow-customvpc-openvpn-server-allow-icmpvpc-openvpn-server-allow-sshにチェックを入れておく。
udp:1184を設定
ファイアウォールを設定

作成ボタンを押してVPCネットワークを作成する。作成が完了するまで数分待つ。

1-2. VMインスタンスを作成

GCEでVMインスタンスを作成していく。

名前とリージョンを設定する。名前はvm-openvpn-serverとした。リージョンは1-1でサブネットに設定したのと同じasia-northeast-1としておく。

名前とリージョンを選択

マシンの構成を選択する。今回はe2-smallとしたが、一般的なvpnサーバとしての用途であればこのスペックで事足りると思う。

参考までに、40名ほどが利用している環境で`e2-medium`はオーバースペック気味。

そこまでCPUとメモリはあまり積む必要はない。ヘビーな使い方でなければネットワーク帯域幅もそこまで気にすることはないと思う。

帯域幅が気になる方はここを参照。

マシンの構成を選択

ブートディスクをUbuntu22.04(x86/64)に設定する。デフォルトでDebianになっているので変更を忘れずに。
ブートディスクをUbuntu22.04(x86/64)に設定

IP転送を有効にする。VM作成後に設定を変更できないのでこれも忘れずに。
IP転送を有効にする

ネットワークインターフェースを設定する。defaultは削除して「ネットワーク インターフェースを追加」を押して1-1で作成したVPCネットワークvpc-openvpn-serverとサブネットワークsubnet-openvpn-serverを選択する。

ネットワーク インターフェースを設定
この時、「外部IPv2アドレス」の項目から静的外部IPv4アドレスを予約する。vmを再起動してもIPを固定するために必要。
静的外部IPv4アドレスを予約

作成ボタンを押してVMインスタンスを作成する。作成が完了するまで数分待つ。

1-3. OpenVPNのインストールとVPNサーバの設定

1-2で作成したVMインスタンスにsshし、以下のコマンドを叩いてoepnvpnをインストールする。

$ sudo apt-get -y install dnsutils
$ wget -O openvpn.sh https://get.vpnsetup.net/ovpn
$ sudo bash openvpn.sh --auto

引用: https://github.com/hwdsl2/openvpn-install

ログが以下で終わっていれば問題なくインストールとclient.ovpnファイルの作成が完了しているはず。

OpenVPN Script
https://github.com/hwdsl2/openvpn-install

(中略)

Finished!

The client configuration is available in: /home/XXXX/client.ovpn
New clients can be added by running this script again.
正常に完了したことの確認
$ openvpn --version
OpenVPN 2.5.9 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Sep 29 2023
library versions: OpenSSL 3.0.2 15 Mar 2022, LZO 2.10
Originally developed by James Yonan
Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
Compile time defines: enable_async_push=no enable_comp_stub=no enable_crypto_ofb_cfb=yes enable_debug=yes enable_def_auth=yes enable_dependency_tracking=no enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_fast_install=needless enable_fragment=yes enable_iproute2=no enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes enable_maintainer_mode=no enable_management=yes enable_multihome=yes enable_option_checking=no enable_pam_dlopen=no enable_pedantic=no enable_pf=yes enable_pkcs11=yes enable_plugin_auth_pam=yes enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no enable_shared=yes enable_shared_with_static_runtimes=no enable_silent_rules=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no enable_systemd=yes enable_werror=no enable_win32_dll=yes enable_x509_alt_username=yes with_aix_soname=aix with_crypto_library=openssl with_gnu_ld=yes with_mem_check=no with_openssl_engine=yes with_sysroot=no
$ sudo systemctl status openvpn-server@server.service
● openvpn-server@server.service - OpenVPN service for server
     Loaded: loaded (/lib/systemd/system/openvpn-server@.service; enabled; vendor preset: enable>
     Active: active (running) since Mon 2024-02-12 19:28:34 UTC; 3min 48s ago
       Docs: man:openvpn(8)
             https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
             https://community.openvpn.net/openvpn/wiki/HOWTO
   Main PID: 3381 (openvpn)
     Status: "Initialization Sequence Completed"
      Tasks: 1 (limit: 2337)
     Memory: 2.0M
        CPU: 134ms
     CGroup: /system.slice/system-openvpn\x2dserver.slice/openvpn-server@server.service
             └─3381 /usr/sbin/openvpn --status /run/openvpn-server/status-server.log --status-ve(略)

上記から自動的にopenvpn2のインストールと、daemonの作成/再起動設定が行われていることがわかる。

1-4. .ovpnファイルを作成してVPNクライアント端末に配布

以下のコマンドを実行すると、client-0001.ovpnという名前のファイルが生成される。

$ sudo bash openvpn.sh <<ANSWERS
1
client-0001
ANSWERS

以下のようなログが出力される。

OpenVPN Script
https://github.com/hwdsl2/openvpn-install

OpenVPN is already installed.

Select an option:
   1) Add a new client
   2) Export config for an existing client
   3) List existing clients
   4) Revoke an existing client
   5) Remove OpenVPN
   6) Exit

Provide a name for the client:
+ ./easyrsa --batch --days=3650 build-client-full client-0001 nopass

client-0001 added. Configuration available in: /home/XXXX/client-0001.ovpn

この.ovpn ファイルはVPNクライアントで使用する際に必要な設定ファイルのため、クライアント端末に配布しておく。

追加で別のユーザーを作成したい場合は、以下のようなコマンドを実行すれば<ユーザー名>.ovpnという形式で新たにファイルが生成される。

$ sudo bash openvpn.sh <<ANSWERS
1
<ユーザー名>
ANSWERS

2. OpenVPNクライアントの設定(Mac Ventura編)

2-1. OpenVPN Connect をインストール

OpenVPN公式リンクからMacOSに対応したOpenVPN Connectをダウンロード&インストールする。

※ もし上記で解決しなかった場合はこちらの「インストール」セクションも併せて参照してください

2-2. .ovpn をインポート

  1. OpenVPN Connectを開いて「+」ボタンを押す
    +
  2. Import Profileで.ovpnファイルをimportして「Connect」を押す
    Import Profile

以下のようにCONNECTEDとなればVPN接続成功。
CONNECTED

2. OpenVPNクライアントの設定(Ubuntu22.04編)

2-1. インストール

$ sudo apt update && sudo apt install -y openvpn
インストールできたことを確認する。
$ openvpn --version
OpenVPN 2.5.9 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Sep 29 2023
library versions: OpenSSL 3.0.2 15 Mar 2022, LZO 2.10
Originally developed by James Yonan
Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
Compile time defines: enable_async_push=no enable_comp_stub=no enable_crypto_ofb_cfb=yes enable_debug=yes enable_def_auth=yes enable_dependency_tracking=no enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_fast_install=needless enable_fragment=yes enable_iproute2=no enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes enable_maintainer_mode=no enable_management=yes enable_multihome=yes enable_option_checking=no enable_pam_dlopen=no enable_pedantic=no enable_pf=yes enable_pkcs11=yes enable_plugin_auth_pam=yes enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no enable_shared=yes enable_shared_with_static_runtimes=no enable_silent_rules=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no enable_systemd=yes enable_werror=no enable_win32_dll=yes enable_x509_alt_username=yes with_aix_soname=aix with_crypto_library=openssl with_gnu_ld=yes with_mem_check=no with_openssl_engine=yes with_sysroot=no

2-2. 設定

sudo openvpn --config "$CONFIG_FILE"でvpnをopenできるが、今後sshでshellにログインする際に毎回上記のコマンドを実行するのは大変なので、以下のカスタムshellscriptを作成しておく。

/usr/local/bin/vpn.shを作成する。

$ sudo touch /usr/local/bin/vpn.sh
$ sudo chmod +x /usr/local/bin/vpn.sh
$ sudo vi /usr/local/bin/vpn.sh

以下のshellscriptをコピペする。

#!/bin/bash
# setup:
# $ sudo apt update && sudo apt install openvpn -y
# create client.ovpn
# $ sudo vi /etc/openvpn/client.ovpn

CONFIG_FILE="/etc/openvpn/client.ovpn"
LOG_FILE="/tmp/openvpn-status.log"
PID_FILE="/var/run/openvpn-client.pid"

function get_current_ip() {
    curl -s https://api.ipify.org
}

function start_vpn() {
    if pgrep -F "$PID_FILE" >/dev/null 2>&1; then
        echo "VPN is already running."
        return
    fi

    echo "Current IP: $(get_current_ip)"
    echo "Starting VPN..."
    sudo openvpn --config "$CONFIG_FILE" --log "$LOG_FILE" --daemon --writepid "$PID_FILE"
    sleep 5
    echo "New IP: $(get_current_ip)"
}

function stop_vpn() {
    if [ -f "$PID_FILE" ]; then
        sudo kill "$(cat $PID_FILE)"
        sudo rm -f "$PID_FILE"
        echo "VPN closed."
    else
        echo "VPN is not running."
        exit 1
    fi
}

function check_vpn_status() {
    if pgrep -F "$PID_FILE" >/dev/null 2>&1; then
        echo "VPN is running."
        echo "Current IP: $(get_current_ip)"
    else
        echo "VPN is not running."
        exit 1
    fi
}

case "$1" in
    open)
        start_vpn
        exit 0
        ;;
    close)
        stop_vpn
        exit 0
        ;;
    status)
        check_vpn_status
        exit 0
        ;;
    *)
        echo "Usage: $0 {open|close|status}"
        exit 1
        ;;
esac

vpn.shが実行できるか確認する。

$ vpn.sh status # 接続確認
VPN is not running.
$ vpn.sh open # 接続
Current IP: ?.?.?.?
Starting VPN...
New IP: X.X.X.X
$ vpn.sh close # 切断
VPN closed.

これにより、以降はvpn.sh {open|close|status}で接続, 切断, 接続確認ができるようになった。

3. 固定IPでアクセス制限

AWSの場合はセキュリティグループ、GCPの場合はファイアウォールのようなマネージドサービスを利用してIP制限ができる。今回はGCPの場合で説明する。

以下のように特定のネットワーク(dafault)にファイアウォールルールを作成する。
この場合、defaultネットワークを利用しているすべてのリソースのうち、vpnというタグがついたものはVPNからのすべての通信が許可されるルールが適用される。

このように、自由にstatic-ip-openvpn-serverで予約した静的IPを固定IPとして任意の環境でアクセス制限を行える。

備考

  • https://github.com/angristan/openvpn-install で起動したVPNサーバはwebに繋がらなくて断念した
  • ssh中のVMでLinux版のopenvpn3 clientを使うと一生VMにsshできなくなる問題にぶち当たったのでopenvpn2を使っている
  • openvpn3を使いたい方はこちらを参照

おわりに

弊社Acompanyは次世代暗号技術である秘密計算やゼロ知識証明、k匿名化、差分プライバシーなどのプライバシーテックの研究開発と、それらを用いてプライバシー保護した状態でデータ分析が利用可能なデータクリーンルームの開発・提供を行っています。

絶賛採用中なので興味があれば下記採用ページ内のGoogleFormか、XのDMから連絡お願いします。
https://recruit.acompany.tech/25fe8241328e4d988ce124faad6e989f

Discussion