1 line
9.2 KiB
JSON
1 line
9.2 KiB
JSON
{"repo": "canyonroad/agentsh-runloop", "files": {"config.yaml": {"6c2a69a532673dd37a5f7541bf396d25fd99f477": {"t": 1785674471.1911724, "n": 8841, "h": "8851aa0d73be227e", "c": "# agentsh server configuration for Runloop Devboxes\n# Based on: agentsh detect (v0.20.3) | Security Mode: minimal (65/100)\n# Platform: linux | Kernel: 6.18.5 | Runloop harness: 70 pass / 3 platform-limited\n#\n# Detected capabilities on Runloop (agentsh detect, v0.20.3):\n# ✓ seccomp_user_notify + seccomp-execve + seccomp-notify (file) — ONLY with\n# the unix_sockets wrapper DISABLED (see seccomp section); the wrapper and\n# agentsh's own NEW_LISTENER fight over the single listener slot (EBUSY).\n# ✓ seccomp BPF (basic), capabilities_drop (41/41)\n# ✗ fuse (not mounting, /dev/fuse 0600), cgroups-v2 (no pids controller)\n# ✗ ebpf (no BTF), landlock-network, ptrace (no CAP_SYS_PTRACE)\n#\n# Enforcement: agentsh installs the seccomp_user_notify listener itself, giving\n# real execve + openat interception for SERVER-routed commands — so network\n# policy (exfil/SSRF/metadata) and command policy are genuinely enforced.\n# Combined with seccomp BPF (signals/syscalls), chmod'd priv-esc binaries,\n# BASH_ENV builtin disabling, capability drop, and the shell shim.\n# NOTE: Runloop launches the shim directly, so the shim's OWN process is not\n# under the listener — shell-RC writes via redirect and sh-builtin signals are\n# not seccomp-policed (see README \"Known limitations on Runloop\").\n\nserver:\n http:\n addr: \"127.0.0.1:18080\"\n read_timeout: \"30s\"\n write_timeout: \"60s\"\n max_request_size: \"10MB\"\n grpc:\n enabled: true\n addr: \"127.0.0.1:9090\"\n\nauth:\n type: \"none\"\n\nlogging:\n level: \"info\"\n format: \"text\"\n output: \"stderr\"\n\nsessions:\n base_dir: \"/var/lib/agentsh/sessions\"\n max_sessions: 100\n default_timeout: \"1h\"\n default_idle_timeout: \"15m\"\n cleanup_interval: \"5m\"\n\naudit:\n enabled: true\n storage:\n sqlite_path: \"/var/lib/agentsh/events.db\"\n\nsandbox:\n enabled: true\n allow_degraded: true\n\n limits:\n max_memory_mb: 4096\n max_cpu_percent: 100\n max_processes: 256\n\n # FUSE: /dev/fuse exists on Runloop Devboxes but has root-only permissions\n # (crw------- 0600). Since the agent user has passwordless sudo, deferred\n # mode chmod's the device to world-accessible on first session start.\n # This enables VFS-level file interception, soft-delete, and redirect.\n fuse:\n enabled: true\n deferred: true\n deferred_enable_command: [\"sudo\", \"/bin/chmod\", \"666\", \"/dev/fuse\"]\n\n network:\n enabled: true\n intercept_mode: \"all\"\n proxy_listen_addr: \"127.0.0.1:0\"\n\n # cgroups_v2: DISABLED on Runloop (v0.18.0+).\n # The v0.18.0 capability probe is stricter and requires proper cgroup\n # delegation (subtree_control writable, leaf-move on EBUSY). Runloop's\n # cgroup setup doesn't satisfy this, so enforcement is unavailable and\n # the probe fails closed. Resource limits are still enforced by Runloop.\n cgroups:\n enabled: false\n\n # Seccomp: BPF filter + seccomp_user_notify both work on Runloop — but ONLY\n # when agentsh installs the user-notify listener itself. Do NOT enable the\n # unix_sockets wrapper (see below): it grabs the single NEW_LISTENER slot so\n # agentsh's own install fails with EBUSY, dropping to minimal mode (15/100).\n # With unix_sockets disabled, detect reports seccomp-notify + seccomp-execve\n # active (65/100): real openat/stat and execve interception.\n seccomp:\n enabled: true\n # shellc.opaque (v0.20.2, #386): how the shell shim treats an unparseable\n # `bash -c`/`sh -c` script body. Runloop's execute_sync spawns the shim as\n # `/bin/bash -lc \"<cmd>\"` DIRECTLY, so the shim's own process is not under\n # the agentsh server's user-notify filter — it cannot police the execs\n # inside an opaque script (anything with a pipe, redirect, `&&`, `$()`, or\n # nested `bash -c`).\n # deny — always reject (rule=shellc-opaque-script)\n # enforce — police-if-interception-active, else fail closed. On Runloop\n # this denies EVERY opaque command (curl ... | head, echo > f,\n # `agentsh detect 2>&1`, etc.) because the shim isn't traced.\n # allow — run opaque scripts. REQUIRED here. Parseable single commands\n # are still routed through policy by the shim; the inner execs\n # of opaque scripts fall back to the kernel/chmod/BASH_ENV/DAC\n # layers. (file_monitor/network proxy only cover server-spawned\n # processes, which the directly-launched shim children are not.)\n shellc:\n opaque: allow\n file_monitor:\n enabled: true\n enforce_without_fuse: true\n syscalls:\n on_block: \"errno\"\n block:\n # Default dangerous syscalls\n - ptrace\n - process_vm_readv\n - process_vm_writev\n - personality\n - mount\n - umount2\n - pivot_root\n - reboot\n - kexec_load\n - init_module\n - finit_module\n - delete_module\n - sethostname\n - setdomainname\n # Signal syscalls — blocks dash builtin kill at kernel level\n - kill\n - tgkill\n - tkill\n\n # unix_sockets wrapper (agentsh-unixwrap): DISABLED on the v0.20.x line.\n # On v0.16–v0.18 this wrapper was the mechanism that made seccomp_user_notify\n # work. Since v0.20.2 agentsh installs the user-notify NEW_LISTENER itself,\n # and the wrapper now CONFLICTS: it occupies the single listener slot, so\n # agentsh's install fails with \"device or resource busy (errno 16)\" and the\n # server falls back to minimal mode. Disabling it lets agentsh own the\n # listener directly — restoring seccomp-notify (file) + seccomp-execve\n # (command) interception.\n unix_sockets:\n enabled: false\n\n # Ptrace: disabled. ptrace enforcement needs CAP_SYS_PTRACE, which Runloop\n # does not grant (raw PTRACE_TRACEME returns EPERM; the agentsh ptrace backend\n # stays inactive). Not needed — seccomp_user_notify is the active backend.\n ptrace:\n enabled: false\n\n # env_inject: Inject environment variables into spawned commands.\n # BASH_ENV disables dangerous bash builtins (kill, enable, ulimit, etc.) as a\n # defense-in-depth layer alongside the seccomp_user_notify interception.\n env_inject:\n BASH_ENV: \"/usr/lib/agentsh/bash_startup.sh\"\n\nproxy:\n mode: \"embedded\"\n port: 0\n rate_limits:\n enabled: true\n requests_per_minute: 60\n tokens_per_minute: 90000\n providers:\n anthropic: \"https://api.anthropic.com\"\n openai: \"https://api.openai.com\"\n\n# DLP: Redact sensitive data before it reaches AI models\ndlp:\n mode: \"redact\"\n patterns:\n email: true\n phone: true\n credit_card: true\n ssn: true\n api_keys: true\n custom_patterns:\n # Runloop API keys\n - name: runloop_key\n display: RUNLOOP_KEY\n regex: \"rl_[a-zA-Z0-9]{32,}\"\n # OpenAI keys\n - name: openai_key\n display: OPENAI_KEY\n regex: \"sk-[a-zA-Z0-9]{48,}\"\n # Anthropic keys\n - name: anthropic_key\n display: ANTHROPIC_KEY\n regex: \"sk-ant-[a-zA-Z0-9-]{95,}\"\n # AWS access keys\n - name: aws_access_key\n display: AWS_KEY\n regex: \"AKIA[0-9A-Z]{16}\"\n # GitHub tokens\n - name: github_pat\n display: GITHUB_TOKEN\n regex: \"ghp_[a-zA-Z0-9]{36}\"\n - name: github_oauth\n display: GITHUB_OAUTH\n regex: \"gho_[a-zA-Z0-9]{36}\"\n # JWT tokens\n - name: jwt_token\n display: JWT\n regex: \"eyJ[a-zA-Z0-9_-]*\\\\.eyJ[a-zA-Z0-9_-]*\\\\.[a-zA-Z0-9_-]*\"\n # Private keys\n - name: private_key\n display: PRIVATE_KEY\n regex: \"-----BEGIN [A-Z]+ PRIVATE KEY-----\"\n # Slack tokens\n - name: slack_token\n display: SLACK_TOKEN\n regex: \"xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}\"\n\nthreat_feeds:\n enabled: true\n action: deny\n feeds:\n - name: urlhaus\n url: https://urlhaus.abuse.ch/downloads/hostfile/\n format: hostfile\n sync_interval: 6h\n\npackage_checks:\n enabled: true\n scope: new_packages_only\n providers:\n osv:\n enabled: true\n timeout: 10s\n on_failure: warn\n\npolicies:\n dir: \"/etc/agentsh/policies\"\n default_policy: \"default\"\n\n# Capability drop: Remove dangerous capabilities from spawned commands.\n# Uses allowlist approach — only listed capabilities are kept, all others\n# are dropped from the bounding set via prctl(PR_CAPBSET_DROP).\n# Always dropped (cannot be allowed): CAP_SYS_ADMIN, CAP_SYS_PTRACE,\n# CAP_SYS_MODULE, CAP_DAC_OVERRIDE, CAP_SETUID, CAP_SETGID, CAP_CHOWN,\n# CAP_FOWNER, CAP_MKNOD, CAP_NET_ADMIN, CAP_SYS_CHROOT, etc.\n# Default dropped (can be allowed): CAP_KILL, CAP_NET_RAW, CAP_NET_BIND_SERVICE.\n# This is a kernel-level safety net — even if ptrace races and sudo executes,\n# it can't escalate privileges without CAP_SETUID.\ncapabilities:\n allow: []\n\napprovals:\n enabled: false\n mode: \"async\"\n timeout: \"5m\"\n\nmetrics:\n enabled: true\n path: \"/metrics\"\n\nhealth:\n path: \"/health\"\n readiness_path: \"/ready\"\n\ndevelopment:\n disable_auth: true\n verbose_errors: false\n"}}}} |