Temporary SSH Access for AI Agent Support Defense-in-depth temporary SSH access for AI agents — combines certificate TTL, OS account expiration, command-restricted dispatch, and automated cleanup. Zero session artifacts survive — all agent accounts, keys, certificates, dispatch shells, and cleanup timers are destroyed on session end or TTL expiry. The CA private key is a persistent operator-side credential requiring separate protection (see Security Considerations). Platform: Linux (requires standard user-management tools). Prerequisites Scripts check for required tools at startup and report…

\\\\n'* ]] || [[ \"\\${CMD}\" == *\\

Temporary SSH Access for AI Agent Support Defense-in-depth temporary SSH access for AI agents — combines certificate TTL, OS account expiration, command-restricted dispatch, and automated cleanup. Zero session artifacts survive — all agent accounts, keys, certificates, dispatch shells, and cleanup timers are destroyed on session end or TTL expiry. The CA private key is a persistent operator-side credential requiring separate protection (see Security Considerations). Platform: Linux (requires standard user-management tools). Prerequisites Scripts check for required tools at startup and report…

\\\\r'* ]]; then\n log_event \"BLOCKED\"\n printf 'ERROR: Newlines are not allowed.\\n'\n exit 1\nfi\n\nread -ra PARTS \u003c\u003c\u003c \"\\${CMD}\"\n[[ \\${#PARTS[@]} -gt 0 ]] || { log_event \"DENIED\"; printf 'ERROR: Empty command.\\n'; exit 1; }\n\nCOMMAND=\"\\${PARTS[0]}\"\nARGS=()\nif [[ \\${#PARTS[@]} -gt 1 ]]; then\n ARGS=(\"\\${PARTS[@]:1}\")\nfi\n\nif [[ \"\\${COMMAND}\" == /* ]] || [[ \"\\${COMMAND}\" == ./* ]] || [[ \"\\${COMMAND}\" == ../* ]]; then\n log_event \"BLOCKED\"\n printf 'ERROR: Commands must be specified by name, not by path.\\n'\n exit 1\nfi\n\nfor arg in \"\\${ARGS[@]+\"\\${ARGS[@]}\"}\"; do\n if [[ \"\\${arg}\" == *\"..\"* ]]; then\n log_event \"BLOCKED\"\n printf 'ERROR: Path traversal (..) is not allowed.\\n'\n exit 1\n fi\ndone\n\nrun_cmd() {\n log_event \"EXEC\"\n if [[ \\${#ARGS[@]} -eq 0 ]]; then\n timeout 300 \"\\${COMMAND}\"\n else\n timeout 300 \"\\${COMMAND}\" \"\\${ARGS[@]}\"\n fi\n}\n\ncheck_paths() {\n local -n allowed_prefixes=\"\\${1}\"\n shift\n for arg in \"\\$@\"; do\n [[ \"\\${arg}\" == -* ]] && continue\n if [[ \"\\${arg}\" == /* ]]; then\n local resolved\n resolved=\\$(readlink -f \"\\${arg}\" 2>/dev/null || printf '%s' \"\\${arg}\")\n local matched=false\n for prefix in \"\\${allowed_prefixes[@]}\"; do\n if [[ \"\\${resolved}\" == \"\\${prefix}\"* ]]; then\n matched=true\n break\n fi\n done\n if [[ \"\\${matched}\" == false ]]; then\n log_event \"BLOCKED\"\n printf 'ERROR: Path '\\''%s'\\'' outside allowed directories.\\n' \"\\${arg}\"\n exit 1\n fi\n fi\n done\n}\nSHELL_COMMON\n\n case \"${target_profile}\" in\n diagnostic)\n cat >> \"${shell_path}\" \u003c\u003c'DIAG_END'\n\nALLOWED_READ_PATHS=(\"/var/log/\" \"/proc/\" \"/sys/\" \"/run/\" \"/tmp/\")\n\ndispatch() {\n local sub\n case \"${COMMAND}\" in\n uptime|hostname|whoami|id|w|uname|df|free|lsblk|mount|ps|journalctl|dmesg|ss|netstat|lsof)\n run_cmd ;;\n dig|nslookup|ping|traceroute|mtr)\n run_cmd ;;\n top)\n if [[ \" ${ARGS[*]:-} \" == *\" -b \"* ]] || [[ \"${ARGS[0]:-}\" == \"-bn\"* ]]; then\n run_cmd\n else\n log_event \"DENIED\"; printf 'ERROR: '\\''top'\\'' requires -b (batch mode).\\n'; exit 1\n fi ;;\n cat|head|tail|less|wc|grep|ls)\n check_paths ALLOWED_READ_PATHS \"${ARGS[@]}\"\n run_cmd ;;\n systemctl)\n sub=\"${ARGS[0]:-}\"\n case \"${sub}\" in\n status|list-units|is-active|is-enabled|show) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''systemctl %s'\\'' not allowed in diagnostic.\\n' \"${sub}\"; exit 1 ;;\n esac ;;\n ip)\n sub=\"${ARGS[0]:-}\"\n case \"${sub}\" in\n addr|route|link|neigh|rule) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''ip %s'\\'' not allowed.\\n' \"${sub}\"; exit 1 ;;\n esac ;;\n docker)\n sub=\"${ARGS[0]:-}\"\n case \"${sub}\" in\n ps|logs|stats|inspect|images|info|version|top) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''docker %s'\\'' not allowed in diagnostic.\\n' \"${sub}\"; exit 1 ;;\n esac ;;\n nginx)\n case \"${ARGS[0]:-}\" in\n -t|-T|-v) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: Only '\\''nginx -t/-T/-v'\\'' allowed.\\n'; exit 1 ;;\n esac ;;\n apache2ctl|apachectl)\n case \"${ARGS[0]:-}\" in\n -t|-S|-v) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: Only config test/status allowed.\\n'; exit 1 ;;\n esac ;;\n *)\n log_event \"DENIED\"\n printf 'ERROR: '\\''%s'\\'' not in allowlist for diagnostic profile.\\n' \"${COMMAND}\"\n exit 1 ;;\n esac\n}\ndispatch\nDIAG_END\n ;;\n\n remediation)\n cat >> \"${shell_path}\" \u003c\u003c'REMED_END'\n\nALLOWED_READ_PATHS=(\"/var/log/\" \"/proc/\" \"/sys/\" \"/run/\" \"/tmp/\" \"/etc/\" \"/home/\")\nALLOWED_WRITE_PATHS=(\"/etc/\" \"/var/\" \"/tmp/\" \"/home/\")\n\ndispatch() {\n local sub\n case \"${COMMAND}\" in\n uptime|hostname|whoami|id|w|uname|df|free|lsblk|mount|ps|kill|pkill|journalctl|dmesg|ss|netstat|lsof|curl|wget)\n run_cmd ;;\n dig|nslookup|ping|traceroute|mtr)\n run_cmd ;;\n top)\n if [[ \" ${ARGS[*]:-} \" == *\" -b \"* ]] || [[ \"${ARGS[0]:-}\" == \"-bn\"* ]]; then\n run_cmd\n else\n log_event \"DENIED\"; printf 'ERROR: '\\''top'\\'' requires -b (batch mode).\\n'; exit 1\n fi ;;\n cat|head|tail|less|wc|grep|sed|awk|ls)\n check_paths ALLOWED_READ_PATHS \"${ARGS[@]}\"\n run_cmd ;;\n cp|mv|mkdir|chmod|chown)\n check_paths ALLOWED_WRITE_PATHS \"${ARGS[@]}\"\n run_cmd ;;\n systemctl)\n sub=\"${ARGS[0]:-}\"\n case \"${sub}\" in\n status|list-units|is-active|is-enabled|show|restart|start|stop|reload|daemon-reload) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''systemctl %s'\\'' not allowed.\\n' \"${sub}\"; exit 1 ;;\n esac ;;\n ip)\n sub=\"${ARGS[0]:-}\"\n case \"${sub}\" in\n addr|route|link|neigh|rule) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''ip %s'\\'' not allowed.\\n' \"${sub}\"; exit 1 ;;\n esac ;;\n docker)\n sub=\"${ARGS[0]:-}\"\n case \"${sub}\" in\n ps|logs|stats|inspect|images|info|version|top|restart|stop|start|exec) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''docker %s'\\'' not allowed.\\n' \"${sub}\"; exit 1 ;;\n esac ;;\n nginx)\n case \"${ARGS[0]:-}\" in\n -t|-T|-v|-s) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''nginx %s'\\'' not allowed.\\n' \"${ARGS[*]:-}\"; exit 1 ;;\n esac ;;\n apache2ctl|apachectl)\n case \"${ARGS[0]:-}\" in\n -t|-S|-v|graceful|restart) run_cmd ;;\n *) log_event \"DENIED\"; printf 'ERROR: '\\''apache %s'\\'' not allowed.\\n' \"${ARGS[*]:-}\"; exit 1 ;;\n esac ;;\n *)\n log_event \"DENIED\"\n printf 'ERROR: '\\''%s'\\'' not in allowlist for remediation profile.\\n' \"${COMMAND}\"\n exit 1 ;;\n esac\n}\ndispatch\nREMED_END\n ;;\n esac\n\n chmod 755 \"${shell_path}\"\n}\n\nif [[ \"${profile}\" != \"full\" ]]; then\n generate_support_shell \"${profile}\" \"${support_shell}\" \"${log_file}\"\n _cleanup_support_shell=\"${support_shell}\"\n printf ' Restricted shell installed at %s\\n' \"${support_shell}\"\nelse\n printf ' Profile '\\''full'\\'': no command restrictions (use with extreme caution)\\n'\nfi\n\nprintf '[3/6] Setting up key material...\\n'\n\npubkey_path=\"\"\n\nif [[ -n \"${agent_pubkey}\" ]]; then\n pubkey_path=\"${agent_pubkey}\"\n printf ' Using agent-provided public key: %s\\n' \"${agent_pubkey}\"\nelse\n ssh-keygen -t ed25519 -N \"\" -f \"${key_path}\" -C \"agent-support-${session_id}\" -q\n _cleanup_key_path=\"${key_path}\"\n chmod 600 \"${key_path}\"\n chmod 644 \"${key_path}.pub\"\n pubkey_path=\"${key_path}.pub\"\n printf ' Generated session key pair: %s\\n' \"${key_path}\"\n warn \"You must securely deliver ${key_path} to the AI agent.\"\nfi\n\nprintf '[4/6] Configuring authentication...\\n'\n\ncert_options=\"\"\nif [[ \"${with_pty}\" == false ]]; then\n cert_options=\"-O no-pty\"\nfi\nif [[ \"${profile}\" != \"full\" ]]; then\n cert_options=\"${cert_options} -O force-command=${support_shell}\"\nfi\n\nif [[ \"${use_ca}\" == true ]]; then\n ca_private=\"${ca_dir}/${ca_name}\"\n if [[ ! -f \"${ca_private}\" ]]; then\n userdel -r \"${agent_user}\" 2>/dev/null || true\n die \"CA private key not found at ${ca_private}. Run setup-ca.sh first, or use --no-ca.\"\n fi\n\n # shellcheck disable=SC2086\n if ! ssh-keygen -s \"${ca_private}\" \\\n -I \"agent-support-${session_id}\" \\\n -n \"${agent_user}\" \\\n -V \"+${duration}\" \\\n -O no-port-forwarding \\\n -O no-x11-forwarding \\\n -O no-agent-forwarding \\\n ${cert_options} \\\n -q \"${pubkey_path}\"; then\n userdel -r \"${agent_user}\" 2>/dev/null || true\n rm -f \"${support_shell}\" \"${key_path}\" \"${key_path}.pub\" 2>/dev/null || true\n die \"Certificate signing failed. Account and artifacts cleaned up.\"\n fi\n\n cert_path=\"${pubkey_path%.pub}-cert.pub\"\n printf ' Certificate signed: %s (valid: +%s)\\n Principals: %s\\n' \"${cert_path}\" \"${duration}\" \"${agent_user}\"\nelse\n expiry_timestamp=$(date -u -d \"+${duration_secs} seconds\" +%Y%m%d%H%M%S 2>/dev/null || \\\n date -u -v+\"${duration_secs}\"S +%Y%m%d%H%M%S 2>/dev/null)\n\n ak_options=\"expiry-time=\\\"${expiry_timestamp}\\\",restrict\"\n if [[ \"${profile}\" != \"full\" ]]; then\n ak_options=\"${ak_options},command=\\\"${support_shell}\\\"\"\n fi\n if [[ \"${with_pty}\" == true ]]; then\n ak_options=\"${ak_options},pty\"\n fi\n\n printf '%s %s\\n' \"${ak_options}\" \"$(cat \"${pubkey_path}\")\" > \"${agent_home}/.ssh/authorized_keys\"\n chmod 600 \"${agent_home}/.ssh/authorized_keys\"\n chown \"${agent_user}:${agent_user}\" \"${agent_home}/.ssh/authorized_keys\"\n chattr +i \"${agent_home}/.ssh/authorized_keys\" 2>/dev/null || true\n chattr +i \"${agent_home}/.ssh\" 2>/dev/null || true\n\n printf ' authorized_keys configured with expiry: %s UTC\\n' \"${expiry_timestamp}\"\nfi\n\nprintf '[5/6] Scheduling automatic cleanup...\\n'\n\nshred_cmd=\"shred -u\"\ncommand -v shred &>/dev/null || shred_cmd=\"rm -f\"\n\ncat > \"${cleanup_script}\" \u003c\u003cCLEANUP\n#!/usr/bin/env bash\nset -uo pipefail\n\nprintf '[%s] SESSION_END: ${session_id} reason=ttl_expired\\n' \"\\$(date -u +%Y-%m-%dT%H:%M:%SZ)\" >> \"${log_file}\"\n\npkill -u \"${agent_user}\" 2>/dev/null || true\nsleep 2\npkill -9 -u \"${agent_user}\" 2>/dev/null || true\n\nchattr -i \"${agent_home}/.ssh/authorized_keys\" 2>/dev/null || true\nchattr -i \"${agent_home}/.ssh\" 2>/dev/null || true\n\nmkdir -p /var/log/agent-support-archive\ngrep \"${session_id}\" \"${log_file}\" > \"/var/log/agent-support-archive/${session_id}.log\" 2>/dev/null || true\n\nuserdel -r \"${agent_user}\" 2>/dev/null || true\n${shred_cmd} \"${key_path}\" \"${key_path}.pub\" \"${key_path}-cert.pub\" 2>/dev/null || true\nrm -f \"${support_shell}\"\nrm -f \"\\${0}\"\n\nprintf '[%s] CLEANUP_COMPLETE: ${session_id}\\n' \"\\$(date -u +%Y-%m-%dT%H:%M:%SZ)\" >> \"${log_file}\"\nCLEANUP\n\nchmod 700 \"${cleanup_script}\"\n_cleanup_cleanup_script=\"${cleanup_script}\"\n\ncleanup_delay_mins=$(( (duration_secs + 600 + 59) / 60 ))\n\nif command -v at &>/dev/null; then\n printf 'bash %s\\n' \"${cleanup_script}\" | at \"now + ${cleanup_delay_mins} minutes\" 2>&1 | tail -1\n printf ' Cleanup scheduled via at(1) in %d minutes\\n' \"${cleanup_delay_mins}\"\nelif command -v systemd-run &>/dev/null; then\n systemd-run --on-active=\"$((cleanup_delay_mins * 60))s\" \\\n --unit=\"agent-cleanup-${session_id}\" \\\n --description=\"Cleanup agent support session ${session_id}\" \\\n bash \"${cleanup_script}\"\n printf ' Cleanup scheduled via systemd timer in %d minutes\\n' \"${cleanup_delay_mins}\"\nelse\n printf ' WARNING: No scheduler available. Manual cleanup required:\\n'\n printf ' sudo bash %s\\n' \"${cleanup_script}\"\nfi\n\nprintf '[%s] SESSION_START: %s user=%s host=%s ttl=%s profile=%s\\n' \\\n \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"${session_id}\" \"${agent_user}\" \\\n \"${target_host}\" \"${duration}\" \"${profile}\" >> \"${log_file}\"\n\nprintf '[6/6] Done.\\n\\n'\nprintf '===========================================\\n'\nprintf ' Temporary SSH Access Granted\\n'\nprintf '===========================================\\n'\nprintf ' Session ID: %s\\n' \"${session_id}\"\nprintf ' Target Host: %s\\n' \"${target_host}\"\nprintf ' Username: %s\\n' \"${agent_user}\"\nprintf ' Duration: %s (%ds)\\n' \"${duration}\" \"${duration_secs}\"\nprintf ' Profile: %s\\n' \"${profile}\"\n\nexpiry_time=$(date -u -d \"+${duration_secs} seconds\" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || \\\n date -u -v+\"${duration_secs}\"S +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)\nprintf ' Expires: %s\\n' \"${expiry_time}\"\n\nif [[ -n \"${agent_pubkey}\" ]]; then\n printf '\\n Agent connects with their own key:\\n'\n printf ' ssh -i \u003cagent-private-key> %s@%s\\n' \"${agent_user}\" \"${target_host}\"\n if [[ \"${use_ca}\" == true ]]; then\n printf '\\n Deliver this certificate to the agent:\\n %s\\n' \"${cert_path}\"\n fi\nelse\n printf '\\n Private Key: %s\\n' \"${key_path}\"\n if [[ \"${use_ca}\" == true ]]; then\n printf ' Certificate: %s-cert.pub\\n' \"${key_path}\"\n fi\n printf '\\n IMPORTANT: Securely deliver the private key to the AI agent.\\n'\n printf ' Connect with:\\n ssh -i %s %s@%s\\n' \"${key_path}\" \"${agent_user}\" \"${target_host}\"\nfi\n\nprintf '\\n Example commands:\\n'\nprintf ' ssh ... %s@%s '\\''uptime'\\''\\n' \"${agent_user}\" \"${target_host}\"\nprintf ' ssh ... %s@%s '\\''df -h'\\''\\n' \"${agent_user}\" \"${target_host}\"\nprintf ' ssh ... %s@%s '\\''journalctl -n 50 --no-pager'\\''\\n' \"${agent_user}\" \"${target_host}\"\nprintf '\\n Revoke immediately:\\n'\nprintf ' sudo bash scripts/revoke-access.sh --session %s\\n' \"${session_id}\"\nprintf '===========================================\\n'\n\n# Success — disable failure cleanup trap\ntrap - EXIT\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":24279,"content_sha256":"0d373159da459ee9d891d185b7d209f8ac5d4a5128a35764c4b13723246abb2f"},{"filename":"scripts/revoke-access.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nreadonly LOG_FILE=\"/var/log/agent-support.log\"\nreadonly ARCHIVE_DIR=\"/var/log/agent-support-archive\"\n\nshred_cmd=\"shred -u\"\ncommand -v shred &>/dev/null || shred_cmd=\"rm -f\"\n\nsession_id=\"\"\nagent_user=\"\"\nrevoke_all=false\n\ndie() { printf 'ERROR: %s\\n' \"${1}\" >&2; exit 1; }\n\nusage() {\n cat \u003c\u003cEOF\nUsage: $(basename \"${0}\") [OPTIONS]\n\nImmediately revoke temporary SSH access for an AI agent.\n\nOptions:\n --session ID Revoke by session ID\n --user NAME Revoke by account username\n --all Revoke ALL active agent support sessions\n --help Show this help message\nEOF\n exit 0\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"${1}\" in\n --session) [[ $# -ge 2 ]] || die \"--session requires a value\"; session_id=\"${2}\"; shift 2 ;;\n --user) [[ $# -ge 2 ]] || die \"--user requires a value\"; agent_user=\"${2}\"; shift 2 ;;\n --all) revoke_all=true; shift ;;\n --help) usage ;;\n *) die \"Unknown option: ${1}\" ;;\n esac\ndone\n\n[[ -n \"${session_id}\" || -n \"${agent_user}\" || \"${revoke_all}\" == true ]] || die \"Specify --session, --user, or --all\"\n[[ ${EUID} -eq 0 ]] || die \"This script must be run as root (sudo)\"\n\nfor cmd in usermod userdel pkill getent; do\n command -v \"${cmd}\" &>/dev/null || die \"Required command '${cmd}' not found\"\ndone\n\nresolve_user_from_session() {\n grep \"AI Agent Support \\[${1}\\]\" /etc/passwd 2>/dev/null | cut -d: -f1 || true\n}\n\nresolve_session_from_user() {\n local gecos\n gecos=$(getent passwd \"${1}\" 2>/dev/null | cut -d: -f5)\n printf '%s' \"$(printf '%s' \"${gecos}\" | sed -n 's/.*\\[\\([^]]*\\)\\].*/\\1/p')\"\n}\n\nrevoke_single() {\n local target_user=\"${1}\"\n local target_sid=\"${2:-unknown}\"\n\n printf '%s Revoking access for: %s (session: %s) ---\\n' '---' \"${target_user}\" \"${target_sid}\"\n\n printf ' [1/7] Locking account...\\n'\n usermod -L \"${target_user}\" 2>/dev/null && printf ' Locked.\\n' || printf ' Already locked or not found.\\n'\n\n printf ' [2/7] Terminating active sessions...\\n'\n if pkill -u \"${target_user}\" 2>/dev/null; then\n sleep 2\n pkill -9 -u \"${target_user}\" 2>/dev/null || true\n printf ' Sessions terminated.\\n'\n else\n printf ' No active sessions found.\\n'\n fi\n\n printf ' [3/7] Removing file protections...\\n'\n local target_home\n target_home=$(getent passwd \"${target_user}\" 2>/dev/null | cut -d: -f6) || target_home=\"/home/${target_user}\"\n chattr -i \"${target_home}/.ssh/authorized_keys\" 2>/dev/null || true\n chattr -i \"${target_home}/.ssh\" 2>/dev/null || true\n printf ' Done.\\n'\n\n printf ' [4/7] Archiving session logs...\\n'\n mkdir -p \"${ARCHIVE_DIR}\"\n if [[ -f \"${LOG_FILE}\" ]] && grep -q \"${target_sid}\" \"${LOG_FILE}\" 2>/dev/null; then\n grep \"${target_sid}\" \"${LOG_FILE}\" > \"${ARCHIVE_DIR}/${target_sid}.log\"\n printf ' Archived to %s/%s.log\\n' \"${ARCHIVE_DIR}\" \"${target_sid}\"\n else\n printf ' No log entries found for session.\\n'\n fi\n\n printf ' [5/7] Removing user account...\\n'\n userdel -r \"${target_user}\" 2>/dev/null && printf ' Account removed.\\n' || printf ' Account already removed.\\n'\n\n printf ' [6/7] Destroying session keys...\\n'\n local key_base=\"/tmp/agent_session_${target_sid}\"\n local destroyed=0\n for key_file in \"${key_base}\" \"${key_base}.pub\" \"${key_base}-cert.pub\"; do\n if [[ -f \"${key_file}\" ]]; then\n ${shred_cmd} \"${key_file}\" 2>/dev/null && destroyed=$((destroyed + 1))\n fi\n done\n printf ' Destroyed %d key file(s).\\n' \"${destroyed}\"\n\n printf ' [7/7] Cancelling scheduled cleanup...\\n'\n if command -v atq &>/dev/null; then\n local pending_jobs\n pending_jobs=$(atq 2>/dev/null | awk '{print $1}')\n for job_id in ${pending_jobs}; do\n if at -c \"${job_id}\" 2>/dev/null | grep -q \"${target_sid}\"; then\n atrm \"${job_id}\" 2>/dev/null\n printf ' Cancelled at job #%s.\\n' \"${job_id}\"\n fi\n done\n fi\n systemctl stop \"agent-cleanup-${target_sid}\" 2>/dev/null || true\n systemctl reset-failed \"agent-cleanup-${target_sid}\" 2>/dev/null || true\n\n for artifact in \"/usr/local/sbin/agent-cleanup-${target_sid}.sh\" \"/usr/local/bin/agent-support-shell-${target_sid}\"; do\n if [[ -f \"${artifact}\" ]]; then\n rm -f \"${artifact}\"\n printf ' Removed %s\\n' \"$(basename \"${artifact}\")\"\n fi\n done\n\n printf '[%s] SESSION_REVOKED: %s user=%s reason=manual_revoke\\n' \\\n \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"${target_sid}\" \"${target_user}\" >> \"${LOG_FILE}\"\n\n # Post-revocation verification\n printf ' [VERIFY] Checking artifact removal...\\n'\n local verify_pass=true\n if id \"${target_user}\" &>/dev/null; then\n printf ' FAIL: Account %s still exists\\n' \"${target_user}\"\n verify_pass=false\n else\n printf ' PASS: Account removed\\n'\n fi\n for vf in \"/tmp/agent_session_${target_sid}\" \"/tmp/agent_session_${target_sid}.pub\" \"/tmp/agent_session_${target_sid}-cert.pub\"; do\n if [[ -f \"${vf}\" ]]; then\n printf ' FAIL: Key file still exists: %s\\n' \"${vf}\"\n verify_pass=false\n fi\n done\n if [[ ! -f \"/tmp/agent_session_${target_sid}\" ]] && [[ ! -f \"/tmp/agent_session_${target_sid}.pub\" ]]; then\n printf ' PASS: Session keys removed\\n'\n fi\n if [[ -f \"/usr/local/bin/agent-support-shell-${target_sid}\" ]]; then\n printf ' FAIL: Support shell still exists\\n'\n verify_pass=false\n else\n printf ' PASS: Support shell removed\\n'\n fi\n if [[ -f \"/usr/local/sbin/agent-cleanup-${target_sid}.sh\" ]]; then\n printf ' FAIL: Cleanup script still exists\\n'\n verify_pass=false\n else\n printf ' PASS: Cleanup script removed\\n'\n fi\n if [[ \"${verify_pass}\" == true ]]; then\n printf ' [VERIFY] All artifacts confirmed removed.\\n'\n else\n printf ' [VERIFY] WARNING: Some artifacts could not be removed. Manual cleanup required.\\n'\n fi\n\n printf '%s Access revoked for %s ---\\n\\n' '---' \"${target_user}\"\n}\n\nif [[ \"${revoke_all}\" == true ]]; then\n printf '=== Revoking ALL Agent Support Sessions ===\\n\\n'\n\n found_users=$(getent passwd 2>/dev/null | grep '^agent_support_' | cut -d: -f1)\n [[ -n \"${found_users}\" ]] || found_users=$(grep '^agent_support_' /etc/passwd 2>/dev/null | cut -d: -f1)\n\n if [[ -z \"${found_users}\" ]]; then\n printf 'No active agent support accounts found.\\n'\n exit 0\n fi\n\n for found_user in ${found_users}; do\n revoke_single \"${found_user}\" \"$(resolve_session_from_user \"${found_user}\")\"\n done\n\n if ! grep -q '^agent_support_' /etc/passwd 2>/dev/null; then\n rm -f /usr/local/bin/agent-support-shell-* /usr/local/sbin/agent-cleanup-*.sh 2>/dev/null || true\n printf 'All session-specific files removed.\\n'\n fi\n\n printf '=== All sessions revoked ===\\n'\n\nelif [[ -n \"${session_id}\" ]]; then\n if [[ -z \"${agent_user}\" ]]; then\n agent_user=$(resolve_user_from_session \"${session_id}\")\n if [[ -z \"${agent_user}\" ]]; then\n printf 'WARNING: Could not find account for session %s.\\n' \"${session_id}\"\n printf 'The account may have already been cleaned up.\\nAttempting key destruction anyway...\\n'\n for orphan in \"/tmp/agent_session_${session_id}\" \"/tmp/agent_session_${session_id}.pub\" \"/tmp/agent_session_${session_id}-cert.pub\"; do\n ${shred_cmd} \"${orphan}\" 2>/dev/null || true\n done\n rm -f \"/usr/local/bin/agent-support-shell-${session_id}\" \"/usr/local/sbin/agent-cleanup-${session_id}.sh\" 2>/dev/null || true\n exit 1\n fi\n fi\n revoke_single \"${agent_user}\" \"${session_id}\"\n\nelif [[ -n \"${agent_user}\" ]]; then\n revoke_single \"${agent_user}\" \"$(resolve_session_from_user \"${agent_user}\")\"\nfi\n\nprintf 'Done. Review archived logs at: %s/\\n' \"${ARCHIVE_DIR}\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":7563,"content_sha256":"16db6092b66b4835a27704fa803c27ea7080e2b26b379d4c6538dd0d8c6f3759"},{"filename":"scripts/setup-ca.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nreadonly CA_NAME=\"agent_ca\"\n\nca_dir=\"/etc/ssh\"\nkey_type=\"ed25519\"\n\nusage() {\n printf 'Usage: %s [OPTIONS]\\n\\n' \"$(basename \"${0}\")\"\n printf 'Initialize an SSH Certificate Authority for signing temporary agent certificates.\\n\\n'\n printf 'Options:\\n'\n printf ' --ca-dir DIR Directory to store CA keys (default: /etc/ssh)\\n'\n printf ' --key-type TYPE Key type: ed25519 (default) or rsa\\n'\n printf ' --help Show this help message\\n'\n exit 0\n}\n\ndie() { printf 'ERROR: %s\\n' \"${1}\" >&2; exit 1; }\n\nwhile [[ $# -gt 0 ]]; do\n case \"${1}\" in\n --ca-dir) [[ $# -ge 2 ]] || die \"--ca-dir requires a value\"; ca_dir=\"${2}\"; shift 2 ;;\n --key-type) [[ $# -ge 2 ]] || die \"--key-type requires a value\"; key_type=\"${2}\"; shift 2 ;;\n --help) usage ;;\n *) die \"Unknown option: ${1}\" ;;\n esac\ndone\n\nreadonly ca_private=\"${ca_dir}/${CA_NAME}\"\nreadonly ca_public=\"${ca_dir}/${CA_NAME}.pub\"\n\n[[ \"${key_type}\" == \"ed25519\" || \"${key_type}\" == \"rsa\" ]] || die \"Key type must be 'ed25519' or 'rsa'\"\ncommand -v ssh-keygen &>/dev/null || die \"ssh-keygen is required but not found\"\n[[ ${EUID} -eq 0 ]] || die \"This script must be run as root (sudo)\"\n\nif [[ -f \"${ca_private}\" ]]; then\n printf 'WARNING: CA key already exists at %s\\n' \"${ca_private}\"\n printf 'To regenerate, first back up and remove the existing key.\\n'\n\n # Check key age — warn if older than 90 days\n if command -v stat &>/dev/null; then\n key_epoch=$(stat -c %Y \"${ca_private}\" 2>/dev/null || stat -f %m \"${ca_private}\" 2>/dev/null)\n if [[ -n \"${key_epoch:-}\" ]]; then\n now_epoch=$(date +%s)\n age_days=$(( (now_epoch - key_epoch) / 86400 ))\n if [[ ${age_days} -gt 90 ]]; then\n printf '\\nWARNING: CA key is %d days old. Consider rotating:\\n' \"${age_days}\"\n printf ' 1. Back up the current key\\n'\n printf ' 2. Remove %s and %s\\n' \"${ca_private}\" \"${ca_public}\"\n printf ' 3. Re-run this script to generate a new CA\\n'\n printf ' 4. Redistribute the new public key to all target servers\\n'\n printf ' 5. Revoke any active sessions signed with the old CA\\n'\n else\n printf '\\nCA key age: %d days (rotation recommended after 90 days)\\n' \"${age_days}\"\n fi\n fi\n fi\n\n if [[ -f \"${ca_public}\" ]]; then\n printf '\\nExisting CA public key:\\n'\n cat \"${ca_public}\"\n else\n printf '\\nWARNING: Public key missing at %s. Regenerate with:\\n' \"${ca_public}\"\n printf ' ssh-keygen -y -f %s > %s\\n' \"${ca_private}\" \"${ca_public}\"\n fi\n exit 0\nfi\n\nmkdir -p \"${ca_dir}\"\n\nprintf '=== SSH Certificate Authority Setup ===\\n'\nprintf 'Directory: %s\\nKey type: %s\\n\\n' \"${ca_dir}\" \"${key_type}\"\n\nif [[ \"${key_type}\" == \"ed25519\" ]]; then\n ssh-keygen -t ed25519 -f \"${ca_private}\" -N \"\" -C \"agent-support-ca@$(hostname)\"\nelse\n ssh-keygen -t rsa -b 4096 -f \"${ca_private}\" -N \"\" -C \"agent-support-ca@$(hostname)\"\nfi\n\nchmod 400 \"${ca_private}\"\nchmod 444 \"${ca_public}\"\n\nprintf '\\n=== CA Created Successfully ===\\n'\nprintf 'Private key: %s (chmod 400)\\nPublic key: %s\\n\\n' \"${ca_private}\" \"${ca_public}\"\nprintf 'CA Public Key (copy this to target servers):\\n---\\n'\ncat \"${ca_public}\"\nprintf '%s\\n\\n=== Next Steps ===\\n' '---'\nprintf 'On each target server:\\n'\nprintf ' 1. Copy the CA public key to /etc/ssh/agent_ca.pub\\n'\nprintf ' 2. Add to /etc/ssh/sshd_config:\\n'\nprintf ' TrustedUserCAKeys /etc/ssh/agent_ca.pub\\n'\nprintf ' 3. Recommended hardening (also in sshd_config):\\n'\nprintf ' MaxSessions 1\\n MaxAuthTries 3\\n AuthenticationMethods publickey\\n'\nprintf ' 4. Restart sshd:\\n systemctl restart sshd\\n\\n'\nprintf 'Then use grant-access.sh to provision temporary agent sessions.\\n'\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3728,"content_sha256":"36f20f5fcfbb4fbbc7b3508cfbcf5db58c746f77948ad6f9fb4de3eef60e680e"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Temporary SSH Access for AI Agent Support","type":"text"}]},{"type":"paragraph","content":[{"text":"Defense-in-depth temporary SSH access for AI agents — combines certificate TTL, OS account expiration, command-restricted dispatch, and automated cleanup. Zero session artifacts survive — all agent accounts, keys, certificates, dispatch shells, and cleanup timers are destroyed on session end or TTL expiry. The CA private key is a persistent operator-side credential requiring separate protection (see Security Considerations).","type":"text"}]},{"type":"paragraph","content":[{"text":"Platform:","type":"text","marks":[{"type":"strong"}]},{"text":" Linux (requires standard user-management tools).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"paragraph","content":[{"text":"Scripts check for required tools at startup and report what is missing. Install them on common distributions:","type":"text"}]},{"type":"paragraph","content":[{"text":"Debian / Ubuntu:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sudo apt-get update && sudo apt-get install -y openssh-client coreutils passwd at e2fsprogs procps","type":"text"}]},{"type":"paragraph","content":[{"text":"Alpine Linux:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"apk add openssh-keygen bash shadow coreutils util-linux procps at e2fsprogs","type":"text"}]},{"type":"paragraph","content":[{"text":"RHEL / CentOS / Fedora:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sudo dnf install -y openssh-clients coreutils shadow-utils at e2fsprogs procps-ng","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tool","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Package (Debian)","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Package (Alpine)","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ssh-keygen","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"openssh-client","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"openssh-keygen","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"useradd","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"userdel","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"passwd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"shadow","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"passwd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"passwd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"shadow","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pkill","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"procps","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"procps","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"getent","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"libc-bin","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"musl-utils","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"at","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"at","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"at","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"One of ","type":"text"},{"text":"at","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"systemd-run","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"shred","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"coreutils","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"coreutils","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optional (falls back to ","type":"text"},{"text":"rm","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"chattr","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"e2fsprogs","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"e2fsprogs","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optional (for immutable authorized_keys)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"If a required tool is missing, the script exits with an error listing missing dependencies. Optional tools produce warnings but do not block execution.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Target Host Requirements","type":"text"}]},{"type":"paragraph","content":[{"text":"The target server (the machine the agent will SSH into) must have:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sshd running","type":"text","marks":[{"type":"strong"}]},{"text":" and reachable on port 22 (or a configured port)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"~/.ssh/","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" directory","type":"text","marks":[{"type":"strong"}]},{"text":" for the user whose ","type":"text"},{"text":"authorized_keys","type":"text","marks":[{"type":"code_inline"}]},{"text":" will receive the agent's public key (the agent's one-liner creates this if missing)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For CA-based access: ","type":"text"},{"text":"TrustedUserCAKeys","type":"text","marks":[{"type":"code_inline"}]},{"text":" configured in ","type":"text"},{"text":"/etc/ssh/sshd_config","type":"text","marks":[{"type":"code_inline"}]},{"text":" pointing to the CA public key","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If the target is a fresh container or VM, install and start sshd first:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Alpine\napk add openssh && ssh-keygen -A && rc-service sshd start\n\n# Debian/Ubuntu\napt-get install -y openssh-server && systemctl start sshd","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"An AI agent needs temporary shell access to diagnose or remediate a remote server","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"You require auditable, time-boxed access that self-revokes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"You need to restrict what the agent can execute — no arbitrary commands","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"You want zero persistent credentials once the session ends","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Agent-Initiated Access Flow (Recommended)","type":"text"}]},{"type":"paragraph","content":[{"text":"When an AI agent determines it needs shell access, it briefly states its intent: ","type":"text"},{"text":"\"I need shell access to [target] to [reason]. I'll generate a temporary key — you decide where it goes. What username should I connect as?\"","type":"text","marks":[{"type":"em"}]},{"text":" Then:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify target is reachable","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"nc -z -w 2 TARGET 22","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or equivalent). If the port is closed, stop and inform the user: ","type":"text"},{"text":"\"Port 22 is not open on [target]. Please start sshd, then I'll continue.\"","type":"text","marks":[{"type":"em"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check for existing session","type":"text","marks":[{"type":"strong"}]},{"text":" — look for stale keys in ","type":"text"},{"text":"/tmp/agent_access_key*","type":"text","marks":[{"type":"code_inline"}]},{"text":". If found, test connectivity (","type":"text"},{"text":"ssh -i /tmp/agent_access_key ... 'echo ok'","type":"text","marks":[{"type":"code_inline"}]},{"text":"). If the key still works, skip to step 7. If expired or revoked, shred stale keys and continue.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate keypair locally","type":"text","marks":[{"type":"strong"}]},{"text":" — Ed25519, stored in ","type":"text"},{"text":"/tmp/","type":"text","marks":[{"type":"code_inline"}]},{"text":" with a session-tagged comment.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask for the connect username","type":"text","marks":[{"type":"strong"}]},{"text":" — default to the current user on the target, not root. Ask: ","type":"text"},{"text":"\"What username should I connect as? (default: your current user)\"","type":"text","marks":[{"type":"em"}]},{"text":". If the task requires root, inform the user: ","type":"text"},{"text":"\"This task requires root. Please add the key to root's authorized_keys (or use ","type":"text","marks":[{"type":"em"}]},{"text":"sudo","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":").\"","type":"text","marks":[{"type":"em"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Present public key with expiry","type":"text","marks":[{"type":"strong"}]},{"text":" — provide a one-liner with ","type":"text"},{"text":"expiry-time","type":"text","marks":[{"type":"code_inline"}]},{"text":" for crash safety. The one-liner creates ","type":"text"},{"text":"~/.ssh/","type":"text","marks":[{"type":"code_inline"}]},{"text":" if absent and is tailored to the target username:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo 'expiry-time=\"YYYYMMDDHHMMSS\" ssh-ed25519 AAAA... agent-session-SID' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys","type":"text"}]},{"type":"paragraph","content":[{"text":"expiry-time","type":"text","marks":[{"type":"code_inline"}]},{"text":" (OpenSSH 8.2+) ensures the key is server-rejected after the timestamp, even if the agent crashes and never cleans up.","type":"text"}]},{"type":"paragraph","content":[{"text":"Important:","type":"text","marks":[{"type":"strong"}]},{"text":" The one-liner must run as the target user so ","type":"text"},{"text":"~/.ssh/","type":"text","marks":[{"type":"code_inline"}]},{"text":" resolves to the correct home directory. If adding the key for a different account (e.g., root), either ","type":"text"},{"text":"su - root","type":"text","marks":[{"type":"code_inline"}]},{"text":" first or use the absolute path (e.g., ","type":"text"},{"text":"/root/.ssh/authorized_keys","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User adds the key","type":"text","marks":[{"type":"strong"}]},{"text":" — the user decides where and how to grant access (maintains control).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify connection","type":"text","marks":[{"type":"strong"}]},{"text":" — connect as the agreed username: ","type":"text"},{"text":"ssh -i /tmp/agent_access_key USER@TARGET 'echo ok'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schedule dead-man cleanup","type":"text","marks":[{"type":"strong"}]},{"text":" — immediately after connecting, schedule automatic key removal on the target:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"echo \"sed -i '/agent-session-SID/d' ~/.ssh/authorized_keys\" | at now + 4 hours","type":"text"}]},{"type":"paragraph","content":[{"text":"This fires independently of the agent. If the agent finishes early, it cancels the job and cleans up itself.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run fast recon","type":"text","marks":[{"type":"strong"}]},{"text":" — OS, key packages, ","type":"text"},{"text":"screen","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"tmux","type":"text","marks":[{"type":"code_inline"}]},{"text":" availability. Under 10 seconds.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create shared session","type":"text","marks":[{"type":"strong"}]},{"text":" — start a ","type":"text"},{"text":"screen","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"tmux","type":"text","marks":[{"type":"code_inline"}]},{"text":" session on the target so the user can attach and observe in real time.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Work","type":"text","marks":[{"type":"strong"}]},{"text":" — run diagnostics, remediation, or other tasks within the granted scope.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clean up","type":"text","marks":[{"type":"strong"}]},{"text":" — remove the public key from ","type":"text"},{"text":"authorized_keys","type":"text","marks":[{"type":"code_inline"}]},{"text":", cancel the ","type":"text"},{"text":"at","type":"text","marks":[{"type":"code_inline"}]},{"text":" job, destroy the local keypair.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Crash Safety (Simple Mode)","type":"text"}]},{"type":"paragraph","content":[{"text":"Two independent mechanisms protect against agent crashes:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Safeguard","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fires without agent?","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mechanism","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"expiry-time","type":"text","marks":[{"type":"code_inline"}]},{"text":" in authorized_keys","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Server-side — OpenSSH rejects the key after the timestamp","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scheduled ","type":"text"},{"text":"at","type":"text","marks":[{"type":"code_inline"}]},{"text":" job","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runs on target's scheduler — removes the key entry on timer","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"If the agent crashes and restarts, it reuses the stale keys from ","type":"text"},{"text":"/tmp/","type":"text","marks":[{"type":"code_inline"}]},{"text":" (step 2) rather than re-provisioning, avoiding repeated user interaction.","type":"text"}]},{"type":"paragraph","content":[{"text":"This flow requires no script transfer to the target and leaves no persistent artifacts. For stronger guarantees (certificate TTL, command restriction, scheduled cleanup), layer the CA-based workflow below on top.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Observability","type":"text"}]},{"type":"paragraph","content":[{"text":"After establishing access, the agent should create a shared terminal session:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# On the target — agent creates a named session\nscreen -dmS agent-work\nscreen -S agent-work -X multiuser on\nscreen -S agent-work -X acladd root\n\n# User attaches to watch\nscreen -x agent-work","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"screen","type":"text","marks":[{"type":"code_inline"}]},{"text":" is unavailable, ","type":"text"},{"text":"tmux","type":"text","marks":[{"type":"code_inline"}]},{"text":" works similarly:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"tmux new-session -d -s agent-work\n# User attaches:\ntmux attach -t agent-work","type":"text"}]},{"type":"paragraph","content":[{"text":"The agent should check for ","type":"text"},{"text":"screen","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"tmux","type":"text","marks":[{"type":"code_inline"}]},{"text":" availability during initial recon and install if needed (with user permission).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Architecture: Defense in Depth","type":"text"}]},{"type":"paragraph","content":[{"text":"Four independent expiration and restriction mechanisms ensure no single failure leaves access open:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Layer","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mechanism","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What It Does","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SSH Certificate TTL","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cryptographically enforced expiry (","type":"text"},{"text":"-V +Nh","type":"text","marks":[{"type":"code_inline"}]},{"text":") — server rejects expired certs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OS Account Expiration","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"useradd --expiredate","type":"text","marks":[{"type":"code_inline"}]},{"text":" — login denied after date","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Forced Command / Safe Dispatch","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exact command-name matching, path-restricted arguments, no ","type":"text"},{"text":"eval","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scheduled Cleanup","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"at","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"systemd-run","type":"text","marks":[{"type":"code_inline"}]},{"text":" timer — removes account, keys, and config after TTL","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"paragraph","content":[{"text":"All scripts run on the ","type":"text"},{"text":"operator's machine","type":"text","marks":[{"type":"strong"}]},{"text":" (the admin/signing host), not on the target server. Only public artifacts (CA public key, dispatch shell, agent account) are deployed to the target.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 1. One-time: Initialize the SSH Certificate Authority (run on admin/signing host)\nsudo bash scripts/setup-ca.sh\n\n# 2a. Preview what would happen (no changes made):\nsudo bash scripts/grant-access.sh --host TARGET_HOST --duration 4h --agent-pubkey /path/to/agent_key.pub --dry-run\n\n# 2b. Grant access using the agent's existing public key (recommended):\nsudo bash scripts/grant-access.sh --host TARGET_HOST --duration 4h --agent-pubkey /path/to/agent_key.pub\n\n# 2c. Or generate a keypair (you must securely deliver the private key to the agent):\nsudo bash scripts/grant-access.sh --host TARGET_HOST --duration 4h --allow diagnostic\n\n# 3. Revoke access: Immediately revoke before TTL expires (if needed)\nsudo bash scripts/revoke-access.sh --session SESSION_ID\n\n# 4. Audit: Scan for orphaned artifacts from previous sessions\nsudo bash scripts/audit.sh","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Detailed Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: Certificate Authority Setup (One-Time)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"scripts/setup-ca.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" once on the signing host (your admin machine, ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" the target server).","type":"text"}]},{"type":"paragraph","content":[{"text":"This script:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generates an Ed25519 CA key pair at ","type":"text"},{"text":"/etc/ssh/agent_ca","type":"text","marks":[{"type":"code_inline"}]},{"text":" (private) and ","type":"text"},{"text":"/etc/ssh/agent_ca.pub","type":"text","marks":[{"type":"code_inline"}]},{"text":" (public)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sets strict permissions (","type":"text"},{"text":"chmod 400","type":"text","marks":[{"type":"code_inline"}]},{"text":") on the private key","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Outputs the public key for distribution to target servers","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"On each target server, add to ","type":"text"},{"text":"/etc/ssh/sshd_config","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"TrustedUserCAKeys /etc/ssh/agent_ca.pub","type":"text"}]},{"type":"paragraph","content":[{"text":"Then restart sshd to trust certificates signed by your CA.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: Granting Access","type":"text"}]},{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"scripts/grant-access.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" to provision a temporary session:","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step 2a: Create Ephemeral Agent Account","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Create account with OS-level expiration (Layer 2)\n# Account suffix uses 4 bytes of entropy (8 hex chars) with collision check\nsudo useradd \\\n --expiredate \"$(date -d '+5 hours' +%Y-%m-%d)\" \\\n --shell /bin/bash \\\n --create-home \\\n --comment \"AI Agent Support [SESSION_ID]\" \\\n agent_support_XXXXXXXX","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Account name includes a random 8-hex-char suffix with collision detection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--expiredate","type":"text","marks":[{"type":"code_inline"}]},{"text":" sets an OS-level hard cutoff (rounded to midnight — a safety net, not the primary TTL)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Home directory resolved via ","type":"text"},{"text":"getent passwd","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no ","type":"text"},{"text":"eval","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step 2b: Key Material","type":"text"}]},{"type":"paragraph","content":[{"text":"Recommended: Agent provides its own public key (","type":"text","marks":[{"type":"strong"}]},{"text":"--agent-pubkey","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":")","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"The agent retains its private key — no secret transfer required. The script signs the agent's public key with the CA (or installs it in authorized_keys).","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sudo bash scripts/grant-access.sh --host TARGET --duration 4h --agent-pubkey /path/to/agent.pub","type":"text"}]},{"type":"paragraph","content":[{"text":"Alternative: Script generates a keypair","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"--agent-pubkey","type":"text","marks":[{"type":"code_inline"}]},{"text":" is omitted, the script generates an ephemeral Ed25519 keypair in ","type":"text"},{"text":"/tmp/","type":"text","marks":[{"type":"code_inline"}]},{"text":". The operator must then securely deliver the private key to the agent.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"ssh-keygen -t ed25519 -N \"\" -f /tmp/agent_session_XXXX -C \"agent-support-SESSION_ID\"","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Empty passphrase (","type":"text"},{"text":"-N \"\"","type":"text","marks":[{"type":"code_inline"}]},{"text":") — the AI agent cannot enter passphrases interactively","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keypair persists in ","type":"text"},{"text":"/tmp/","type":"text","marks":[{"type":"code_inline"}]},{"text":" until cleanup (securely deleted with ","type":"text"},{"text":"shred -u","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step 2c: Sign Certificate with TTL (Layer 1)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Sign the public key with the CA, enforcing time limit\nssh-keygen -s /etc/ssh/agent_ca \\\n -I \"agent-support-SESSION_ID\" \\\n -n agent_support_XXXXXXXX \\\n -V +4h \\\n -O no-port-forwarding \\\n -O no-x11-forwarding \\\n -O no-agent-forwarding \\\n -O no-pty \\\n -O force-command=/usr/local/bin/agent-support-shell-SESSION_ID \\\n /path/to/agent.pub","type":"text"}]},{"type":"paragraph","content":[{"text":"Certificate options explained:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-s /etc/ssh/agent_ca","type":"text","marks":[{"type":"code_inline"}]},{"text":" — sign with the CA private key","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-I \"agent-support-SESSION_ID\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" — identity string for audit logs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-n agent_support_XXXXXXXX","type":"text","marks":[{"type":"code_inline"}]},{"text":" — principal must match the account name","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-V +4h","type":"text","marks":[{"type":"code_inline"}]},{"text":" — certificate valid for exactly 4 hours (cryptographic enforcement)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-O no-port-forwarding","type":"text","marks":[{"type":"code_inline"}]},{"text":" — prevent SSH tunneling","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-O no-x11-forwarding","type":"text","marks":[{"type":"code_inline"}]},{"text":" — no GUI forwarding","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-O no-agent-forwarding","type":"text","marks":[{"type":"code_inline"}]},{"text":" — no agent key forwarding","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-O no-pty","type":"text","marks":[{"type":"code_inline"}]},{"text":" — omit if the agent needs an interactive shell (","type":"text"},{"text":"--with-pty","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"-O force-command=...","type":"text","marks":[{"type":"code_inline"}]},{"text":" — all commands routed through the safe dispatch shell","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step 2d: Command Restriction Shell (Layer 3)","type":"text"}]},{"type":"paragraph","content":[{"text":"Each session receives its own restriction shell at ","type":"text"},{"text":"/usr/local/bin/agent-support-shell-SESSION_ID","type":"text","marks":[{"type":"code_inline"}]},{"text":". The shell uses ","type":"text"},{"text":"safe dispatch","type":"text","marks":[{"type":"strong"}]},{"text":" — no ","type":"text"},{"text":"eval","type":"text","marks":[{"type":"code_inline"}]},{"text":", no prefix matching:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shell metacharacters blocked","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":";","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"|","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"&","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"$","type":"text","marks":[{"type":"code_inline"}]},{"text":", backticks, ","type":"text"},{"text":"()","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003c>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\\","type":"text","marks":[{"type":"code_inline"}]},{"text":" all rejected before parsing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Command parsed into name + arguments","type":"text","marks":[{"type":"strong"}]},{"text":" via ","type":"text"},{"text":"read -ra","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Command name matched exactly","type":"text","marks":[{"type":"strong"}]},{"text":" via ","type":"text"},{"text":"case","type":"text","marks":[{"type":"code_inline"}]},{"text":" statement (not prefix matching)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Subcommands validated","type":"text","marks":[{"type":"strong"}]},{"text":" for multi-part commands (","type":"text"},{"text":"systemctl status","type":"text","marks":[{"type":"code_inline"}]},{"text":" allowed, ","type":"text"},{"text":"systemctl enable","type":"text","marks":[{"type":"code_inline"}]},{"text":" denied)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Path arguments validated","type":"text","marks":[{"type":"strong"}]},{"text":" against allowed directory prefixes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Commands executed directly","type":"text","marks":[{"type":"strong"}]},{"text":" as ","type":"text"},{"text":"\"$COMMAND\" \"${ARGS[@]}\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" with a 5-minute timeout","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Absolute paths rejected","type":"text","marks":[{"type":"strong"}]},{"text":" as command names (prevents ","type":"text"},{"text":"/sbin/shutdown","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"For broader access profiles, use the ","type":"text"},{"text":"--allow","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Profile","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Commands Permitted","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"diagnostic","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read-only: system info, logs (path-restricted to ","type":"text"},{"text":"/var/log/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/proc/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/sys/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/run/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/tmp/","type":"text","marks":[{"type":"code_inline"}]},{"text":"), service status, network diagnostics, Docker inspect","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"remediation","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Diagnostic + service restarts, process kills, file operations (path-restricted to ","type":"text"},{"text":"/etc/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/var/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/tmp/","type":"text","marks":[{"type":"code_inline"}]},{"text":"), Docker management, curl/wget","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"full","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unrestricted (use with extreme caution — no restriction shell installed)","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step 2e: Schedule Automatic Cleanup (Layer 4)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Scheduled via at(1) or systemd-run with ceiling-rounded delay\necho \"bash /usr/local/sbin/agent-cleanup-SESSION_ID.sh\" | at now + 80 minutes","type":"text"}]},{"type":"paragraph","content":[{"text":"Each session receives its own cleanup script (","type":"text"},{"text":"agent-cleanup-SESSION_ID.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":") to prevent concurrent-session collisions.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Agent Connects","type":"text"}]},{"type":"paragraph","content":[{"text":"The AI agent uses the session credentials:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# With agent-provided key:\nssh -i ~/.ssh/agent_key agent_support_XXXXXXXX@TARGET_HOST 'uptime'\n\n# With generated key:\nssh -i /tmp/agent_session_XXXX agent_support_XXXXXXXX@TARGET_HOST 'df -h'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Access Revocation","type":"text"}]},{"type":"paragraph","content":[{"text":"Access terminates automatically through multiple independent mechanisms and can also be revoked on demand.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Automatic Expiration","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Certificate TTL","type":"text","marks":[{"type":"strong"}]},{"text":" (Layer 1): Server rejects the cert after ","type":"text"},{"text":"-V","type":"text","marks":[{"type":"code_inline"}]},{"text":" window. Server logs: ","type":"text"},{"text":"error: Certificate invalid: expired","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Account expiration","type":"text","marks":[{"type":"strong"}]},{"text":" (Layer 2): OS denies login after ","type":"text"},{"text":"--expiredate","type":"text","marks":[{"type":"code_inline"}]},{"text":". Logs: ","type":"text"},{"text":"User account has expired","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scheduled cleanup","type":"text","marks":[{"type":"strong"}]},{"text":" (Layer 4): Removes account, keys, support shell, and cleanup script","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Immediate Revocation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sudo bash scripts/revoke-access.sh --session SESSION_ID\n# or\nsudo bash scripts/revoke-access.sh --user agent_support_XXXXXXXX\n# or revoke all:\nsudo bash scripts/revoke-access.sh --all","type":"text"}]},{"type":"paragraph","content":[{"text":"The revocation script:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Locks the account (","type":"text"},{"text":"usermod -L","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Kills active SSH sessions for the user","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Removes immutable-file protections (","type":"text"},{"text":"chattr -i","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Archives session logs to ","type":"text"},{"text":"/var/log/agent-support-archive/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Removes the account and home directory (","type":"text"},{"text":"userdel -r","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Securely deletes session keys (","type":"text"},{"text":"shred -u","type":"text","marks":[{"type":"code_inline"}]},{"text":"; falls back to ","type":"text"},{"text":"rm -f","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cancels scheduled cleanup timers and removes session-specific files","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 5: Audit","type":"text"}]},{"type":"paragraph","content":[{"text":"All agent activity is logged to ","type":"text"},{"text":"/var/log/agent-support.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" with sanitized entries (via ","type":"text"},{"text":"printf '%q'","type":"text","marks":[{"type":"code_inline"}]},{"text":" to prevent log injection):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[2026-03-15T16:00:00Z] SESSION_START: 20260315160000-a1b2c3d4 user=agent_support_e5f6g7h8 host=TARGET ttl=4h profile=diagnostic\n[2026-03-15T16:00:05Z] AGENT_EXEC: uptime\n[2026-03-15T16:00:10Z] AGENT_EXEC: df\\ -h\n[2026-03-15T16:00:15Z] AGENT_BLOCKED: rm\\ -rf\\ /\n[2026-03-15T20:00:00Z] SESSION_END: 20260315160000-a1b2c3d4 reason=ttl_expired","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Manual Approach (Without Scripts)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Grant Access Manually","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 1. Create temporary account (expires tomorrow as safety net)\nsudo useradd --expiredate \"$(date -d '+1 day' +%Y-%m-%d)\" \\\n --shell /bin/bash --create-home agent_support\n\n# 2. Accept the agent's public key (recommended) or generate a keypair\n# Option A: Agent provides their pubkey\nPUBKEY=\"/path/to/agent_pubkey.pub\"\n# Option B: Generate\nssh-keygen -t ed25519 -N \"\" -f /tmp/agent_key -C \"agent-support\"\nPUBKEY=\"/tmp/agent_key.pub\"\n\n# 3. Sign with CA (4-hour window, restricted)\nssh-keygen -s /etc/ssh/agent_ca \\\n -I \"agent-manual-session\" \\\n -n agent_support \\\n -V +4h \\\n -O no-port-forwarding \\\n -O no-x11-forwarding \\\n -O no-agent-forwarding \\\n \"$PUBKEY\"\n\n# 4. Verify the certificate\nssh-keygen -L -f \"${PUBKEY%.pub}-cert.pub\"\n\n# 5. Schedule cleanup\necho \"userdel -r agent_support && shred -u /tmp/agent_key*\" | at now + 5 hours\n\n# 6. Deliver the certificate (and private key if generated) to the AI agent","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Revoke Access Manually","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Lock the account immediately\nsudo usermod -L agent_support\n\n# Kill active sessions\nsudo pkill -u agent_support\n\n# Remove account and home directory\nsudo userdel -r agent_support\n\n# Destroy session keys\nshred -u /tmp/agent_key /tmp/agent_key-cert.pub /tmp/agent_key.pub 2>/dev/null\n\n# Remove scheduled cleanup\natq | grep agent && atrm JOB_NUMBER","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Alternative: authorized_keys-Only Approach (No CA)","type":"text"}]},{"type":"paragraph","content":[{"text":"For environments where a CA is not feasible:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sudo bash scripts/grant-access.sh --host TARGET --duration 4h --no-ca --agent-pubkey /path/to/agent.pub","type":"text"}]},{"type":"paragraph","content":[{"text":"This installs the agent's public key in ","type":"text"},{"text":"authorized_keys","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the following restrictions:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"expiry-time=\"YYYYMMDDHHMMSS\",restrict,command=\"/usr/local/bin/agent-support-shell-SID\" ssh-ed25519 AAAA...","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"expiry-time","type":"text","marks":[{"type":"code_inline"}]},{"text":" — key rejected after this UTC timestamp","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"restrict","type":"text","marks":[{"type":"code_inline"}]},{"text":" — disables port/agent/X11 forwarding and PTY allocation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"command=\"...\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" — forces all commands through the safe dispatch shell","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"chattr +i","type":"text","marks":[{"type":"code_inline"}]},{"text":" makes the file immutable to prevent modification","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Limitation:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"expiry-time","type":"text","marks":[{"type":"code_inline"}]},{"text":" is server-side only and lacks cryptographic enforcement. Always pair with ","type":"text"},{"text":"chattr +i","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Considerations","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"AI Agent-Specific Concerns","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No passphrase on session keys","type":"text","marks":[{"type":"strong"}]},{"text":" — mitigated by short-lived certificates (hours, not days), immediate key destruction, and immutable authorized_keys","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Command injection","type":"text","marks":[{"type":"strong"}]},{"text":" — the dispatch shell blocks all shell metacharacters, matches command names exactly via ","type":"text"},{"text":"case","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not prefix), validates argument paths against directory allowlists, resolves symlinks before comparison, and rejects absolute paths as command names; commands execute directly without ","type":"text"},{"text":"eval","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Key delivery","type":"text","marks":[{"type":"strong"}]},{"text":" — use ","type":"text"},{"text":"--agent-pubkey","type":"text","marks":[{"type":"code_inline"}]},{"text":" (recommended) so the private key never leaves the agent; if generating keys, the operator must deliver the private key securely","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scope creep","type":"text","marks":[{"type":"strong"}]},{"text":" — start with ","type":"text"},{"text":"diagnostic","type":"text","marks":[{"type":"code_inline"}]},{"text":"; escalate to ","type":"text"},{"text":"remediation","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"full","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when explicitly needed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Symlink traversal","type":"text","marks":[{"type":"strong"}]},{"text":" — path validation resolves symlinks via ","type":"text"},{"text":"readlink -f","type":"text","marks":[{"type":"code_inline"}]},{"text":" before checking directory prefixes; a symlink from ","type":"text"},{"text":"/var/log/evil","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"/etc/shadow","type":"text","marks":[{"type":"code_inline"}]},{"text":" is resolved and rejected","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Break-Glass Recovery","type":"text"}]},{"type":"paragraph","content":[{"text":"If the agent loses access mid-session (certificate expired, context lost, network failure):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Console access","type":"text","marks":[{"type":"strong"}]},{"text":" — use hypervisor/cloud console (LXC ","type":"text"},{"text":"lxc-attach","type":"text","marks":[{"type":"code_inline"}]},{"text":", AWS SSM, GCP serial console) to regain access","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Revoke stale sessions","type":"text","marks":[{"type":"strong"}]},{"text":" — run ","type":"text"},{"text":"sudo bash scripts/revoke-access.sh --all","type":"text","marks":[{"type":"code_inline"}]},{"text":" to clean up orphaned accounts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-provision","type":"text","marks":[{"type":"strong"}]},{"text":" — run ","type":"text"},{"text":"grant-access.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" again for a fresh session","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Critical rule:","type":"text","marks":[{"type":"strong"}]},{"text":" never disable existing access paths (e.g., root SSH) before verifying the agent's own access works. Always keep at least one independent recovery path available.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"General Hardening","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep the CA private key on a separate, secured host — never on the target server","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use Ed25519 keys for all operations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set ","type":"text"},{"text":"MaxSessions 1","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the agent account in ","type":"text"},{"text":"sshd_config","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set ","type":"text"},{"text":"MaxAuthTries 3","type":"text","marks":[{"type":"code_inline"}]},{"text":" to limit brute-force attempts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set ","type":"text"},{"text":"AuthenticationMethods publickey","type":"text","marks":[{"type":"code_inline"}]},{"text":" to restrict to key-based auth","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enable ","type":"text"},{"text":"LogLevel VERBOSE","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"sshd_config","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Monitor ","type":"text"},{"text":"/var/log/auth.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"/var/log/agent-support.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" during active sessions","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Symptom","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cause","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fix","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Certificate invalid: expired","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Certificate TTL elapsed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Re-run ","type":"text"},{"text":"grant-access.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" for a new session","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Certificate invalid: name is not a listed principal","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Username mismatch between cert ","type":"text"},{"text":"-n","type":"text","marks":[{"type":"code_inline"}]},{"text":" and account","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ensure ","type":"text"},{"text":"-n","type":"text","marks":[{"type":"code_inline"}]},{"text":" matches the created account name","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User account has expired","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OS-level ","type":"text"},{"text":"--expiredate","type":"text","marks":[{"type":"code_inline"}]},{"text":" passed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Create a new account with later expiry","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Permission denied (publickey)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CA public key not in ","type":"text"},{"text":"TrustedUserCAKeys","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Verify server config and restart sshd","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"WARNING: UNPROTECTED PRIVATE KEY FILE","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key file permissions too open","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"chmod 600 /tmp/agent_session_XXXX","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Shell metacharacters are not allowed","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Agent sent ","type":"text"},{"text":";","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"|","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"$","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Send simple commands without shell operators","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Command not in allowlist","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Agent tried an unlisted command","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use a broader ","type":"text"},{"text":"--allow","type":"text","marks":[{"type":"code_inline"}]},{"text":" profile or add the command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Path outside allowed directories","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Agent accessed a path outside allowed prefixes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Restrict to ","type":"text"},{"text":"/var/log/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/proc/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/sys/","type":"text","marks":[{"type":"code_inline"}]},{"text":" (diagnostic) or add ","type":"text"},{"text":"/etc/","type":"text","marks":[{"type":"code_inline"}]},{"text":" (remediation)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Missing required dependencies","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Linux tools not installed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Install ","type":"text"},{"text":"openssh-client","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"coreutils","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"at","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"systemd","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Connection reset by peer","type":"text","marks":[{"type":"code_inline"}]},{"text":" (before auth)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"IP auto-blocked by brute-force protection","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unblock the IP (fail2ban, Synology autoblock, etc.), then retry","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key added but auth fails","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AuthorizedKeysFile","type":"text","marks":[{"type":"code_inline"}]},{"text":" points elsewhere","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"sshd_config","type":"text","marks":[{"type":"code_inline"}]},{"text":"; Proxmox uses ","type":"text"},{"text":"/etc/pve/priv/authorized_keys","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Platform-Specific Notes","type":"text"}]},{"type":"paragraph","content":[{"text":"Synology DSM","type":"text","marks":[{"type":"strong"}]},{"text":" — not fully compatible. DSM wraps sshd behind ","type":"text"},{"text":"synorelayd","type":"text","marks":[{"type":"code_inline"}]},{"text":", which intercepts SSH on port 22 and may silently block connections after repeated failures:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dual sshd instances: port 22 (via synorelayd) and port 222 (SFTP-only, pubkey)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Home directories at ","type":"text"},{"text":"/var/services/homes/USER/","type":"text","marks":[{"type":"code_inline"}]},{"text":" (symlinked from ","type":"text"},{"text":"/volume1/homes/USER/","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DSM may reset ","type":"text"},{"text":"sshd_config","type":"text","marks":[{"type":"code_inline"}]},{"text":" on service restart","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Default home directory permissions are ","type":"text"},{"text":"777","type":"text","marks":[{"type":"code_inline"}]},{"text":" — SSH silently rejects pubkey auth; fix with ","type":"text"},{"text":"chmod 755","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PubkeyAuthentication yes","type":"text","marks":[{"type":"code_inline"}]},{"text":" must be explicitly uncommented in ","type":"text"},{"text":"sshd_config","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Connect as ","type":"text"},{"text":"admin","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"root","type":"text","marks":[{"type":"code_inline"}]},{"text":") via the DSM-configured SSH port","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If connections reset, restart synorelayd: ","type":"text"},{"text":"sudo /usr/syno/bin/synosystemctl restart synorelayd","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Proxmox VE","type":"text","marks":[{"type":"strong"}]},{"text":" — may store authorized keys in ","type":"text"},{"text":"/etc/pve/priv/authorized_keys","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of ","type":"text"},{"text":"~/.ssh/","type":"text","marks":[{"type":"code_inline"}]},{"text":". Check ","type":"text"},{"text":"AuthorizedKeysFile","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"sshd_config","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Files Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Location","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"setup-ca.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"One-time CA key pair generation (warns on key age >90 days)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"grant-access.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Provision temporary agent access (supports ","type":"text"},{"text":"--dry-run","type":"text","marks":[{"type":"code_inline"}]},{"text":", sshd pre-flight check)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"revoke-access.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Immediate access revocation with post-removal verification","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"audit.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scan for orphaned artifacts (accounts, keys, shells, at jobs)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CA private key","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/etc/ssh/agent_ca","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Signs session certificates (keep secure)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CA public key","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/etc/ssh/agent_ca.pub","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Distributed to target servers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Support shell","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/usr/local/bin/agent-support-shell-SID","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Per-session command dispatch","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cleanup script","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/usr/local/sbin/agent-cleanup-SID.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Per-session auto-cleanup","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Session log","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/var/log/agent-support.log","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audit trail of all agent commands","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Archive","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/var/log/agent-support-archive/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Archived logs from expired sessions","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Manifest","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Category","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Details","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Environment variables","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"None accessed","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"External endpoints","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"None — this skill makes zero network calls","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Local files read","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/etc/passwd","type":"text","marks":[{"type":"code_inline"}]},{"text":" (user lookup), ","type":"text"},{"text":"/etc/ssh/agent_ca","type":"text","marks":[{"type":"code_inline"}]},{"text":" (CA signing), agent public key (if provided)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Local files written","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/etc/ssh/agent_ca{,.pub}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (setup-ca.sh), ","type":"text"},{"text":"/usr/local/bin/agent-support-shell-SID","type":"text","marks":[{"type":"code_inline"}]},{"text":" (per-session), ","type":"text"},{"text":"/usr/local/sbin/agent-cleanup-SID.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" (per-session), ","type":"text"},{"text":"/tmp/agent_session_SID{,.pub,-cert.pub}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (ephemeral keys), ","type":"text"},{"text":"/var/log/agent-support.log","type":"text","marks":[{"type":"code_inline"}]},{"text":" (audit), ","type":"text"},{"text":"~agent/.ssh/authorized_keys","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no-ca mode)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OS state mutated","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Creates/deletes Linux user accounts (","type":"text"},{"text":"useradd","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"userdel","type":"text","marks":[{"type":"code_inline"}]},{"text":"), schedules cleanup timers (","type":"text"},{"text":"at","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"systemd-run","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data transmitted","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No data leaves the local machine. All operations are local to the host.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Trust & Privacy","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"This skill operates entirely offline — no telemetry, no analytics, no data transmitted to external services. All sensitive material (private keys, certificates) is destroyed after session expiration via ","type":"text"},{"text":"shred -u","type":"text","marks":[{"type":"code_inline"}]},{"text":" (falling back to ","type":"text"},{"text":"rm -f","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Audit logs remain on the local filesystem under ","type":"text"},{"text":"/var/log/","type":"text","marks":[{"type":"code_inline"}]},{"text":" for operator review.","type":"text"}]}]},"metadata":{"date":"2026-06-05","author":"@skillopedia","source":{"stars":2012,"repo_name":"openclaw-master-skills","origin_url":"https://github.com/leoyeai/openclaw-master-skills/blob/HEAD/skills/sparkey/SKILL.md","repo_owner":"leoyeai","body_sha256":"560e9ba47225661dc82d7cacebaeb72c633f183b4eda5502cf682cb7074b1530","cluster_key":"60b81ab468540c54b4f8d9f3ddfa77879e4e5880f80f6b04140c44ed469bfe94","clean_bundle":{"format":"clean-skill-bundle-v1","source":"leoyeai/openclaw-master-skills/skills/sparkey/SKILL.md","attachments":[{"id":"bfd8407f-d59f-5c08-84d0-118ed636dfdf","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bfd8407f-d59f-5c08-84d0-118ed636dfdf/attachment.md","path":"README.md","size":5499,"sha256":"980d9d03587937a3e6fcd74d5758fa37786eaa35c7166e22b705f2553878c44c","contentType":"text/markdown; charset=utf-8"},{"id":"50129043-e18a-5f8b-b189-46271e1b63d0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/50129043-e18a-5f8b-b189-46271e1b63d0/attachment.json","path":"_meta.json","size":270,"sha256":"2e6c4a3b5a8eb25ce1d22d44c2e44d7e8a4e9dbcb4a14d9bf7031d4a769929d0","contentType":"application/json; charset=utf-8"},{"id":"c3f76daa-b857-5201-ab96-1e8839afa62d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c3f76daa-b857-5201-ab96-1e8839afa62d/attachment.svg","path":"diagrams/sparkey_access_lifecycle.svg","size":26347,"sha256":"d83361090eb06ee78dc960948375abcdce5909be362a6afaddfe17a37e095468","contentType":"image/svg+xml"},{"id":"b7df3dd8-d379-5fdf-8da0-c99cf27e63b2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b7df3dd8-d379-5fdf-8da0-c99cf27e63b2/attachment.svg","path":"diagrams/sparkey_defense_in_depth.svg","size":12044,"sha256":"0115809225578787393fc0a95d5b8d28b1c5ef5015414a3d2c0b952d486aff70","contentType":"image/svg+xml"},{"id":"8a8a8b70-5878-5ead-93db-b6fc04df3f6e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8a8a8b70-5878-5ead-93db-b6fc04df3f6e/attachment.sh","path":"scripts/audit.sh","size":4289,"sha256":"81ed02836d83a0cbca48fbc311bd846b9fe3aebccbc99539cb0af9f69a01d339","contentType":"application/x-sh; charset=utf-8"},{"id":"04d805fc-4a78-59b9-8d95-930aabaa6404","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/04d805fc-4a78-59b9-8d95-930aabaa6404/attachment.sh","path":"scripts/grant-access.sh","size":24279,"sha256":"0d373159da459ee9d891d185b7d209f8ac5d4a5128a35764c4b13723246abb2f","contentType":"application/x-sh; charset=utf-8"},{"id":"973a2d0b-44fa-59e4-ac60-ab79f15ecd12","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/973a2d0b-44fa-59e4-ac60-ab79f15ecd12/attachment.sh","path":"scripts/revoke-access.sh","size":7563,"sha256":"16db6092b66b4835a27704fa803c27ea7080e2b26b379d4c6538dd0d8c6f3759","contentType":"application/x-sh; charset=utf-8"},{"id":"c7efedfc-b134-5e81-8f09-ed1dad2a00b2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c7efedfc-b134-5e81-8f09-ed1dad2a00b2/attachment.sh","path":"scripts/setup-ca.sh","size":3728,"sha256":"36f20f5fcfbb4fbbc7b3508cfbcf5db58c746f77948ad6f9fb4de3eef60e680e","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"c35dc3864a7833f17e3a71d169446b13443f68bad4365c89cd60e7c8e035933b","attachment_count":8,"text_attachments":4,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":4,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/sparkey/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"general","category_label":"General"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"general","import_tag":"clean-skills-v1","_yaml_error":"YAMLException: bad indentation of a mapping entry (2:98)\n\n 1 | ... \n 2 | ... ts. Four-layer defense-in-depth: certificate TTL, OS account ex ...\n-----------------------------------------^\n 3 | ... "}},"renderedAt":1782987341455}

Temporary SSH Access for AI Agent Support Defense-in-depth temporary SSH access for AI agents — combines certificate TTL, OS account expiration, command-restricted dispatch, and automated cleanup. Zero session artifacts survive — all agent accounts, keys, certificates, dispatch shells, and cleanup timers are destroyed on session end or TTL expiry. The CA private key is a persistent operator-side credential requiring separate protection (see Security Considerations). Platform: Linux (requires standard user-management tools). Prerequisites Scripts check for required tools at startup and report…