セキュリティ

Docker環境でのNetskope証明書設定とファイル共有のセキュリティリスク

3行でまとめると

  • Netskope環境下のDockerビルド/実行で発生するSSLエラーを、イメージに証明書を残さず解決する方法を解説します。
  • Docker DesktopのFile SharingでNetskopeのデータフォルダを直接マウントするのは、秘密鍵漏洩のリスクがあるためNGです。
  • Build SecretsBind Mount を組み合わせることで、セキュリティと利便性を両立した「ポータブルなDockerfile」が作れます。

はじめに

ごきげんよう、今回は、Netskope環境下でのDocker開発において避けては通れない「SSL証明書エラー」の解決方法について紹介します。
特に、証明書をDockerイメージに埋め込まずにセキュアに扱う方法と、Docker DesktopのFile Sharing設定に潜むセキュリティリスクについて解説します。

この記事の対象読者

  • Netskopeが導入されている企業・組織で働く開発者
  • Dockerビルド (docker build, docker compose build) でSSLエラーに悩まされている方
  • 社内プロキシ環境下でもセキュアでポータブルなDockerfileを書きたい方

検証環境

本記事の内容は以下の環境で検証しています。

  • OS: macOS Tahoe 26.1
  • Docker Desktop: 4.49.0
  • Netskope Client: 132.0.7.2513

どんな時に発生する問題か?

Netskopeが導入されているPCでDockerを利用する際、以下のようなエラーに遭遇することがあります。

  • docker build 中の npm installpip installself-signed certificate in certificate chain エラーで失敗する
  • コンテナ内のアプリから外部API(AWS, Google Mapsなど)へリクエストすると接続エラーになる

これは、NetskopeがSSL通信を検査(SSLインスペクション)するために、通信の間に自身の証明書を割り込ませていることが原因です。ホストOS(macOSやWindows)はNetskopeの証明書を信頼していますが、Dockerコンテナの中にはその証明書が入っていないため、通信が「信頼できない」と判定されて遮断されてしまいます

以前、ホスト環境でのNetskope設定について紹介していましたが、今回はそのDocker編となります。

Note: 今回は Node.js(npm)を例に解説しますが、Python(REQUESTS_CA_BUNDLE)や Go(SSL_CERT_FILE)など他の言語でも、環境変数を変えれば同様のアプローチで対応可能です。

Build Secrets とは

Docker の Build Secrets は、ビルド時にのみ必要な機密情報(パスワード、APIキー、証明書など)を、Dockerイメージに残さずにコンテナに渡すための機能です。

通常の COPY コマンドで証明書を配置すると、最終的なイメージのレイヤーにファイルが含まれてしまいます。CA証明書自体は公開情報に近いものなので、コピーしても大きな問題にはなりませんが、以下の理由からBuild Secretsを使う方がより良いアプローチです:

  • 社内プロキシの証明書が含まれていることで、攻撃者に対して「この組織はNetskopeを使っている」という内部構造のヒントを与えてしまう可能性がある(パブリックなレジストリにPushする場合)
  • 万が一誤って秘密鍵などを含めてしまった場合には大きなセキュリティリスクとなる
  • 証明書が更新された場合にイメージのリビルドが必要になる

Build Secrets(--mount=type=secret)を使用すると、ファイルはビルド中の一時的なマウントとして扱われ、ビルド完了後のイメージには一切残りません。これにより、セキュアかつクリーンなDockerイメージを作成できます。

解決策の全体像

Docker環境で証明書を扱うには、大きく2つのアプローチがあります。

1. 証明書をコンテナ内にコピーしてしまう方法 (COPY)

一番簡単なのは、DockerfileCOPY コマンドを使って証明書をイメージ内に配置する方法です。

COPY .certs/netskope.pem /app/certs/netskope.pem
ENV NODE_EXTRA_CA_CERTS=/app/certs/netskope.pem
RUN npm install

この方法はシンプルで、CA証明書をコピーするだけなら大きな問題はありません。ただし、以下の点でBuild Secretsを使う方がより柔軟で推奨されます:

  1. Dockerfile自体が特定の証明書ファイルに依存してしまう: チームメンバーが異なる環境(Netskopeがない環境など)でビルドしようとした場合、証明書ファイルが存在しないためにビルドエラーになったり、ダミーファイルを用意する手間が発生します。
  2. 証明書更新時のリビルドが必要: 証明書が更新された場合、イメージのリビルドが必要になります。

2. Build Secretsを使う方法 (推奨)

Build Secretsを使うことで、証明書ファイルがある場合のみマウントして使うという条件分岐が可能になります。
これにより、以下のメリットがあります。

  1. ポータブルなDockerfile: Netskope環境の人は証明書を使ってビルドし、そうでない環境の人はそのステップをスキップする、という柔軟な対応が可能。
  2. リビルド不要: 証明書ファイルがイメージに含まれないため、証明書の更新や変更があった場合でも、Dockerイメージのリビルドなしで対応可能です(Run時はBind Mountで最新の証明書が渡されるため)。
  3. クリーンなイメージ: 不要な証明書ファイルがイメージに残りません。

今回はこの Build Secrets を使った実装方法を解説します。

実装解説 (Build Secrets方式)

対応の流れは以下の通りです。

  1. ホストから証明書をコピー: ホストOSの信頼された証明書をプロジェクト内に一時的にコピー(gitには含めない)
  2. Build時: Build Secretsを使って、イメージに証明書を残さずにビルド処理を行う
  3. Run時: Bind Mountを使って、コンテナ実行時に証明書をマウントする(コンテナ内にコピーしない

1. ホストの証明書をコピーする

まず、ホストマシン上のNetskope証明書をプロジェクト内の .certs/ ディレクトリにコピーします。
ホスト環境の環境変数(NODE_EXTRA_CA_CERTSなど)から証明書の場所を自動検出し、必要なCA証明書(netskope.pem)だけをコピーするスクリプトを作成しました。

重要なお作法として、この証明書はリポジトリにコミットしないよう .gitignore に追加します。

netskope-setup.sh:

#!/bin/bash
set -e

# 証明書パスを取得(複数の環境変数をチェック)
get_cert_path() {
    # 優先順位: NODE_EXTRA_CA_CERTS > SSL_CERT_FILE > REQUESTS_CA_BUNDLE > CURL_CA_BUNDLE
    if [ -n "$NODE_EXTRA_CA_CERTS" ] && [ -f "$NODE_EXTRA_CA_CERTS" ]; then
        echo "$NODE_EXTRA_CA_CERTS"
    elif [ -n "$SSL_CERT_FILE" ] && [ -f "$SSL_CERT_FILE" ]; then
        echo "$SSL_CERT_FILE"
    elif [ -n "$REQUESTS_CA_BUNDLE" ] && [ -f "$REQUESTS_CA_BUNDLE" ]; then
        echo "$REQUESTS_CA_BUNDLE"
    elif [ -n "$CURL_CA_BUNDLE" ] && [ -f "$CURL_CA_BUNDLE" ]; then
        echo "$CURL_CA_BUNDLE"
    else
        echo ""
    fi
}

# .certsディレクトリ作成
CERT_DIR=".certs"
mkdir -p "$CERT_DIR"
CERT_PATH=$(get_cert_path)

if [ -n "$CERT_PATH" ]; then
    # 値が存在する場合のみコピーを実行
    cp "$CERT_PATH" "$CERT_DIR/netskope.pem"
    echo "証明書をコピーしました: $CERT_DIR/netskope.pem"
else
    echo "Netskope証明書が見つかりませんでした"
    # 空ファイルを作成して、Dockerのマウントエラーを回避
    touch "$CERT_DIR/netskope.pem"
fi

# .gitignoreに追加
if ! grep -q "^\.certs" .gitignore 2>/dev/null; then
    echo ".certs/*" >> .gitignore
    echo "!.certs/.gitkeep" >> .gitignore
fi

2. Build時はBuild Secretsを使う

Dockerfile で Build Secrets 機能を使用します。
今回は npm install 時の通信エラーを回避するために使用します。

Dockerfile:

# syntax=docker/dockerfile:1

FROM node:20-alpine

WORKDIR /app

# npm install (Build Secretsで証明書を使用)
RUN --mount=type=secret,id=nscacert,required=false \
    if [ -s /run/secrets/nscacert ]; then \
        export NODE_EXTRA_CA_CERTS=/run/secrets/nscacert; \
    fi && \
    npm install

COPY . .

重要なポイント:

  • # syntax=docker/dockerfile:1: Build Secretsを使うために必須の宣言(ファイルの1行目に記載
  • --mount=type=secret,id=nscacert: 証明書を /run/secrets/nscacert にマウントします。
  • npm install時: コピーを行わず環境変数 NODE_EXTRA_CA_CERTS で直接マウントポイントを参照しているため、この証明書ファイルはイメージに残りません
  • required=false: Netskope環境以外でもビルドが落ちないようにしています。
  • [ -s ... ]: ファイルが存在し、かつ空でない場合のみ環境変数をセットします。セットアップスクリプトが空ファイルを作成した場合でもエラーになりません。

docker-compose.yml (Build設定):

services:
  app:
    build:
      context: .
      secrets:
        - nscacert
    # ...

# Build Secrets定義
secrets:
  nscacert:
    file: .certs/netskope.pem

これで、ビルド時のみ安全に証明書を利用できます。

3. Run時はBind Mountで渡す

実行時は、docker-compose.ymlvolumes で証明書をマウントします。
ここで重要なのは、マウントしているだけで、コンテナ内にコピーしていないということです。

docker-compose.yml (Run設定):

services:
  app:
    environment:
      # Node.jsが証明書を参照するための環境変数
      - NODE_EXTRA_CA_CERTS=/app/certs/netskope.pem
    volumes:
      # 証明書を読み取り専用でマウント
      - ./.certs/netskope.pem:/app/certs/netskope.pem:ro
  • :ro オプションで読み取り専用としてマウントしています
  • 証明書ファイルはホスト側の .certs/netskope.pem を直接参照しており、コンテナ内にコピーされません

これにより、コンテナ内のアプリケーションもホストと同じ証明書を利用して通信が可能になりますが、証明書がコンテナイメージやコンテナのファイルシステムに残ることはありません

4. 完全なdocker-compose.ymlの例

Build設定とRun設定を組み合わせた、完全な docker-compose.yml の例です。
これをコピペして利用できます。

services:
  app:
    build:
      context: .
      secrets:
        - nscacert
    environment:
      # Node.jsが証明書を参照するための環境変数
      - NODE_EXTRA_CA_CERTS=/app/certs/netskope.pem
    volumes:
      # 証明書を読み取り専用でマウント
      - ./.certs/netskope.pem:/app/certs/netskope.pem:ro

# Build Secrets定義
secrets:
  nscacert:
    file: .certs/netskope.pem

⚠️ 【重要】Docker File Sharing設定に潜む「秘密鍵漏洩」のリスク

ここで一つ、非常に重要な注意点があります。

「ホストの証明書が必要なら、Netskopeのデータフォルダごとマウントすればいいのでは?」

と考える方がいるかもしれません。具体的には、Docker Desktopの File sharing 設定で /Library/Application Support/Netskope/STAgent/data/ などを許可してしまうケースです。

これはセキュリティ上のリスクが高いため、推奨されません。

理由:秘密鍵が含まれている

Netskopeのデータディレクトリには、CA証明書だけでなく、以下のような非常に機密性の高いファイルも含まれています。

  • npaccesskey.pem: Netskope Private Access用の秘密鍵
  • nsusercert.p12: ユーザー個人の認証情報(秘密鍵含む)

これらをDockerのFile Sharingに追加するということは、コンテナ内から、ホスト上の秘密鍵へアクセスする経路を開けてしまうことを意味します。

正しいアプローチ

必要なのは「CA証明書」だけです。
今回紹介したスクリプトのように、必要なCA証明書ファイルだけをプロジェクト内の安全な場所にコピーし、それをマウントするようにしましょう。
面倒でも「最小権限の原則」を守ることが、Docker環境のセキュリティを守る鍵となります。

まとめ

  • Docker環境でのSSL問題は、Build SecretsBind Mount で解決しましょう。
  • COPY コマンドで証明書をイメージに埋め込む方法も使えますが、Build Secretsを使う方がより柔軟でポータブルなDockerfileが作れます。
  • DockerのFile Sharing設定で、機密情報を含むシステムディレクトリを安易に共有しないようにしましょう。

セキュアな開発環境を構築して、より安全で効率的な開発を進めましょう。

keiji

開発チーム所属。おっさんエンジニア。人間というレガシーシステムを運用中。次のアップデート(AI)に対応できません。