okhttpでSocks Proxy経由のHTTPリクエストをする

備忘録的小ネタ。

動機

  • ネットワーク的に制限されている社内基盤のAPIを利用するプログラムを書いているが、オフィスのネットワークからじゃ直接アクセスできないので通信が許可されているサーバからじゃないとHTTPでアクセスできないのでタルい
  • Mock使ってテスト書くわけだけど、それ以前に実際にリクエスト投げてトライアンドエラーしたい。

このようなネットワーク制限を飛び越えてHTTPアクセスするためによく利用されるのがHTTP Proxyであり、それをSSH経由でセキュアに実現するのがSocks Proxyです。

そもそもSocks Proxyとは

素のHTTP Proxyとは違い、踏み台まではSSHトンネルすることでクライアントから目的地までセキュアにHTTPリクエストをすることができる仕組みです。イメージは以下のような感じですね。

クライアント <==SSH===>踏み台<===HTTP/HTTPS===>目的の閉じられたサーバ

Socks Proxyを立てる

まずローカルにSocks Proxyを立てます。そして目的地となるAPIサーバに実際にHTTPリクエストを投げる踏み台となるサーバを用意します。 以下のようなsshコマンドで1080ポートがSocks ProxyのポートとしてListenします(-f -Nオプションをつけてバックグラウンド実行させてます)

ssh -f -N -D localhost:1080 username@your_fumidai

1080がListenしたら、curl等で実際にリクエストできるかを確認してみるとよいですね。

curl --socks5 localhost:1080 http://your_closed_host/api/hogehoge

okhttpでリクエストする

okhttpといえばJava界(というかAndroidメインだと思うけど)で広く利用されているHTTPクライアントです。

square.github.io

ちょっと今JVM系言語であるKotlinを使って書いているので、こんな感じでSocks Proxy経由でHTTPリクエストができるようになります。

class SocksTest {

    @Test
    fun testRequest() {

        val socketAddress = InetSocketAddress("localhost", 1080)
        val proxy = Proxy(Proxy.Type.SOCKS, socketAddress)

        val client = OkHttpClient.Builder().proxy(proxy).build()
        val request = Request.Builder()
            .url("http://your_closed_host/api/hogehoge")
            .build()

        val response = client.newCall(request).execute()
        println(response.body().string())
    }
}

OkHttpClientにproxyで、Socks Proxy情報を持つjava.net.Proxyオブジェクトを渡すだけです。あとはいつものokhttp。Javaでやりたい場合はJavaに置き換えてください。 別にokhttpじゃなくとも、proxyサポートしてるHTTPクライアントであれば同じ感じで応用が効くと思われます。

おわり。