LoginSignup
7
7

さくらのVPS上にVPNサーバーを建てる

Last updated at Posted at 2023-05-10

さくらのVPS上にOpenVPNサーバーを建てた奮闘記です。
OpenSSL 3.1.0を使ってビルドし、もちろんIPv6にも対応します。
加えてOpenVPN 2.6.0からサポートされた高速化機能であるDCO(Data Channel Offload)を有効にします。

きっかけ

僕は大のアニメ好きでdアニメストアを心から愛用しています。
電車や駅、ホテルなどの公共Wi-Fiでdアニメストアに接続することも多々ありますが、セキュリティ的にVPNを通して接続したいと思っていました。
しかし、dアニメストアは海外からの接続を拒否しており、一般のVPNやAWSに建てていたOpenVPNサーバーからでは接続できませんでした。
VPNで日本のサーバーを選択したり、AWSで東京リージョンを通して接続しても海外からのアクセスと判定されてしまいます。
そこでさくらのVPSならば日本からのアクセスとして認定していただけるのではと思い建設を決意した次第です。
一応結論を先に申し上げると、無事にさくらのVPS上のOpenVPNを通してdアニメストアにアクセスできました。

環境

さくらのVPS 大阪
512MBプラン
Rockey LINUX 5.14.0

必要なもの

入ってるものも多いと思いますが一応。

sudo yum install wget git tar make gcc perl libnl3-devel libcap-ng-devel systemd-devel pam-devel

OpenSSL

OpenSSL 3.1.0が正式にリリースされましたので折角ですから導入します。
標準のOpenSSLでよい人は読み飛ばしてください。

wget https://www.openssl.org/source/openssl-3.1.0.tar.gz # 下記リンクを参照して最新版を入れてください
tar -xf openssl-3.1.0.tar.gz
cd openssl-3.1.0
./config shared --prefix=/opt/openssl # お好きなディレクトリにどうぞ
make
make test # 結構時間かかります
          # PASS とでればOK
sudo make install

OpenSSL公式に最新版へのリンクがありますので必ず最新のものを引っ張ってきてください。

--prefixを付けないと現行版と衝突します。
ライブラリを置き換えちゃったりするとsudoやらsshが使えなくなって死するのでお気を付けください。
(経験者)

使えるように

上記手順ではopenssl versionしても既存のものが出てきます。
パッケージ管理ツールではない管理をするのは望ましくないので問題ないと思いますが、新しいものを常用するには以下の手順を踏んでください。

cd
nano .bashrc

以下を追記。

.bashrc
alias openssl="/opt/openssl/bin/openssl"

環境変数を設定します。

cd
nano .bash_profile

以下を追記。

.bash_profile
export LD_LIBRARY_PATH="/opt/openssl/lib64"

環境によっては/opt/openssl/libに入っている場合があります。
エラーになったら確認してみてください。
その場合は以降のコマンド全てを読み替えてください。

OpenVPN

それでは本命のOpenVPNを導入していきます。

wget https://swupdate.openvpn.org/community/releases/openvpn-2.6.3.tar.gz # 下記リンクを参照
tar -xf openvpn-2.6.3.tar.gz
cd openvpn-2.6.3
./configure --enable-systemd --disable-lz4 --disable-lzo PKG_CONFIG_PATH=/opt/openssl/lib64/pkgconfig # 環境によっては"/opt/openssl/lib/pkgconfig"
make
make check # "All * tests passed" とでればOK
sudo make install

OpenVPN公式を参照して最新のものを導入してください。
スクロールしていってSource archive fileとあるものです。

何もしないとpkg-configがデフォルトのOpenSSLを探しに行くのでビルドした方のパスを渡して上書きします。
ここに行きつくまで./configureCFLAGやらLDFLAGSやら渡してごにょごにょしていましたが、これで一発です。
pkg-configに乾杯。

openvpn --versionして正しく表示されれば無事インストール完了です。

easyrsa

VPN接続用の認証局を作ります。

cd /opt # お好きなディレクトリにどうぞ
sudo git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3
sudo ./easyrsa init-pki
sudo nano pki/vars

Easy-RSAの設定であるvarsファイルを編集します。
僕の設定で変更している変数だけ書き出します。
該当する箇所を探してコメントアウトを外して書き換えてください。
私的に好みな強めセキュア設定になっていますのでお好みでどうぞ。

pki/vars
# "openssl 3.1.0"のインストール先を指定します
set_var EASYRSA_OPENSSL       "/opt/openssl/bin/openssl"

# 安全で速くてつよつよのツイステッドエドワーズ曲線を使います
set_var EASYRSA_ALGO          ed
set_var EASYRSA_CURVE         ed25519

# 暗号の桁数を上げます
set_var EASYRSA_DIGEST        "sha512"


##
# 以下はちょくちょく見かける誤った情報の訂正です
##

# 上のEASYRSA_ALGOがrsaの場合以外は鍵長の設定は不要で変更しても無視されます
# 安全性的にはデフォルトで十分で、変更する場合も4096が多くのデバイスで使用できる現実的な最長です
#set_var EASYRSA_KEY_SIZE      2048

# EASYRSA_DNを"cn_only"に設定している場合(デフォルト)、以下の値は無視されるので設定不要です
#set_var EASYRSA_REQ_COUNTRY  "US"
#set_var EASYRSA_REQ_PROVINCE "California"
#set_var EASYRSA_REQ_CITY     "San Francisco"
#set_var EASYRSA_REQ_ORG      "Copyleft Certificate Co"
#set_var EASYRSA_REQ_EMAIL    "me@example.net"
#set_var EASYRSA_REQ_OU       "My Organizational Unit"

編集が完了したら作業を続行します。

# [1] 認証局を作成します
sudo env LD_LIBRARY_PATH=/opt/openssl/lib64 ./easyrsa build-ca
# パスワードを求められるので設定します

# [2] サーバーの証明書と鍵を作ります
sudo env LD_LIBRARY_PATH=/opt/openssl/lib64 ./easyrsa build-server-full {server_name}
# サーバー用のパスワードと、先ほど設定した認証局のパスワードを入力します

# [3] クライアントの証明書と鍵を作ります
sudo env LD_LIBRARY_PATH=/opt/openssl/lib64 ./easyrsa build-client-full {client_name}
# サーバーと同様の作業をします

備考

デフォルトではないOpenSSLを使う場合、何もしないとライブラリが衝突してエラーが出ます。
easyrsaインストール後に設定した環境変数はsudoには引き継がれないので、エラー対策としてenv LD_LIBRARY_PATH=/opt/openssl/lib64で環境変数を設定しながらeasyrsaを実行します。

ちなみに、init-pkiのときはvarsファイルの内容が反映されずデフォルトのopensslコマンドの方が使われるようですので、環境変数の設定は不要です。

設定

それではOpenVPNを設定していきます。

# 設定ファイルを補完するディレクトリを作成します
sudo mkdir -p /etc/openvpn/server
# 移動します
cd /etc/openvpn/server
# "ta.key"を生成します
sudo env LD_LIBRARY_PATH=/opt/openssl/lib64 openvpn --genkey secret ta.key
# "DH鍵"を生成します
sudo env LD_LIBRARY_PATH=/opt/openssl/lib64 /opt/openssl/bin/openssl dhparam -out dh2048.pem 2048
# サンプルファイルをコピーします
sudo cp /opt/openvpn-2.6.3/sample/sample-config-files/server.conf ./server.conf.example
# サーバーファイルを編集します
sudo nano server.conf

コピーしたサンプルを参考に、設定ファイルを編集します。
私の使用している設定を書き出します。

server.conf
port 1194
proto udp6
dev tun
ca /opt/easy-rsa/easyrsa3/pki/ca.crt
cert /opt/easy-rsa/easyrsa3/pki/issued/server.crt
key /opt/easy-rsa/easyrsa3/pki/private/server.key
dh dh2048.pem
topology subnet
server 10.8.0.0 255.255.255.0
server-ipv6 fdxx:xxxx:xxxx::/64 # 下記参照
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp ipv6 block-local"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
push "dhcp-option DNS 2001:4860:4860::8888"
push "dhcp-option DNS 2001:4860:4860::8844"
keepalive 10 120
tls-auth ta.key 0
cipher AES-256-GCM
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
log-append  /var/log/openvpn.log
verb 3
explicit-exit-notify 1
auth SHA512
askpass pass.txt # パスワードを記述したファイル

OpenVPNは起動時にサーバー用証明書のパスワードを聞かれます。
そのままだとsystemdで自動起動する際に不便ですので回避する設定をします。

# "build-server-full"の時に入力したパスワードを記載したファイルを作成します
sudo nano /etc/openvpn/server/pass.txt

# 一応root以外が読めないようにしておきます
sudo chmod 600 /etc/openvpn/server/pass.txt

server-ipv6

このディレクティブに指定するユニークローカルアドレスは下記サイトで生成します。
https://www.jdoodle.com/a/20tR

MACアドレスを入力するとULA Prefixというのが生成されるので、生成されたプレフィックスから好きなサブネットを選びます。
基本的には無難にFirst Subnetに出てくる一番最初のサブネットでよいと思います。

備考

fd00::/8のアドレスブロックに割り当てられるアドレスはipv4ではプライベートアドレスに相当します。
基本的にはユーザーが自由に設定できるものになりますが、プレフィックスが重複しないようにMACアドレスと乱数から生成する方法がRFCで定められています。
先のウェブページはその方法に則ってプレフィックスを生成してくれます。

生成されたプレフィックスから選んだサブネットが、VPNからクライアントに割り当てられるアドレスのサブネットになります。

転送

クライアントに鍵等を転送します。
私はWinSCPでコピーしてしまいますが、皆さんお好きなようにクライアント用のカギと証明書をコピーしてください。
クライアントで必要なものは以下の通りです。

ファイル名 場所
ca.crt {path_to_easyrsa3}/pki/ca.crt
ta.key 出力した場所(/etc/openvpn/server/ta.key)
{client_name}.crt {path_to_easyrsa3}/pki/issued/{client_name}.crt
{client_name}.key {path_to_easyrsa3}/pki/private/{client_name}.key

systemd

systemdの設定をします。
まずはユニットファイルをコピーします。

# ダウンロードしたユニットファイルをコピーします
sudo cp /opt/openvpn-2.6.3/distro/systemd/openvpn-server@.service /etc/systemd/system/

# OpenSSLライブラリの変更を反映するよう書き換えます
sudo nano /etc/systemd/system/openvpn-server@.service

ユニットファイルのExecStart部分を書き換えます。
実行コマンドの先頭に/bin/env LD_LIBRARY_PATH=/opt/openssl/lib64を追記します。

openvpn-server@.service
#         |=>                追記部分                <=|
ExecStart=/bin/env LD_LIBRARY_PATH=/opt/openssl/lib64 /usr/local/sbin/openvpn (以後同)

書き換えが完了したら作業を続行します。

# 反映させます
sudo systemctl daemon-reload
# 起動します
sudo systemctl start openvpn-server@server

もしエラーが出たらcat /var/log/openvpn.logでそれらしいメッセージを探して解決します。

DCOの有効化

ついでですので、最近サポートされた高速化機能のDCOを有効にするためカーネルモジュールovpn-dcoを導入します。
カーネルの方でパケットの処理をするので通信速度が向上するそうです。

sudo yum install kernel-devel
git clone https://github.com/OpenVPN/ovpn-dco.git
cd ovpn-dco
make
#make tests # 以下参照: 現状では失敗します
sudo make install
sudo systemctl restart openvpn-server@server

# 無事に反映されているか確認します
sudo cat /var/log/openvpn.log | grep "DCO version"
# "DCO version: 0.2.20230426"とバージョンが表示されれば成功です

テスト

テストの実行に少なくとも以下のものが必要です。
が、これを入れても失敗します…。
試行錯誤しましたが解決できませんでした。
GitHubで開発者の方からテストコードはメンテしていないと回答がありました。
現在は動かないだろうとのことなのでこれで問題ないのでしょう。
今後も修正される予定はないそうですし、ちゃんと起動できて反映もされているのでよしとします。

#sudo yum install python3-jinja2 python-jsonschema
#git clone https://github.com/Mbed-TLS/mbedtls.git
#cd mbedtls
#make
#make check
#sudo make install

ファイアウォール

前提としてfirewalldで管理しているものとします。
さくらのパケットフィルターは有効無効どっちでも構いません。
余談ですが僕はあれにほとんどセキュリティ上の機能を期待していません。
TCP/UDP 32768-65535番ポート、NTP UDP 123番ポート、IPv6通信、ICMP、フラグメントパケットは全て許可されますってやばくないですか??
あーしは専門家じゃないのでなんも言えませんが、この辺の機能はAWSしか勝たんです。(多分)
もしさくらのパケットフィルタを有効にしている場合は1194許可にしておいてください。

それではfirewalldの作業をしていきます。

sudo firewall-cmd --add-service=openvpn

# 正しく反映されているか確認
sudo firewall-cmd --list-all
# "services"に"openvpn"があればOK

sudo firewall-cmd --runtime-to-permanent

接続

ここまでの作業で接続自体は可能ですのでクライアントから接続してみます。
OpenVPN GUIをインストールし、起動します。

ダウンロード | OpenVPN.JP

コンフィグディレクトリ(デフォルト: C:\Users\{User_Name}\OpenVPN\config)に以下の設定ファイルと転送した鍵・証明書を配置していきます。

設定ファイルは以下の通りです。
remote example.comはご自身のドメインに変更してください。

client.ovpn
client
dev tun
windows-driver wintun
proto udp
remote example.com 1194
resolv-retry infinite
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-GCM
verb 3
topology subnet
auth SHA512
data-ciphers AES-256-GCM

配置が完了したらタスクトレイにあるOpenVPN GUIのアイコンを右クリックしてclientメニュー(または.ovpnのファイル名)から接続をクリックします。
パスワードを聞かれるのでeasy-rsaでの証明書作成作業中build-client-fullの時に入力したパスワードを入力します。

割り当てられたIPアドレスを通知するポップアップが出れば接続成功です。
接続成功してもインターネットには接続できませんが、問題ありませんので切断して作業を続けます。

接続失敗したらログなどを見ながらググって試行錯誤して下さい。
ファイアウォールやドメインなどが正しく設定されてサーバーに到達できていれば、サーバー側の(/var/log/openvpn.log)にもログが残っているはずです。

転送設定

まずはカーネルのパケット転送を許可します。
カーネルパラメータの設定ファイルである/etc/sysctl.confを開いて以下を追記します。

/etc/sysctl.conf
# Forward packet for VPN
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

追記したら以下のコマンドで反映させます。

sudo sysctl -p

ファイアーウォールにもフォワードとマスカレードの設定を行います。
以下のコマンドを実行します。
さくらのRockey Linuxはデフォルトではネットワークインターフェースの名前がens3になりますが、環境が異なる場合ip aで調べて適切な名前に置換してください。

sudo firewall-cmd --add-masquerade
sudo firewall-cmd --direct --add-rule ipv6 filter INPUT 1 -p icmpv6 -j ACCEPT
sudo firewall-cmd --direct --add-rule ipv6 filter FORWARD 1 -i ens3 -o tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo firewall-cmd --direct --add-rule ipv6 filter FORWARD 1 -i tun0 -o ens3 -j ACCEPT
sudo firewall-cmd --direct --add-rule ipv6 nat POSTROUTING 0 -o ens3 -j MASQUERADE

sudo firewall-cmd --runtime-to-permanent

少しだけ解説します

  • IPv6での通信に必須なIPMPv6をすべて受け入れるようにして
  • ens3 -> tun0(内向き通信)では返答パケット(フラグがrelatedまたはestablished)を許可
  • tun0 -> ens3(外向き通信)ではすべて許可
  • ens3からの外向きはマスカレードする

というようなことをしています。

マスカレードは簡単に言うと差出人書き換えです。
tun0配下のAというクライアントデバイスから送られたパケットをそのままVPNを通して外に送ると、帰りのパケットを返信の際にAに直接届けようとして届きません。
マスカレードすることでVPNのアドレスが送信元になりますのでVPNにちゃんと帰ってくるようになるわけです。

接続

ここまでの作業でVPNを経由してインターネットに接続できるようになっているはずです。
先の接続と同様にOpenVPN GUIで接続してみましょう。
ブラウザなどで確認するかpingで確認してもよいでしょう。

ちなみにIPv6で接続できているかは、IPv6テストにて確認します。
アクセスした結果のIPv6アドレスがVPNサーバーのものになっていれば接続できています。

もし接続できない場合はVPNサーバーdns.googleIPv4IPv6など色々変えながらpingするとよいでしょう。
どこまで接続できていてどこで止まっているのかがわかるとトラブルシュートしやすいです。

中々解決しない場合はWireSharkでの分析もおすすめです。
pingが行ったっきり戻ってこないときはマスカレードやフォワードなどの設定が疑わしいです。
頑張ってください。

まとめ

以上、無事に接続できましたでしょうか?
僕はさんざん試行錯誤して接続しました。
特にマスカレードなど、接続した後が大変でした。
IPv6での接続も日本語で情報が少ない気がするので参考になれば幸いです。

それでは皆様、よきセキュアな日々をお過ごし下さい!

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7