第14章:Linux capabilitiesを減らす:できることを最小化🧤✂️
この章は「privilegedに逃げないで、コンテナの“できること”を必要最小限にする」回です😎🔒 ポイントは cap_drop / cap_add を “設計の道具” として使うこと✨
1) capabilitiesって何?ざっくり理解🧠💡
Linuxの root権限 って、実は「超強い権限の寄せ集め」なんです。
その“権限のボタン”を小分けにしたのが capabilities(ケーパビリティ)🎛️
root = 全部入りリモコン📺capabilities = ボタン単位でON/OFFできるリモコン🎛️
そしてDockerは、デフォルトで“いくつかのボタン”をコンテナに渡しています(全部は渡してない)📦🔧 どれがデフォルトで付くかは、Docker公式の一覧がそのまま参考になります。(Docker Documentation)
2) Dockerのデフォルト権限、実はけっこう強い😱
Dockerはデフォルトで、たとえばこんなcapabilityを「付けた状態」で起動します👇
(例:CHOWN, DAC_OVERRIDE, NET_RAW, NET_BIND_SERVICE など)(Docker Documentation)
特に初心者が「うっかり危ない」やつはこれ:
DAC_OVERRIDE:ファイル権限チェックをかなり無視できる系(読み書き関連)😵💫(Docker Documentation)NET_RAW:RAWソケット系(雑に言うと“ネットを低レベル操作”寄り)🕸️(Docker Documentation)NET_BIND_SERVICE:1024未満のポート(80とか)で待ち受けできる📮(Docker Documentation)
つまり、何も考えずに起動すると「Webアプリには要らない強さ」が混ざりがち、ってことです🙃
3) まず覚える必勝パターン:全部落として、必要分だけ戻す🧹➡️🔧
- まず
cap_drop: [ALL]で全部落とす🧹 - 動かしてみて、必要なcapabilityだけ
cap_addで戻す🔧
Docker CLIでも --cap-drop / --cap-add が使えて、ALL も指定できます。(Docker Documentation)
Composeでも cap_add / cap_drop が使えます。(Docker Documentation)
4) ハンズオン:Nodeサーバを「cap全部なし」で動かす🧪✨
狙い:普通のNode/TS Webサーバは、capability無しでも大体動く…を体験します😄👍
(※ 80番待ち受けだけは例外で、あとで NET_BIND_SERVICE を戻します)
4-1) ファイル用意📁✍️
プロジェクト直下にこんな構成を作ります:
compose.yamlDockerfilepackage.jsontsconfig.jsonsrc/server.ts
src/server.ts(超ミニHTTP)🌐
import http from "node:http";
const PORT = Number(process.env.PORT ?? 3000);
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader("content-type", "text/plain; charset=utf-8");
res.end(`OK 👋 path=${req.url}\n`);
});
server.listen(PORT, "0.0.0.0", () => {
console.log(`listening on ${PORT}`);
});
package.json(ビルドして実行)📦
{
"name": "cap-demo",
"private": true,
"type": "module",
"scripts": {
"build": "tsc -p tsconfig.json",
"start": "node dist/server.js"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.0.0"
}
}
tsconfig.json(最小)🧩
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "Bundler",
"outDir": "dist",
"strict": true
},
"include": ["src/**/*.ts"]
}
Dockerfile(ビルド→実行)🏗️
FROM node:current-bookworm-slim AS build
WORKDIR /app
COPY package.json tsconfig.json ./
RUN npm i
COPY src ./src
RUN npm run build
FROM node:current-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
4-2) まず普通に起動(基準)🚀
compose.yaml(まずはcap指定なし)
services:
web:
build: .
environment:
- PORT=3000
ports:
- "3000:3000"
起動:
docker compose up --build
別ターミナルで確認:
curl http://localhost:3000/
4-3) 次に「cap全部落とし」で起動🧹✨
compose.yaml をこう変えます👇(これが本題!)
services:
web:
build: .
environment:
- PORT=3000
ports:
- "3000:3000"
cap_drop:
- ALL
起動:
docker compose up --build
また curl:
curl http://localhost:3000/
✅ 多くの環境で、そのまま動くはずです🎉 → これが「Webアプリにcapability要らんこと多い」の体験🔥
4-4) 80番で待ち受けたい時だけ:NET_BIND_SERVICE を戻す📮🔧
Linuxでは 1024未満のポートでlistenするのに NET_BIND_SERVICE が必要です。(Docker Documentation)
(NET_BIND_SERVICE の説明はLinuxのcapabilitiesの定義にもあります。(man7.org))
たとえば「コンテナ内は80でlisten、ホストは8080でアクセス」にしてみます👇 (Windowsで80を直接使うと競合しがちなので、ホスト8080に逃がすのが楽😄)
services:
web:
build: .
environment:
- PORT=80
ports:
- "8080:80"
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
確認:
docker compose up --build
curl http://localhost:8080/
5) 「Operation not permitted 😇」が出た時の考え方🧯
capabilityを落とすと、エラーがこうなりがち👇
| 症状🥲 | だいたいの原因🧠 | まず疑うcapability🔍 |
|---|---|---|
| 80番でlistenできない | 低ポート制限 | NET_BIND_SERVICE (Docker Documentation) |
ip link / ルーティング操作ができない | ネットワーク管理操作 | NET_ADMIN(※原則アプリには不要)(Docker Documentation) |
| マウント/FUSE系ができない | マウント操作 | SYS_ADMIN(危険寄り)(Docker Documentation) |
コツ:エラーが出た操作を言語化してから、必要最小限のcapabilityだけ足す✨
6) “足しちゃダメ寄り”代表:SYS_ADMIN 😱🚫
CAP_SYS_ADMIN は Linux側でも「過剰に詰め込まれた(overloaded)危険枠」って扱いです。(man7.org)
つまり、雑に付けると“ほぼ何でもあり”に近づくと思ってOKです💥
なので判断基準はこれ👇
SYS_ADMINを足すくらいなら まず 別方式(設計) を探す🔎✨- どうしても必要なら 「そのコンテナだけ隔離」「マウント/デバイスも最小」「目的を書いたコメント」🧱📝
7) 便利メモ:Composeでの書き方(最小)📌
Composeの cap_add / cap_drop はサービス属性として定義できます。(Docker Documentation)
Docker CLIだと --cap-add/--cap-drop で同じことができます。(Docker Documentation)
あと地味に重要:Dockerはcapabilityの選択に合わせて デフォルトseccompプロファイルが調整されるので、普通はseccompをいじらなくてOKです。(Docker Documentation)
8) AIに相談する時の“安全な聞き方”🤖🛡️(コピペ用)
AIが SYS_ADMIN とか privileged を雑に提案してくること、あります😂
なので、こう聞くのが安全です👇
次のエラーを解消したいが、privilegedは禁止。
cap_drop: [ALL] のまま、必要最小限のcap_addだけで解決したい。
「なぜそのcapabilityが必要か」を1行で説明してから提案して。
9) 章末チェック✅🎯(これだけできれば勝ち)
-
cap_drop: [ALL]で起動してもWebアプリが動くのを確認した😄 - 80番待ち受けが必要な時だけ
NET_BIND_SERVICEを戻せる📮 -
SYS_ADMINは“最後の最後”だと理解した😱
次の章(第15章)で「privilegedを使う前のチェックリスト🧾🛑」に行くと、今日の内容がそのまま武器になりますよ🔥