Skip to content

Claude Code – Dev Container Oppsett

Det er også mulig å bruke Claude Code i en isolert utviklingscontainer. Det gjør vi for å kunne gi Claude mer tilgang på en trygg måte. Målet med dette er å kunne kjøre Claude med:

claude --permission-mode bypassPermissions

Dette gir Claude tilgang til å kjøre kommandoer uten begrensninger. Som utvikler har du bred tilgang til Github, AWS og din egen maskin. Derfor er det viktig å isolere Claude i en container slik at den ikke kan gjøre unødvendig skade.

Hva får du med dette oppsettet?

  • Komplett Claude Code-miljø: Node.js, Claude Code CLI og alle avhengigheter
  • AWS-integrasjon: Automatisk oppsett av AWS-legitimasjon via SSO
  • Sikkerhet: Valgfri begresning på domener som agenten kan bruke og isolert miljø
  • Vedvarende konfigurasjon: Claude-innstillinger hentes fra prosjektet og settes opp i containeren.

Når skal du bruke dette oppsettet?

Dette er slik du burde bruke Claude Code fordi det gjør at verktøyet kan levere større verdi. Det gjør at det er mulig å ha full kontroll over hva Claude kan gjøre, og det gjør at du kan bruke verktøyet uten å bekymre deg for sikkerheten på din egen maskin. Ulempen er at man må aktivt gi tilganger og at det kan være litt mer tungvint å komme i gang. Både VSCode og Intellij har støtte for utviklingscontainere.

Forutsetninger

Før du starter, sørg for at du har:

  • Docker installert på din lokale maskin
  • VSCode eller IntelliJ med støtte for Dev Containers
  • AWS CLI konfigurert med SSO-tilgang til ki-bedrock-inference-261916864828-profilen
  • Claude Code innstillinger i .claude/ i prosjektroten (se Claude Code - Lokalt oppsett)

Komplett oppsett - steg for steg

Tips

  • Under finner du et stegvis oppsett for Claude Code. Om du ønsker en raskere start kan du kopiere hele .devcontainer/-mappen og .claude fra kunstig-intelligens.

Steg 1: Opprett .devcontainer-mappen

Opprett en .devcontainer/-mappe i prosjektroten:

mkdir .devcontainer

Steg 2: Opprett Dockerfile

Opprett .devcontainer/Dockerfile med følgende innhold:

FROM node:20

ARG TZ
ENV TZ="$TZ"

ARG CLAUDE_CODE_VERSION=latest

# Install basic development tools and iptables/ipset
RUN apt-get update && apt-get install -y --no-install-recommends \
  less \
  git \
  procps \
  sudo \
  fzf \
  zsh \
  man-db \
  unzip \
  gnupg2 \
  gh \
  iptables \
  ipset \
  iproute2 \
  dnsutils \
  aggregate \
  jq \
  nano \
  vim \
  curl \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

# Ensure default node user has access to /usr/local/share
RUN mkdir -p /usr/local/share/npm-global && \
  chown -R node:node /usr/local/share

ARG USERNAME=node

# Persist bash history.
RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
  && mkdir /commandhistory \
  && touch /commandhistory/.bash_history \
  && chown -R $USERNAME /commandhistory

# Set `DEVCONTAINER` environment variable to help with orientation
ENV DEVCONTAINER=true

# Create workspace and config directories and set permissions
RUN mkdir -p /workspace /home/node/.claude && \
  chown -R node:node /workspace /home/node/.claude

WORKDIR /workspace

ARG GIT_DELTA_VERSION=0.18.2
RUN ARCH=$(dpkg --print-architecture) && \
  wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
  sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
  rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"

# Set up non-root user
USER node

# Install global packages
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
ENV PATH=$PATH:/usr/local/share/npm-global/bin

# Set the default shell to zsh rather than sh
ENV SHELL=/bin/zsh

# Set the default editor and visual
ENV EDITOR nano
ENV VISUAL nano

# Default powerline10k theme
ARG ZSH_IN_DOCKER_VERSION=1.2.0
RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
  -p git \
  -p fzf \
  -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
  -a "source /usr/share/doc/fzf/examples/completion.zsh" \
  -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
  -x

# Install Claude
RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}

# Install UV (Python package manager)
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/home/node/.local/bin:$PATH"

# Install AWS CLI
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
USER root
RUN ./aws/install

# Install Claude Squad
RUN curl -fsSL https://raw.githubusercontent.com/smtg-ai/claude-squad/main/install.sh | bash
RUN cp /root/.local/bin/cs /usr/local/bin/cs && chmod +x /usr/local/bin/cs

# Copy and set up firewall script
COPY init-firewall.sh /usr/local/bin/
USER root
RUN chmod +x /usr/local/bin/init-firewall.sh && \
  echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
  chmod 0440 /etc/sudoers.d/node-firewall

# Add node user to sudoers
RUN echo "node ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/node && \
  chmod 0440 /etc/sudoers.d/node

Steg 3: Opprett devcontainer.json

Opprett .devcontainer/devcontainer.json med følgende innhold:

{
  "name": "Claude Code AI Sandbox",
  "build": {
    "dockerfile": "Dockerfile",
    "args": {
      "TZ": "${localEnv:TZ:Europe/Oslo}",
      "CLAUDE_CODE_VERSION": "latest",
      "GIT_DELTA_VERSION": "0.18.2",
      "ZSH_IN_DOCKER_VERSION": "1.2.0"
    }
  },
  "runArgs": [
    "--cap-add=NET_ADMIN",
    "--cap-add=NET_RAW",
    "--env-file",
    ".devcontainer/devcontainer.env"
  ],
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "eamodio.gitlens"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "editor.codeActionsOnSave": {
          "source.fixAll.eslint": "explicit"
        },
        "terminal.integrated.defaultProfile.linux": "zsh",
        "terminal.integrated.profiles.linux": {
          "bash": {
            "path": "bash",
            "icon": "terminal-bash"
          },
          "zsh": {
            "path": "zsh"
          }
        }
      }
    }
  },
  "remoteUser": "node",
  "mounts": [
    "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
    "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
  ],
  "containerEnv": {
    "NODE_OPTIONS": "--max-old-space-size=4096",
    "CLAUDE_CONFIG_DIR": "/home/node/.claude",
    "POWERLEVEL9K_DISABLE_GITSTATUS": "true"
  },
  "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
  "workspaceFolder": "/workspace",
  "initializeCommand": ".devcontainer/create-env.sh",
  "postCreateCommand": "sudo /usr/local/bin/init-firewall.sh"
}

Steg 4: Opprett AWS-legitimasjonsskript

Opprett .devcontainer/create-env.sh med følgende innhold:

#!/bin/bash

set -e

ENV_FILE=".devcontainer/devcontainer.env"

echo "Ensuring devcontainer.env has valid AWS credentials..."

# If env file exists, check remaining validity and reuse if > 1 hour left
if [ -f "$ENV_FILE" ]; then
    # shellcheck disable=SC1090
    source "$ENV_FILE"
    if [ ! -z "$AWS_CREDENTIAL_EXPIRATION" ] && [ "$AWS_CREDENTIAL_EXPIRATION" != "null" ]; then
        NOW_EPOCH=$(date -u +%s)
        EXPIRY_EPOCH=$(date -d "$AWS_CREDENTIAL_EXPIRATION" +%s 2>/dev/null || echo 0)
        if [ "$EXPIRY_EPOCH" -gt 0 ]; then
            SECONDS_LEFT=$(( EXPIRY_EPOCH - NOW_EPOCH ))
            ONE_HOUR=3600
            if [ $SECONDS_LEFT -gt $ONE_HOUR ]; then
                HOURS_LEFT=$(awk -v s=$SECONDS_LEFT 'BEGIN { printf "%.2f", s/3600 }')
                echo "✅ Existing credentials valid for another ~${HOURS_LEFT}h (>=1h). Skipping refresh."
                exit 0
            else
                echo "ℹ️ Credentials expiring soon (<=1h left). Refreshing..."
            fi
        else
            echo "⚠️ Could not parse existing AWS_CREDENTIAL_EXPIRATION. Refreshing credentials..."
        fi
    else
        echo "ℹ️ No expiration info found in existing env file. Refreshing credentials..."
    fi
fi

# Check if AWS CLI is available
if ! command -v aws &> /dev/null; then
    echo "Error: AWS CLI is not installed. Please install AWS CLI first."
    exit 1
fi

# Get AWS credentials using AWS CLI
echo "Fetching AWS credentials..."

aws sso login

# Export credentials using AWS CLI configure export-credentials
PROFILE="ki-bedrock-inference-261916864828"
echo "Using AWS profile: $PROFILE"

# Export credentials in env format
CREDENTIALS_OUTPUT=$(aws configure export-credentials --profile "$PROFILE" --format env)

if [ $? -ne 0 ]; then
    echo "Error: Failed to export credentials for profile $PROFILE"
    echo "Make sure the profile exists and is properly configured."
    exit 1
fi

# Parse the exported credentials
eval "$CREDENTIALS_OUTPUT"

# Get default region from the profile
AWS_DEFAULT_REGION=eu-south-2

# Create the env file with exported credentials
cat > "$ENV_FILE" << EOF
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION
EOF

# Add session token if available
if [ ! -z "$AWS_SESSION_TOKEN" ] && [ "$AWS_SESSION_TOKEN" != "null" ]; then
    echo "AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" >> "$ENV_FILE"
fi

# Add credential expiration if available
if [ ! -z "$AWS_CREDENTIAL_EXPIRATION" ] && [ "$AWS_CREDENTIAL_EXPIRATION" != "null" ]; then
    echo "AWS_CREDENTIAL_EXPIRATION=$AWS_CREDENTIAL_EXPIRATION" >> "$ENV_FILE"
fi

echo "✅ devcontainer.env created successfully"

# Format the ISO timestamp nicely for display
FORMATTED_EXPIRATION=$(date -d "$AWS_CREDENTIAL_EXPIRATION" '+%Y-%m-%d at %H:%M:%S UTC' 2>/dev/null || echo "$AWS_CREDENTIAL_EXPIRATION")
echo "🕒 Credentials will expire on: $FORMATTED_EXPIRATION"

# Ensure the env file is in .gitignore
GITIGNORE_FILE=".gitignore"
if [ -f "$GITIGNORE_FILE" ]; then
    if ! grep -q "devcontainer.env" "$GITIGNORE_FILE"; then
        echo ".devcontainer/devcontainer.env" >> "$GITIGNORE_FILE"
        echo "✅ Added devcontainer.env to .gitignore"
    fi
else
    echo ".devcontainer/devcontainer.env" > "$GITIGNORE_FILE"
    echo "✅ Created .gitignore and added devcontainer.env"
fi

Gjør scriptet kjørbart:

chmod +x .devcontainer/create-env.sh

Dette scriptet henter AWS-legitimasjon ved hjelp av AWS SSO og lagrer dem i .devcontainer/devcontainer.env. Det sjekker også om eksisterende legitimasjon fortsatt er gyldig i mer enn en time før det henter nye.

Steg 5: Opprett nettverkssikkerhetsskript (valgfritt)

Opprett .devcontainer/init-firewall.sh for å begrense nettverkstilgang:

#!/bin/bash
# set -euo pipefail  # Exit on error, undefined vars, and pipeline failures
# IFS=$'\n\t'       # Stricter word splitting

# MERK: Dette scriptet er for øyeblikket deaktivert (kommentert ut)
# for å unngå nettverksproblemer. Aktiver kun hvis du forstår konsekvensene.

# # 1. Extract Docker DNS info BEFORE any flushing
# DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)

# # Flush existing rules and delete existing ipsets
# iptables -F
# iptables -X
# iptables -t nat -F
# iptables -t nat -X
# iptables -t mangle -F
# iptables -t mangle -X
# ipset destroy allowed-domains 2>/dev/null || true

# # 2. Selectively restore ONLY internal Docker DNS resolution
# if [ -n "$DOCKER_DNS_RULES" ]; then
#     echo "Restoring Docker DNS rules..."
#     iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
#     iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
#     echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
# else
#     echo "No Docker DNS rules to restore"
# fi

# # First allow DNS and localhost before any restrictions
# # Allow outbound DNS
# iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
# # Allow inbound DNS responses
# iptables -A INPUT -p udp --sport 53 -j ACCEPT

echo "Firewall script is currently disabled for safety. Enable if needed."

Gjør scriptet kjørbart:

chmod +x .devcontainer/init-firewall.sh

Kom i gang

Starte containeren

  1. Åpne prosjektet i VS Code
  2. Trykk Ctrl+Shift+P og velg "Dev Containers: Reopen in Container"
  3. VSCode kjører create-env.sh for å hente AWS-legitimasjon
  4. VSCode bygger Docker-imaget (kan ta flere minutter)

Kjør claude --permission-mode bypassPermissions i terminalen

Fornyelse av AWS token

AWS-legitimasjon utløper etter en tid. For å fornye:

  1. Rebuild container (Ctrl+Shift+P -> "Dev Containers: Rebuild Container")
  2. Logg inn i AWS på nytt

Filen .devcontainer/devcontainer.env oppdateres automatisk og bør ikke commites. Legg den til i .gitignore hvis den ikke allerede er der.

Claude innstillinger

Claude-konfigurasjonen hentes fra .claude/ i prosjektroten.

Nettverkssikkerhet (valgfritt)

init-firewall.sh inneholder et eksempeloppsett for å begrense utgående nettverkstrafikk. Scriptet er deaktivert som standard for å unngå tilkoblingsproblemer. Denne kan aktiveres dersom du vil begrense agenten til kun enkelte domener.