Open7

さくらのVPSにWireGuard入れて個人用途のVPN環境作った

ピン留めされたアイテム
hiroe_orz17hiroe_orz17

ネットワーク図

  • 個人VPN作った目的は、「高速でセットアップが簡単なVPN」としてオープンソースで提供されているWireGuardが、サービス提供者による個人情報収集に対する現実的な解になりうるかどうかを実験してみたかったから。
  • 最終的に以下のようになりました。
  • インターネット側のサービスからは、さくらのネットワークからのアクセスに見えるため、私の自宅やスマートフォンに割り当てられたIPアドレスは見えません。
  • DNSクエリは自前のサーバーに対して行い、上位へのクエリは、さくらのVPSから行われるので、クエリ内容と私の個人情報は結びつきません。
  • [自宅 | スマートフォン]と、さくらのVPSの間はVPNによって暗号化されているので盗聴される可能性は限りなく低いです。

  • なお、IPアドレスによる位置情報の特定もできなくなります。私の所在地は島根ですが、IPアドレスから特定された、おおよその位置は東京になってました(石狩じゃないんだ...)
  • ただし、アプリやブラウザのGPSによる位置情報を使われた場合は、このとおりではありません。

*)ちなみに、さくらVPSにはWireGuard機能を提供するVPCルータを使用することができます。ただし、こちらは最も低スペックなスタンダードプランでも ¥2619/月 するため、個人ユースであれば価格の安さでVPS上にサーバーを立てるのもありだと思います。

hiroe_orz17hiroe_orz17

サーバーの用意

  • クラウドかVPSでサーバーを探し、最終的にデータ転送に課金されず、かつサーバー代金の安いさくらのVPSに決めた。
  • さくらのVPSは使ったことなかったので、新たにさくらに会員登録して、一番安い月額600円台のプランでサーバー起動した。
  • 拠点は用途を考えると東京が良いかなと思ったけど、まあとりあえず一番安い石狩でやってみる。
  • 注意事項として、さくらのVPSは登録後2週間は無料でサーバーを使うこともできるが、それだと最大転送速度が20Mbpsに制限されるので、今回のような用途ならはじめから本契約して有料サーバーを使ったほうが良いと思う。

パッケージのインストール

$ sudo apt install wireguard

サーバー側の秘密鍵を作成

$ wg genkey > private
$ cat private
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

インターフェイスの作成(sudo vim /etc/wireguard/wg0.conf)

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

一旦起動。

$ sudo wg-quick up /etc/wireguard/wg0.conf

ネットワークインターフェイスに新たに wg0 が追加されたことを確認。

$ ip addr
...

5: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none 
    inet 10.0.0.1/24 scope global wg0
       valid_lft forever preferred_lft forever

システム起動時に自動起動するように設定

$ sudo systemctl enable wg-quick@wg0
hiroe_orz17hiroe_orz17

サーバーのネットワーク設定

さくらのVPSにはAWSのようなマネージドなアクセス制御はなさそうなので、iptables を使って手動設定する。

転送許可設定

/etc/sysctl.conf を修正してフォワードを許可

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

設定を反映。

$ sudo sysctl -p

iptablesの設定

さくらのVPSではネットワークインターフェイスがeth0ではなくens3だったので、ens3に設定する。

まずは初期設定として、

  • 入ってくるパケットは拒否
  • 出ていくパケットは許可
  • 転送は拒否

を入れるが、これらの設定をすると即座につながらなく可能性があるので、一旦 INPUT を ACCEPTにして設定した上で、最後に INPUT を DROPにする。

$ sudo iptables -P INPUT ACCEPT
$ sudo iptables -P OUTPUT ACCEPT
$ sudo iptables -P FORWARD DROP

続いて、今回の用途等に必要な許可を入れていく。ついでに外部からの不正なアクセスを遮断する。

$ iptables -A INPUT -i lo -j ACCEPT
$ sudo iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
$ sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$ sudo iptables -I INPUT 2 -p tcp ! --syn -m state --state NEW -j DROP
$ sudo iptables -I INPUT 2 -p tcp --tcp-flags ALL ALL -j DROP

$ sudo iptables -t nat -I POSTROUTING 1 -s 10.0.0.0/24 -o ens3 -j MASQUERADE
$ sudo iptables -I INPUT 1 -i ens3 -p udp --dport 51820 -j ACCEPT
$ sudo iptables -I INPUT 1 -i wg0 -j ACCEPT
$ sudo iptables -I FORWARD 1 -i ens3 -o wg0 -j ACCEPT
$ sudo iptables -I FORWARD 1 -i wg0 -o ens3 -j ACCEPT
$ sudo iptables -I FORWARD 1 -i wg0 -o wg0 -j ACCEPT

最後の $ sudo iptables -I FORWARD 1 -i wg0 -o wg0 -j ACCEPT によって、同じWireGuardのVPNに参加しているクライアント同士で通信ができるようになります。

全て設定したら INPUT を DROP に。

$ sudo iptables -P INPUT DROP

設定を保存するために iptables-persistent をインストールする。

$ sudo apt install iptables-persistent

保存。次回起動時にも自動的に読み込まれる。

$ sudo /etc/init.d/netfilter-persistent save

インターネットからのSSH接続を拒否

なお、後述のクライアントからの接続等も確認できたら、SSH(22)ポートへの接続許可はwireguard経由のものだけにし、インターネットからの直接の接続は拒否するようにする。

$ sudo iptables -A INPUT -i wg0 -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
$ sudo iptables -A INPUT -i ens3 -m state --state NEW -m tcp -p tcp --dport 22 -j DROP
$ sudo iptables -D INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
hiroe_orz17hiroe_orz17

Linuxクライアントの設定

自宅のマシンがLinux Mintなので。
まずは、サーバー側と同じようにwireguardをセットアップする

なお、クライアント側はIPアドレスを 10.0.0.2 とする。
また、iptablesによるアクセス制御は特に必要ない。

サーバーと同様に秘密鍵を生成。

$ wg genkey > private
$ cat private 
BBBBBBBBBBBBBBBBBBBBBBBBBBBB

生成した秘密鍵を使って /etc/wireguard/wg0.conf は一旦以下のように。

[Interface]
Address = 10.0.0.2/24
ListenPort = 51820
PrivateKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBB

一旦起動

$ sudo wg-quick up /etc/wireguard/wg0.conf

サーバー側にクライアントを登録。

クライアントの公開鍵を確認

以下のコマンドをクライアントで実行して、クライアント側の公開鍵を確認。

$ sudo wg show

interface: wg0
  public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  private key: (hidden)
  listening port: 51820

クライアント側の公開鍵は XXXXXXX..... のようだ。

サーバーにLinuxクライアントを登録

サーバー側の /etc/wireguard/wg0.conf[Peer] を登録する。
PublicKey には先程のクライアント側の公開鍵を。
AllowedIPs にはクライアント側で設定したIPアドレスを設定する。

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[Peer]
PublicKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
AllowedIPs = 10.0.0.2/32

サーバー側のwireguardを再起動する。

$ sudo wg-quick down /etc/wireguard/wg0.conf
$ sudo wg-quick up /etc/wireguard/wg0.conf

Linuxクライアントにサーバーを登録

今度はサーバー側の公開鍵を確認する。サーバー上で以下を実行。

$ sudo wg show
interface: wg0
  public key: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
  private key: (hidden)
  listening port: 51820

peer: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  allowed ips: 10.0.0.2/32

サーバー側の公開鍵は YYYY... のようなので、これをLinuxクライアントの /etc/wireguard/wg0.conf の Peerに登録。
編集後の /etc/wireguard/wg0.conf は以下のようになった。

[Interface]
Address = 10.0.0.2/24
ListenPort = 51820
PrivateKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

[Peer]
PublicKey = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AllowedIPs = 10.0.0.0/24
Endpoint = 153.126.131.xxx:51820

クライアントにはEndpoint という項目があり、ここにはサーバー側のグローバルアドレスとwireguardが待機しているUDPポート番号を設定している。
Linuxクライアントのwireguardを再起動する。

$ sudo wg-quick down /etc/wireguard/wg0.conf
$ sudo wg-quick up /etc/wireguard/wg0.conf

クライアント側からpingを打ってみる。

$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) バイトのデータ
64 バイト応答 送信元 10.0.0.1: icmp_seq=1 ttl=64 時間=119 ミリ秒
64 バイト応答 送信元 10.0.0.1: icmp_seq=2 ttl=64 時間=57.8ミリ秒

無事つながったようだ。この時点では 10.0.0.0/24 へのパケットはwireguardのサーバーへ流れ、それ以外のパケットはインターネットに直接出ていきます。

hiroe_orz17hiroe_orz17

すべてのトラフィックがVPN(wireguard)を通って出ていくように設定する

この場合、クライアント側の AllowedIPsに、全ての宛先( 0.0.0.0/0 )がwireguard側に流れるように指定すれば良い。

クライアント側の /etc/wireguard/wg0.conf を以下のように設定する。

[Interface]
Address = 10.0.0.2/24
ListenPort = 51820
PrivateKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

[Peer]
PublicKey = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 153.126.131.xxx:51820

上記設定をしてwireguardを再起動すると、全てのトラフィックがVPN(wireguard)を抜けていくようになる。

自宅Linuxから traceroute で google.com までの経路を確認すると、 10.0.0.1 を通り、さくらのネットワークから出ていっている様子がわかる。

$ traceroute google.com

traceroute to google.com (172.217.161.46), 30 hops max, 60 byte packets
 1  10.0.0.1 (10.0.0.1)  55.886 ms  56.246 ms  56.854 ms
 2  153.126.130.2 (153.126.130.2)  57.688 ms  58.970 ms  58.957 ms
 3  192.168.107.1 (192.168.107.1)  57.585 ms  57.558 ms  58.869 ms
 4  iskrt301b-vps-is1a-rt01-1.sakura.ad.jp (103.10.115.113)  57.111 ms  57.439 ms  57.401 ms
 5  iskrt1s-rt301b.bb.sakura.ad.jp (103.10.113.125)  57.371 ms  57.345 ms...
 6  iskrt3-rt301s.bb.sakura.ad.jp (103.10.113.205)  57.288 ms iskrt3-rt1s.bb.sakura.ad.jp...
 7  tkort4-iskrt3.bb.sakura.ad.jp (157.17.131.33)  72.142 ms tkert1-iskrt301.bb.sakura.ad.jp...
 8  as15169.ix.jpix.ad.jp (210.171.224.96)  72.461 ms tkort4-ert1.bb.sakura.ad.jp (157.17.130....
...
 ms nrt12s23-in-f14.1e100.net (172.217.161.46)  73.673 ms
hiroe_orz17hiroe_orz17

自分専用のDNSキャッシュサーバーをセットアップする

  • 上記で一応、プライベートな高速VPN環境ができたが、企業等が個人情報を収集する上で結構使われてるんじゃないかな〜と思うのがDNS。
  • 現在は通信がTLSにより暗号化されているので、そこからなにか情報を盗むのは難しい。
  • しかし通常、DNSへのクエリは暗号化されないし、仮に暗号化されていてもDNSサーバー自体には、どのホストをクエリしたかはわかるので、企業から提供されているDNSはかっこうの情報収集装置になり得る。

というわけで野良DNSキャッシュサーバーを仕立て上げる。

WireGuardを仕込んだサーバーにDNSキャッシュサーバー Unbound をインストールする。

$ sudo apt install unbound

インストールは成功するけど、さくらのVPSはポート53を使っているようで起動に失敗する。
unboundはwireguardのネットワークからのクエリに応答してくれればいいので、以下のファイルを作って 11.0.0.1で接続待機するように設定する。

$ sudo vi /etc/unbound/unbound.conf.d/listen.conf

ファイルの中身はこう

server:

  interface:10.0.0.1
  do-ip4:yes
  do-ip6:no

  access-control:0.0.0.0/0   deny
  access-control:10.0.0.0/24 allow

unboundを再起動する。

$ sudo systemctl restart unbound

LinuxクライアントがDNSにunboundを使うように設定する

Linuxクライアント側の /etc/wireguard/wg0.confDNS 行を追加して再起動すれば、先程セットアップした unbound をDNSキャッシュサーバーとして使うようになる。

[Interface]
Address = 10.0.0.2/24
ListenPort = 51820
PrivateKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
DNS = 10.0.0.1

[Peer]
PublicKey = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 153.126.131.xxx:51820

最初は何もキャッシュされておらず、その都度上位に問い合わせるので結構遅いと感じると思う。

hiroe_orz17hiroe_orz17

iPhoneの通信がVPN(WireGuard)を通り、自前のDNS( Unbound )を使うようにする

iPhone側の設定

  1. まずは AppStore で WireGuard をインストールする。
  2. インストールした WireGuardアプリを起動し、右上の + マークをタップし、「空の状態から作成」をタップ。
  3. 名前は sakura のように適当に決める。
  4. キーペアの生成 をタップし、キーペアを生成する。
    1. ここで仮に、生成された公開鍵が ZZZZZZZZZZZZZZZZZZZZZZZZZZ だとする。
  5. IPアドレスは今回は 10.0.0.3/32 とする。
  6. 待受ポートは 51820
  7. DNSサーバーは 10.0.0.1
  8. ピアを追加 をタップ。
  9. 公開鍵には サーバー側で $ sudo wg show して表示された PublicKey を貼り付け。
  10. エンドポイントにはサーバー側のエンドポイント(この場合は 153.126.131.xxx:51820 )を設定。
  11. Allowed IPs には 0.0.0.0/0 を設定。

サーバーへiPhoneを登録

先程のiPhoneのwireguardアプリの設定画面で、生成した公開鍵をコピーし、 /etc/wireguard/wg0.conf に登録する。
最初に登録したLinuxクライアントと合わせると以下のような感じ。

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[Peer]
PublicKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
AllowedIPs = 10.0.0.2/32

[Peer]
PublicKey = ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
AllowedIPs = 10.0.0.3/32

wireguardを再起動した上で、wiregaurdアプリで先程の設定を 有効 にする。
以上で全てのトラフィックはwireguardを通過するようになる。