Dexadorを認証プロキシに対応させました

tamuraです。

Dexadorを認証プロキシに対応させた内容です。 プロキシには対応しているのですが、プロキシサーバがユーザ/パスワードを必要とした時にも対応させました。

(dex:get "https://www.yahoo.co.jp"
         :proxy "http://proxyuser:proxypass@proxyserver:8081")

この形式で認証プロキシを通すことができます。

認証プロキシ対応

HTTP

HTTPの場合は超簡単です。 リクエストヘッダに Proxy-Authorization パラメータを設定するだけです。

1 file changed, 7 insertions(+)
src/backend/usocket.lisp | 7 +++++++

modified   src/backend/usocket.lisp
@@ -499,6 +499,13 @@
                                            (format nil "~A:~A"
                                                    (car basic-auth)
                                                    (cdr basic-auth))))))
+                 (when proxy
+                   (let* ((uri (quri:uri proxy))
+                          (proxy-auth (quri:uri-userinfo uri)))
+                     (when proxy-auth
+                       (write-header* :proxy-authorization
+                                      (format nil "Basic ~A"
+                                              (string-to-base64-string proxy-auth))))))
                  (cond
                    (multipart-p
                     (write-header* :content-type (format nil "multipart/form-data; boundary=~A" boundary))

quriを使ってURLにユーザ名とパスワードを仕込んでいます。

HTTPS

HTTPSでちょっとハマりました。

HTTPヘッダではなく、CONNECTのタイミングでProxy-Authorizationを入れる必要があるとのことでした。

2 files changed, 21 insertions(+), 10 deletions(-)
src/backend/usocket.lisp | 24 +++++++++++++++---------
src/util.lisp            |  7 ++++++-

modified   src/backend/usocket.lisp
@@ -361,14 +361,20 @@
          buffer)
         (fast-write-sequence +crlf+ buffer)))))
 
-(defun make-connect-stream (uri version stream)
+(defun make-connect-stream (uri version stream &optional proxy-auth)
   (let ((header (with-fast-output (buffer)
-                  (write-connect-header uri version buffer))))
+                  (write-connect-header uri version buffer proxy-auth))))
     (write-sequence header stream)
     (force-output stream)
     (read-until-crlf*2 stream)
     stream))
 
+(defun make-proxy-authorization (uri)
+  (let ((proxy-auth (quri:uri-userinfo uri)))
+    (when proxy-auth
+      (format nil "Basic ~A"
+              (string-to-base64-string proxy-auth)))))
+
 (defun-careful request (uri &rest args
                             &key (method :get) (version 1.1)
                             content headers
@@ -416,7 +422,7 @@
                                                            (t :default)))))
                            (cl+ssl:with-global-context (ctx :auto-free-p t)
                              (cl+ssl:make-ssl-client-stream (if proxy
-                                                                (make-connect-stream uri version stream)
+                                                                (make-connect-stream uri version stream (make-proxy-authorization con-uri))
                                                                 stream)
                                                             :hostname (uri-host uri)
                                                             :verify (not insecure)
@@ -500,12 +506,12 @@
                                                    (car basic-auth)
                                                    (cdr basic-auth))))))
                  (when proxy
-                   (let* ((uri (quri:uri proxy))
-                          (proxy-auth (quri:uri-userinfo uri)))
-                     (when proxy-auth
-                       (write-header* :proxy-authorization
-                                      (format nil "Basic ~A"
-                                              (string-to-base64-string proxy-auth))))))
+                   (let ((scheme (quri:uri-scheme uri)))
+                     (when (string= scheme "http")
+                       (let* ((uri (quri:uri proxy))
+                              (proxy-authorization (make-proxy-authorization uri)))
+                         (when proxy-authorization
+                           (write-header* :proxy-authorization proxy-authorization))))))
                  (cond
                    (multipart-p
                     (write-header* :content-type (format nil "multipart/form-data; boundary=~A" boundary))
modified   src/util.lisp
@@ -141,7 +141,7 @@
      (let ((*header-buffer* ,buffer))
        ,@body)))
 
-(defun write-connect-header (uri version buffer)
+(defun write-connect-header (uri version buffer &optional proxy-auth)
   (fast-write-sequence (ascii-string-to-octets "CONNECT") buffer)
   (fast-write-byte #.(char-code #\Space) buffer)
   (fast-write-sequence (ascii-string-to-octets (format nil "~A:~A"
@@ -160,6 +160,11 @@
                                                        (uri-host uri)
                                                        (uri-port uri)))
                        buffer)
+  (when proxy-auth
+    (fast-write-sequence +crlf+ buffer)
+    (fast-write-sequence (ascii-string-to-octets "Proxy-Authorization:") buffer)
+    (fast-write-byte #.(char-code #\Space) buffer)
+    (fast-write-sequence (ascii-string-to-octets proxy-auth) buffer))
   (fast-write-sequence +crlf+ buffer)
   (fast-write-sequence +crlf+ buffer))
 

テスト

手元でのテストでもいいんですが、travis-ciでテストしたいな〜と思いいろいろやっていたら2ヶ月くらいたってしまいました。

squid

squidでhttpsを通すproxyを作ります。

http_access allow all

http_port 8081

auth_param basic program /usr/lib/squid3/basic_ncsa_auth /home/travis/squid-passwd
auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
auth_param basic credentialsttl 2 hours

acl basic_nsa proxy_auth REQUIRED
http_access allow basic_nsa

nginx

nginxでhttpsを喋るサーバを作ります。

events {
    worker_connections  1024;
}

http {
    include /home/travis/nginx/conf/mime.types;
    access_log off;
    server {
        listen       5000;
        listen       5001 ssl;
        server_name  localhost;

        ssl_certificate     /home/travis/server.crt;
        ssl_certificate_key /home/travis/server.key;

        ssl_session_timeout 5m;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers HIGH:!aNULL:!MD5;

        location / {
            root /home/travis/benchmark/;
        }
    }
}

travis

travis上でsquidを使ってproxyサーバを立てます。

:install
  .. 省略 ..
  # proxy
  - openssl req -x509 -nodes -new -keyout $HOME/server.key -out $HOME/server.crt -days 365 -subj "/C=JP/ST=Tokyo/L=Shinagawa/O=test/OU=test/CN=dexador"
  - sudo apt-get -qq update
  - sudo apt-get -qq install squid3 apache2-utils
  - htpasswd -b -c $HOME/squid-passwd testuser testpass


before_script:
  - nginx -c "$TRAVIS_BUILD_DIR/t/nginx-proxy.conf" -p "$HOME/nginx"
  - sudo squid3 -f "$TRAVIS_BUILD_DIR/t/squid.conf"
  - sudo squid3 -f "$TRAVIS_BUILD_DIR/t/squid-auth.conf"
  - ros --version
  - ros config
  - cp -R $TRAVIS_BUILD_DIR/benchmark $HOME

テスト

proxyサーバとHTTPサーバ・HTTPSサーバを定義してテストします。

(defparameter *AUTH-PROXY* "http://testuser:testpass@localhost:8081")

(defparameter *HTTP* "http://localhost:5000/181B.html")
(defparameter *HTTPS* "https://localhost:5001/181B.html")


(subtest "HTTP with auth proxy"
  (subtest "GET"
    (multiple-value-bind (body code headers)
        (dex:get *HTTP*
                 :proxy *AUTH-PROXY*)
      (is code 200)
      (is body BODY)
      (is (gethash "content-length" headers) 181))))

(subtest "HTTPS with auth proxy"
  (subtest "GET"
    (multiple-value-bind (body code headers)
        (dex:get *HTTPS*
                 :proxy *AUTH-PROXY*
                 :insecure T)
      (is code 200)
      (is body BODY)
      (is (gethash "content-length" headers) 181))))

いかのとおり、テストOKです!

最後に

https://github.com/fukamachi/dexador/pull/58/files

マージしていただきました。

関連記事

comments powered by Disqus