1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeの managed-settings.d で「消せない組織ルール」をファイル分割して配布する

1
Posted at

はじめに:ルールを「お願い」で済ませて事故った話

当社(合同会社ジョインクラス)では、Claude Code を launchd の 17 ジョブから毎日ヘッドレスで動かし、記事公開・営業・出版まで自動化率 98% で回しています。

その過程で、僕は一度痛い目を見ています。AI が git push --force を自動実行してブランチが消えた事故です。当時の対策は CLAUDE.md に「force push 禁止」と書くことでしたが、CLAUDE.md は 1,000 行を超えたあたりからルール同士が矛盾し、AI が指示と逆の動作を始めました。

結論として学んだのは、

  • CLAUDE.md は「お願い」。モデルが読み飛ばすことがある
  • settings の permissions.deny は「強制」。ハーネスがツール呼び出し自体を止める
  • そしてユーザーやプロジェクトが上書きできない場所に置かないと、強制にならない

この「上書きできない場所」が managed settings で、それをファイル分割して配れるのが managed-settings.d/ です。この記事では、当社の実スクリプトをベースに、導入手順・検証方法・落とし穴までを書きます。

managed-settings.d とは

Claude Code の settings は優先度順に次のように解決されます(上ほど強い)。

  1. managed settings(組織管理。ユーザーは上書き不可)
  2. コマンドライン引数(--allowedTools など)
  3. .claude/settings.local.json
  4. .claude/settings.json
  5. ~/.claude/settings.json

managed settings は従来 managed-settings.json 1 ファイルでしたが、ドロップインディレクトリ managed-settings.d/ に複数の JSON を置けるようになりました。ファイル名順に読み込まれてマージされるので、

  • 10-security.json はセキュリティチーム
  • 20-cost.json は経理(コスト)
  • 30-telemetry.json は情シス

のように担当ごとに独立して配布・更新できます。1 ファイルを奪い合って JSON を壊す、という運用事故がなくなるのが最大の利点です。

配置先(システム全体・root 権限が必要):

OS パス
macOS /Library/Application Support/ClaudeCode/managed-settings.d/
Linux / WSL /etc/claude-code/managed-settings.d/

※ パスやマージ規則はバージョンで変わり得ます。導入前に claude --version と公式ドキュメント(settings のページ)を必ず確認してください。

当社の前提:ヘッドレス実行のラッパー

当社の自動ジョブは、すべて次のラッパー経由で Claude を呼んでいます(.company/scripts/lib/claude-run.sh から抜粋)。

lib/claude-run.sh
claude_run() {
  local label="$1"; shift
  claude --output-format json \
         --fallback-model claude-sonnet-5 \
         --max-budget-usd 3 \
         "$@" 2>/dev/null \
    | python3 "$_CLAUDE_RUN_LIB/record-cost.py" "$label"
}

呼び出し側はジョブごとにツールを絞っています。

auto-replenish-articles.sh(抜粋)
claude_run replenish --effort low --allowedTools "Read,WebSearch,WebFetch" -p "..."
claude_run replenish --effort low --allowedTools "Write,Read,Edit" -p "..."

問題は、これらは全部「呼び出し側の善意」に依存していることです。新しいスクリプトを書いた人(あるいは AI)が --allowedTools を付け忘れれば、何でもできてしまう。だから「どのジョブからでも絶対に破れない床」を managed settings に置きます。

手順1:ポリシーを役割ごとに分割して書く

10-security.json(破壊的操作の禁止)

10-security.json
{
  "permissions": {
    "deny": [
      "Bash(git push --force:*)",
      "Bash(git push -f:*)",
      "Bash(git reset --hard:*)",
      "Bash(rm -rf:*)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(**/secrets/**)"
    ],
    "disableBypassPermissionsMode": "disable"
  }
}

ポイント:

  • git push --force は当社の事故そのもの。CLAUDE.md ではなくここに書く
  • disableBypassPermissionsMode で --dangerously-skip-permissions を潰す。これがないと deny 以外は全部素通しにできてしまう

20-cost.json(コスト・モデルの統制)

20-cost.json
{
  "model": "claude-sonnet-5",
  "env": {
    "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "16000"
  }
}

当社の AI 運用コストは月約 ¥20,000 です。ラッパーで --max-budget-usd 3 を付けていますが、これも呼び出し側依存なので、既定モデルは managed 側で固定しています。

30-hooks.json(監査ログ)

30-hooks.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{t: now|todate, cmd: .tool_input.command}' >> /var/log/claude-code/bash-audit.jsonl"
          }
        ]
      }
    ]
  },
  "allowManagedHooksOnly": true
}

allowManagedHooksOnly を入れると、ユーザー/プロジェクト側の hooks は無効になり、managed の hooks だけが走ります。監査ログを勝手に外されない一方、開発者個人の便利 hooks も止まるので、入れるかはチームと相談してください。

手順2:配布スクリプト

当社は単一 Mac ですが、複数台でも同じスクリプトを MDM や Ansible から叩けば済む形にしています。

install-managed-settings.sh
#!/bin/bash
set -euo pipefail

SRC_DIR="$(cd "$(dirname "$0")" && pwd)/managed-settings.d"

case "$(uname)" in
  Darwin) DEST="/Library/Application Support/ClaudeCode/managed-settings.d" ;;
  Linux)  DEST="/etc/claude-code/managed-settings.d" ;;
  *) echo "unsupported OS" >&2; exit 1 ;;
esac

# 1. 配布前に JSON 構文を検証(壊れた JSON を配ると全体が効かなくなる)
for f in "$SRC_DIR"/*.json; do
  jq empty "$f" || { echo "invalid JSON: $f" >&2; exit 1; }
done

# 2. root 所有・一般ユーザー書き込み不可で配置
sudo mkdir -p "$DEST"
sudo install -m 0644 -o root "$SRC_DIR"/*.json "$DEST"/
sudo mkdir -p /var/log/claude-code && sudo chmod 0733 /var/log/claude-code

echo "installed:"; ls -l "$DEST"

ソースの managed-settings.d/ は Git で管理し、PR レビューを通ったものだけが配られるようにしています。

手順3:検証する(ここを飛ばさない)

設定は「置いた」だけでは効いている保証になりません。当社では次の 3 段で確認しています。

verify-managed-settings.sh
#!/bin/bash
# 1) 対話セッションで /status を開き、managed settings の読み込み元が表示されるか目視確認

# 2) deny が本当に効くかをヘッドレスで確認
claude -p "git push --force origin test-branch を実行して" \
  --allowedTools "Bash" --output-format json \
  | jq -r '.result' | grep -qiE "deny|拒否|not allowed|permission" \
  && echo "OK: force push blocked" || echo "NG: force push NOT blocked"

# 3) ユーザー側で上書きを試みても負けることを確認
mkdir -p /tmp/override-test/.claude && cd /tmp/override-test
echo '{"permissions":{"allow":["Bash(git push --force:*)"]}}' > .claude/settings.local.json
claude -p "git push --force を実行して" --allowedTools "Bash" --output-format json | jq -r '.result'
  1. が重要です。local の allow より managed の deny が勝つことを、自分の目で確かめておくと安心して自動化を広げられます。

ハマりどころ

  1. JSON が 1 つ壊れると、そのファイルが丸ごと無視される
    → 配布スクリプトで jq empty を必須にした
  2. 配列はマージ、スカラーは後勝ち
    deny などの配列は結合されますが、model のような単一値は後ろのファイルが勝ちます。番号の付け方=優先順位の設計です
  3. パーミッションが緩いと意味がない
    一般ユーザーが書き換えられる状態なら「組織ルール」ではありません。root 所有・0644 を確認
  4. deny のパターンは前方一致
    Bash(git push --force:*) は git push origin main --force を止められません。当社では PreToolUse hook で --force を含むコマンド全体を弾く二重化をしています
  5. launchd ジョブは別ユーザーで動くことがある
    システム全体パスに置く managed なら関係ありませんが、~/.claude/settings.json で済ませていると、ジョブの実行ユーザーによっては読まれません。当社が cron→launchd 移行時に一度ハマったポイントです

まとめ

  • ルールは CLAUDE.md(お願い)→ settings(強制)→ managed(上書き不可) の順に格上げする
  • managed-settings.d/ なら、セキュリティ・コスト・監査を担当ごとのファイルで独立運用できる
  • 配布は「JSON 検証 → root 所有で配置 → 上書きテスト」までをスクリプト化する

経営目線で言えば、AI に仕事を任せる範囲を広げられるかどうかは「絶対にやらせないこと」をどれだけ確実に保証できるかで決まります。当社が自動化率 98% まで踏み込めたのも、この床があるからです。

もっと深く知りたい方へ

managed settings を含む権限設計、deny ルールの設計パターン、hooks による監査・多層防御は、拙著で体系的にまとめています。

『企業のためのClaude Codeセキュリティガイド』
組織導入時の権限設計・情報漏えい対策・監査ログの実装までを解説しています。

書籍一覧はこちら: https://zenn.dev/joinclass?tab=books

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?