proxy
OpenVPN
Socks
ubuntu18.04

VPNに常時接続されたプロキシサーバを立てよう(openvpn with usocksd on ubuntu server 18.04)


はじめに

PCからスマートフォンまで、我々が日常で接する機会のあるコンピュータの多くは、標準でVPNに対応しています。しかし、標準で提供されるVPNクライアント機能は、コンピュータが行う通信すべてをVPN経由にすることを前提としています。あるアプリケーションはVPNで保護したいけれども、その他のアプリケーションについてはそうではないというようなケースでは、特定のアプリケーションにだけ、VPNを経由することが保証されたプロキシサーバを設定することで、最適な環境を構築できます。

この記事ではOpenVPNプロトコルでVPNサービスに接続し、SOCKS経由で他のマシンにセキュアな回線を提供するプロシキサーバを構築します。


必要なものは?


  • インターネット回線

  • サーバにするPC 1台(仮想PCでよい)

  • OpenVPNサーバへの接続権(この記事の用途が各種規約に反しないこと)


どうやって使うの?

オススメはFirefoxです。設定画面からプロキシを簡単に設定でき、この設定は、OSや、他のプロファイルから独立しています。複数のFirefoxの特定のプロファイルだけにプロキシを設定することができ、使い勝手が大変よいです。

SnapCrab_NoName_2018-8-15_7-21-58_No-00.png


準備


テスト環境


  • 仮想PC 1コア メモリ1GB ディスク20GB

  • Ubuntu 18.04 LTS

イメージファイルはubuntu-18.04.1-live-server-amd64.isoです。

デフォルトでファイアウォールは設定されていないこととします。次のコマンドで確認できます。

sudo ufw status

Status: inactive


ssh

必要があればインストールしてください。

sudo apt install ssh


aptのuniverseレポジトリを追加

sudo cat /etc/apt/sources.list

deb http://archive.ubuntu.com/ubuntu bionic main

deb http://archive.ubuntu.com/ubuntu bionic-security main
deb http://archive.ubuntu.com/ubuntu bionic-updates main

universeレポジトリが設定されていなければ追加します。

cat << "EOF" | sudo tee -a /etc/apt/sources.list

deb http://archive.ubuntu.com/ubuntu bionic universe
deb http://archive.ubuntu.com/ubuntu bionic-security universe
deb http://archive.ubuntu.com/ubuntu bionic-updates universe
EOF


update/upgrade

sudo apt update

sudo apt upgrade
sudo apt dist-upgrade


DNS

cat << "EOF" | sudo tee -a /etc/systemd/resolved.conf

DNS=8.8.8.8 8.8.4.4
EOF

sudo systemctl restart systemd-resolved

sudo systemd-resolve --status


NTP

sudo apt install ntp


時刻の設定

タイムゾーンが東京でない場合は以下のコマンドで変更できます。

sudo rm -rf /etc/localtime

sudo ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime


IPv6の無効化

cat << "EOF" | sudo tee /etc/sysctl.conf

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF

sudo sysctl -p


OpenVPN

sudo apt install openvpn


証明書、鍵、設定ファイルの用意

証明書、鍵、設定ファイルは、サービス側が提供するものをそのまま使います。ただし、下記の手順の中で、opvnファイルはconfファイルにリネームします。また、証明書、鍵ファイルへのパスについても変更の必要があるかもしれません。

mkdir ~/openvpn

上のディレクトリにSFTPなどで転送します。

また、ユーザー名とパスワードを記したsecretファイルも用意します。

cat << "EOF" | sudo tee ~/openvpn/secret

YOUR_USERID
YOUR_PASSWORD
EOF

ls ~/openvpn

ca.crt client.crt client.key client.opvn secret

client.opvnだけconfとして/etc/openvpnに、他のファイルは/etc/openvpn/clientに移動します。

chmod 600 ~/openvpn/*

sudo mv ~/openvpn/client.opvn /etc/openvpn/client.conf
sudo mv ~/openvpn/* /etc/openvpn/client
rm -rf ~/openvpn


設定ファイルの確認

ここで、client.confの確認を行います。

sudo cat /etc/openvpn/client.conf

以下は一例です。

client

dev tun
proto udp
remote xxx.xxx.xxx.xxx 1194
remote-cert-tls server
resolv-retry infinite
redirect-gateway def1
nobind
tun-mtu 1500
tun-mtu-extra 32
mssfix 1450
persist-key
persist-tun
ping 15
ping-restart 0
ping-timer-rem
reneg-sec 0
explicit-exit-notify 3

comp-lzo
verb 3
pull
fast-io
cipher AES-256-CBC
auth SHA512

auth-user-pass client/secret
ca client/ca.crt
cert client/client.crt
key client/client.key

auth-user-passcacertkeyの値について注意してください。デフォルトで、/usr/sbin/openvpnの起動時に--cd /etc/openvpnが引数として渡されているため、各ファイルのパスは、デーモンからみたcd/etc/openvpnになります。この値は、あるいは絶対パスで書いてもいいでしょう。

nobindは動的にポートを確保するという設定を意味します。

redirect-gatewayについてはOpenVPNが自動でルーティングを設定してくれないを参照してください。


テスト

OpenVPNクライアントを開始し、うまく接続できているか確認します。

まず、次のコマンドで現在のipはインターネットの外側からどう見えているかチェックしておきましょう。

curl httpbin.org/ip

{

"origin": "143.212.233.219"
}

次にOpenVPNクライアントを起動します。

sudo systemctl start openvpn@client

sudo systemctl status openvpn@client

Aug 15 09:20:35 vpn-proxy-00 ovpn-client[4082]: TUN/TAP device tun0 opened

Aug 15 09:20:35 vpn-proxy-00 ovpn-client[4082]: TUN/TAP TX queue length set to 100
Aug 15 09:20:35 vpn-proxy-00 ovpn-client[4082]: do_ifconfig, tt->did_ifconfig_ipv6_setup=0
Aug 15 09:20:35 vpn-proxy-00 ovpn-client[4082]: /sbin/ip link set dev tun0 up mtu 1500
Aug 15 09:20:35 vpn-proxy-00 ovpn-client[4082]: /sbin/ip addr add dev tun0 local 10.250.203.130 peer 10.250.203.129
Aug 15 09:20:40 vpn-proxy-00 ovpn-client[4082]: /sbin/ip route add 0.0.0.0/1 via 10.250.203.129
Aug 15 09:20:40 vpn-proxy-00 ovpn-client[4082]: /sbin/ip route add 128.0.0.0/1 via 10.250.203.129
Aug 15 09:20:40 vpn-proxy-00 ovpn-client[4082]: /sbin/ip route add 10.250.200.1/32 via 10.250.203.129
Aug 15 09:20:40 vpn-proxy-00 ovpn-client[4082]: Initialization Sequence Completed

ipコマンドがいくつか実行された後、Initialization Sequence Completedと表示されていれば正しく接続されています。

さて、外側から見えるipは変わっているでしょうか。

curl httpbin.org/ip

{

"origin": "24.127.96.129"
}

問題なさそうならサービスを有効にしましょう。

sudo systemctl enable openvpn@client


usocksd(SOCKSサーバ)

usocksdというSOCKSサーバを使用します。BINDUDP associate命令には対応していませんが、設定がある程度柔軟に行なえて、パフォーマンスがよいのでオススメです。

cybozu-go/usocksd: SOCKS4/5 server library and command in Go

まず、サービスを実行するユーザーを作ります。

sudo useradd usocks

必須というわけではありませんが、iptablesで-m owner --uid-owner usocksのようにユーザー名を指定できるので、特別なユーザー名を使うように設定しておくと便利なケースがあります。


インストール

golangで書かれているのでgo getした後、go buildします。

sudo apt install golang

go get -u github.com/cybozu-go/usocksd

go build -a -o usocksd go/src/github.com/cybozu-go/usocksd/cmd/usocksd/main.go

うまくビルドできたら/usr/binへ移動します。

chmod +x usocksd

sudo mv ./usocksd /usr/bin


設定ファイルを用意する

SOCKSのデフォルトポートは1080です。

ここではコメントアウトしていますが、設定ファイルの後半でallow_sitesdeny_sitesdeny_portsaddressesなどを設定できます。

cat << "EOF" | sudo tee /etc/usocksd.toml

[log]
#filename = "/var/log/usocksd/usocks.log" # default to stderr.
level = "error" # critical, error, warning, info, debug
format = "plain" # plain, logfmt, json

[incoming]
port = 1080
addresses = ["0.0.0.0"] # List of listening IP addresses
allow_from = ["0.0.0.0/0"] # CIDR network or IP address

#[outgoing]
#allow_sites = [ # List of FQDN to be granted.
# "www.amazon.com", # exact match
# ".google.com", # subdomain match
#]
#deny_sites = [ # List of FQDN to be denied.
# ".2ch.net", # subdomain match
# "bad.google.com", # deny a domain of *.google.com
# "", # "" matches non-FQDN (IP) requests.
#]
#deny_ports = [22, 25] # Black list of outbound ports
#addresses = ["12.34.56.78"] # List of source IP addresses
#dnsbl_domain = "some.dnsbl.org" # to exclude black listed IP addresses
EOF


systemd用のファイルを用意する

cat << "EOF" | sudo tee /etc/systemd/system/usocksd.service

[Unit]
Description = usocksd Micro SOCKS server
After=openvpn@client.service

[Service]
Type = simple
StandardOutput = syslog
StandardError = syslog
SyslogIdentifier = usocksd
ExecStart = /usr/bin/usocksd
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -INT $MAINPID
KillMode = process
User = usocks
Group = usocks
Restart=always

[Install]
WantedBy = multi-user.target
EOF

After=openvpn@client.serviceでOpenVPNクライアントより後に起動することを明示しています。


テスト

sudo systemctl daemon-reload

sudo systemctl restart usocksd
sudo systemctl status usocksd

curlコマンドでSOCKSプロキシの動作を確認できます。

curl --socks5 localhost:1080  httpbin.org/ip

あるいは、ローカルネットワーク上の別のマシンから接続してみてもいいでしょう。

うまく動いたらサービスを有効化しましょう。

sudo systemctl enable usocksd


cockpit(管理用Webインターフェイス)

cockpitは軽量なシステム管理用のWebインターフェイスです。簡単に負荷を確認したり、問題のあるサービスを再起動したり、シェルを叩いたりと、今回のようなケースには十分な機能を備えています。


インストール

sudo apt install cockpit

デフォルトのポートは9090です。変更する場合は次のURLを参考にしてください。

TCP Port and Address


スクリーンショット

192.168.0.3_9090_system(iPad).png

192.168.0.3_9090_system(iPad) (1).png

192.168.0.3_9090_system(iPad) (2).png

192.168.0.3_9090_system(iPad) (3).png

192.168.0.3_9090_system(iPad) (4).png

192.168.0.3_9090_system(iPad) (5).png


ufw (ファイアウォール)

ファイアウォールを適切に設定し、OpenVPNクライアントのみがインターネットに接続できるようにすることで、SOCKSサーバへのリクエストが常にVPNを経由することを保証できます。

以下のようにルールの設定をしていきます。

ここで、ネットワークインターフェイスをeth0、ローカルネットワークは192.168.1.0/24とします。OpenVPNサーバのデフォルトポートは1194ですが、443などが使用されている場合もあります。適宜変更してください。

# デフォルトでin/outを禁止

sudo ufw default deny incoming
sudo ufw default deny outgoing

# OpenVPNのインターフェイスのin/outを許可
sudo ufw allow in on tun0
sudo ufw allow out on tun0

# ローカルネットワークについてin/outを許可
sudo ufw allow in on eth0 from 192.168.1.0/24
sudo ufw allow out on eth0 to 192.168.1.0/24

# OpenVPNサーバのポートへのoutを許可
sudo ufw allow out on eth0 to any port 1194

# SSHポートへの接続にリミットを設定
sudo ufw limit ssh

# NTPポートへのin/outを許可
sudo ufw allow ntp

# DNSポートへのin/outを許可
sudo ufw allow in from any port 53
sudo ufw allow out to any port 53

ファイアウォールを有効にします。

sudo ufw enable

sudo ufw status


OpenVPNが落ちているときにクライアントがインターネットに直接通信できてしまう穴を塞ぐ

# OpenVPNサーバのポートへのoutを許可

sudo ufw allow out on eth0 to any port 1194

上記の設定のこの箇所について、ヤバみを感じませんか?

そう、OpenVPNクライアントが落ちているときに、usocksdもこのポートに対して直接通信することができてしまいます。当然、SOCKS経由で通信してるクライアントもインターネットに直接…。

適宜変更してくださいと書きましたが、このポートが443であったりするとヤバヤバです。ご存知の通り、Webサービスもこのポートをよく使っているのです。

# OpenVPNサーバのポートへのoutを許可…しちゃう?

sudo ufw allow out on eth0 to any port 443

HTTPSで使用するのはTCPプロトコルなので、UDPプロトコルを指定しておけばいいようにも思えますが、そもそもインターネットに直接接続できているのがよくありません。

この問題に対処する方法はいろいろ考えられますが、ここは伏線を生かしましょう。覚えていますか? usocksdデーモンの所有者がusocksユーザーであることを。

ps aux | grep usocks

usocks     1367  0.0  0.6 385204  5296 ?    Ssl  03:31   0:00 /usr/bin/usocksd

usocks 1387 0.4 7.1 609412 56132 ? Sl 03:31 0:43 /usr/bin/usocksd

iptablesではuidを使ったフィルタリングも可能なので、素直に書くと次にようになります。443ポートへのoutについて、uidがusocksでないときに許可します。

iptables -A ufw-before-output -p udp -m owner ! --uid-owner usocks --dport 443 -j ACCEPT

iptables -A ufw-before-output -p tcp -m owner ! --uid-owner usocks --dport 443 -j ACCEPT

今回はファイアウォールの設定にufwを使ったので、ufwのルールにこれを組み込む必要があります。

sudo vi /etc/ufw/before.rules

一番最後のCOMMITの手前に以下の2行を追加します。

-A ufw-before-output -p udp -m owner ! --uid-owner usocks --dport 443 -j ACCEPT

-A ufw-before-output -p tcp -m owner ! --uid-owner usocks --dport 443 -j ACCEPT

ファイルの最後の10行は次のようになります。

# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above

# is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT

# allow outgoing OpenVPN traffic
-A ufw-before-output -p udp -m owner ! --uid-owner usocks --dport 443 -j ACCEPT
-A ufw-before-output -p tcp -m owner ! --uid-owner usocks --dport 443 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

ファイアウォールを更新しましょう。

sudo ufw reload

うまくいきましたか?

なにか問題があればこのときに表示されます。

なにか問題があったとしても、慌てず焦らず、このチャプタの最初から、ただし次の魔法のおまじないの後に続けて、もう一度やり直してください。

sudo ufw reset


参考

ファイアウォールの設定

VPN が落ちた時に漏洩を防止


FAQ


OpenVPNが自動でルーティングを設定してくれない

client.confredirect-gatewayが含まれているかチェックしてください。OpenVPNサービスのログで、接続開始時にサーバ側からのこのパラメータの強制的なpushが行われていないか、併せて確認してください。指定がない場合、自動的なルーティングの設定が行われません。

設定が適切に行われていない場合、client.confに以下を追加してください。

redirect-gateway def1


OpenVPNが頻繁に落ちる/再起動する

client.confについて、inactiveがあるとデーモンが落ちます。ping-exitも同様です。後者はping-restartに変更したほうがよさそうです。

そして、設定に問題がない場合、何らかの原因でデーモンが死んでしまったときも、常に再起動するようにしておいたほうがよいでしょう。当該サービスのsystemd設定ファイル内、[Service]セクションにRestart=alwaysを追加する必要がありますが、systemdは標準で/lib/systemd/system/openvpn-client@.serviceを読み込んでいます。これを/etc/systemd/system/にコピーすればsystemdはこちらを優先度が高いものとして読み込みます。後者を編集するのが正式な手順になります。

sudo cp /lib/systemd/system/openvpn-client@.service /etc/systemd/system/openvpn-client@.service

sudo vi /etc/systemd/system/openvpn-client@.service

systemd-deltaで変更を確認できます。

sudo systemd-delta

[OVERRIDDEN] /etc/systemd/system/openvpn-client@.service → /lib/systemd/system/openvpn-client@.service

--- /lib/systemd/system/openvpn-client@.service 2018-02-11 05:27:56.000000000 +0900
+++ /etc/systemd/system/openvpn-client@.service 2018-08-15 00:45:42.245019600 +0900
@@ -18,6 +18,7 @@
ProtectSystem=true
ProtectHome=true
KillMode=process
+Restart=always

[Install]
WantedBy=multi-user.target


負荷を掛けるとSOCKSサーバの応答がなくなる

ファイルディスクリプタ数制限に引っかかってるのかもしれません。この値は次のように設定できます。

sudo mkdir /etc/systemd/system/openvpn-client@.service.d

cat << "EOF" | sudo tee /etc/systemd/system/openvpn-client@.service.d/override.conf
[Service]
LimitNOFILE=81915
EOF

sudo mkdir /etc/systemd/system/usocksd.service.d

cat << "EOF" | sudo tee /etc/systemd/system/usocksd.service.d/override.conf
[Service]
LimitNOFILE=81915
EOF

sudo systemd-delta

[EXTENDED]   /etc/systemd/system/openvpn-client@.service → /etc/systemd/system/openvpn-client@.service.d/override.c

[EXTENDED] /etc/systemd/system/usocksd.service → /etc/systemd/system/usocksd.service.d/override.conf

また、次のようなパラメータの設定も定番の対処方法です。

cat << "EOF" | sudo tee /etc/sysctl.conf

net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_intvl = 10

net.core.somaxconn = 8192
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_max_syn_backlog = 8192

net.ipv4.ip_local_port_range = 2048 65535

net.ipv4.tcp_tw_reuse = 1

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF

sudo sysctl -p

上記の値は一例です。負荷や回線環境、OpenVPNサーバの設定などに合わせて調節してください。


おわりに

毎年、夏休み時間を作ってなんやかんやわちゃわちゃしているのですが、今年はあまり長くとれずこの記事が唯一の成果となりました。

ソックスソックス!