自分用メモです。
過去に、VPNルータとかのHW製品を使ってVPNを構築したことはあるのだけど、諸般の事情によりOpenVPNを使うことになりました。OpenVPN使ったことないしVPNネットワークもあまり良くわかってないので、例によってVagrantを使ってこんな感じのネットワークを作って試してみます。
基本的な構成
最終的に作るのはざっくりこんな感じになります。
- a.privateとb.privateが別のネットワークになっていて、それぞれのgwでつながっている想定です。
- vpn-client.a.privateからvpn-server.b.privateにVPNで接続して、web-server.b.privateに接続します。OpenVPNを使ってルーティング方式(TUN)で接続します。
- gw-a、gw-bはルータ兼FWです。 a.privateとb.privateのそれぞれ内部から外向きにIPマスカレードしたり、gw-b.b.privateではvpn-serverへのudp:1194をポートフォワードしたりします。
- 一つ大事なこととして、Vagrantの場合、eth0はいろいろ手当が必要です。
- ホスト・ゲスト間の通信(vagrant sshとか)に使うため必須ですが、ゲスト間の通信には使えません。
- 各ネットワーク内のホストでeth0がデフォルトゲートウェイになってしまうと、お互いのネットワークへの経路がない状態ではこちらのIFから抜けようとします。それだと都合が悪いので、それぞれのgwをデフォルトゲートウェイにします。
- そうなると今度はyumのアップデートなどでインターネットへの経路がなくなってしまいますので、gwのeth0にもIPマスカレードを指定してここから抜けるようにします。
- 名前解決は今回あまり凝ったことはせずに、dnsmasqを使って最低限の名前解決だけができればよいという感じにしてます。
まずは以下のレポジトリをcloneしてvagrant upします。ちょっと時間がかかります。
これで5台のCentOS7サーバと最低限の設定が入ったネットワークが出来上がります。細かいところはプロビジョニングで使っているシェルスクリプトを見てもらえればと思うのですが、a.private側でかんたんに説明します。
gw-aにログインします。
$ vagrant ssh gw-a [vagrant@gw ~]$
gw-aのインタフェースは以下の3つです。
$ nmcli eth0: 接続済み to System eth0 ・・・ ip4 デフォルト inet4 10.0.2.15/24 route4 0.0.0.0/0 route4 10.0.2.0/24 ・・・ eth1: 接続済み to System eth1 ・・・ inet4 10.0.0.10/24 route4 10.0.0.0/24 ・・・ eth2: 接続済み to System eth2 ・・・ inet4 192.168.0.10/24 route4 192.168.0.0/24 ・・・
eth0がVagrant(というかVirtualbox)デフォルトのホスト・ゲスト間をつなぐIF、eth1がprivate.bとつながる外部NW想定、eth2がLANの想定です。
これにあわせてfirewalldの設定は以下となっています。
$ sudo firewall-cmd --get-active-zones external interfaces: eth1 eth0 trusted interfaces: eth2
以下のようにexternalにはIPマスカレードを設定してあります。これによりLAN内の複数のホストからこのIFを通じて外にアクセスができるようになっています。
$ sudo firewall-cmd --list-all --zone=external external (active) target: default icmp-block-inversion: no interfaces: eth1 sources: services: ssh ports: protocols: masquerade: yes forward-ports: source-ports: icmp-blocks: rich rules:
ということで、実際に疎通を確認します。vpn-clientにログインします。
$ vagrant ssh vpn-cleint [vagrant@vpn-cleint ~]$
最初に記載したとおり、Vagrantのデフォルトだと、eth0がホスト・ゲスト間をつなぐIFになっていて、その先にあるホスト(つまりMac)がデフォルトゲートウェイになるので、gwをデフォルトゲートウェイに変更してあります。
$ ip route default via 192.168.0.10 dev eth1 proto static metric 103 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 102 192.168.0.0/24 dev eth1 proto kernel scope link src 192.168.0.20 metric 103
では、疎通を確認します。
$ ping gw # 同一ネットワーク内のgw $ ping gw.b.private # 対向のgw $ ping www.google.com # インターネット(gw→ホストのMac経由)
すべて疎通OKですね。b.private側も同じです。それぞれのネットワーク内の全ホストと、対向のGWだけが名前解決できるようにしてあります。
OpenVPNサーバの設定
ここからが本題です(前フリが長い)。b.privateのvpn-serverにログインして設定していきます。
$ vagrant ssh vpn-server [vagrant@vpn-server ~]$
OpenVPNとeasyrsaはすでにインストールされていますので、設定をしていくだけです。まずは各種鍵・証明書を作っていきましょう。面倒なのでrootで。
$ sudo su - # cd /etc/openvpn/easyrsa3
初期化。これで/etc/openvpn/easyrsa3/pkiディレクトリが作成されます。
# ./easyrsa init-pki init-pki complete; you may now create a CA or requests. Your newly created PKI dir is: /etc/openvpn/easyrsa3/pki
CAの証明書・秘密鍵を作成します。ここで入力したパスフレーズはサーバ/クライアント証明書を作成する際に必要になります。
# ./easyrsa build-ca Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Enter New CA Key Passphrase: ※CA用パスフレーズを入力 Re-Enter New CA Key Passphrase: ※確認のため再度入力 Generating RSA private key, 2048 bit long modulus ..................................+++ ..............................................................................................................................................+++ e is 65537 (0x10001) 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. ----- Common Name (eg: your user, host, or server name) [Easy-RSA CA]: vpn-server.b.private ※vpnサーバのホスト名を入力 CA creation complete and you may now import and sign cert requests. Your new CA certificate file for publishing is at: /etc/openvpn/easyrsa3/pki/ca.crt
サーバー証明書を作成します。パスフレーズを聞かれたら、先程のパスフレーズで。
# ./easyrsa build-server-full vpn-server.b.private nopass Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Generating a 2048 bit RSA private key ...+++ ..+++ writing new private key to '/etc/openvpn/easyrsa3/pki/easy-rsa-21404.ahmyOb/tmp.dAJJUu' ----- Using configuration from /etc/openvpn/easyrsa3/pki/easy-rsa-21404.ahmyOb/tmp.SxJ5M6 Enter pass phrase for /etc/openvpn/easyrsa3/pki/private/ca.key: ※CA用パスフレーズを入力 Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows commonName :ASN.1 12:'vpn-server.b.private' Certificate is to be certified until Aug 13 11:36:13 2022 GMT (825 days) Write out database with 1 new entries Data Base Updated
DHパラメータを作成。鍵交換アルゴリズムに使う素数の作成だそうです。
# ./easyrsa gen-dh Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Generating DH parameters, 2048 bit long safe prime, generator 2 This is going to take a long time ..............................................................................................+ ..........................+..............+....................+.........................+.. ・・・ DH parameters of size 2048 created at /etc/openvpn/easyrsa3/pki/dh.pem
で、クライアント証明書の作成の前に、証明書廃止のための証明書失効リストを作成しておきます。そのために一旦ダミーでクライアント証明書を作成します。
# ./easyrsa build-client-full dummy nopass Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Generating a 2048 bit RSA private key ................................................+++ .............................+++ writing new private key to '/etc/openvpn/easyrsa3/pki/easy-rsa-21504.5lPkNt/tmp.wC2u8r' ----- Using configuration from /etc/openvpn/easyrsa3/pki/easy-rsa-21504.5lPkNt/tmp.7H0LDk Enter pass phrase for /etc/openvpn/easyrsa3/pki/private/ca.key: ※CA用パスフレーズを入力 Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows commonName :ASN.1 12:'dummy' Certificate is to be certified until Aug 13 11:49:42 2022 GMT (825 days) Write out database with 1 new entries Data Base Updated
ダミーのクライアント証明書を廃止します。
# ./easyrsa revoke dummy Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Please confirm you wish to revoke the certificate with the following subject: subject= commonName = dummy Type the word 'yes' to continue, or any other input to abort. Continue with revocation: yes ※確認のためyesを入力 Using configuration from /etc/openvpn/easyrsa3/pki/easy-rsa-21573.GoXnz4/tmp.Rw7Etz Enter pass phrase for /etc/openvpn/easyrsa3/pki/private/ca.key: ※CA用パスフレーズを入力 Revoking Certificate 6D1DE2D9B68DF1306278AF56E4F165C0. Data Base Updated IMPORTANT!!! Revocation was successful. You must run gen-crl and upload a CRL to your infrastructure in order to prevent the revoked cert from being accepted.
証明書失効リストを作成します。
# ./easyrsa gen-crl Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Using configuration from /etc/openvpn/easyrsa3/pki/easy-rsa-21636.7Nbh6C/tmp.EBwBUj Enter pass phrase for /etc/openvpn/easyrsa3/pki/private/ca.key: ※CA用パスフレーズを入力 An updated CRL has been created. CRL file: /etc/openvpn/easyrsa3/pki/crl.pem
TLS認証鍵を作成します。
# openvpn --genkey --secret /etc/openvpn/server/ta.key
最後に出力された各種鍵・証明書を/etc/openvpn/server配下にコピーします。
# cp pki/ca.crt pki/issued/vpn-server.b.private.crt pki/private/vpn-server.b.private.key pki/dh.pem pki/crl.pem /etc/openvpn/server/.
ではOpenVPNサーバの設定です。雛形から作成します。
# cp /usr/share/doc/openvpn-2.4.9/sample/sample-config-files/server.conf /etc/openvpn/server/. # vi /etc/openvpn/server/server.conf
関連するところだけ。
port 1194 proto udp dev tun ca ca.crt cert vpn-server.b.private.crt key vpn-server.b.private.key dh dh.pem server 172.16.0.0 255.255.255.0 push "route 192.168.100.0 255.255.255.0" tls-auth ta.key 0 user nobody group nobody log-append openvpn.log management localhost 7505 crl-verify crl.pem
server 172.16.0.0 255.255.255.0
のところはVPNネットワークのセグメントを指定します。このセグメントの172.16.0.0.1がVPNサーバになり、残りをクライアント側に割り当てます。push "route 192.168.100.0 255.255.255.0"
により、クライアント側にIPアドレスが払い出されると同時にルーティングが設定されます。つまりb.privateのLANである192.168.100.0/24へのルーティングに172.16.0.Xを経由するようになります。
OpenVPNサーバを起動します。
# sudo systemctl start openvpn-server@server.service # sudo systemctl status openvpn-server@server.service # sudo systemctl enable openvpn-server@server.service
systemctl statusでactiveになっていて、tunデバイスが上がっていればOKです。
$ nmcli ・・・ tun0: connected to tun0 "tun0" tun, sw, mtu 1500 inet4 172.16.0.1/32 route4 172.16.0.2/32 route4 172.16.0.0/24 ・・・
172.16.0.1がVPNセグメント上のVPNサーバのIPアドレスですね。
で、vpn-server.b.privateはgw.b.privateの中にあるのでポートフォワードを行います。
$ vagrant ssh gw-b [vagrant@gw ~]$
$ sudo firewall-cmd --zone=external --add-forward-port=port=1194:proto=udp:toport=1194:toaddr=192.168.100.20 --permanent $ sudo firewall-cmd --reload
OpenVPNクライアントの設定
では、クライアント側の設定です。まずサーバ側でクライアント証明書を作成します。
$ vagrant ssh vpn-server [vagrant@vpn-server ~]$ sudo su -
# cd /etc/openvpn/easyrsa3 # ./easyrsa build-client-full vpn-client.a.private nopass
CA証明書といっしょにクライアント証明書・鍵をクライアントにコピーします。/shareが共有パスになっているのでそこにコピーします。
# cp pki/ca.crt pki/private/vpn-client.a.private.key pki/issued/vpn-client.a.private.crt /etc/openvpn/server/ta.key /share
では、クライアント側です。
$ vagrant ssh vpn-client [vagrant@vpn-client ~]$
/shareから証明書等を/etc/openvpn/client以下にコピーします。
$ sudo cp -pi /share/vpn-client.a.private.key /share/vpn-client.a.private.crt /share/ca.crt /share/ta.key /etc/openvpn/client/.
サーバと同じように設定ファイルを雛形からコピーして作成します。
$ sudo cp -pi /usr/share/doc/openvpn-2.4.9/sample/sample-config-files/client.conf /etc/openvpn/client/. $ sudo vi /etc/openvpn/client/client.conf
設定ファイルのポイントはこんな感じです。
client dev tun proto udp remote vpn-server.b.private 1194 user nobody group nobody ca ca.crt cert vpn-client.a.private.crt key vpn-client.a.private.key tls-auth ta.key 1
サービスを立ち上げます。
$ sudo systemctl start openvpn-client@client.service $ sudo systemctl status openvpn-client@client.service $ sudo systemctl enable openvpn-client@client.service
systemctl statusで確認、tunデバイスが作成されていることを確認できればOKです。
$ nmcli ・・・ tun0: connected to tun0 "tun0" tun, sw, mtu 1500 inet4 172.16.0.6/32 route4 172.16.0.5/32 route4 192.168.100.0/24 route4 172.16.0.1/32 ・・・
172.16.0.6がクライアント側に割り当てられたIPアドレスです。
VPNサーバへの疎通を確認してみましょう。
$ ping 172.16.0.1 PING 172.16.0.1 (172.16.0.1) 56(84) bytes of data. 64 bytes from 172.16.0.1: icmp_seq=1 ttl=64 time=2.28 ms 64 bytes from 172.16.0.1: icmp_seq=2 ttl=64 time=1.45 ms
つながってますね!VPNサーバ内のLANにつながるか確認します。
$ ping 192.168.100.20 PING 192.168.100.20 (192.168.100.20) 56(84) bytes of data. 64 bytes from 192.168.100.20: icmp_seq=1 ttl=64 time=1.74 ms 64 bytes from 192.168.100.20: icmp_seq=2 ttl=64 time=1.55 ms
$curl http://192.168.100.30/ ^C $ ping 192.168.100.30 PING 192.168.100.30 (192.168.100.30) 56(84) bytes of data. ^C --- 192.168.100.30 ping statistics --- 4 packets transmitted, 0 received, 100% packet loss, time 3001ms
VPNサーバまでは繋がりますが、LAN内のホストにはつながらないですね。はい、ここでもIPマスカレードの設定は必要です。
ということで、vpn-server内でIPマスカレードの設定をします。ここはちょっと手抜きします・・・
$ vagrant ssh vpn-server [vagrant@vpn-server ~]$
$ sudo systemctl start firewalld $ sudo firewall-cmd --get-active-zones public interfaces: eth0 eth1 $ firewall-cmd --permanent --zone=trusted --add-interface=tun+ $ firewall-cmd --zone=trusted --add-interface=tun+ $ firewall-cmd --direct --passthrough ipv4 -t nat -A POSTROUTING -s 172.16.0.0/24 -o eth1 -j MASQUERADE $ firewall-cmd --permanent --direct --passthrough ipv4 -t nat -A POSTROUTING -s 172.16.0.0/24 -o eth1 -j MASQUERADE
では確認してみましょう。
[root@vpn-client ~]# ping 192.168.100.30 PING 192.168.100.30 (192.168.100.30) 56(84) bytes of data. 64 bytes from 192.168.100.30: icmp_seq=1 ttl=63 time=1.94 ms 64 bytes from 192.168.100.30: icmp_seq=2 ttl=63 time=2.18 ms --- 192.168.100.30 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2006ms rtt min/avg/max/mdev = 1.554/1.895/2.182/0.259 ms [root@vpn-client ~]# curl 192.168.100.30 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Apache HTTP Server Test Page powered by CentOS</title> ・・・
b.privateのLAN内にもアクセスできていますねー
その他
実はVagrantのprivate networkでちょいちょいハマりました。vagrant reloadしちゃうと多分インタフェースが初期化されちゃうのですね。nmcliとかfirewalldとあいまって非常に相性が悪い感じです。やっつけですが、起動時に必ずネットワーク設定するようにしました。
c.vm.provision :shell, :path => "set-host-nw.sh", run: "always"
nmcli使うのであれば:auto_config => false
にしちゃってプロビジョニングスクリプトの中で全部やっちゃうほうがいいかもしれません。
あと、きちんと確認できてないのですが、private networkで異なるセグメントからping通っちゃうという事象があって、見てる感じprivate networkの第4オクテット.1、つまりホストのMac経由で流れてきているようなのですね。なので、Vagrantfileの中で、virtualbox__intnetでネットワーク名を設定しました。これが効いてるのかどうかわからないけど、その後は再現しなくなってます。
c.vm.network "private_network", ip: "192.168.0.20", virtualbox__intnet: "a.private"
今更ながらVagrantとてもいいんですけど、ネットワークで凝った設定が必要な場合はちょっと厳しいかなというのが今回の気づきでした。
参考にさせていただいたサイト
ありがとうございました。