Configure Claude Code for sandboxed workspaces

- Add credentials bind mount in container.nix
- Create claude-credentials-dir service to copy credentials for VMs
- Generate .claude.json with onboarding skipped and workspace trusted
- Add allowUnfree to container config
This commit is contained in:
2026-02-08 14:53:31 -08:00
parent 87db330e5b
commit 5178ea6835
2 changed files with 75 additions and 28 deletions

View File

@@ -1,4 +1,4 @@
{ config, lib, ... }: { config, lib, pkgs, ... }:
# Container-specific configuration for sandboxed workspaces using systemd-nspawn # Container-specific configuration for sandboxed workspaces using systemd-nspawn
# This module is imported by default.nix for workspaces with type = "container" # This module is imported by default.nix for workspaces with type = "container"
@@ -47,10 +47,16 @@ in
hostPath = "/home/googlebot/sandboxed/${name}/ssh-host-keys"; hostPath = "/home/googlebot/sandboxed/${name}/ssh-host-keys";
isReadOnly = false; isReadOnly = false;
}; };
# Per-workspace claude config for isolated session data
"/home/googlebot/claude-config" = { "/home/googlebot/claude-config" = {
hostPath = "/home/googlebot/sandboxed/${name}/claude-config"; hostPath = "/home/googlebot/sandboxed/${name}/claude-config";
isReadOnly = false; isReadOnly = false;
}; };
# Share credentials from host (read-only)
"/home/googlebot/claude-config/.credentials.json" = {
hostPath = "/home/googlebot/.claude/.credentials.json";
isReadOnly = true;
};
}; };
config = { config, lib, pkgs, ... }: { config = { config, lib, pkgs, ... }: {
@@ -65,6 +71,8 @@ in
]; ];
networking.useHostResolvConf = false; networking.useHostResolvConf = false;
nixpkgs.config.allowUnfree = true;
}; };
}) })
containerWorkspaces; containerWorkspaces;

View File

@@ -118,40 +118,79 @@ in
cfg.workspaces); cfg.workspaces);
# Automatically generate SSH host keys and directories for all workspaces # Automatically generate SSH host keys and directories for all workspaces
systemd.services = lib.mapAttrs' systemd.services = lib.mkMerge [
(name: ws: # Create credentials-only directory for VMs (symlinks to actual credentials)
let {
serviceName = if ws.type == "vm" then "microvm@${name}" else "container@${name}"; claude-credentials-dir = {
in description = "Setup Claude credentials directory for VM workspaces";
lib.nameValuePair "workspace-${name}-setup" {
description = "Setup directories and SSH keys for workspace ${name}";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
before = [ "${serviceName}.service" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
User = "googlebot";
Group = "users";
}; };
script = '' script = ''
# Create directories if they don't exist mkdir -p /home/googlebot/.claude-credentials
mkdir -p /home/googlebot/sandboxed/${name}/workspace # Copy credentials file (not symlink - virtiofs can't follow host symlinks)
mkdir -p /home/googlebot/sandboxed/${name}/ssh-host-keys if [ -f /home/googlebot/.claude/.credentials.json ]; then
mkdir -p /home/googlebot/sandboxed/${name}/claude-config cp /home/googlebot/.claude/.credentials.json /home/googlebot/.claude-credentials/.credentials.json
chmod 600 /home/googlebot/.claude-credentials/.credentials.json
# Fix ownership
chown -R googlebot:users /home/googlebot/sandboxed/${name}
# Generate SSH host key if it doesn't exist
if [ ! -f /home/googlebot/sandboxed/${name}/ssh-host-keys/ssh_host_ed25519_key ]; then
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" \
-f /home/googlebot/sandboxed/${name}/ssh-host-keys/ssh_host_ed25519_key
chown googlebot:users /home/googlebot/sandboxed/${name}/ssh-host-keys/ssh_host_ed25519_key*
echo "Generated SSH host key for workspace ${name}"
fi fi
''; '';
} };
) }
cfg.workspaces; # Per-workspace setup services
(lib.mapAttrs'
(name: ws:
let
serviceName = if ws.type == "vm" then "microvm@${name}" else "container@${name}";
claudeConfig = builtins.toJSON {
hasCompletedOnboarding = true;
theme = "dark";
projects = {
"/home/googlebot/workspace" = {
hasTrustDialogAccepted = true;
};
};
};
in
lib.nameValuePair "workspace-${name}-setup" {
description = "Setup directories and SSH keys for workspace ${name}";
wantedBy = [ "multi-user.target" ];
before = [ "${serviceName}.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
# Create directories if they don't exist
mkdir -p /home/googlebot/sandboxed/${name}/workspace
mkdir -p /home/googlebot/sandboxed/${name}/ssh-host-keys
mkdir -p /home/googlebot/sandboxed/${name}/claude-config
# Fix ownership
chown -R googlebot:users /home/googlebot/sandboxed/${name}
# Generate SSH host key if it doesn't exist
if [ ! -f /home/googlebot/sandboxed/${name}/ssh-host-keys/ssh_host_ed25519_key ]; then
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" \
-f /home/googlebot/sandboxed/${name}/ssh-host-keys/ssh_host_ed25519_key
chown googlebot:users /home/googlebot/sandboxed/${name}/ssh-host-keys/ssh_host_ed25519_key*
echo "Generated SSH host key for workspace ${name}"
fi
# Create claude-code config to skip onboarding and trust ~/workspace
if [ ! -f /home/googlebot/sandboxed/${name}/claude-config/.claude.json ]; then
echo '${claudeConfig}' > /home/googlebot/sandboxed/${name}/claude-config/.claude.json
chown googlebot:users /home/googlebot/sandboxed/${name}/claude-config/.claude.json
fi
'';
}
)
cfg.workspaces)
];
}; };
} }