--- name: polish-security description: Security hardening for Archipelago systemd services, nginx headers, secrets management, and rate limiting. Use when user says "polish security", "harden services", "security headers", "rate limiting", or "secrets management". --- # Skill: Polish Security Security hardening pass for systemd, nginx, secrets management, and rate limiting. ## 1. Systemd Service Hardening File: `image-recipe/configs/archipelago.service` Add these directives to the `[Service]` section: ```ini PrivateTmp=yes NoNewPrivileges=true ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/lib/archipelago SystemCallFilter=@system-service SystemCallFilter=~@privileged @resources ``` After editing, sync to server and verify: ```bash ssh archipelago@192.168.1.228 "sudo systemd-analyze security archipelago" ``` ## 2. Nginx Security Headers File: `image-recipe/configs/nginx-archipelago.conf` ### Add HSTS (HTTPS block only): ```nginx add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; ``` ### Fix CSP (remove unsafe-inline): Replace: ```nginx add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' ws: wss:; frame-src 'self' http://localhost:* http://192.168.*:*;" always; ``` With CSP that uses nonces or hashes for inline scripts/styles. If inline scripts can't be removed yet, document which ones and plan their removal. ### Add rate limiting zones: ```nginx # In http block: limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m; # On login/auth endpoints: limit_req zone=auth burst=3 nodelay; # On API endpoints: limit_req zone=api burst=50 nodelay; ``` ### Custom log format (strip tokens): ```nginx log_format no_tokens '$remote_addr - $remote_user [$time_local] "$request_method $uri $server_protocol" $status $body_bytes_sent "$http_referer"'; access_log /var/log/nginx/archipelago_access.log no_tokens; ``` ## 3. Secrets Management ### Remove hardcoded RPC credentials from scripts File: `scripts/deploy-to-target.sh` Replace: ```bash -e CORE_RPC_USERNAME=archipelago -e CORE_RPC_PASSWORD=archipelago123 ``` With: ```bash -e CORE_RPC_USERNAME=archipelago -e CORE_RPC_PASSWORD=$(cat /var/lib/archipelago/secrets/bitcoin-rpc-pass) ``` ### Generate secrets on first boot File: `scripts/first-boot-containers.sh` Add at the top: ```bash SECRETS_DIR="/var/lib/archipelago/secrets" mkdir -p "$SECRETS_DIR" chmod 700 "$SECRETS_DIR" # Generate Bitcoin RPC password if not exists if [ ! -f "$SECRETS_DIR/bitcoin-rpc-pass" ]; then openssl rand -base64 24 > "$SECRETS_DIR/bitcoin-rpc-pass" chmod 600 "$SECRETS_DIR/bitcoin-rpc-pass" fi ``` ### Remove hardcoded credentials from Rust backend File: `core/archipelago/src/api/rpc/bitcoin.rs` Replace: ```rust .basic_auth("archipelago", Some("archipelago123")) ``` With: ```rust let rpc_user = std::env::var("ARCHIPELAGO_BITCOIN_RPC_USER").unwrap_or_else(|_| "archipelago".into()); let rpc_pass = std::env::var("ARCHIPELAGO_BITCOIN_RPC_PASS").unwrap_or_else(|_| "archipelago123".into()); .basic_auth(&rpc_user, Some(&rpc_pass)) ``` ## 4. Rate Limiting on Backend File: `core/archipelago/src/api/handler.rs` Add rate limiting to unauthenticated endpoints: - `/archipelago/node-message` — 10 req/min per IP - `/electrs-status` — 30 req/min per IP Use an in-memory `HashMap` with cleanup on access. ## 5. SSH Hardening File: `scripts/deploy-to-target.sh` Replace: ```bash SSH_OPTS="-o StrictHostKeyChecking=no" ``` With: ```bash SSH_OPTS="-o StrictHostKeyChecking=accept-new" ``` And add SSH key validation: ```bash if [ ! -f "$SSH_KEY" ]; then echo "ERROR: SSH key not found at $SSH_KEY" exit 1 fi ``` ## Verification Checklist - [ ] `systemd-analyze security archipelago` score < 5.0 (lower is better) - [ ] Nginx headers pass: `curl -I http://192.168.1.228 | grep -i 'strict-transport\|content-security\|x-frame'` - [ ] No hardcoded passwords in scripts: `grep -rn 'archipelago123' scripts/ core/` - [ ] Rate limiting works: rapid-fire requests get 429 - [ ] SSH key required (no password fallback) ## Deploy After changes, sync configs and deploy: ```bash ./scripts/deploy-to-target.sh --live ``` Then sync to ISO recipe: ```bash # Run /sync-configs skill ```