# Security Audit Report: Homelab Infrastructure **Report Date:** April 12, 2026 **Auditor:** FrankGPT Security Analysis **Scope:** Complete repository scan for exposed secrets, credentials, and security vulnerabilities **Status:** 🔴 **CRITICAL ISSUES FOUND** --- ## Executive Summary A comprehensive security audit of the homelab repository has identified **1 critical vulnerability** and **3 high-priority security enhancements** needed to align with industry best practices. While the infrastructure demonstrates strong GitOps practices with environment variable usage in most services, several hardcoded credentials and exposed ports present immediate security risks. **Risk Level:** 🔴 **HIGH** (Immediate remediation required) --- ## 🔴 Critical Findings ### 1. Hardcoded Plex Claim Token in Git Repository **Severity:** 🔴 **CRITICAL** **File:** `nodes/waldorf/plex/compose.yaml` (Line 12) **Issue:** Plex claim token committed directly to version control ```yaml # CURRENT (INSECURE) environment: - PLEX_CLAIM=claim-sxFpsPTDzzF-9RZAxtUL ``` **Risk Assessment:** - **Impact:** Anyone with repository access can claim/hijack your Plex server - **Exposure:** Token visible in Git history, even if removed from HEAD - **Validity:** Plex claim tokens typically expire after 4 minutes, but this pattern indicates potential for other secrets **Remediation Required:** 1. **Immediate Action:** ```bash # Revoke/invalidate this claim token in Plex # Visit: https://www.plex.tv/claim/ ``` 2. **Code Fix:** ```yaml # CORRECTED (SECURE) environment: - PLEX_CLAIM=${PLEX_CLAIM} # Placeholder ``` 3. **Komodo Configuration:** - Add via Komodo UI → Stack → Environment Variables - Variable: `PLEX_CLAIM=claim-NEW_TOKEN_HERE` 4. **Git History Cleanup (Optional but Recommended):** ```bash # WARNING: Rewriting history affects all collaborators git filter-branch --force --index-filter \ "git rm --cached --ignore-unmatch nodes/waldorf/plex/compose.yaml" \ --prune-empty --tag-name-filter cat -- --all ``` **Status:** ⏳ **PENDING REMEDIATION** --- ## 🟡 High-Priority Security Enhancements ### 2. Redis Exposed Without Authentication **Severity:** 🟡 **HIGH** **File:** `nodes/heimdall/core/compose.yaml` (Line 40) **Issue:** Redis cache exposed on host port 6379 without password protection ```yaml # CURRENT (INSECURE) redis: image: redis:7-alpine ports: - "6379:6379" # ⚠️ Publicly exposed on LAN command: redis-server --appendonly yes # NO PASSWORD CONFIGURED ``` **Risk Assessment:** - **Impact:** Unauthorized access to cached session data, potential data manipulation - **Attack Surface:** Any device on LAN (10.0.0.x) can connect to Redis - **Exploitability:** HIGH (Redis has no default authentication) **Remediation Options:** **Option A: Remove Public Port Binding (Recommended)** ```yaml redis: image: redis:7-alpine # ports: # REMOVED - Only accessible via Docker network # - "6379:6379" networks: - proxy-net # Internal-only access command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} environment: - REDIS_PASSWORD=${REDIS_PASSWORD} ``` **Option B: Add Password Authentication (If external access needed)** ```yaml redis: image: redis:7-alpine ports: - "127.0.0.1:6379:6379" # Bind to localhost only command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} environment: - REDIS_PASSWORD=${REDIS_PASSWORD} ``` **Komodo Environment Variable:** - Add `REDIS_PASSWORD=` via Komodo UI **Status:** 🟡 **RECOMMENDED** --- ### 3. Docker Socket Proxy Configured with Broad Write Access **Severity:** 🟡 **MEDIUM** **File:** `nodes/heimdall/core/compose.yaml` (Lines 28-33) **Issue:** Docker socket proxy allows POST operations and container lifecycle management ```yaml # CURRENT environment: - POST=1 - ALLOW_START=1 - ALLOW_STOP=1 - ALLOW_RESTARTS=1 ``` **Risk Assessment:** - **Impact:** Compromised Traefik or Komodo could manipulate all containers - **Mitigation:** Proxy runs as privileged (`privileged: true`), limiting lateral movement - **Trade-off:** Required for Komodo's container orchestration functionality **Recommendation:** - **Accept Risk:** This configuration is **required** for Komodo Core functionality - **Monitoring:** Enable Docker audit logging to track container lifecycle events - **Network Isolation:** Ensure `proxy-net` is not exposed beyond trusted services **Status:** ✅ **ACCEPTED RISK** (Required for functionality) --- ### 4. MongoDB Credentials Use Environment Variables (GOOD) **Severity:** ✅ **LOW** (Best Practice Followed) **File:** `nodes/heimdall/core/compose.yaml` (Lines 109-110) ```yaml # ✅ CORRECT IMPLEMENTATION environment: MONGO_INITDB_ROOT_USERNAME: ${KOMODO_DATABASE_USERNAME} MONGO_INITDB_ROOT_PASSWORD: ${KOMODO_DATABASE_PASSWORD} ``` **Assessment:** - **Status:** ✅ **SECURE** - No hardcoded credentials found - **Verification:** Confirm `.env` file is excluded from Git (see `.gitignore` section below) --- ### 5. Traefik Cloudflare API Tokens Use Environment Variables (GOOD) **Severity:** ✅ **LOW** (Best Practice Followed) **File:** `nodes/heimdall/core/compose.yaml` (Lines 66-67) ```yaml # ✅ CORRECT IMPLEMENTATION environment: - CLOUDFLARE_DNS_API_TOKEN=${CF_API_TOKEN} - CLOUDFLARE_ZONE_API_TOKEN=${CF_ZONE_TOKEN} ``` **Assessment:** - **Status:** ✅ **SECURE** - No hardcoded tokens found - **Recommendation:** Rotate Cloudflare API tokens quarterly --- ## 🔒 Secret Management Options for This Environment ### Current State: Komodo UI Environment Variables **How It Works:** 1. Define placeholders in `compose.yaml`: `${VARIABLE_NAME}` 2. Store actual secrets in Komodo UI → Stack → Environment Variables 3. Komodo injects secrets at container runtime **Pros:** - ✅ Simple to configure via web UI - ✅ Secrets not committed to Git - ✅ Supports per-stack secret isolation **Cons:** - ⚠️ No centralized secret rotation - ⚠️ Manual backup of secrets required - ⚠️ No audit trail for secret access --- ### Recommended Enhancements #### Option 1: `.env` File (Quick Win) **Implementation:** 1. Create `.env` file in stack directory: ```bash # /nodes/heimdall/core/.env CF_API_TOKEN=your_cloudflare_api_token_here CF_ZONE_TOKEN=your_zone_token_here KOMODO_DATABASE_USERNAME=komodo_admin KOMODO_DATABASE_PASSWORD=strong_random_password_here KOMODO_ONBOARDING_KEY_HEIMDALL=onboarding_key_here REDIS_PASSWORD=another_strong_password ``` 2. Update `compose.yaml`: ```yaml services: komodo-core: env_file: .env # Individual environment vars still supported ``` 3. **Critical:** Add to `.gitignore`: ```bash # Create/update .gitignore at repository root echo "**/.env" >> .gitignore echo "**/*.env" >> .gitignore git add .gitignore git commit -m "chore(security): prevent .env file commits" ``` **Pros:** - ✅ Quick to implement - ✅ Standard Docker Compose pattern - ✅ Easy to backup (manual copy) **Cons:** - ⚠️ File must exist on host (NFS or local) - ⚠️ No encryption at rest - ⚠️ Risk of accidental Git commit --- #### Option 2: Docker Secrets (Recommended for Production) **Implementation:** 1. Create secrets on each node: ```bash # On Heimdall echo "your_cloudflare_token" | docker secret create cf_api_token - echo "your_database_password" | docker secret create komodo_db_password - ``` 2. Update `compose.yaml`: ```yaml services: traefik: secrets: - cf_api_token environment: - CLOUDFLARE_DNS_API_TOKEN_FILE=/run/secrets/cf_api_token secrets: cf_api_token: external: true ``` **Pros:** - ✅ Encrypted at rest and in transit - ✅ Native Docker Swarm integration - ✅ Secret rotation without container restart **Cons:** - ⚠️ Requires Docker Swarm mode (or transition to Kubernetes) - ⚠️ More complex to configure - ⚠️ May not integrate seamlessly with Komodo --- #### Option 3: HashiCorp Vault (Enterprise-Grade) **Implementation:** 1. Deploy Vault container: ```yaml vault: image: hashicorp/vault:latest ports: - "8200:8200" environment: - VAULT_DEV_ROOT_TOKEN_ID=myroot # CHANGE THIS ``` 2. Store secrets in Vault: ```bash vault kv put secret/homelab/cloudflare api_token=xxx zone_token=yyy ``` 3. Use Vault Agent for injection or API calls in startup scripts **Pros:** - ✅ Centralized secret management - ✅ Audit logging and access control - ✅ Automatic secret rotation - ✅ Encryption at rest **Cons:** - ⚠️ Significant operational overhead - ⚠️ Requires learning curve - ⚠️ Another service to maintain --- #### Option 4: Ansible Vault (Hybrid Approach) **Implementation:** 1. Encrypt variables file: ```bash ansible-vault create ansible/vars/secrets.yml ``` 2. Store secrets: ```yaml # ansible/vars/secrets.yml (encrypted) cf_api_token: your_token_here plex_claim: your_claim_here ``` 3. Deploy with Ansible playbook: ```bash ansible-playbook -i hosts deploy.yml --ask-vault-pass ``` **Pros:** - ✅ Works with existing `ansible/` directory structure - ✅ Encrypted at rest (AES-256) - ✅ Can commit encrypted file to Git safely **Cons:** - ⚠️ Requires Ansible expertise - ⚠️ Not integrated with Komodo workflow - ⚠️ Manual decryption for ad-hoc changes --- ## 📋 Recommended Action Plan ### Immediate Actions (Within 24 Hours) | Priority | Action | Effort | Impact | |----------|--------|--------|--------| | 🔴 **CRITICAL** | Remove hardcoded PLEX_CLAIM from Git | 15 min | HIGH | | 🔴 **CRITICAL** | Create `.gitignore` to prevent `.env` commits | 5 min | HIGH | | 🟡 **HIGH** | Remove Redis public port binding OR add authentication | 10 min | MEDIUM | | 🟡 **HIGH** | Audit all compose files for other hardcoded secrets | 30 min | MEDIUM | ### Short-Term Actions (Within 1 Week) | Priority | Action | Effort | Impact | |----------|--------|--------|--------| | 🟡 **MEDIUM** | Standardize on `.env` files for all stacks | 2 hours | MEDIUM | | 🟡 **MEDIUM** | Document secret rotation procedures in SOP | 1 hour | MEDIUM | | 🟢 **LOW** | Set calendar reminder for quarterly API token rotation | 5 min | LOW | ### Long-Term Actions (Optional) | Priority | Action | Effort | Impact | |----------|--------|--------|--------| | 🟢 **LOW** | Evaluate Docker Secrets vs. Vault for centralized management | 4 hours | HIGH | | 🟢 **LOW** | Implement automated secret scanning in CI/CD pipeline | 3 hours | MEDIUM | --- ## 🛡️ Security Best Practices Checklist ### ✅ Already Implemented - [x] Most services use environment variable placeholders (`${VAR_NAME}`) - [x] Komodo UI used for secret injection at runtime - [x] Docker socket access mediated through proxy (not direct mount) - [x] Traefik handles SSL termination with Cloudflare DNS challenge - [x] Network segmentation via Docker networks (`proxy-net`) ### ⏳ Needs Implementation - [ ] `.gitignore` configured to exclude `.env` files - [ ] All hardcoded secrets removed from compose files - [ ] Redis authentication enabled or port binding removed - [ ] Secret rotation schedule documented - [ ] Backup strategy for Komodo-stored environment variables ### 🔮 Future Enhancements - [ ] Centralized secret management (Vault or similar) - [ ] Automated secret scanning (pre-commit hooks) - [ ] Audit logging for sensitive operations - [ ] Encrypted backups of configuration data --- ## 📚 Related Documentation - [SOP-001: Migrate Stack from UI to Git](SOPs/SOP-001-Migrate-Stack-from-UI-to-Git.md) - Section on Secrets Management - [TECHNICAL_RUNBOOK.md](TECHNICAL_RUNBOOK.md) - Credential Management section - [Repository Memory: Active Tasks](/memories/repo/active-tasks.md) --- ## 🔍 Audit Methodology **Scan Coverage:** - ✅ All `compose.yaml` files in `nodes/` directory - ✅ Documentation files for exposed patterns - ✅ README files for accidental credential leakage - ✅ Repository structure for `.env` or secret files **Tools Used:** - Pattern matching for common secret keywords: `password|token|secret|api_key|claim` - Manual inspection of environment variable usage - Cross-reference with Komodo UI documentation **Exclusions:** - Scripts directory (no compose files found) - Ansible directory (no active playbooks with secrets) --- **Report Version:** 1.0 **Next Review Date:** July 12, 2026 (Quarterly) **Validation Status:** Ready for Implementation ✅