diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..49c8b11 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,111 @@ +name: Security Checks + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + security-events: write + +jobs: + shell-security: + name: Shell Security (ShellCheck) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install ShellCheck + run: | + sudo apt-get update + sudo apt-get install -y shellcheck + + - name: Run ShellCheck on bash scripts and .envrc + run: | + set -euo pipefail + mapfile -d '' shell_files < <(find . -type f \( -name "*.sh" -o -name "*.bash" \) -not -path "./.git/*" -print0) + mapfile -d '' envrc_files < <(find . -type f -name ".envrc" -not -path "./.git/*" -print0) + + if [ "${#shell_files[@]}" -gt 0 ]; then + printf 'Checking shell files:\n' + printf ' - %s\n' "${shell_files[@]}" + shellcheck -x -S error "${shell_files[@]}" + fi + + if [ "${#envrc_files[@]}" -gt 0 ]; then + printf 'Checking .envrc files (bash mode):\n' + printf ' - %s\n' "${envrc_files[@]}" + shellcheck -s bash -S error "${envrc_files[@]}" + fi + + if [ "${#shell_files[@]}" -eq 0 ] && [ "${#envrc_files[@]}" -eq 0 ]; then + echo "No shell files or .envrc found." + fi + + envrc-policy: + name: .envrc Policy Checks + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Validate .envrc policy + run: | + set -euo pipefail + if [ ! -f .envrc ]; then + echo "No .envrc found." + exit 0 + fi + + # Basic parser sanity check. + bash -n .envrc + + # Block common risky patterns in direnv files. + if grep -En '(^|[^[:alnum:]_])(curl|wget)[^\n]*\|[[:space:]]*(bash|sh)\b|\beval[[:space:]]+|\bsource_url\b' .envrc; then + echo "Found unsafe pattern(s) in .envrc." + exit 1 + fi + + echo ".envrc policy check passed." + + terraform-security: + name: Terraform Security (Checkov) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Checkov on Terraform + uses: bridgecrewio/checkov-action@v12 + with: + directory: terraform/digitalOcean + framework: terraform + skip_check: CKV_DIO_4 + quiet: true + output_format: cli,sarif + output_file_path: console,checkov.sarif + + - name: Upload Checkov SARIF + if: always() + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: checkov.sarif + + secrets-scan: + name: Secret Scan (Gitleaks) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run Gitleaks + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/openclaw.json b/openclaw.json deleted file mode 100644 index c0a73b7..0000000 --- a/openclaw.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "gateway": { - "bind": "lan", - "auth": { - "mode": "token" - } - }, - "agents": { - "defaults": { - "model": { - "primary": "openrouter/meta-llama/llama-3.3-70b-instruct:free", - "fallbacks": [ - "openrouter/auto" - ] - }, - "models": { - "openrouter/meta-llama/llama-3.3-70b-instruct:free": {"alias": "llama"}, - "openrouter/cognitivecomputations/dolphin-mistral-24b-venice-edition:free": {"alias": "uncensored"}, - "openrouter/google/gemini-2.0-flash-exp:free": {"alias": "gemini"}, - "openrouter/auto": {"alias": "auto"} - }, - "compaction": { - "mode": "safeguard", - "reserveTokensFloor": 20000 - } - } - }, - "tools": { - "web": { - "search": { - "enabled": true, - "provider": "brave" - }, - "fetch": { - "enabled": false - } - }, - "deny": ["browser"] - }, - "plugins": { - "entries": { - "openrouter": { "enabled": true }, - "brave": { - "enabled": true, - "config": { - "webSearch": { - "apiKey": "${BRAVE_API_KEY}" - } - } - } - } - }, - "channels": { - "telegram": { - "enabled": true, - "accounts": { - "default": { - "botToken": "${TELEGRAM_BOT_TOKEN}", - "dmPolicy": "open", - "groupPolicy": "open" - } - } - } - } -} diff --git a/terraform/.DS_Store b/terraform/.DS_Store index 7dab34a..2d8024f 100644 Binary files a/terraform/.DS_Store and b/terraform/.DS_Store differ diff --git a/terraform/digitalOcean/main.tf b/terraform/digitalOcean/main.tf index 189224f..8d25895 100644 --- a/terraform/digitalOcean/main.tf +++ b/terraform/digitalOcean/main.tf @@ -28,14 +28,14 @@ resource "digitalocean_firewall" "openclaw" { inbound_rule { protocol = "tcp" port_range = "22" - source_addresses = ["0.0.0.0/0", "::/0"] + source_addresses = var.ssh_allowed_cidrs } # OpenClaw gateway inbound_rule { protocol = "tcp" port_range = "18789" - source_addresses = ["0.0.0.0/0", "::/0"] + source_addresses = var.gateway_allowed_cidrs } diff --git a/terraform/digitalOcean/terraform.tfvars.example b/terraform/digitalOcean/terraform.tfvars.example index b82838f..1321610 100644 --- a/terraform/digitalOcean/terraform.tfvars.example +++ b/terraform/digitalOcean/terraform.tfvars.example @@ -15,6 +15,11 @@ region = "tor1" # Sizes: s-1vcpu-1gb ($6/mo), s-1vcpu-2gb ($12/mo), s-2vcpu-2gb ($18/mo), s-2vcpu-4gb ($24/mo) droplet_size = "s-1vcpu-1gb" +# Restrict ingress to trusted CIDRs. +# Replace with your own public IP/CIDR ranges. +ssh_allowed_cidrs = ["203.0.113.10/32"] +gateway_allowed_cidrs = ["203.0.113.10/32"] + # Swap: 2G (min for 1GB RAM), 3G (recommended), 4G (comfortable) swap_size = "3G" diff --git a/terraform/digitalOcean/variables.tf b/terraform/digitalOcean/variables.tf index 5d4b62a..07a5b11 100644 --- a/terraform/digitalOcean/variables.tf +++ b/terraform/digitalOcean/variables.tf @@ -20,6 +20,18 @@ variable "droplet_size" { default = "s-1vcpu-1gb" } +variable "ssh_allowed_cidrs" { + description = "CIDR blocks allowed to SSH to the droplet (recommended: your current public IP as /32)." + type = list(string) + default = ["0.0.0.0/0", "::/0"] +} + +variable "gateway_allowed_cidrs" { + description = "CIDR blocks allowed to access OpenClaw gateway on port 18789." + type = list(string) + default = ["0.0.0.0/0", "::/0"] +} + variable "swap_size" { description = "Swap file size (e.g. 2G, 3G, 4G) — prevents OOM during npm install" default = "3G"