chore: add Cloud Agent development environment - #4
donny-devops wants to merge 2 commits into
Conversation
Adds .cursor/environment.json plus idempotent install/start scripts that provision Docker Engine + Compose, start dockerd (fuse-overlayfs) per boot, enable shared mount propagation for node-exporter, install yamllint, and seed per-stack .env files. Co-authored-by: Adonis Jimenez <donny-devops@users.noreply.github.com>
ECC Tools / Security EvidenceCommit: Security evidence gate passed (success) No security-sensitive scanner-evidence gap detected. Mode: enforce Scanned 3 changed file(s). No missing scanner-evidence signal was detected. Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
ECC Tools / PR Risk TaxonomyCommit: PR taxonomy clear (success) Scanned 3 changed file(s). No taxonomy bucket signals were detected. Scanned 3 changed file(s). No PR taxonomy bucket signals were detected. Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
ECC Tools / Reference Set ReadinessCommit: Reference set readiness gaps detected (neutral) Reference evidence present for 0/7 areas (0%) across 3 changed file(s). This check is based on files changed in this PR. Repository-level readiness is still reported by
Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
ECC Tools / Hosted Promotion ReadinessCommit: Hosted promotion readiness passed (success) No hosted promotion evidence gaps detected across 3 changed file(s); 0 corpus scenarios had matching evidence. This check compares PR file changes against the evaluator/RAG promotion corpus in No evaluator corpus scenarios matched this PR. Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
There was a problem hiding this comment.
Summary
This PR adds a Cloud Agent development environment with Docker-in-Docker support. The implementation is mostly solid, but there are three critical security and logic issues that must be addressed before merge:
Critical Issues:
- Security: GPG key download lacks fingerprint verification (install.sh:10-11)
- Security: yamllint installed without version pinning (install.sh:32)
- Logic Error: dockerd startup timeout detection is broken (start.sh:13-18)
All three issues have commit-able fixes provided. Please apply the suggested changes to ensure secure and reliable environment provisioning.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
| sudo usermod -aG docker "$(id -un)" || true | ||
|
|
||
| # ── CI tooling: yamllint ─────────────────────────────────────── | ||
| python3 -m pip install --user --quiet --upgrade yamllint |
There was a problem hiding this comment.
🛑 Security Vulnerability: Installing yamllint without version pinning allows arbitrary code execution if the package is compromised between releases.1
Pin to a specific version to ensure reproducible and auditable builds.
| python3 -m pip install --user --quiet --upgrade yamllint | |
| python3 -m pip install --user --quiet yamllint==3.0.0 |
Footnotes
-
CWE-494: Download of Code Without Integrity Check - https://cwe.mitre.org/data/definitions/494.html ↩
There was a problem hiding this comment.
Micro-Learning Topic: Download of Code Without Integrity Check (CWE 494)
Matched on "CWE-494"
The product downloads source code or an executable from a remote location and executes the code without sufficiently verifying the origin and integrity of the code.
Try a challenge in Secure Code Warrior
| for _ in $(seq 1 30); do | ||
| if sudo docker info >/dev/null 2>&1; then | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done |
There was a problem hiding this comment.
🛑 Logic Error: The timeout check after the wait loop never triggers because the loop doesn't track whether it exhausted all iterations. If dockerd fails to start within 30 seconds, the script silently continues to line 21 where the check will catch it, but this creates a race condition if dockerd becomes ready between line 18 and line 21.
Add explicit timeout detection within the loop.
| for _ in $(seq 1 30); do | |
| if sudo docker info >/dev/null 2>&1; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| timeout=30 | |
| for i in $(seq 1 "$timeout"); do | |
| if sudo docker info >/dev/null 2>&1; then | |
| break | |
| fi | |
| if [ "$i" -eq "$timeout" ]; then | |
| echo "dockerd failed to start within ${timeout}s; see /var/log/dockerd.log" >&2 | |
| exit 1 | |
| fi | |
| sleep 1 | |
| done |
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | ||
| | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg |
There was a problem hiding this comment.
🛑 Security Vulnerability: The GPG key download lacks checksum verification, allowing potential MITM attacks to inject malicious keys that could authenticate compromised Docker packages.1
Verify the GPG key fingerprint after download to ensure authenticity.
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | |
| | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg | |
| curl -fsSL \ | |
| | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg | |
| # Verify the key fingerprint (Docker's official fingerprint) | |
| if ! sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/docker.gpg --list-keys | grep -q "9DC858229FC7DD38854AE2D88D81803C0EBFCD88"; then | |
| echo "ERROR: Docker GPG key fingerprint verification failed" >&2 | |
| sudo rm -f /etc/apt/keyrings/docker.gpg | |
| exit 1 | |
| fi |
Footnotes
-
CWE-494: Download of Code Without Integrity Check - https://cwe.mitre.org/data/definitions/494.html ↩
…s it A backgrounded dockerd launched from start and then abandoned can be reaped once the start command returns, leaving new agents without a running daemon. Foreground the daemon (exec) so it stays attached for the agent lifetime. Co-authored-by: Adonis Jimenez <donny-devops@users.noreply.github.com>
ECC Tools / Security EvidenceCommit: Security evidence gate passed (success) No security-sensitive scanner-evidence gap detected. Mode: enforce Scanned 3 changed file(s). No missing scanner-evidence signal was detected. Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
ECC Tools / PR Risk TaxonomyCommit: PR taxonomy clear (success) Scanned 3 changed file(s). No taxonomy bucket signals were detected. Scanned 3 changed file(s). No PR taxonomy bucket signals were detected. Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
ECC Tools / Reference Set ReadinessCommit: Reference set readiness gaps detected (neutral) Reference evidence present for 0/7 areas (0%) across 3 changed file(s). This check is based on files changed in this PR. Repository-level readiness is still reported by
Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
ECC Tools / Hosted Promotion ReadinessCommit: Hosted promotion readiness passed (success) No hosted promotion evidence gaps detected across 3 changed file(s); 0 corpus scenarios had matching evidence. This check compares PR file changes against the evaluator/RAG promotion corpus in No evaluator corpus scenarios matched this PR. Check publication was denied or unavailable. An app owner must enable Checks: read and write, and the installation owner must approve the updated permission. |
What & why
This repo is a collection of Docker Compose stacks, so a usable Cloud Agent environment needs Docker-in-Docker plus the tooling CI relies on. This PR adds a repository-managed environment under
.cursor/that provisions Docker Engine + Compose, runs the Docker daemon on every boot, installs the YAML linter used by CI, and seeds per-stack.envfiles.Changes
.cursor/environment.json— repo-managed environment (user: ubuntu,install,start). A committed environment.json is the highest-precedence environment source, so merging this makes every future Cloud Agent on the repo pick it up automatically..cursor/install.sh— idempotent bootstrap: installs Docker Engine + Compose plugin (Docker apt repo), switches toiptables-legacy(needed for bridge networking in the nested VM), adds the agent user to thedockergroup, installsyamllint, and seedshomelab-dashboard/.envandmonitoring-analytics/.envfrom their.env.examplefiles (never overwriting an existing.env)..cursor/start.sh— per-boot startup: enables shared mount propagation (mount --make-rshared /, required by node-exporter'srslavebind mount) and runsdockerd(with thefuse-overlayfsstorage driver) in the foreground so the platform supervises it for the agent's lifetime.Docker installation lives in
install(one-time, captured into the environment snapshot); daemon startup is per-boot instart. The stacks are intentionally not auto-started — the two stacks both bind port 3000, so a developer brings up one stack at a time withdocker compose up.How to use
Validation
yamllint(CI config) passes on all YAML;docker compose config --quietpasses for both stacks.monitoring-analyticsbrought up: Prometheus actively scrapesprometheus,node-exporter, andcadvisor(allup=1); Alertmanager healthy and firing rules; live PromQL returns data.homelab-dashboardbrought up: Traefik, Homepage, Portainer, and Uptime Kuma all serve HTTP.install.shrun twice (idempotent).start.shforeground design verified: launched detached (as the platform does on boot),dockerdpersists after the launcher exits and runs containers..envfiles, valid compose config,hello-world, and Prometheus scraping targetsup.Follow-ups for full fidelity (optional services)
These need external access not in the egress allowlist / secrets and are not required for the core environment:
monitoring-analytics) installs its configured plugins fromgrafana.comon startup — addgrafana.comto the egress allowlist.homelab-dashboard) image is pulled fromlscr.io— addlscr.ioto the egress allowlist.homelab-dashboard) is configured for Slack notifications — set a validSLACK_WEBHOOK_URL.Separately, two pre-existing stack issues (independent of this environment) are worth a follow-up: the
lokihealthcheck useswget, which is not present in the loki image (so promtail'sdepends_on: service_healthynever unblocks), andjaegercannot create its badger dirs in the root-owned named volume because it runs as a non-root user.