T06 · System Persistence
Error
- Location
- scripts/tunnel-setup.sh:97
- Finding
- Reboot-Persistent Cloudflare Tunnel Runs with Unnecessary Root Privileges<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tunnel-setup.sh:97-117` **Additional Locations**: `SKILL.md:96-118`, `references/custom-domains.md:135-154` **Vulnerability Type**: System-level persistence and violation of least privilege **Risk Level**: High ### Complete Code Snippet ```bash # Create systemd service SERVICE_NAME="cloudflared-${AGENT}" SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" cat > "$SERVICE_FILE" << EOF [Unit] Description=Cloudflare Tunnel for OpenClaw agent: ${AGENT} After=network.target [Service] Type=simple User=root ExecStart=/usr/bin/cloudflared tunnel --no-autoupdate --config ${CONFIG_FILE} run Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable "$SERVICE_NAME" systemctl restart "$SERVICE_NAME" ``` The documented equivalent in `SKILL.md` also explicitly enables the persistent service: ```bash systemctl daemon-reload systemctl enable cloudflared-koda systemctl start cloudflared-koda systemctl is-active cloudflared-koda ``` The multi-agent instructions in `references/custom-domains.md` enable several persistent services: ```bash systemctl daemon-reload systemctl enable cloudflared-koda cloudflared-alex cloudflared-jordan systemctl start cloudflared-koda cloudflared-alex cloudflared-jordan ``` ### Technical Analysis The setup writes a system-level unit into `/etc/systemd/system`, enables it for startup under `multi-user.target`, and configures `Restart=always`. The resulting Cloudflare tunnel survives completion of the Skill, automatically starts after reboot, and reconnects whenever it exits. Cross-session persistence is relevant to the declared permanent-tunnel feature and is openly documented rather than concealed. Nevertheless, the implementation exceeds minimum necessary privileges because `cloudflared` is explicitly run as `root`. A tunnel process generally only needs permission to read its own configuration and credential file and ...[truncated 1834 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create a dedicated system account with no interactive login: ```bash useradd --system --home /var/lib/cloudflared --create-home \ --shell /usr/sbin/nologin cloudflared ``` 2. Store each tunnel credential outside `/root`, make it readable only by the dedicated account, and use restrictive permissions: ```bash install -d -o root -g cloudflared -m 0750 /etc/cloudflared install -o root -g cloudflared -m 0640 \ /root/.cloudflared/TUNNEL_UUID.json \ /etc/cloudflared/TUNNEL_UUID.json ``` 3. Run the service as the dedicated account: ```ini [Service] User=cloudflared Group=cloudflared ExecStart=/usr/bin/cloudflared tunnel --no-autoupdate --config /etc/cloudflared/openclaw-koda.yml run ``` 4. Add systemd sandboxing and privilege restrictions: ```ini NoNewPrivileges=true PrivateTmp=true PrivateDevices=true ProtectSystem=strict ProtectHome=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true RestrictSUIDSGID=true LockPersonality=true CapabilityBoundingSet= AmbientCapabilities= ReadOnlyPaths=/etc/cloudflared ``` 5. Ask for explicit operator confirmation before writing or enabling a persistent service. Offer a foreground or non-enabled service mode where cross-reboot persistence is unnecessary. 6. Use `systemctl enable --now` only after configuration validation succeeds, and document complete cleanup: ```bash systemctl disable --now cloudflared-koda rm -f /etc/systemd/system/cloudflared-koda.service systemctl daemon-reload systemctl reset-failed ``` 7. Document credential revocation and tunnel deletion as part of uninstalling the Skill-created resources. ]]>
