Skip to content

feat(claude-code): add managed_settings input for policy delivery via /etc/claude-code#863

Open
morganl-ant wants to merge 3 commits intocoder:mainfrom
morganl-ant:anthropic/managed-settings
Open

feat(claude-code): add managed_settings input for policy delivery via /etc/claude-code#863
morganl-ant wants to merge 3 commits intocoder:mainfrom
morganl-ant:anthropic/managed-settings

Conversation

@morganl-ant
Copy link
Copy Markdown

Problem

The module configures Claude Code's permission posture by reaching around the permission system rather than through it:

  • scripts/install.sh writes bypassPermissionsModeAccepted, autoModeAccepted, and primaryApiKey directly into the user-writable ~/.claude.json. Any process in the workspace can read the API key or flip the acceptance flags back.
  • scripts/start.sh adds --dangerously-skip-permissions to every task launch, even when the template author set an explicit permission_mode. The README has to carry a security warning telling people the module bypasses permission checks.
  • permission_mode, allowed_tools, and disallowed_tools each plumb through a different ad-hoc path (CLI flag, coder subcommand) instead of a single policy surface.

Change

Add a managed_settings input that renders to /etc/claude-code/managed-settings.d/10-coder.json. Claude Code reads that drop-in directory at startup with the highest configuration precedence (above ~/.claude/settings.json and project settings), so template authors get an admin-controlled policy file that users inside the workspace cannot override. The mechanism is a local file read with no API call, so it works identically for the Anthropic API, AWS Bedrock, Google Vertex AI, and AI Bridge / AI Gateway.

managed_settings = {
  permissions = {
    defaultMode                  = "acceptEdits"
    disableBypassPermissionsMode = "disable"
    deny                         = ["Bash(curl:*)", "WebFetch"]
  }
}

Supporting changes:

  • install.sh writes the policy file (root-owned, 0644) and stops writing bypassPermissionsModeAccepted, autoModeAccepted, and primaryApiKey into ~/.claude.json. The API key is already exported via coder_env as CLAUDE_API_KEY; duplicating it on disk is unnecessary. hasCompletedOnboarding stays because there is no env-var alternative for it.
  • start.sh only adds --dangerously-skip-permissions for tasks when no explicit permission_mode is set (same fix as fix(claude-code): don't pass --dangerously-skip-permissions in auto mode #846; included here so this PR is self-contained, happy to drop if fix(claude-code): don't pass --dangerously-skip-permissions in auto mode #846 lands first).
  • permission_mode, allowed_tools, and disallowed_tools are marked deprecated and shimmed into managed_settings.permissions for one release when managed_settings is not provided.
  • README security warning rewritten to point at the policy mechanism instead of telling people the module is unsafe by design.

Relationship to #861

#861 strips this module to install-and-configure and removes permission_mode / allowed_tools / disallowed_tools outright. managed_settings is the natural replacement for those: it is install-time (survives the start.sh removal), it covers everything the dropped variables did plus hooks, env, model, apiKeyHelper, and the rest of the settings schema, and it does not require the module to know anything about how Claude is launched. If #861 lands first I will rebase this on top and drop the deprecation shim and the start.sh hunk.

Validation

  • terraform fmt / terraform validate clean
  • New tests: claude-managed-settings-written, claude-managed-settings-legacy-shim, claude-no-policy-keys-in-claudejson, plus an assertion in claude-auto-permission-mode that --dangerously-skip-permissions is absent when a mode is set
  • Manually verified /etc/claude-code/managed-settings.d/*.json precedence in the Claude Code CLI source

Closes #818. Relates to #284, #846, #861.

Disclosure: I work at Anthropic on the Claude Code team. Happy to adjust scope or split this further if that is easier to review.

…licy delivery

The module currently configures permission posture by writing
bypassPermissionsModeAccepted, autoModeAccepted, and primaryApiKey
directly into the user-writable ~/.claude.json, and forces
--dangerously-skip-permissions on every task launch regardless of the
configured permission_mode. Both bypass Claude Code's permission system
rather than configuring it.

This adds a managed_settings input that renders to
/etc/claude-code/managed-settings.d/10-coder.json, the sanctioned
drop-in directory Claude Code reads at highest precedence. The file is
root-owned so users cannot override it from inside the workspace, and
the mechanism is purely client-side so it works with any inference
backend (Anthropic API, Bedrock, Vertex, AI Gateway).

permission_mode, allowed_tools, and disallowed_tools are deprecated in
favor of managed_settings.permissions and are shimmed into the policy
file for one release when managed_settings is not set.

start.sh now only adds --dangerously-skip-permissions for tasks when no
explicit permission_mode is configured (same approach as coder#846), and
install.sh no longer writes permission-acceptance flags or the API key
into ~/.claude.json.
@morganl-ant morganl-ant marked this pull request as ready for review April 22, 2026 20:57
@matifali matifali added the version:minor Add to PRs requiring a minor version upgrade label Apr 23, 2026
- The legacy-shim test set disallowed_tools, which triggered a
  pre-existing 'coder --disallowedTools' call in
  setup_claude_configurations. coder is not present in the test
  container so set -e aborted before the policy file was written.
  Those calls are redundant now that the legacy shim writes
  allow/deny via managed-settings.d, so remove them.
- claude-no-policy-keys-in-claudejson: configure_standalone_mode
  guards on CLAUDE_API_KEY in the environment, which coder_env
  provides in production but not in the test container. Pass
  coderEnvVars to execModuleScript so the file is created.
@matifali
Copy link
Copy Markdown
Member

This would need rebasing after #861 is merged. Thanks for your contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

version:minor Add to PRs requiring a minor version upgrade

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude-code: Add 'auto' and 'dontAsk' to permission_mode validation

2 participants