# Security Audit Report — Bastion Project **Date**: 2026-03-02 --- ## CRITICAL | # | Issue | Location | |---|-------|----------| | 1 | **Plaintext database credentials committed to repo** | `ghost/.env:18-20`, `ghost/stack.env:18-20` — Passwords like `ghostuserp@ssword` and `ghostghostpasswOrd` are in version control. `.gitignore` excludes `.env` but **not** `stack.env`. | | 2 | **Jenkins Docker socket mount (DooD)** | `jenkins/docker-compose.yaml:14` — `/var/run/docker.sock` is mounted into Jenkins. Any compromised plugin or malicious job gets **full host Docker access**, enabling container escape and root-level host compromise. | | 3 | **Netdata: near-root host access** | `netdata/docker-compose.yaml:8-13` — `SYS_PTRACE` + `SYS_ADMIN` capabilities, `apparmor:unconfined`, `pid: host`, and Docker socket mount. This is effectively root on the host. | | 4 | **World-readable credential files** | `ghost/.env` and `ghost/stack.env` have `0644` permissions — any local user can read the database passwords. | --- ## HIGH | # | Issue | Location | |---|-------|----------| | 5 | **No security headers on any nginx server block** | `nginx/nginx.conf` — Missing `Strict-Transport-Security`, `X-Frame-Options`, `X-Content-Type-Options`, `Content-Security-Policy`, `Referrer-Policy` on all proxied services. | | 6 | **No rate limiting** | `nginx/nginx.conf` — No `limit_req` or `limit_conn` directives anywhere. All endpoints are vulnerable to brute-force and DoS. | | 7 | **Unpinned image tags (`:latest`)** | `git-server/Dockerfile`, `git-server/cgit/Dockerfile`, `jenkins/docker-compose.yaml` (Gerrit), `nginx/docker-compose.yaml`, `netdata/docker-compose.yaml` — All use `:latest`, risking supply-chain attacks and breaking changes. | | 8 | **Cockpit: SSL verification disabled** | `cockpit/nginx-cockpit.conf:15` — `proxy_ssl_verify off` for the Cockpit proxy, vulnerable to MITM. | | 9 | **SQL injection risk in DB init script** | `ghost/mysql-init-script/create-multiple-databases.sh:6-14` — Database names from env var interpolated into SQL with only backtick escaping; no input validation. | --- ## MEDIUM | # | Issue | Location | |---|-------|----------| | 10 | **No resource limits on any container** | All compose files — No CPU/memory limits. A runaway process in any service can starve the host. | | 11 | **Containers run as root** | Ghost, MySQL, Gerrit, git-server — No `user:` directive or `USER` instruction to drop privileges. | | 12 | **SSH hardening incomplete** | `git-server/sshd_config` — `PermitRootLogin` not explicitly set to `no`, no `MaxAuthTries`, SFTP subsystem enabled (unnecessary for git-only server). | | 13 | **Unquoted variables in shell script** | `nginx/run_certbot.sh:9-10` — `mkdir $lts_dir/...` without quotes; path injection risk. | | 14 | **DB password in healthcheck CLI args** | `ghost/compose.yml:56` — `mysqladmin ping -p$$MYSQL_ROOT_PASSWORD` exposes the password in process listing. | | 15 | **Public DNS for OCSP stapling** | `nginx/nginx.conf` — `resolver 8.8.8.8` with no fallback and no timeout. DNS queries leak to Google; single point of failure. | | 16 | **No certificate auto-renewal** | `nginx/run_certbot.sh` — Manual certbot execution only. Certs will silently expire. | | 17 | **Email address hardcoded** | `nginx/run_certbot.sh:3` — Personal email in source control. | --- ## Recommended Actions ### Immediate 1. Rotate all database passwords on the live server 2. `chmod 600` on `.env` and `stack.env` files 3. Add `stack.env` to `.gitignore` and scrub credentials from git history (`git filter-repo`) 4. Add security headers to all nginx HTTPS server blocks ### This week 5. Pin all Docker images to specific version digests 6. Add `limit_req` rate limiting to nginx 7. Add resource limits (`deploy.resources.limits`) to all compose services 8. Harden `sshd_config`: set `PermitRootLogin no`, `MaxAuthTries 3`, remove SFTP subsystem ### Short-term 9. Evaluate alternatives to Jenkins DooD (rootless Docker, dedicated build agents, or a restricted Docker daemon) 10. Reduce Netdata capabilities — remove `SYS_ADMIN`, keep only `SYS_PTRACE` if needed, re-enable AppArmor 11. Add `user:` directives to drop root in Ghost, MySQL, and Gerrit containers 12. Set up automated certbot renewal (cron or systemd timer) 13. Validate input in `create-multiple-databases.sh` (allow only `[a-zA-Z0-9_]`)