Skip to content

OpenVPNサーバ構築

2017/11/15

自宅にVPNサーバを建てたいなぁと思っていて、いろいろ試行錯誤したところ、 OpenVPNで落ち着いたため、その構築記録。
OpenVPNはシンプルで設定もあまり量もなく、比較的構築は簡単だった。

OpenVPN自体はOpenSSLの技術を用いているとのこと。
したがって、サーバ証明書、クライアント証明書、CAを作成する必要がある。

今回構築環境は以下の通り。

サーバ側の構築

サーバ側の情報は以下
* CentOS 7.1(KVM上の仮想マシン)

必要なパッケージのインストール

# yum --enablerepo=epel -y install openvpn easy-rsa net-tools

サーバ証明書等を作る必要があるのでeasy-rsaを活用する。

証明書類の作成

CA証明書の作成

easy-rsaインストール後、環境変数を設定するファイルがあるので編集する。 必要な設定値は下記の通り。

# vim /usr/share/easy-rsa/2.0/vars

export KEY_COUNTRY="JP" ←国
export KEY_PROVINCE="Tokyo" ←都道府県
export KEY_CITY="Shinjuku" ←市区町村
export KEY_ORG="hogehoge" ←団体名
export KEY_EMAIL="hoge@hogehoge.com" ←メールアドレス

ちなみに、このファイルの編集はデフォルトの設定値として設定するだけなので、ファイルの編集はしなくとも、鍵生成時に鍵生成に必要な情報を入力しても良い。
個人用途なため、値はわりと適当で良い。(VPNのサーバとクライアント間でしか使わないので)

ファイルを編集後、環境変数を設定し、CAを作る。

# cd /usr/share/easy-rsa/2.0/

## sourceコマンドで、環境変数を設定
# source ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /usr/share/easy-rsa/2.0/keys
# ./clean-all 

## ca作成のコマンド
# ./build-ca
Generating a 2048 bit RSA private key
.......................................+++
............+++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----

## 以下パラメータ入力。デフォルトとして上記の設定が表示されるようになるので、変更がなければそのままEnter
Country Name (2 letter code) [JP]:
State or Province Name (full name) [Tokyo]:
Locality Name (eg, city) [Shinjuku]:
Organization Name (eg, company) [hogehoge]:
Organizational Unit Name (eg, section) []:
## 以下にはサーバのホスト名を入れておく
Common Name (eg, your name or your server's hostname) []:vpn 
Name [EasyRSA]:
Email Address [hoge@hogehoge.com]:
{1}

/usr/share/easy-rsa/2.0/keysの配下にca.crt, ca.key等が生成される。
主にcrtの方を使う。

サーバ証明書の作成

環境変数は設定済みなので、コマンドを変えて生成する。

## ここの引数の"server"の箇所は鍵ファイルの名前なので、好きな名前をつけても良い
# ./build-key-server server
Generating a 2048 bit RSA private key
.......................................................+++
.......+++
writing new private key to 'server.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----

Country Name (2 letter code) [JP]:
State or Province Name (full name) [Tokyo]:
Locality Name (eg, city) [Shinjuku]:
Organization Name (eg, company) [hogehoge]:
Organizational Unit Name (eg, section) []:
## 以下はサーバのホスト名を入れておく
Common Name (eg, your name or your server's hostname) [server]:vpn

## サーバ側としてわかりやすい名称をつけておく
Name [EasyRSA]:server-CRT
Email Address [hoge@hogehoge.com]:
{1}
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /usr/share/easy-rsa/2.0/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'JP'
stateOrProvinceName   :PRINTABLE:'Tokyo'
localityName          :PRINTABLE:'Shinjuku'
organizationName      :PRINTABLE:'hogehoge'
organizationalUnitName:PRINTABLE:''
commonName            :PRINTABLE:'vpn'
name                  :PRINTABLE:'server-CRT'
emailAddress          :IA5STRING:'hoge@hogehoge.com'
Certificate is to be certified until Oct 14 22:57:53 2027 GMT (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

server のcsr, crt, keyが生成される。

クライアント証明書の作成

こちらもコマンドを変えるだけで良い。

## 引数の"client"は好きな名前で良い。
# ./build-key client
Generating a 2048 bit RSA private key
.........................+++
......+++
writing new private key to 'client.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [JP]:
State or Province Name (full name) [Tokyo]:
Locality Name (eg, city) [Shinjuku]:
Organization Name (eg, company) [hogehoge]:
Organizational Unit Name (eg, section) []:
## ここはクライアントのホスト名にしといても良いと思うがそのままで大丈夫なはず。
Common Name (eg, your name or your server's hostname) [client]:
## クライアント側としてわかりやすい名称をつけておく
Name [EasyRSA]:client
Email Address [hoge@hogehoge.com]:
{1}
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /usr/share/easy-rsa/2.0/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'JP'
stateOrProvinceName   :PRINTABLE:'Tokyo'
localityName          :PRINTABLE:'Shinjuku'
organizationName      :PRINTABLE:'hogehoge'
organizationalUnitName:PRINTABLE:''
commonName            :PRINTABLE:'client'
name                  :PRINTABLE:'client'
emailAddress          :IA5STRING:'hoge@hogehoge.com'
Certificate is to be certified until Oct 14 23:00:18 2027 GMT (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

client のcsr, crt, keyが生成される。

DHパラメータ生成

こちらも生成する必要あり。

# ./build-dh
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time

dh2048.pem が生成される

ta鍵の生成

こちらはtls-auth認証を使わない設定にした場合、生成は不要だが、よりセキュアなものにしたいのであれば作ったほうが良い。
こちらはeasy-rsaではなく、openvpnの機能で生成する。

# openvpn --genkey --secret ta.key

ta.keyが生成される。

以上で鍵の生成は終わりとなる。 まとめて以下に配置しておくと良い

# cp -Rp /usr/share/easy-rsa/2.0/vars/keys /etc/openvpn/
# cd /etc/openvpn/keys
## 鍵の情報なのでroot以外の参照はできなくしておく
# chmod 600 *

サーバ側の設定

サーバ側コンフィグの設定 まずは、設定のサンプルファイルをコピーして持ってくる

# cp /usr/share/doc/openvpn-2.4.4/sample/sample-config-files/server.conf /etc/openvpn/

openvpnバージョンによって、途中の数値部分がかわるので注意
vpnのセッティングとしては、2パターンがある。

  • ルーティング方式
  • 仮想ブリッジ方式

ルーティング方式
クライアントにVPN用セグメントのIPを割り振り、VPNサーバ上でNAPT(IPマスカレード)して別のネットワーク(ホームネットワーク等)に通信を流す。
ルーティングの設定が必要(設定ファイル上にルートを記載する)

仮想ブリッジ方式
その名の通り、仮想的なブリッジ、L2レベルでつなぐ。
パッケージとしてbridge-utilsもインストールする必要がある。
イメージとしてはVPNを動かしているサーバを繋げるL2スイッチを仮想的に作って、そこにVPNクライアントのLANケーブルを指しているような感じ。
ルーティングは不要。IPアドレスもVPNサーバと同じセグメントのIPアドレスを割り振ることになる。

私の環境だとKVM上の仮想マシンがすでに仮想ブリッジでホームネットワークにつながっており、仮想マシン上で更に仮想ブリッジを動かすのも複雑だなと思ったのでルーティング方式にした。
ルーティング方式のほうがVPNクライアントからの通信管理もしやすい。

サーバ側の設定は以下の通り。

  • /etc/openvpn/server.conf
port 1194
# tcpも設定できるがudpのほうが高速
proto udp
dev tun
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/server.crt
key /etc/openvpn/keys/server.key
dh /etc/openvpn/keys/dh2048.pem

# VPNクライアントに割り振るNWのセグメント
server 192.168.3.0 255.255.255.0

ifconfig-pool-persist ipp.txt

# ルーティングテーブルに追加する設定
# ホームネットワークのセグメント等を記載
# VPNクライアントからの192.168.10.0宛通信を流せるようになる
push "route 192.168.10.0 255.255.255.0"

keepalive 10 120

# クライアントと合わせないとエラーを吐く
cipher AES-256-CBC
persist-key
persist-tun
status openvpn-status.log

# ログ出力先
# /var/log/openvpnというディレクトリは生成されないので、自分で作成する必要がある
# ログローテーションもやったほうがよい
log         /var/log/openvpn/openvpn.log
log-append  /var/log/openvpn/openvpn.log
verb 3

# UDP接続方式のみ設定できる項目
explicit-exit-notify 1

上記の通り、あまり設定項目は多くない。

サーバ側のサービス起動設定

systemdで管理するための設定。

パッケージインストール時に配置されているserviceファイルについて、コピーする。
これはやらなくてもOKだが、起動停止時にコマンド打ちにくいので消す。

# cd /usr/lib/systemd/system
# cp openvpn-server@.service openvpn-server.service

serviceファイルを編集する。
下記1行のconfigのパスだけ変更。
(設定しなくても読み込めるっぽい?が、明示的に記載)

# vim openvpn-server.service

ExecStart=/usr/sbin/openvpn --status %t/openvpn-server/status-%i.log --status-version 2 --suppress-timestamps --config /etc/openvpn/server.conf

サービス起動と、自動起動設定を行う。

# systemctl start openvpn-server
# systemctl enable openvpn-server

サービス起動後、ipコマンドで確認すると、tun0といったインタフェースが生成されている。
上記の設定の通りだと192.168.3.0のセグメントのIPが振られている。

# ip a
...
3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100
    link/none 
    inet 192.168.3.1 peer 192.168.3.2/32 scope global tun0
...

ipv4 forwardの設定

通信をフォワードするため、設定する必要あり sysctl.conf に以下の1行を記載。

# vim /etc/sysctl.conf
net.ipv4.ip_forward = 1

設定変更後、 sysctl -p で設定を即座に反映させる。
もしくは、OS再起動してもOK。

firewalldの設定

openvpnはクライアントからの通信は1194/udpだけ使う。
そのポートを開放する他、IPマスカレードの設定も投入する必要がある。

ポートの開放

# firewall-cmd --permanent --add-port=1194/udp
# systemctl restart firewalld

## マスカレードを有効
# firewall-cmd --permanent --add-masquerade
# systemctl restart firewalld

# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources: 
  services: dhcpv6-client ssh
  ports: 1194/udp # 開放されたポート
  protocols: 
  masquerade: yes # マスカレードが有効化されている
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

なお、ゾーンにpublic使うかは自由である。
ゾーン分け等、よりセキュアに設定したければ細かく設定もできる。

以上でサーバの設定は終わり。

家庭用ルータの設定

外部のネットワークからOpenVPN宛の通信が来たらVPNサーバへ通信を飛ばす設定をルータに設定する必要がある。
上記の通りOpenVPNは(デフォルトだと)1194/updの設定しか使わないのでその通信のポートマッピングをしてあげれば良い。
この当たりの設定はルータによって変わる。
またOpenVPNサーバを自宅に作りたいレベルの人であれば説明も不要だと思うので省略。

クライアントの設定

クライアントのインストール

クライアントOSによって異なるので、クライアントのインストールについてはLinuxのみ記載する。

Debian系

# sudo apt install openvpn

RedHat系 ※epelのリポジトリを入れておく必要あり

# sudo yum install --enablerepo=epel openvpn

パッケージが用意されいるので簡単。サーバと変わらない。

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

クライアント用設定ファイルを用意した後、それをクライアントのopenvpnのプログラムに読み込ませるだけでvpn接続できるようになる。
公式のスマートフォンアプリも用意されているのでスマートフォンからも簡単に接続できる。
設定ファイルには鍵も組み込むことができる。
鍵はサーバ上にあるので、クライアントの設定ファイルもサーバ上で作って配布したほうが速い。
(設定ファイルの扱いには注意が必要となる)

クライアントのサンプルファイルがサーバの以下にあるのでコピーする

# cd /etc/openvpn/
# cp /usr/share/doc/openvpn-2.4.4/sample/sample-config-files/client.conf .

クライアント用ファイルの設定項目は以下の通り。
基本的にサーバと設定を一緒にする。

# vim client.ovpn

client
dev tun
proto udp

# サーバのアドレスか、グローバルIPアドレスを入力
# ポート番号も入力する。1194がポート番号となる
remote hogehoge.jp 1194

resolv-retry infinite
nobind
persist-key
persist-tun

# 以下ように証明書、鍵を設定ファイル内に組み込むことができる
# サーバと同様に鍵を外部ファイルとして読みこませることも可
<ca>
-----BEGIN CERTIFICATE-----
ca.crtの中身をここに記載
-----END CERTIFICATE-----
</ca>

<cert>
-----BEGIN CERTIFICATE-----
client.crtの中身をここに記載
-----END CERTIFICATE-----
</cert>

<key>
-----BEGIN PRIVATE KEY-----
client.keyの中身をここに記載
-----END PRIVATE KEY-----
</key>


remote-cert-tls server

# tls-authを設定ファイルに組み込む場合、TLS認証のクライアントとして認識させるために必要な設定
key-direction 1

# 外部からファイルを読み込む場合は tls-auth /home/hogehoge/ta.key 1  と記載(1がクライアント側。サーバは0)
<tls-auth>
-----BEGIN OpenVPN Static key V1-----
ta.keyの中身をここに記載
-----END OpenVPN Static key V1-----
</tls-auth>

# サーバと揃えないとエラーをはく
cipher AES-256-CBC
verb 3

以上で設定は終わり。 作成した client.ovpn をクライアントに配布する。

クライアントからVPN接続する場合、以下のコマンドを実行する。

# sudo openvpn client.ovpn

引数としてクライアント設定ファイルを指定してあげれば良い。
--config [クライアント設定ファイル] というオプションもあるが、それつけなくても大丈夫。

正しく接続できればOpenVPN用のアドレスが配布される

# ip a
...
7: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
    link/none 
    inet 192.168.3.6 peer 192.168.3.5/32 scope global tun0
       valid_lft forever preferred_lft forever

# route
カーネルIP経路テーブル
受信先サイト    ゲートウェイ    ネットマスク   フラグ Metric Ref 使用数 インタフェース
default         [デフォGWアドレス]  0.0.0.0         UG    100    0        0 enp0s20f0u2
link-local      *               255.255.0.0     U     1000   0        0 virbr0
192.168.10.0     192.168.3.5    255.255.255.0   UG    0      0        0 tun0
192.168.3.1    192.168.3.5    255.255.255.255 UGH   0      0        0 tun0
192.168.3.5    *               255.255.255.255 UH    0      0        0 tun0
...

上記の通り、ルーティングも行われる。
上記の例だと自分に配布されたIPは192.168.3.6、ホームネットワーク等の192.168.10.0宛の通信はルーティングテーブルに従い192.168.3.5へ送られる。
トンネリングされてデフォルトゲートウェイから出て行く。

通信のキャプチャ

ついでにパケットキャプチャして通信内容を軽く確かめてみる。
登場するIPアドレスは以下の通り。

  • クライアント:30.1.1.101
  • ルータ(外部NW):10.1.1.1
  • ルータ(内部NW):192.168.10.254
  • VPNサーバ自身のアドレス:192.168.10.5
  • VPNサーバのVPN用のアドレス:192.168.3.1
  • Webサーバ:192.168.10.8

ホームネットワークのwebサーバに接続を試す。
以下はtcpdumpのログ。


vpnサーバのログ
クライアントからVPNサーバへの通信

07:54:37.723051 IP 30.1.1.101.49285 > 192.168.10.5.1194: UDP, length 104

1194/UDPで通信が送られていることがわかる。from port として57969が設定されている。

VPNサーバからWebサーバへの通信

07:54:37.723206 IP 192.168.10.5.39468 > 192.168.10.8.80: Flags [F.], seq 1697, ack 15630, win 537, options [nop,nop,TS val 344620 ecr 501049428], length 0

webサーバのログ
VPNサーバからWebサーバの通信
(通信内容同じなのでVPNサーバ上のログと同じになる。届いている時間だけ違う)

07:54:37.723457 IP 192.168.10.5.39468 > 192.168.10.8.80: Flags [F.], seq 1697, ack 15630, win 537, options [nop,nop,TS val 344620 ecr 501049428], length 0

上記の通信がwebサーバに届いている。

Webサーバの応答通信

07:54:37.723493 IP 192.168.10.8.80 > 192.168.10.5.39468: Flags [.], ack 1699, win 254, options [nop,nop,TS val 501049528 ecr 344620], length 0

webサーバからvpnサーバのfrom port宛てに返信を返している。


VPNサーバのログ
VPNサーバからクライアントへの通信応答

07:57:57.683105 IP 192.168.10.5.1194 > 30.1.1.101.49285: UDP, length 101

VPNサーバからクライアントの49285ポート宛てに、webサーバからの内容を転送している。


図でまとめると以下の通り。

こんな感じでわりとシンプルにOpenVPNサーバは立てることができる。
クライアント側も1つコンフィグファイルを作ってしまえばクライアント側で細かい設定も不要で簡単に接続できるようになる。