はじめに
PCからスマートフォンまで、我々が日常で接する機会のあるコンピュータの多くは、標準でVPNに対応しています。しかし、標準で提供されるVPNクライアント機能は、コンピュータが行う通信すべてをVPN経由にすることを前提としています。あるアプリケーションはVPNで保護したいけれども、その他のアプリケーションについてはそうではないというようなケースでは、特定のアプリケーションにだけ、VPNを経由することが保証されたプロキシサーバを設定することで、最適な環境を構築できます。
この記事ではOpenVPNプロトコルでVPNサービスに接続し、SOCKS経由で他のマシンにセキュアな回線を提供するプロシキサーバを構築します。
必要なものは?
- インターネット回線
- サーバにするPC 1台(仮想PCでよい)
- OpenVPNサーバへの接続権(この記事の用途が各種規約に反しないこと)
どうやって使うの?
オススメはFirefoxです。設定画面からプロキシを簡単に設定でき、この設定は、OSや、他のプロファイルから独立しています。複数のFirefoxの特定のプロファイルだけにプロキシを設定することができ、使い勝手が大変よいです。
準備
テスト環境
- 仮想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-pass
、ca
、cert
、key
の値について注意してください。デフォルトで、/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サーバを使用します。BIND
とUDP 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_sites
、deny_sites
、deny_ports
、addresses
などを設定できます。
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
スクリーンショット
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
参考
FAQ
OpenVPNが自動でルーティングを設定してくれない
client.conf
にredirect-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サーバの設定などに合わせて調節してください。
おわりに
毎年、夏休み時間を作ってなんやかんやわちゃわちゃしているのですが、今年はあまり長くとれずこの記事が唯一の成果となりました。
ソックスソックス!