Ubuntu Weekly Recipe

第617回 SOCKSを利用してSSHのみで簡易VPNを構築する

この記事を読むのに必要な時間:およそ 4 分

SOCKSサーバーの立ち上げ

それでは,さっそくクライアントマシンからSSHコマンドでSOCKSサーバーを立ち上げてみましょう。

$ ssh -fNC -D 1080 -oExitOnForwardFailure=yes ubuntu@10.93.9.120

「10.93.9.120」はコンテナのIPアドレスです。実際に利用する場合は,組織内LAN上にあるSSHログイン可能なマシンのアドレスになるでしょう。踏み台を経由した多段接続が必要な場合は-oProxyCommand='ssh -W %h:%p ユーザー名@ホスト名'なども組み合わせてください。より新しいOpenSSHなら-J ユーザー名@ホスト名という記述(ProxyJump)も使えます。⁠ubuntu@」とユーザー名を明示しているのはコンテナのユーザー名がクライアントマシンのそれと違うからです。もし同じなら指定しなくても大丈夫でしょう。

他のオプションは次のとおりです。

  • -f⁠:sshコマンドをバックグラウンドで動かす。-oExitOnForwardFailure=yesがついているので,何かエラーが発生したらsshコマンドは終了します。
  • -N⁠:単純にSSHのコネクションのみを作成します。SSH接続先で何かコマンドを実行することはありません。
  • -C⁠:コネクションでやりとりするデータをすべて圧縮します。これは指定しなくても問題ありません。
  • -D 1080⁠:SOCKSサーバーとしてポート1080で待ち受けます。

これでクライアントマシンから,コンテナの中のサイトが閲覧できるようになりました。curlコマンドにはSOCKSプロキシにも対応しているので実際に試してみます。

$ curl -sL --proxy socks5://localhost:1080/ http://10.93.9.120/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(以下略)

期待通り組織内LAN(コンテナネットワーク)内部のウェブサーバーにアクセスできました。ちなみにここまでの手順はWindowsでもPuTTYなどのSSHクライアントやWSL上のsshコマンドでも実現できるはずです。

たとえば組織内LANからのみアクセス可能な領域のgitリポジトリをcloneしたい場合は,次のように実行できます。

$ git -c http.proxy=socks5://localhost:1080 clone https://foo.example.co.jp/git/repo.git

設定が問題なさそうなら,~/.ssh/configに書いてしまうと,再利用しやすくなります。

Host socks
    HostName 10.93.9.120
    User ubuntu
    DynamicForward 1080
    Compression yes
    ExitOnForwardFailure yes
    RequestTTY no

あとはssh -f socksで,さきほどのコマンドと同じ状態になります。

ウェブブラウザー側の設定

次にウェブブラウザーから,特定のアドレスにアクセスした際に指定したSOCKSサーバーを経由するようにします。まずは次のような内容の「socks.pac」ファイルを作成します。ファイル名はなんでもかまいません。

function FindProxyForURL(url, host) {
  if (isInNet(host, "10.93.9.0", "255.255.255.0")) {
    return "SOCKS5 localhost:1080";
  }
  return "DIRECT";
}

これはProxy Auto Configuration(PAC)と呼ばれるプロキシの自動構成スクリプトです。JavaScriptの関数としてロジックも書けるので,特定のURLなら特定のSOCKSサーバーに繋ぐといった判定も可能です。

今回はドメイン名の指定がないため,IPアドレスでマッチングを行います。isInNet()は指定したhostが,第二引数のアドレスパターンと第三引数のネットワークマスクで指定したネットワークに存在する場合に真になります。第二引数はドメイン名で指定した場合,IPアドレスに変換した上で第三引数とかけ合わせます。

真になった場合はプロキシとしてSOCKS5 localhost:1080を使い,そうでない場合はDIRECTつまりはプロキシを使わないという寸法です。

PACファイルの指定方法はウェブブラウザーごとで異なります。

まずFirefoxの場合は,設定の「一般」もしくはabout:preference#generalからアクセスしたページの「接続設定」からネットワークの設定画面を開きます。

図1 Firefoxのインターネット接続設定

画像

「自動プロキシ構成スクリプト」に先程作ったPACファイルのパスを指定してください。ちなみにURLとしてネットワークの先のファイルを指定することも可能です。

あとは普通に「http://10.93.9.120/」にアクセスすれば,本来クライアントから直接見れないウェブページが表示されるはずです。

Chrome/Chromiumに関しては,プロキシ関連の設定が削除されてしまったため,そのままではPACファイルを利用できません。たとえばSwitchyOmegaなどの拡張機能を入れる必要があります※3⁠。

※3
以前はコマンドラインオプションで--proxy-pac-url=ファイル名で指定も可能でしたが,現在はそれらの機能は削除されています。

より一般的には特定の組織内LANには特定のドメイン名が付けられていることでしょう。そうすると次のような設定が考えられます。

function FindProxyForURL(url, host) {
  if (dnsDomainIs(host, ".example.co.jp") && !isResolvable(host)) {
    return "SOCKS5 localhost:1080";
  }
  return "DIRECT";
}

dnsDomainIs()を使ってアクセスしようとしているサイトのホスト名が「.example.co.jp」にマッチしているかを確認します。さらにisResolvable()でそのホスト名が名前解決できるかをチェックしています。⁠名前解決できない=インターネットに公開されていないサイト」なのでSOCKSプロキシを経由させます。ちなみにFirefoxの場合,名前解決自体をSOCKS経由で行えます※4⁠。つまりisResolvable()がFALSEの状態でSOCKSに経由すれば名前解決が行えるようになるはずだ,ということです。

※4
ネットワーク設定の「SOCKS v5 を使用するときは DNS もプロキシーを使用する」にチェックを入れる必要があります。

Proxy Auto Configuration(PAC)には他にも「平日の特定の時間帯のみSOCKSを利用する」とか「ホスト名ではなくURLに特定の文字列が含まれていた場合のみSOCKSを利用する」などの設定が可能になる各種ユーテリティー関数が列挙されています。より細かく調整したい場合には参考になるでしょう。

ちなみに今回紹介した方法を使うと,本来は組織内にのみ公開が限定されていたウェブリソースに,公衆回線から簡単にアクセスできるようになります。仕事で使って良いかどうかについては,必ずあらかじめ組織内のセキュリティ担当部門に問い合わせましょう。問い合わせる際の質問の仕方が悪いと「ダメ」と言わざるを得なくなるので,よくよく考えてから問い合わせてください。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。