メインコンテンツまでスキップ

第24章:private repoや社内パッケージ:SSH/トークンを安全に使う🧷🔑

「社内ライブラリ(private repo)を npm ci で取ってきたい」「GitHub Packages / private npm レジストリから依存を入れたい」——この瞬間が、秘密が漏れやすい最大の山場です😵‍💫💥 ここを安全に越えるコツは一言でいうと👇

✅ “ビルドに渡す。でもイメージに残さない” 🔥

そのための主役が BuildKit の SSH マウント / Secret マウントです。(Docker Documentation)


この章でできるようになること🎯✨

  • private repo を SSH鍵をイメージへコピーせずにビルドできる🔐
  • private npm レジストリのトークンを レイヤ・ログ・履歴に残さずに依存導入できる🫥
  • 「どの認証を使うべき?」が選べるようになる(Deploy key / fine-grained PAT / CI用トークン)🧠
  • AI拡張を使っていても、秘密を巻き込まない手順が作れる🤖🛡️

1) まず知っておく“事故り方”あるある😇💣

事故パターンA:Dockerfile にトークンを ARG/ENV で入れる

  • ARG NPM_TOKEN=... とか ENV とかで渡すと、ビルド履歴・キャッシュ・ログに残る可能性が出ます⚠️
  • 「消したつもり」でも docker history などでバレることがあります🫣

事故パターンB:.npmrc~/.sshCOPY してしまう

  • これはもう漏れてくださいって言ってるのに近いです😱
  • さらに .dockerignore が甘いと、ビルドコンテキストに混入して事故率UP📦💥

事故パターンC:URLにトークン埋め込み(例:https://TOKEN@...

  • コマンド履歴・ログ・ツールの表示・AIへの貼り付けで漏れやすいです🧨

2) 正攻法マップ🗺️✨「これを使えば“残らない”】【本命】

本命①:SSHエージェント転送(BuildKitの RUN --mount=type=ssh)🧑‍🚀🔐

  • 秘密鍵ファイルをコンテナに入れない
  • “今このビルド中だけ” SSH を使えるようにする方式です。(Docker Documentation)

本命②:BuildKit secret(RUN --mount=type=secret)🤫🧪

  • npmトークンや .npmrcそのRUN中だけ見せる方式
  • レイヤに残りにくい設計になっています。(Docker Documentation)

Compose でもできる(build.ssh)🧩


3) 認証の選び方(どれが一番安全?)🥇🔍

ここ、迷いがちなので “ざっくり優先順位” を置きます👇

✅ private repo を「読むだけ」なら:Deploy key(読み取り専用)🗝️📖

  • リポジトリ単位で鍵を付けられて、用途が限定できるのが強い💪
  • 「書き込み許可」は基本オフで運用するのが安心。(GitHub Docs)

✅ “人のトークン”を使うなら:fine-grained PAT(期限短め)⏳🔐

  • GitHub は classic PATより fine-grained を推奨していて、リポジトリや権限を絞れます。(GitHub Docs)

✅ CI(GitHub Actions)なら:GITHUB_TOKEN を最小権限で🎛️🛡️

  • 最小権限(例:contents: read)が基本、必要なときだけ増やすのが推奨です。(GitHub Docs)

✅ npm系(private packages)なら:CIは env + .npmrc が公式推奨🧾🔑

  • npm公式は、CIでは トークンを環境変数で持たせて .npmrc で使う流れを説明しています。(npm ドキュメント)
  • ただし Dockerビルド中は BuildKit secret の方が安全設計に寄せやすいです(後でやります)😎(Docker Documentation)

4) ハンズオン①:private repo を SSHで取ってきてビルドする🧪🐙

ゴール:npm cigit+ssh 依存を含んでいても、鍵をイメージに入れずにビルドできる✨

(1) .dockerignore で「そもそも混入させない」🧹

まずは物理ガードです(超大事)🛡️

## secrets
.env
.npmrc
**/.npmrc
.ssh
**/.ssh

## node
node_modules
dist

(2) Dockerfile(SSH が必要な RUN だけに --mount=type=ssh)🔐

例は Node の Active LTS 系(2026年2月時点だと v24 が Active LTS)を使う想定で書きます。(Node.js)

## syntax=docker/dockerfile:1
FROM node:24-alpine AS deps
WORKDIR /app

## git + ssh クライアントだけ入れる
RUN apk add --no-cache git openssh-client

## 先に依存定義だけコピー(キャッシュ効かせる)
COPY package.json package-lock.json ./

## known_hosts を固定(対話プロンプト回避)
## ※安全のため本当は known_hosts を厳密に管理したいが、学習段階ではまず固定の考え方を掴む
RUN mkdir -p -m 700 /root/.ssh && \
ssh-keyscan github.com >> /root/.ssh/known_hosts

## ここが肝:秘密鍵ファイルは入れず、SSHエージェントだけ借りる
RUN --mount=type=ssh npm ci

FROM node:24-alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

CMD ["node", "src/index.js"]
  • # syntax=docker/dockerfile:1 は「最新の安定構文を使う」推奨の書き方です。(Docker Documentation)
  • RUN --mount=type=ssh は Dockerfile の公式リファレンスにも載っています。(Docker Documentation)

(3) ビルドコマンド(SSHエージェントを渡す)🚀

docker build --ssh default -t myapp:dev .

ポイント:この方式は「エージェントに入ってる鍵」を使うので、先に鍵をエージェントへ登録しておく必要があります🔑➡️🧠 (ここが詰まりポイントになりがち!後でまとめます)


5) ハンズオン②:private npm レジストリのトークンを“残さず”使う🧪🕵️‍♂️

ゴール:.npmrc をイメージに残さず、トークンも残さずに npm ci する✨

(A) BuildKit secret で .npmrc を “そのRUNだけ” 読ませる(おすすめ)🥇

BuildKit は secret を渡して、Dockerfile 側で mount して使う2段構えです。(Docker Documentation)

  1. 手元に “ビルド専用 .npmrc” を用意(トークン直書きでもOKだが、扱い注意!) 例(スコープやレジストリは自分の環境に合わせてね)👇
@my-scope:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=REPLACE_ME
always-auth=true
  1. ビルド時に secret として渡す👇
docker build --secret id=npmrc,src=.npmrc.build -t myapp:dev .
  1. Dockerfile 側は、npm ci の行だけ secret mount する👇
## syntax=docker/dockerfile:1
FROM node:24-alpine AS deps
WORKDIR /app

COPY package.json package-lock.json ./

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
  • これで .npmrc はその RUN でだけ見える形に寄せられます🤫✨(Docker Documentation)

(B) CI なら env + .npmrc(npm公式の基本パターン)🏭

npm公式は CI/CD で「トークンを環境変数に置き、プロジェクト .npmrc を使う」流れを説明しています。(npm ドキュメント) また設定は .npmrc にトークンを置くのが一般的で、npm login で作るのが安全寄り、という注意もあります。(npm ドキュメント)

ただし Dockerの “ビルド” 中に env を渡すのはログやレイヤの観点で事故りやすいので、上の (A) を第一候補にするのが無難です😌🛡️


6) Compose でやる場合(build.ssh)🧩🐳

Compose の build には ssh という指定ができます(例:default エージェントをマウント)🧷(docs.docker.jp)

services:
app:
build:
context: .
ssh:
- default

CLI でやるなら👇(こちらも --ssh が用意されています)(matsuand.github.io)

docker compose build --ssh default

7) AI拡張がいる世界の“秘密の守り方”🤖🧱⚠️

AIが便利なほど、うっかりが増えます😇 ここだけはルール化しちゃうのがおすすめ👇

  • 秘密っぽい文字列をAIに貼らない(ログもそのまま貼らない)🙅‍♂️
  • エラー貼るときは トークン/URL/Authorization/.npmrc 部分を黒塗り🖍️
  • 「AIが提案した COPY ~/.ssh」みたいな案は、即却下できる判断基準を持つ(=この章の内容)🛡️✨

8) よくある詰まりポイント集(ここで沼る)🪤🧯

Permission denied (publickey) が出る😵

だいたいこれ👇

  • ssh-agent に鍵が入ってない(ssh-add してない)
  • そもそも依存取得が SSH じゃなく HTTPS になってる(git+https など)
  • known_hosts 周りで止まっている(対話が発生して build が落ちる)

❌ Compose の ssh: が効かない / “unsupported option” っぽい🤔

  • 古い docker-compose(Python版)系だと対応してないことがあります
  • 今は docker compose(Compose v2)側の機能として扱うのが安全寄りです(この章の例はそっち想定)📌

9) 仕上げ:5分セルフ監査✅🔍(漏れてない?)

  • docker history --no-trunc myapp:dev を見て、トークンや鍵の痕跡が無い

  • リポジトリ内を検索して、トークンっぽい文字列が混入してない✅

  • .dockerignore.npmrc / .ssh / .env が入ってる✅

  • 使ってるトークンは

    • 期限あり
    • 権限しぼり(fine-grained PAT / read-only deploy key)
    • 用途ラベルあり(どの用途か分かる)✅ (GitHub Docs)

まとめ🎉

この章の結論はシンプルです👇😄

  • private repo → SSH agent forwarding(--mount=type=ssh)が第一候補🔐(Docker Documentation)
  • private packages → **BuildKit secret で .npmrc を “その瞬間だけ”**🤫(Docker Documentation)
  • トークンは 最小権限・期限短め・用途固定(fine-grained / deploy key / CIトークン)🛡️(GitHub Docs)

次の第25章(ローテーション&失効)で、ここで作った仕組みに「漏れた前提の復旧力」を足して完成度を上げられます🚑🔁✨