Dockerで別ネットワークにあるコンテナをセキュアにリンクする

Posted: , Modified:   Docker Qiita

本稿は Qiita 投稿記事 のバックアップです.

概要

あるホストで動作している Docker コンテナから別のネットワークにある別ホスト上で動作しているコンテナをリンクしたい.しかし,ネットワーク間の通信は信頼できないためセキュアな通信を張る必要がある.本来ならば,VPNを用意してオーバーレイネットワークを構築すべきかもしれないが,小規模な実験的プロジェクトのため,SSHトンネルとAmbassadorパターンで解決する.

AmbassadorS

AmbassadorS は Ambassador パターンを SSH トンネル上で実行するための Docker イメージである.

現在,Alpine Linux ベースのイメージと Raspberry Pi 用に Raspbian ベースのイメージの二種類を公開している.以下では,Alpine Linux ベースの jkawamoto/ambassadors を使う場合を考える.(Raspberry Pi から利用する場合は jkawamoto/rpi-ambassadors を使う.)

AmbassadorS には,サーバ,クライアントそしてトンネルの三つのモードがある.それぞれ次のように接続して使用する.

つまり,リンク先のコンテナが走っているホスト上にサーバモードの AmbassadorS コンテナを走らせ,リンク元のコンテナが走っているホスト上でクライアントモードとトンネルモードの AmbassadorS コンテナを走らせる.

リンク先のコンテナが複数ある場合でも,各ホストにサーバモードのコンテナは一つあれば良い.トンネルモードのコンテナは,接続先のホスト一台に付き一つ必要になる.クライアントモードのコンテナは,元々の Ambassadorパターンと同じくリンク先のコンテナ数分必要になる.

AmbassadorS の実行は,基本的に

$ docker run -dt jkawamoto/ambassadors (server|client|tunnel) [-v]

Options:
  -v           Verbose mode for debugging.

の形で,第一引数としてモードを指定 (server, client, tunnel のいずれか) する.第二引数に -v を渡すとデバッグ用に詳細なログを出力するようになる.

なお,各モードとも上記に Docker へのオプション指定,マウントやリンクの設定が必要になる.具体的な設定方法は次の接続例で説明する.

接続例

ホスト A で MySQL と MongoDB のコンテナが走っていて,ホスト B からこれらのコンテナを利用する場合を考える.なお,MySQL と MongoDB のコンテナはそれぞれ mysql, mongo という名前とする.

サーバモードコンテナの用意

まず始めに,ホスト A で MySQL と MongoDB のコンテナにリンクするサーバモードの AmbassadorS コンテナを用意する.

$ docker run -d --name ambassadors_server \
             -v ~/.ssh/authorized_keys:/data/authorized_keys -p 10022:22 \
             --link mysql:mysql --link mongo:mongo \
             jkawamoto/ambassadors server

サーバモードの AmbassadorS コンテナ はホスト B からの SSH 接続を認証するためにホスト B の公開鍵が必要で,この公開鍵は /data/authorized_keys に置く必要がある.上の例では,ホスト A の ~/.ssh/authorized_keys にホスト B の公開鍵が含まれているものとして,-v ~/.ssh/authorized_keys:/data/authorized_keys によってマウントしている.

サーバモードの AmbassadorS コンテナは,ホスト B からの SSH 接続用に 22番ポートを公開している.-p 10022:22 では,22番の代わりに 10022番を AmbassadorS 用に指定している.

--link mongo:mongo --link mysql:mysql では,AmbassadorS コンテナを MySQL と MongoDB のコンテナにリンクさせている.

トンネルモードコンテナの用意

ホスト B では,まずホスト A へトンネルを張るトンネルモードの AmbassadorS コンテナを用意する.

$ docker run -dt --name ambassadors_tunnel \
            -v ~/.ssh/id_rsa:/root/.ssh/id_rsa -e PORT=10022 -e HOST=<ホスト A のアドレス> \
            jkawamoto/ambassadors tunnel

トンネルモードでは,サーバモードのコンテナに接続するための情報が必要になる.環境変数 HOST 及び PORT は,それぞれサーバモード AmbassadorS コンテナが走っているホスト A のアドレスと公開しているポート番号を指定する.そして,ホスト A で設定した公開鍵に対応する秘密鍵を /root/.ssh/id_rsa に置く.ここでは,ホスト A の ~/.ssh/id_rsa が秘密鍵だとして -v ~/.ssh/id_rsa:/root/.ssh/id_rsa によってマウントしている.

クライアントモードコンテナの用意

最後に,ホスト B でクライアントモードのコンテナを用意する.これは,リンク先のコンテナ数分,この例では2個用意する.

$ docker run -d --name mysql_ambassadors \
             --link ambassadors_tunnel:tunnel --expose 3306 -e PORT=3306 \
             jkawamoto/ambassadors client
$ docker run -d --name mongo_ambassadors \
             --link ambassadors_tunnel:tunnel --expose 27017 -e PORT=27017 \
             jkawamoto/ambassadors client

クライアントモードのコンテナは,トンネルモードのコンテナを tunnel という名前でリンクしなければならない.また,リンク先のコンテナが公開しているポート番号を,--expose と環境変数 PORT の両方に指定しなければならない.MySQL コンテナの場合,3306番を公開しているため --expose 3306 -e PORT=3306 を指定している.

サービスコンテナの利用

最後に,MySQL と MongoDB を利用するコンテナをクライアントモードのコンテナに接続する.

$ docker run -d --link mysql_ambassadors:mysql --link mongo_ambassadors:mongo some-app

docker-compose

上記の例を docker-compose で行う場合,各ホストの設定ファイルは次の通り.

ホスト A 用

mysql:
  image: mysql
mongo:
  image: mongo
ambassadors:
  image: jkawamoto/ambassadors
  command: server
  volumes:
    - ~/.ssh/authorized_keys:/data/authorized_keys
  ports:
    - "10022:22"
  links:
    - mysql
    - mongo

ホスト B 用

tunnel:
  image: jkawamoto/ambassadors
  command: tunnel
  volumes:
    - ~/.ssh/id_rsa:/root/.ssh/id_rsa
  environment:
    HOST: xxx.xxx.xxx.xxx
    PORT: 10022
mysql_ambassadors:
  image: jkawamoto/ambassadors
  command: client
  expose: 3306
  environment:
    PORT: 3306
  links:
    - tunnel
mongo_ambassadors:
  image: jkawamoto/ambassadors
  command: client
  expose: 27017
  environment:
    PORT: 27017
  links:
    - tunnel
app:
  image: some-app-image
  links:
    - mysql_ambassadors:mysql
    - mongo_ambassadors:mongo

コンテナ間のリンクが多数必要になるので,上記のように docker-compose の利用を検討したい.

まとめ

SSH トンネリングと Ambassador パターンを提供する AmbassadorS コンテナを用いると,別のネットワークにある docker コンテナをセキュアにリンクすることができる.SSH トンエリングが切断した場合,トンネルモードのコンテナが終了するので,長期的に運用する場合には再接続する設定が必要である.