認証代行プロキシサーバーをDockerを使って構築する

# 背景

社内の認証ありプロキシサーバーはソフトウェア開発を行う上では邪魔でしかありません．
認証ありプロキシに対応していないアプリ，パスワード変更による毎回の設定変更，開発メンバー間での設定差分……これらの問題にいつも頭を悩まされていました．
そこで調べてみると先人たちがSquidを使って認証代行プロキシサーバーを立てている[^1]ことを知り，私もMac上にSquidを立てて使っていました．（とても便利！）

しかし，開発メンバーから「そんなの使っててセキュリティは大丈夫なの？」，「他のメンバーと環境変わっちゃうからそんなのだめだよ」みたいなことを言われうようになりました．

仕方がないのでMac用の認証代行プロキシ構築スクリプトをメンバーに展開して環境の統一には一応成功しました．
しかし，中身（特にSquid）の仕組みはあんまり展開していないので，もし問題があった場合は自分が見なきゃいけない状況になってしまっており，他プロジェクトへの展開があまりできていない状況です．

今回はDockerのみで極力シンプルに認証代行プロキシサーバーを構築できるようにしたので，その仕組みをSquidの設定を中心に紹介します．

[^1]: [認証ありプロキシを認証なしにするDockerコンテナ](https://qiita.com/yama07/items/b3ec857b6d5d26102a0d)とか，[squid で認証ありプロキシによるストレスを軽減する](https://qiita.com/scalper/items/e945dd103d356db6eae6)とか，[mac上にHTTPプロキシを立てることで認証プロキシの認証をスキップする](https://qiita.com/mtakayuki/items/6869fa4760afc9a659e9)とか，他にもたくさんあります．

# つくったもの

今回は以下リポジトリを紹介します．
最新の情報はここを参照するようにしてください．

https://github.com/k-ishigaki/proxy-docker

# 前提条件

Docker（Docker Compose）がインストールされていれば動作するはずです．
私が使ったDockerのバージョンは以下でした．

```shell
$ docker version
（中略）
Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
（後略）
$ docker --version
Docker version 19.03.9, build 9d988398e7
$ docker-compose --version
docker-compose version 1.26.2, build eefe0d31
```

# 全体像

Dockerコンテナ上に認証代行プロキシサーバー（Squid）を立てて，認証情報（Authorizationヘッダー）を付与するようにします．
こうすることでプロキシを利用するアプリケーションからは，認証なしでインターネットに接続できるようなります．

[![alt](http://www.plantuml.com/plantuml/svg/VLBTIW915BwVfvWNy0My2FjvW5vXc9rgQThPTcUX7uASGo6I4H7eLVGJP4g8I1Xez317oxjUePbHJ4LQ3dln_Npdu-mMKiqIdUr9mgNA0ehc2Q63vXEXZr1108GIPIaD8osII6X24i4rYxQbe0YlJcQQ2Csf6A7bmPPpISMPfCU4skVFDsfKHlK-5WsQCaBLnHWxNJHlVksLVJjtdAY3Gx9Wtbpt59-apfCRYk-A14q7mSuvGjc90u_E69RL6VGUH_SN_zL9zxCmcCk8b1PKYYsTduHymTdmherGyxSuHNCzwDK6xvTJrmbPqdfswR3qydrJH_FWmGQQjhzc5-4PeUDgi3YM8MSwL14PHzhG6P3ciZYtCcwxqAlOm6ALU6QYyYVEqzLCxwWaFF81URh6qf3JduzRudbgXTFlNn1HOF-NNm00)](http://www.plantuml.com/plantuml/svg/VLBTIW915BwVfvWNy0My2FjvW5vXc9rgQThPTcUX7uASGo6I4H7eLVGJP4g8I1Xez317oxjUePbHJ4LQ3dln_Npdu-mMKiqIdUr9mgNA0ehc2Q63vXEXZr1108GIPIaD8osII6X24i4rYxQbe0YlJcQQ2Csf6A7bmPPpISMPfCU4skVFDsfKHlK-5WsQCaBLnHWxNJHlVksLVJjtdAY3Gx9Wtbpt59-apfCRYk-A14q7mSuvGjc90u_E69RL6VGUH_SN_zL9zxCmcCk8b1PKYYsTduHymTdmherGyxSuHNCzwDK6xvTJrmbPqdfswR3qydrJH_FWmGQQjhzc5-4PeUDgi3YM8MSwL14PHzhG6P3ciZYtCcwxqAlOm6ALU6QYyYVEqzLCxwWaFF81URh6qf3JduzRudbgXTFlNn1HOF-NNm000)

# Squidの設定

squid.conf.templateの設定を1つずつ説明していきます．

## 基本的な設定

待受ポートは8080に設定します．
`visible_hostname`は設定しないと「未設定だよ」とエラーが表示されるため`none`に設定します[^visible_hostname]．
DNSはONにします．これを設定しておくとドメイン名のプロキシサーバーを指定できるようになります．（ならない環境もあるみたいですが……要因わからずです）

```conf
http_port 8080
visible_hostname none
dns_defnames on
```

[^visible_hostname]: [squidを久々に起動しようとしたらerrorになる、visible_hostnameを設定やて！](https://blog.goo.ne.jp/pepolinux/e/5d9563dd59feffd39ce65501fec19fd8)

## ACLの定義

SquidのACL（Access Control）を定義します．
`localnet`はローカルネットワークからのアクセス，`to_localnet_fast`はローカルネットワークへのアクセス，`to_localhost_fast`はローカルホストへのアクセス，`to_direct_access_domains`は指定ドメインへのアクセスを定義しています．

`dst`（デスティネーション）のACLでは`-n`フラグを指定して，DNS Lookupを無効にしています．有効にしてしまうと，ネットワークアクセスがものすごく低速になります．
また，`to_direct_access_domains`は`.direct_access_domains`に指定されたドメインを取り込むように指定しています[^direct_access_domains]．

[^direct_access_domains]: 社内ポータルサイトなどのプロキシ除外ドメインを簡単に設定できるように別ファイルから取り込むようにしています．

```conf
acl localnet src 0.0.0.1-0.255.255.255
acl localnet src 10.0.0.0/8
（中略）
acl to_localnet_fast dst -n 0.0.0.1-0.255.255.255
acl to_localnet_fast dst -n 10.0.0.0/8
（中略）
acl to_localhost_fast dst -n 127.0.0.0/8
acl to_direct_access_domains dstdomain -n "/.direct_access_domains"
```

## Squidへのアクセス許可

Squidへのアクセスを許可するネットワークを指定します．
内部ネットワークからのアクセスのみを許可しています[^http_access]．

[^http_access]: VirtualBoxなどの仮想環境からのアクセスを許可するために，localhostだけでなくlocalnetからのアクセスも許可しています．

```conf
http_access allow localhost
http_access allow localnet
```

## 認証ありプロキシサーバーへの転送設定

認証ありプロキシサーバーへの転送を行うリクエストを指定します．
Squidはデフォルトでは「`cache_peer`で指定されたプロキシへの転送を試みて，無理なら直接接続を試みる」動作なので，`never_direct allow <直接接続を許可する接続先以外の接続先>`で直接接続を禁止する接続先を指定します[^always_direct]．

[^always_direct]: `always_direct allow <直接接続を許可する接続先>`としないのはVPN経由で社内ネットワークに接続しているときはプロキシ経由で接続するからです．

`cache_peer`で親プロキシ（認証ありプロキシ）を指定します．
`$proxy_port`などの変数はDockerコンテナの起動時に`envsubst`で置き換えます．

```conf
never_direct allow !to_localhost_fast !to_localnet_fast !to_direct_access_domains
cache_peer $proxy_host parent $proxy_port 0 proxy-only no-digest no-netdb-exchange login=$proxy_auth
```

## Squidを秘匿化する設定

Squidなどのプロキシサーバーはデフォルトでは`PROXY_VIA`などの「プロキシを通していますよ」という情報を乗せる動作となります．
環境によっては通信ヘッダが異なると通信できなくなってしまう可能性があるので，Squidが勝手に乗せる情報を削除します．
また，転送のみの動作とさせるため，`cache deny all`でSquidのキャッシュを無効化しています．

```conf
forwarded_for delete
via off
cache deny all
request_header_access Cache-Control deny all
request_header_access Connection deny all
request_header_add Proxy-Connection "Keep-Alive" all
```

# Dockerでの認証代行プロキシ立ち上げ

Dockerイメージのビルドと起動はDocker Compose経由で行います．
ビルドするのにもプロキシの設定が必要なので，環境変数に`HTTP_PROXY_FOR_PROXY`を設定してビルド，起動を行うようにします．
ベースイメージ（Alpine）のpullを行うにはDocker Daemon側へのプロキシ設定が必要ですが，Docker実行環境によって設定方法が異なっているので，pullでダウンロードするのではなくloadでローカルのイメージを読み込むようにしています．

`<proxy_user>`，`<proxy_password>`に記号が入っている場合はURLエンコードした値を入れる必要があります．

```shell
cd proxy-docker
docker load ./alpine.tar
HTTP_PROXY_FOR_PROXY=http://<proxy_user>:<proxy_password>@<proxy_host>:<proxy_port> HOST_PORT=8080 docker-compose up -d
```

## Docker Composeの設定

`docker-compose.yml`にビルド，起動を行うときのパラメータを定義しています．
`HTTP_PROXY`と`HTTPS_PROXY`を`args`（ビルド時の環境変数），`environment`（起動時の環境変数）に入れるようにしています．
また，`HOST_PORT`でホスト側のポートを設定できるようにしています．
8080ポートが既に埋まっている場合は別のポートを指定できます．

```yml
version: "3.4"

x-proxy_settings: &proxy_settings
    HTTP_PROXY: "${HTTP_PROXY_FOR_PROXY:?HTTP_PROXY_FOR_PROXY must be set. Prease read README}"
    HTTPS_PROXY: "${HTTP_PROXY_FOR_PROXY}"

services:
    proxy:
        build:
            context: .
            args:
                <<: *proxy_settings
        environment:
            <<: *proxy_settings
        ports:
            - "${HOST_PORT:-8080}:8080"
```

## Dockerfileの設定

長いので中身は割愛しますが，以下を行っています．

 * ビルド時  
`HTTP_PROXY`に設定された値を使ってインターネットに接続し，Squidをインストール
 * 起動時  
`HTTP_PROXY`に設定された値を使ってSquidの設定ファイルを修正，Squidを立ち上げ

# まとめ

Squidを使った認証代行プロキシサーバーの設定について，それをDockerで立ち上げる方法について簡単に紹介しました．
これらの情報を使って少しでも多くの方が認証ありプロキシの不便から脱却できることを願っています．