これは、なにをしたくて書いたもの?
curlの「--proxytunnel」オプションを使うと、フォワードプロキシサーバー+HTTP CONNECTでトンネリングができるというのを知り。
試してみましょうかと。
環境
今回の環境は、Ubuntu Linux 18.04 LTSです。
$ uname -srvmpio Linux 4.15.0-96-generic #97-Ubuntu SMP Wed Apr 1 03:25:46 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic
curlのバージョンはこちら。
$ curl --version curl 7.58.0 (x86_64-pc-linux-gnu) libcurl/7.58.0 OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3 Release-Date: 2018-01-24 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL
お題として、Apacheをフォワードプロキシサーバーに、背後に簡単なHTTPサーバーを立て、curlを使ってHTTP CONNECTで
トンネリングできることを確認してみましょう。
ApacheでProxyサーバーを作る
まずは、Apacheをインストールします。
$ sudo apt install apache2
バージョン。
$ apache2 -v Server version: Apache/2.4.29 (Ubuntu) Server built: 2020-03-13T12:26:16
プロキシ系のモジュールを有効にします。HTTP CONNECTだけを使うという目線なら「proxy_http」は不要なのですが、今回は
比較用に入れておきました。
$ sudo a2enmod proxy proxy_connect proxy_http
フォワードプロキシの設定は、こんな感じで。8080ポートでリッスンするようにしました。
Listen 8080 <VirtualHost *:8080> ProxyRequests On ProxyVia On AllowCONNECT 443 8000 <Proxy *> Require host localhost </Proxy> ErrorLog ${APACHE_LOG_DIR}/proxy_error.log CustomLog ${APACHE_LOG_DIR}/proxy_access.log combined </VirtualHost>
背後のHTTPサーバーはポート8000を使うことにするので、AllowCONNECTで443、8000を指定。
設定したら、Apacheを再起動します。
$ sudo systemctl restart apache2
これで、準備は完了です。
Pythonで簡単なHTTPサーバーを立てる
では、フォワードプロキシ越しにアクセスするHTTPサーバーを用意します。
Pythonで簡単に。
$ echo 'Hello Python!!' > hello.txt $ python3 -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
単体で確認。
$ curl localhost:8000/hello.txt Hello Python!!
HTTP CONNECTでトンネリングする
それでは、プロキシ越しにアクセスしてみましょう。最初はなにも考えずにアクセスしてみます。
$ curl -v localhost:8000/hello.txt --proxy http://localhost:8080 * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) > GET http://localhost:8000/hello.txt HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.58.0 > Accept: */* > Proxy-Connection: Keep-Alive > < HTTP/1.1 200 OK < Date: Thu, 09 Apr 2020 12:46:30 GMT < Server: SimpleHTTP/0.6 Python/3.6.9 < Content-type: text/plain < Content-Length: 15 < Last-Modified: Thu, 09 Apr 2020 12:41:29 GMT < Via: 1.1 ubuntu1804.localdomain:8000 < Vary: Accept-Encoding < Hello Python!! * Connection #0 to host localhost left intact
まあ、ふつうにこうなりますよね。
> GET http://localhost:8000/hello.txt HTTP/1.1
localhost - - [09/Apr/2020:12:46:30 +0000] "GET http://localhost:8000/hello.txt HTTP/1.1" 200 261 "-" "curl/7.58.0"
ここで、「--proxytunnel」オプションを付けると挙動が変わります。
$ curl -v localhost:8000/hello.txt --proxy http://localhost:8080 --proxytunnel * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to localhost:8000 > CONNECT localhost:8000 HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.58.0 > Proxy-Connection: Keep-Alive > < HTTP/1.0 200 Connection Established < Proxy-agent: Apache/2.4.29 (Ubuntu) < * Proxy replied 200 to CONNECT request * CONNECT phase completed! * CONNECT phase completed! * CONNECT phase completed! > GET /hello.txt HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.58.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Server: SimpleHTTP/0.6 Python/3.6.9 < Date: Thu, 09 Apr 2020 12:46:56 GMT < Content-type: text/plain < Content-Length: 15 < Last-Modified: Thu, 09 Apr 2020 12:41:29 GMT < Hello Python!! * Closing connection 0
HTTP CONNECTでのアクセスになりましたね。
> CONNECT localhost:8000 HTTP/1.1
localhost - - [09/Apr/2020:12:47:09 +0000] "CONNECT localhost:8000 HTTP/1.1" 200 276 "-" "curl/7.58.0"
無事、できました、と。
ちなみに、HTTPの場合はhttp_proxy環境変数と「--proxytunnel」オプションでも良いのですが、プロトコルを変えると効かなくなるので、
今回は「--proxy」オプションを使用しています。
$ http_proxy=http://localhost:8080 curl -v http://localhost:8000/hello.txt --proxytunnel
$ curl -v telnet://localhost:8000 --proxy http://localhost:8080 * Rebuilt URL to: telnet://localhost:8000/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to localhost:8000 > CONNECT localhost:8000 HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.58.0 > Proxy-Connection: Keep-Alive > < HTTP/1.0 200 Connection Established < Proxy-agent: Apache/2.4.29 (Ubuntu) < * Proxy replied 200 to CONNECT request * CONNECT phase completed! * CONNECT phase completed! GET /hello.txt Hello Python!!
この場合、実は「--proxytunnel」オプションを使用しなくてもHTTP CONNECTでのアクセスとなります。
localhost - - [09/Apr/2020:12:56:47 +0000] "CONNECT localhost:8000 HTTP/1.1" 200 91 "-" "curl/7.58.0"
「--proxytunnel」オプションをつけてもいいんですけど、挙動は変わらずです。
$ curl -v telnet://localhost:8000 --proxy http://localhost:8080 --proxytunnel * Rebuilt URL to: telnet://localhost:8000/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to localhost:8000 > CONNECT localhost:8000 HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.58.0 > Proxy-Connection: Keep-Alive > < HTTP/1.0 200 Connection Established < Proxy-agent: Apache/2.4.29 (Ubuntu) < * Proxy replied 200 to CONNECT request * CONNECT phase completed! * CONNECT phase completed!
curlのオプションの確認と、HTTP CONNECTでのトンネリングの確認ができました、と。