Skip to the content.

Protecting Production Infrastructure

Recipes for controlling what Claude can do with cloud and cluster tooling. The goal is to let read-only operations through automatically, block irreversible ones unconditionally, and pause on everything in between.

These are starting points. Take what is useful and extend it with the specific services, namespaces, and risk thresholds that make sense for your environment.



AWS CLI

The examples below allow read-only AWS cli operations, but block updates, writes and deletes.

Allow read-only operations

aws:
  - cmd: "* describe-*"
    decide: allow
    reason: describe-* operations are read-only and do not modify resources.
  - cmd: "* list-*"
    decide: allow
    reason: list-* operations are read-only and do not modify resources.
  - cmd: "* get-*"
    decide: allow
    reason: get-* operations are read-only and do not modify resources.

  # aws s3 high-level commands use short names rather than the verb-* convention
  - cmd: "s3 ls"
    decide: allow
    reason: Lists S3 buckets or objects without modifying anything.
  - cmd: "s3 presign"
    decide: allow
    reason: Generates a pre-signed URL for an existing object without modifying it.

Block all write operations

aws:
  # CRUD and state mutation
  - cmd: "* create-*"
    decide: deny
    reason: Creating resources may incur unexpected costs or alter infrastructure state.
  - cmd: "* update-*"
    decide: deny
    reason: Updating resources can cause configuration drift or service disruptions.
  - cmd: "* modify-*"
    decide: deny
    reason: Modifying resources can cause configuration drift or service disruptions.
  - cmd: "* delete-*"
    decide: deny
    reason: Deleting resources is irreversible and can cause data loss or service outages.
  - cmd: "* terminate-*"
    decide: deny
    reason: Terminating instances is irreversible and causes immediate downtime.
  - cmd: "* remove-*"
    decide: deny
    reason: Removing resources may be irreversible and can cause service disruptions.
  - cmd: "* replace-*"
    decide: deny
    reason: Replacing resources causes downtime and bypasses change control.
  - cmd: "* reset-*"
    decide: deny
    reason: Resetting resources can cause data loss or service interruptions.

  # Lifecycle
  - cmd: "* start-*"
    decide: deny
    reason: Starting resources may incur unexpected costs or alter cluster state.
  - cmd: "* stop-*"
    decide: deny
    reason: Stopping resources causes downtime.
  - cmd: "* reboot-*"
    decide: deny
    reason: Rebooting resources causes downtime and should be coordinated.
  - cmd: "* run-*"
    decide: deny
    reason: Running new instances may incur unexpected costs.

  # Configuration
  - cmd: "* put-*"
    decide: deny
    reason: Overwriting configuration can break dependent services.
  - cmd: "* set-*"
    decide: deny
    reason: Changing settings can break dependent services.
  - cmd: "* add-*"
    decide: deny
    reason: Adding resources or permissions may violate least-privilege principles.
  - cmd: "* enable-*"
    decide: deny
    reason: Enabling features may expose infrastructure or incur unexpected costs.
  - cmd: "* disable-*"
    decide: deny
    reason: Disabling features can cause service degradation.
  - cmd: "* tag-*"
    decide: deny
    reason: Changing tags can affect cost allocation or tag-based access policies.
  - cmd: "* untag-*"
    decide: deny
    reason: Removing tags can affect cost allocation or tag-based access policies.

  # Attachment and association
  - cmd: "* attach-*"
    decide: deny
    reason: Attaching resources can change network topology or security boundaries.
  - cmd: "* detach-*"
    decide: deny
    reason: Detaching resources can cause connectivity loss or service interruptions.
  - cmd: "* associate-*"
    decide: deny
    reason: Associating resources can change routing or security group boundaries.
  - cmd: "* disassociate-*"
    decide: deny
    reason: Disassociating resources can cause connectivity loss.
  - cmd: "* register-*"
    decide: deny
    reason: Registering targets changes load balancer or service discovery routing.
  - cmd: "* deregister-*"
    decide: deny
    reason: Deregistering targets removes them from load balancers and causes traffic loss.

  # Access control
  - cmd: "* authorize-*"
    decide: deny
    reason: Granting access can expand the attack surface.
  - cmd: "* revoke-*"
    decide: deny
    reason: Revoking access can break dependent services.

  # Data operations
  - cmd: "* import-*"
    decide: deny
    reason: Importing data can overwrite existing resources.
  - cmd: "* restore-*"
    decide: deny
    reason: Restoring from backup overwrites current data and may be irreversible.
  - cmd: "* copy-*"
    decide: deny
    reason: Copying resources may incur costs or duplicate sensitive data.

  # aws s3 high-level commands
  - cmd: "s3 cp"
    decide: deny
    reason: Copying S3 objects may overwrite existing data or duplicate sensitive content.
  - cmd: "s3 mv"
    decide: deny
    reason: Moving S3 objects is irreversible and can break services that depend on object paths.
  - cmd: "s3 rm"
    decide: deny
    reason: Deleting S3 objects is irreversible and can cause permanent data loss.
  - cmd: "s3 sync"
    decide: deny
    reason: S3 sync can overwrite or delete production data in bulk.

  # Safety flag
  - options-in:
      - force
    decide: deny
    reason: The --force flag bypasses safety checks and confirmation prompts.

  # High-risk services
  - cmd: "iam *"
    decide: deny
    reason: All IAM changes require manual approval; any change to roles, policies, or users is high-risk enough that no automated action should be allowed.
  - cmd: "cloudformation deploy"
    decide: ask
    reason: Confirm CloudFormation deployment before proceeding.

When combined with the allow rules above, reads resolve to allow, writes resolve to deny, IAM is blocked entirely, and CloudFormation deployments pause for confirmation. Anything not covered falls through to the system default.


kubectl

The examples below allow read-only Kubectl CLI operations, surface unknown subcommands for manual review via a catch-all ask, and block writes and exec access unconditionally.

Read-only access with a catch-all

kubectl:
  - get:
      decide: allow
      reason: Read-only resource listing.
  - describe:
      decide: allow
      reason: Read-only resource inspection.
  - logs:
      decide: allow
      reason: Read-only log access.
  - top:
      decide: allow
      reason: Read-only resource usage metrics.
  - version:
      decide: allow
      reason: Read-only version check.
  - cluster-info:
      decide: allow
      reason: Read-only cluster information.
  - decide: ask
    reason: Confirm kubectl operation

Block write and exec operations

kubectl:
  - delete:
      decide: deny
      reason: Deleted resources may not be recoverable; use your deployment pipeline.
  - apply:
      decide: deny
      reason: Direct applies bypass the deployment pipeline and change tracking.
  - patch:
      decide: deny
      reason: Direct patches bypass change tracking and code review.
  - scale:
      decide: deny
      reason: Manual scaling bypasses capacity planning; use your deployment pipeline.
  - exec:
      decide: deny
      reason: Direct pod shell access bypasses audit logging and security controls.
  - drain:
      decide: deny
      reason: Draining nodes evicts running workloads and must be coordinated with the ops team.
  - cordon:
      decide: deny
      reason: Cordoning prevents scheduling on a node and must be coordinated with the ops team.
  - rollout:
      restart:
        decide: deny
        reason: Rolling restarts cause temporary service disruption and should go through the deployment pipeline.

Scoping rules to production contexts

The recipes above apply to every command regardless of environment. The examples below scope the rules allowing for unrestricted operations on a “sandbox” account or context, but locking down tighter rules for non-sandbox (e.g. production) accounts and contexts.

AWS: matching on the active profile

Apply different rules per AWS profile by adding an env match to every rule in the set. Rules without an env match apply to all profiles, so keeping the matches consistent avoids unintended cross-profile matches.

aws:
  # Sandbox: allow everything without prompting
  - env:
      AWS_PROFILE: sandbox

    # Anything goes in sandbox.
    decide: allow

  # Any non-sandbox profile: apply these rules
  - not:
      env:
        AWS_PROFILE: sandbox

    # These rules apply to non-sandbox AWS profile (e.g. the production account).
    rules:
      - cmd: "* delete-*"
        decide: deny
        reason: Destructive deletes on non-sandbox profiles risk permanent data loss.
      - cmd: "* terminate-*"
        decide: deny
        reason: Terminating instances on non-sandbox profiles causes irreversible downtime.
      - cmd: "* create-*"
        decide: deny
        reason: Creating resources on non-sandbox profiles may incur unexpected costs.
      - cmd: "* modify-*"
        decide: deny
        reason: Modifying resources on non-sandbox profiles risks service disruptions.
      - cmd: "iam *"
        decide: deny
        reason: IAM changes on non-sandbox profiles can compromise the entire account's security.

      # Catch-all: ask for anything not explicitly denied above
      - decide: ask
        reason: Confirm AWS operation on non-sandbox profile

The not: block inverts the match, so not: env: AWS_PROFILE: sandbox fires for any profile name except sandbox. The rules: block only runs when the parent not: matches, so the profile check is written once rather than repeated on every rule. Inside the block, strictest-wins still applies: deny beats ask, so the catch-all ask only fires when no deny rule matches (e.g. for describe-* or list-* operations).

Kubectl: matching on the current context via kubeconfig

Match on the active kubectl context by reading ~/.kube/config directly:

kubectl:
  # Sandbox context (detected via kubeconfig): allow everything without prompting
  - file:
      ~/.kube/config:
        contains: "current-context: sandbox"
    # Anything goes in the sandbox account
    decide: allow

  # Any non-sandbox context: apply these rules
  - not:
      file:
        ~/.kube/config:
          contains: "current-context: sandbox"
    rules:
      - cmd: get
        decide: allow
        reason: Read-only resource listing.
      - cmd: describe
        decide: allow
        reason: Read-only resource inspection.
      - cmd: logs
        decide: allow
        reason: Read-only log access.
      - cmd: delete
        decide: deny
        reason: Deleted resources outside sandbox may not be recoverable; use your deployment pipeline.
      - cmd: apply
        decide: deny
        reason: Direct applies outside sandbox bypass the deployment pipeline and change tracking.
      - cmd: exec
        decide: deny
        reason: Pod shell access outside sandbox bypasses audit logging and security controls.

      # Catch-all: ask for anything not explicitly covered above
      - decide: ask
        reason: Confirm kubectl operation outside sandbox

not: file: contains: matches when ~/.kube/config is present but does not contain the given string. If the file is absent, neither rule matches and rules fall through to the default.

Kubectl: matching on the context argument

Match on the active Kubectl context via the --context argument:

kubectl:
  # Sandbox: allow everything without prompting
  - options:
      context: sandbox
    decide: allow

  # Any non-sandbox context: apply these rules
  - not:
      options:
        context: sandbox
    rules:
      # Allow read-only operations
      - cmd: get
        decide: allow
        reason: Read-only resource listing.
      - cmd: describe
        decide: allow
        reason: Read-only resource inspection.
      - cmd: logs
        decide: allow
        reason: Read-only log access.
      - cmd: top
        decide: allow
        reason: Read-only resource usage metrics.
      - cmd: version
        decide: allow
        reason: Read-only version check.
      - cmd: cluster-info
        decide: allow
        reason: Read-only cluster information.

      # Deny known-destructive operations
      - cmd: delete
        decide: deny
        reason: Deleted resources outside sandbox may not be recoverable; use your deployment pipeline.
      - cmd: apply
        decide: deny
        reason: Direct applies outside sandbox bypass the deployment pipeline and change tracking.
      - cmd: exec
        decide: deny
        reason: Pod shell access outside sandbox bypasses audit logging and security controls.
      - cmd: scale
        decide: deny
        reason: Manual scaling outside sandbox bypasses capacity planning; use your deployment pipeline.

      # Catch-all: ask for anything not explicitly covered above
      - decide: ask
        reason: Confirm kubectl operation outside sandbox

This matches when --context is passed explicitly, which is common in scripts and pipelines. If the active context was set with kubectl config use-context and commands run without the flag, these rules will not match; for that case, a custom TypeScript rule can call kubectl config current-context to detect the active context automatically.