読者です 読者をやめる 読者になる 読者になる

GeekFactory

int128.hatenablog.com

認証付きプロキシの内側で共用サーバを運用する

多くの企業や教育機関ではインターネットアクセス用のプロキシサーバが導入されています。プロキシサーバの中にはユーザ認証を必要とするもの(認証付きプロキシ)があり、組織やチームの共用サーバを運用する上でしばしば問題を引き起こします。

例えば、共用サーバで以下のオペレーションを実行する時、認証付きプロキシを越える必要が生じます。

  • OSやミドルウェアのアップデート(yum, apt-get等)
  • パッケージマネージャが外部リポジトリからパッケージをダウンロードする時(npm, RubyGems, EasyInstall, Jenkins plugins等)
  • ビルドツールが依存ライブラリをダウンロードする時(Maven, Gradle, sbt等)

共用サーバから認証付きプロキシを経由してインターネットにアクセスするには、個人の認証情報を共用サーバに設定する必要があります。 認証情報は本来その人しか知り得ない情報であり、その前提が崩れると以下のような問題が生じます。

  1. 共用サーバに格納されている認証情報が盗まれて、他者に悪用されるリスクがある。
  2. 担当者の不在時に問題が発生すると、再現性確認や調査が困難になる。結果として、運用が特定の担当者に依存してしまう。
  3. 担当者がチームから離れる場合に設定変更が必要になる。設定箇所が多いほど手間やミスが増える。

一部の開発現場では、担当者のパスワードをチーム内で共有することでこれらの問題に対処していることがあります。不正アクセスが発生するとその担当者が不利益を被る可能性が高いため、まったくお勧めできません。複数システムで同じパスワードを使用している場合は被害が拡大するおそれがあります。

これまで述べた問題を解決するには、以下のアプローチがあります。

システムアカウントを発行してもらう

一つの方法は、特定の個人とは紐付かないシステムアカウント(非人格のアカウント)を発行してもらうことです。 システムアカウントを使用することで、特定の担当者に依存する運用がなくなり、スケーラビリティが改善します。

システムアカウントのIDは、ログ等で分かりやすいように組織や役割を組み合わせた文字列にするとよいでしょう。 例えば、プロジェクトhogeのJenkinsサーバであればhoge-jenkins、プロジェクトhogeのビルド全般を担うのであればhoge-build-userなどです。 また、システムアカウントはツールだけが使用するので、パスワードは覚えにくいランダムな文字列がよいでしょう。

この方法の前提として、プロキシサーバの管理者がシステムアカウントの運用を許可している必要があります。 また、システムアカウントのパスワードはチーム内で安全に管理する必要があります。 不正アクセスが発生した場合はチーム全員の責任になります。 sshやsudoのログを残す等、問題発生時に確実に調査できる仕組みを作っておきましょう。

個人端末のローカルプロキシを経由する

個人端末にローカルプロキシを立てておき、共用サーバからSSHポートフォワードを経由してアクセスする方法があります。 具体的には、ユーザが共用サーバにログインする時に以下のオペレーションを行います。

  1. 個人端末にローカルプロキシを立てる。
  2. 共用サーバにSSHログインする際にポートフォワードを有効にする。具体的にはリモートポートをローカルポートに転送する。例:ssh -R 9090:localhost:9090
  3. 共用サーバでは転送元のポートをプロキシサーバに指定する。例:export http_proxy=http://localhost:9090/

この方法では共用サーバに認証情報を置く必要がないため、他者に認証情報を盗み見られることはありません。 また、プロキシにアクセスできるのはユーザがログインしている間に限られるため、不正アクセスに使われるリスクは比較的低いといえます。 安全性を高めるため、iptablesで当該ポートにアクセスできるuid/gidを制限することも可能です。

実際に運用する場合は、シェルでプロキシの環境変数を設定しておくと便利です。

# .zshrc
function enable_proxy () {
  local port="$1"
  local host="$2"
  [ -z "$port" ] && port='9090'
  [ -z "$host" ] && host='127.0.0.1'

  export http_proxy="http://$host:$port/"
  export https_proxy="$http_proxy"
  export JAVA_OPTS="-Dhttp.proxyHost=$host -Dhttp.proxyPort=$port -Dhttps.proxyHost=$host -Dhttps.proxyPort=$port"
}

# .zshrc.local
enable_proxy

また、sudoでプロキシの環境変数が引き継がれるようにsudoersを設定しておくと便利です。

# visudoでsudoersに以下を追記
Defaults    env_keep += "http_proxy https_proxy"

ただし、バックグラウンドデーモンにプロキシアクセスを提供するには、ユーザがログイン状態を維持しておく必要があります。 個人端末のローカルプロキシが不正アクセスに利用されるリスクが高まるため、できればiptablesでアクセス制御を行った方がよいでしょう。

まとめ

認証付きプロキシの内側で共用サーバを運用する場合、特定個人の認証情報を共用サーバに格納するとセキュリティや運用体制の問題が生じます。 専用のシステムアカウントを使用する、もしくは個人端末のローカルプロキシを経由してアクセスすることで、安全性や運用性を改善することが可能です。