第03章:3原則:最小権限・最小共有・最小公開✂️🔐📤
この章は「迷ったらこれに戻る」ための“安全のコンパス”を作る回です🧭✨ Dockerを使うとき、設定の選択肢が多すぎて「え、結局どれが安全なの…?」となりがちですが、3つの質問に落とせばほぼ迷いません😄
この章のゴール🎯
次の3つを、自分の言葉で説明できて、さらに Compose/Dockerfileの設定に落とせるようになることです💪😊
- 最小権限:そのコンテナ(プロセス)に「できること」を最小にする👤✂️
- 最小共有:ホストや他サービスと「触れ合う範囲」を最小にする📁🧷
- 最小公開:ネットワーク的に「見える範囲」を最小にする🌐🚪
まずは“3つの質問”に変換しよう🧠🔁
設定を見たら、全部この3つに翻訳します👇
Q1. それ、何ができる権限を与えてる?(最小権限)🔐
- rootで動かしてない?😱
privilegedになってない?🛑- 余計なcapabilityを持ってない?🧤
「最小権限」は、必要なことだけできるように権限を絞る考え方です。NISTの用語集でも“必要最小限の権限”として定義されています。(NIST Computer Security Resource Center)
Q2. それ、何を共有してる?(最小共有)📦
- ホストのフォルダを丸ごと渡してない?🧨
- 書き込み可能(rw)になってない?✍️
- “最強危険カード”の
docker.sockを渡してない?🐙🔥
Q3. それ、誰に見せてる?(最小公開)📡
ports:で外(ホスト/ネットワーク)に公開しすぎてない?🚨- DBまで公開してない?(ありがち)🫣
- 公開するなら “localhost限定” できてる?🏠
ポート公開はデフォルトだと危険寄り(外部から到達可能になり得る)で、
127.0.0.1を付けると「ホストだけ」に限定できます。(Docker Documentation)
3原則の“超ざっくり暗記”📌😄
困ったらこれだけ思い出せばOKです👇
- 最小権限:まず「普通ユーザー」で動かす🙂👟(rootにしない)
- 最小共有:共有するなら「狭く」「できれば読み取り専用」📁🔒
- 最小公開:公開するのは「入口だけ」「できればlocalhost限定」🚪🏠
そして最大のコツはこれ👇
「削って困ったら足す」✂️➡️➕
最初から盛らない!安全側から始める!✨
原則①:最小権限(できることを削る)👤✂️🔐
1) まず“rootで動かさない”🙂🚫
コンテナは放っておくとroot(UID 0)で動きがちです。 公式のセキュリティ説明でも「非特権ユーザーで動かすと安全性が上がる」趣旨が書かれています。(Docker Documentation)
典型パターン✅
- Dockerfileで
USER node(または専用ユーザー)にする - もしくはComposeで
user:を指定する(できる範囲でOK)
例(Dockerfile)
## 例:Nodeイメージは node ユーザーが用意されていることが多い
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
USER node
CMD ["node", "server.js"]
例(compose.yaml)
services:
api:
image: my-api
user: "node" # もしくは "1000:1000" のようにUID:GIDでも
Composeの
userはサービスの属性として定義されています。(Docker Documentation)
2) “できること”を追加で削る:capabilities / no-new-privileges🧤🧯
「rootじゃないなら安心でしょ?」…まだ半分です😇 コンテナにはデフォルトで色々な“できること(capability)”が付くので、削ると攻撃面が減ります🎯 Docker公式も「デフォルトのcapabilitiesやmountの組み合わせはリスクになり得る」と説明しています。(Docker Documentation)
例(まずは雰囲気だけ掴む)
services:
api:
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
cap_add/(同ページ内に)capability関連の属性があり、security_optもサービス属性として定義されています。(Docker Documentation)
※ここは環境やアプリによって「削りすぎて動かない」が起きるので、章のゴールは “判断できるようになる” ことです😊 (困ったら:削ったやつを“必要最小限で戻す”)
原則②:最小共有(触れられる範囲を削る)📦✂️🧷
1) 共有は“狭く・浅く”が正義📁🧠
ありがちな事故:
- プロジェクト直下を丸ごとマウント →
.envや鍵ファイルまでコンテナが読める😱 - ホストの重要フォルダをうっかりマウント → 誤操作で破壊💥
まずはこの一言で判断できます👇 **「そのコンテナが触っていいデータだけ渡す」**📌
2) 共有するなら読み取り専用(ro)で“壊せない”にする🔒🧊
コード共有などは 原則ro が強いです💪
Composeの例でも、bind mountに read_only: true を付ける例が載っています。(Docker Documentation)
例:コードはro、書き込みは専用フォルダだけrw
services:
api:
volumes:
- type: bind
source: .
target: /app
read_only: true
- type: volume
source: api-tmp
target: /app/tmp
volumes:
api-tmp:
3) “これ渡したら強すぎ”の代表:docker.sock🐙🔥
/var/run/docker.sock をコンテナに渡すと、Dockerを操作できてしまいがちで危険度が跳ね上がります📈
Dockerの強化機能(ECI)でも、docker.sockのbind mountは「Docker Engineを制御できてしまう」のでデフォルトでブロックすると明記されています。(Docker Documentation)
覚え方:「docker.sock=だいたい最強権限」🧟♂️💥 必要になったら、この教材の後半で“隔離専用”の考え方で扱います🙂
原則③:最小公開(見える範囲を削る)🌐✂️🚪
1) ports: は“公開”、expose: は“内部向け”📌
Composeでは次のイメージでOKです👇
ports:→ ホスト側に公開する(外から来れる入口になる)🚪expose:→ 同じネットワーク内のサービス向け(ホストには公開しない)🏠
Composeのサービス定義でも expose は「ホストにpublishすべきではない」「内部ポートのみ」と説明されています。(Docker Documentation)
2) 公開するなら“入口だけ”&“localhost限定”🏠🔒
Web/APIだけ公開して、DBは公開しない(内部だけ)🍱✨
そして公開するなら、できれば 127.0.0.1 で縛るのが強いです💪
Docker公式のポート公開ページでも「公開はデフォルトで危険寄り」「localhost IPを付けるとホストだけ」と説明されています。(Docker Documentation)
例:APIはlocalhost限定、DBは公開しない
services:
api:
ports:
- "127.0.0.1:3000:3000"
db:
# ports: は書かない(=ホストに公開しない)
expose:
- "5432"
まとめ:3原則を“1分レビュー”にする✅⏱️🤖
AI拡張(コード提案)を使うと爆速で進む反面、危険な設定も平気で混ざることがあります😇💥 そこで、提案をコピペする前にこの“1分レビュー”を挟みます👇
✅ 1分レビュー(コピペ前に見るやつ)
-
最小権限👤
user:ある? rootで動いてない?privileged: trueになってない?(なってたら赤信号🚨)- capを雑に増やしてない?(
cap_add: ALLとか🧨)
-
最小共有📦
- 共有フォルダ広すぎない?(
.:/appとかは要注意) - roにできるところはro?
docker.sock渡してない?(渡してたら理由を100回確認🐙🔥)
- 共有フォルダ広すぎない?(
-
最小公開🌐
ports:が増えてない?- DB/Redisまで公開してない?
- 公開するなら
127.0.0.1で縛れる?🏠
ミニ演習🧪✨(“危険→安全”を体で覚える)
演習1:ポート公開の被害半径を縮める🚪➡️🏠
-
ports: - "3000:3000"で起動 -
次に
ports: - "127.0.0.1:3000:3000"に変更 -
どっちが“外に開く可能性が高い”か、理由を言えるようにする🗣️💡
- ヒント:公式に「公開はデフォルトで危険寄り」「localhostを付けるとホストだけ」って書いてあります📚(Docker Documentation)
演習2:共有を“ro + 専用rw”に分ける📁🔒🧺
- コード共有を
read_only: trueにする - ログや一時ファイルは
tmpfsか named volume に逃がす - 「書き込みが必要な場所を限定できた?」を確認👀✅
この章の“持ち帰りテンプレ”📦🎁
最後に、3原則を最初から入れた“素の型”を置いておきます😊 (次章以降で、ここに例外だけ足していく感じです✨)
services:
api:
image: my-api
user: "node"
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
ports:
- "127.0.0.1:3000:3000"
volumes:
- type: bind
source: .
target: /app
read_only: true
- type: volume
source: api-tmp
target: /app/tmp
db:
image: postgres:18
# ports: は書かない(ホストに公開しない)
expose:
- "5432"
volumes:
- db-data:/var/lib/postgresql/data
volumes:
api-tmp:
db-data:
read_only/user/ports/expose/security_optなどは、Composeサービス定義の属性として整理されています。(Docker Documentation)
次は第4章の「安全デフォルトの作り方(テンプレ→例外だけ追加)」で、この章の3原則を“毎回自動で効く仕組み”にしていきます📦✨ 必要なら、この章のテンプレをあなたの教材用プロジェクト(Node + 何か)に合わせて、もう少し現実寄りに調整した版も作れます😄🔧