【実務・中級編】 DockerデーモンのUnixソケット権限分離とTCPソケットのTLS認証強制 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

現場で戦うエンジニア諸君。システムを「動かす」ことと「守る」ことの境界線、意識できているか?

多くのプロジェクトが、納期に追われてDockerデーモンの設定を「とりあえず便利だから」という理由でデフォルトのまま放置している。だが、それは自分のサーバーの玄関に「誰でも自由に入っていいですよ」と看板を掲げているのと同義だ。今日は、Dockerという強力な武器を、攻撃者に乗っ取られないための最優先事項――「Unixソケットの権限分離」と「TLSによるmTLS通信」について解説する。

なぜDockerデーモンが「最優先の標的」なのか

Dockerデーモン(dockerd)は、ルート権限で動作する特権プロセスだ。ここに不正にアクセスできれば、攻撃者は即座にコンテナを立ち上げ、ホストOSのファイルシステムをマウントし、カーネルレベルで好き放題できる。

かつて、クラウド上のDocker APIをデフォルト設定(認証なしのTCPポート2375)で公開していた企業が、数分でマイニングスクリプトを仕込まれ、さらに数時間後にはDBのデータを全抽出されたというインシデントがあった。攻撃者はランダムにIPをスキャンし、この「開かれた扉」を探し回っているんだ。

—

1. Unixソケットの権限分離:足元の泥棒を排除する

デフォルトの docker.sock は、rootユーザー以外にアクセス権を与えると、そのユーザーは実質的にroot権限を得るのと変わらない。これを防ぐには、グループ管理を徹底し、最小権限の原則を適用する。

設定手順

まず、docker グループに不要なユーザーを入れていないか確認しろ。もしCI/CDツールやWebサーバーがDockerを叩く必要があるなら、そのユーザーにのみ権限を与え、パーミッションを厳格化する。

# dockerグループのメンバーを確認
grep docker /etc/group

# 不要なユーザーがいたら即座に削除
sudo gpasswd -d [不要なユーザー名] docker

# ソケットのパーミッションを660(所有者とグループのみ読み書き可)に設定
sudo chmod 660 /var/run/docker.sock

これだけで、権限のない他プロセスからの「横からの覗き見」は防げる。だが、リモートからAPIを叩く必要がある場合は、これでは足りない。次に進もう。

—

2. リモートAPIの要塞化:TLS相互認証(mTLS)の実装

リモートからDockerを操作する際、絶対に tcp://0.0.0.0:2375 を認証なしで開けてはいけない。Dockerは標準で「TLS通信」と「クライアント証明書による認証(mTLS)」をサポートしている。これを実装すれば、証明書を持たない攻撃者は、ポートに辿り着いたとしても通信を確立すらできない。

手順:CAと証明書の作成

まずは自己署名のCAを作成し、サーバー用とクライアント用の証明書を生成する。

# 1. CA鍵と証明書の作成(秘密鍵を守れ!)
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem

# 2. サーバー用証明書の発行(IPアドレスは各自の環境に合わせて書き換えること)
openssl genrsa -out server-key.pem 4096
openssl req -new -key server-key.pem -out server.csr
# ※ ここでCommon Name (CN) にサーバーのIPまたはドメインを指定すること
openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem

Dockerデーモンの設定変更

/etc/docker/daemon.json を開き、TLSを強制する設定を記述する。

{
  "tlsverify": true,
  "tlscacert": "/etc/docker/ca.pem",
  "tlscert": "/etc/docker/server-cert.pem",
  "tlskey": "/etc/docker/server-key.pem",
  "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"]
}

設定を反映したら systemctl restart docker を忘れずに行え。これで、証明書を持たない者は門前払いだ。

—

3. 実践:PythonによるセキュアなAPIアクセス

最後に、この環境に対してPythonの docker ライブラリを使って安全に接続する例を示す。このコードは、クライアント証明書が正しく配置されていない限り、サーバーとのハンドシェイクに失敗する。

import docker

# クライアント証明書と鍵のパスを指定
tls_config = docker.tls.TLSConfig(
    client_cert=('/path/to/client-cert.pem', '/path/to/client-key.pem'),
    ca_cert='/path/to/ca.pem',
    verify=True # ここでmTLSの検証を強制する
)

# Dockerデーモンへ接続(TLS必須)
client = docker.DockerClient(
    base_url='tcp://[サーバーのIP]:2376',
    tls=tls_config
)

# 安全な接続経由でコンテナリストを取得
try:
    for container in client.containers.list():
        print(f"Container ID: {container.short_id}")
except Exception as e:
    print(f"接続失敗: 認証エラーまたはネットワークエラー - {e}")

最後に:セキュリティは「設定」ではなく「文化」だ

この実装は、一度設定してしまえば終わりではない。証明書の期限管理(有効期限が切れるとシステム全体が止まる)や、秘密鍵の漏洩リスクなど、運用上の新たな責任が発生する。

しかし、攻撃者にバックドアを放置するリスクに比べれば、この管理コストは安いものだ。「便利さ」を追い求めてセキュリティを切り捨てるのは、エンジニアの仕事じゃない。正しい知識を持ち、泥臭く設定を積み重ねる。その先にある堅牢なシステムこそが、君たちのエンジニアとしての誇りになるはずだ。

明日、出社したらまずは自分のサーバーの docker.sock の権限を確認してくれ。そこからが、要塞化の第一歩だ。

コメント

タイトルとURLをコピーしました