Dockerの注意点
i2b2サーバーの実装にあたって特に注意が必要なDockerの設定があります。このページでは、それらについて説明します。
* もしこれらについて十分にご存知であれば、このページは読み飛ばしてください。
問題 1. ルートパーティションの枯渇リスク
Dockerはデフォルトではimageおよびvolumeなどのデータをルートパーティションに保存します。i2b2に特有のことではなく、一般的にこれらのデータは通常かなり大きな容量になるため、対策をしないとルートパーティションが枯渇してシステムの重大な障害を引き起こす可能性があります。
i2b2サーバーでは、Postgresのデータベースが大きくなる可能性があるため、特に注意が必要です。
対策はいくつも方法が考えられますが、以下のいずれかの対策が簡単でかつ安全です。
対策法: Dockerのdata-rootを変更する
Dockerは data-root と呼ばれるパスにimageやvolumeなどのデータを保存します。デフォルトでは /var/lib/docker ですが、これを別のパーティションに変更することで、ルートパーティションの枯渇を防ぐことができます。
通常は以下のステップで変更できます。しかし、システムの構成やバージョンによっては異なる場合がありますので、詳細はDockerの公式ドキュメントなどを参照してください。
step 1. Docker data-root用のパーティションを用意する
例えば、LVMやパーティションを新たに作成して、 /mnt/ext01/docker_data のようなパスにマウントします。十分な空き容量があることを確認してください。
これ以降、 /mnt/ext01/docker_data を data-root の例として説明します。実際行う場合は、ご自身のパスに置き換えてください。
Note
新しい data-root がシンボリックリンクの場合、エラーが発生することがあります。シンボリックリンクが必要な場合は、他の情報源を参照してください。
step 2. Dockerを停止して既存のデータをマウントしたパーティションに移動する
以下のコマンドを実行します。
sudo systemctl stop docker sudo cp -ar /var/lib/docker /mnt/ext01/docker_data
これで、Dockerサービスを停止し、既存のデータを新しいパーティションにコピーします。
既存データがもう不要であれば、コピーの代わりに移動しても構いません。
step 3. /etc/docker/daemon.jsonを編集してdata-rootを変更する
/etc/docker/daemon.jsonが存在しない場合は、新たに作成します。もしすでに存在する場合は、追記します。以下の内容を追加してください。
{ "data-root": "/mnt/ext01/docker_data" }
すでに他の設定がある場合は、適宜カンマなどを追加して正しいJSON形式にしてください。
step 4. Dockerを再起動する
sudo systemctl daemon-reload sudo systemctl start docker
stop 5. 動作確認
docker info | grep "Docker Root Dir"
これで、Dockerの data-root が新しいパーティションに変更されていることを確認してください。
そのほか、Dockerが正常に動作していることも確認してください。
もし元の /var/lib/docker がもう不要であれば、削除しても構いません。
問題 2. Dockerコンテナがuwfを回避してしまう
Warning
以下の内容はあくまで一例であり、また網羅的なものではありません。
実際の運用にあたっては、システム管理者やセキュリティ専門家と相談の上、適切な対策を講じてください。
これはセキュリティ上の大きな問題です。i2b2に特有のことではなく、一般的にDockerコンテナがホストのFirewallを回避してしまう問題です。i2b2では患者データを取り扱うため、特に注意が必要です。
多くのLinuxディストリビューションでは、セキュリティ強化のために ufw (Uncomplicated Firewall) を使用することが一般的です。
しかし、Dockerはデフォルトで独自のiptablesルールを作成し、これは多くの場合 ufw のルールを回避してしまいます。
例えば、アプリケーションをポート番号8000で公開するためにufwでポート8000へルールを追加しても、Dockerコンテナが直接ホストのポート8000にバインドされている場合、ufwのルールは必ずしも適応されません。
これにより、意図しないアクセスが可能になるなど、セキュリティ上のリスクが生じます。特にPostgreSQLのデフォルトポート5432は攻撃の対象になった場合、患者データベースが漏出するリスクがあります。
以下に、一般的な対策を3つ紹介します。プロダクトにおいては、これらの対策を組み合わせて使用することが推奨されます。
対策 1. Postgresなど、外部に公開する必要のないサービスはホストのlocalhostにバインドする
PostgreSQLは外部に公開する必要はありませんし、公開することは重大なリスクを伴います。
PostgreSQLのコンテナをホストのlocalhostにバインドすることで、外部からのアクセスを防ぐことができます。
具体的には、 docker-compose.yml ファイルのPostgreSQLサービスの設定を以下のように設定します。
下のような例では、意図せずPostgreSQLのポート5432が外部に公開されるリスクがあります。
よくない例 (セキュリティリスクがある)❌services: i2b2-data-pgsql: ports: - "5432:5432" # 5432が外部に公開されている
これを以下のように変更します。
変更後の例 (安全) 🙆♂️services: i2b2-data-pgsql: ports: - "127.0.0.1:5432:5432" # 5432をホストのlocalhostにバインド
これにより、PostgreSQLはホストのlocalhostからのみアクセス可能になり、外部からのアクセスは防止されます。docker-compose.ymlを確認し、適切に設定されていることを確認してください。
もしPostgreSQLデータベースに外部からどうしてもアクセスする必要がある場合は、
ssh -L 5432:127.0.0.1:5432 user@remote-server-ip
のようにSSHトンネルを使用してポートフォワーディングを行い、セキュアにアクセスするなどの方法をとってください。
対策 2. i2b2サーバーの前にロードバランサーやプロキシサーバーを配置する
Note
GitHubリポジトリ i2b2-jp-docker では、Nginxをリバースプロキシとして設置しているはずです。
しかしバージョンなどによっては異なる場合がありますので、プロキシサーバーの設定をご確認ください。
これは現在のウェブサービスでは一般的な構成です。
i2b2サーバーの前にロードバランサーやプロキシサーバーを配置し、これらに対してFirewallなどのルールを適用することを推奨します。
こうすることで、ネットワークに直接公開されるのはロードバランサーやプロキシサーバーのみとなり、ユーザーからのリクエストはこれらを経由してi2b2サーバーに到達します。したがって、i2b2サーバー自体は直接ネットワークに公開されないので、セキュリティが向上します。
例えば、Nginxをリバースプロキシとして使用し、Nginxに対してFirewallのルールを適用することが考えられます
対策 3. 外部公開のポートは、iptablesでDockerコンテナのトラフィックを制御する
Note
対策 2.で説明するように、実際の運営ではアプリケーションポートは直接公開せず、プロキシサーバーやロードバランサーを使ってアクセス制御を行うことが一般的です。
そのため、実際に公開するポートはプロキシサーバーなどのリッスンするポート(通常は80や443)に限定されることが多いです。
80や443といったポートはそもそも一般公開されることを前提としているため、iptablesでのアクセス制御はしないことも多いです。
このため、以下の対策 3. は、特に必要がある場合にのみ適用してください。
iptablesのDockerが提供する特別なチェイン DOCKER-USER にルールを追加し、不要なアクセスを遮断することが可能です。
DOCKER-USER チェインのルールは、Dockerが追加するNATルールよりも先に評価されるため、Dockerコンテナの公開ポートに対して確実にアクセス制御を行うことができます。
例えば、ポート80でサーバーを公開する場合に、日本国外からのアクセスを遮断したいケースを考えます。
以下のようにiptablesでルールを追加することで、特定のポートに対して許可・拒否を制御できます。
例: 80ポートに対するアクセス制御# まずすべてのアクセスを拒否する sudo iptables -A DOCKER-USER -p tcp --dport 80 -j DROP # 信頼できるIPのみを許可する (例: 自分のオフィスIP) sudo iptables -I DOCKER-USER -s 203.0.113.25 -p tcp --dport 80 -j ACCEPT # 必要なら日本のIPレンジだけを許可する (jp.zoneにIPレンジリストを保存している場合) for ip in $(cat jp.zone); do sudo iptables -I DOCKER-USER -p tcp --dport 80 -s $ip -j ACCEPT done
また、作成したiptablesルールは iptables-save コマンドなどを使って保存し、再起動後も有効になるように設定してください。