Skip to content

security: harden default network bindings and container privileges#39

Merged
Miyamura80 merged 11 commits intomasterfrom
security/harden-defaults
Mar 23, 2026
Merged

security: harden default network bindings and container privileges#39
Miyamura80 merged 11 commits intomasterfrom
security/harden-defaults

Conversation

@Miyamura80
Copy link
Contributor

@Miyamura80 Miyamura80 commented Mar 23, 2026

Summary

  • VNC default bind β†’ 127.0.0.1: Was 0.0.0.0 (world-accessible). Users who need LAN access can override via vnc_bind_addr in config.
  • Monitor dashboard β†’ 127.0.0.1: Was 0.0.0.0. The dashboard has no authentication, so localhost-only is the safe default.
  • Conditional SYS_ADMIN + FUSE: Only granted for app_type=appimage (needs FUSE to mount). Folder, docker_image, and vnc_attach containers now run without elevated privileges.
  • Custom image pull warning: Logs a warn! when pulling images from remote registries.
  • Delete IDEA.md: Historical MVP design doc β€” all content already covered by README.md and CLAUDE.md.
  • Add TODO.md: Security hardening roadmap with prioritized items from audit.

Context

These are the low-risk, high-impact fixes from a security audit. They harden defaults without breaking any existing functionality β€” all previous behavior is still available via explicit config overrides.

Test plan

  • cargo test β€” 403 passed, 0 failed, 11 ignored
  • Manual: verify VNC still works with explicit "vnc_bind_addr": "0.0.0.0" override
  • Manual: verify AppImage tests still work (SYS_ADMIN granted)
  • Manual: verify folder/docker_image tests work without SYS_ADMIN

πŸ€– Generated with Claude Code


Open with Devin

- Bind VNC default to 127.0.0.1 instead of 0.0.0.0 (users can override via config)
- Bind monitor dashboard to 127.0.0.1 instead of 0.0.0.0
- Make SYS_ADMIN + /dev/fuse conditional on app_type=appimage (only app type that needs FUSE)
- Add warning log when pulling custom images from remote registries
- Delete historical IDEA.md (content already covered by README.md and CLAUDE.md)
- Add TODO.md with security hardening roadmap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR delivers a focused set of security hardening changes that harden default network bindings, remove unnecessary container privileges, and improve observability around trust-sensitive operations β€” all without breaking existing functionality (prior behavior is recoverable via explicit config overrides).

Key changes:

  • vnc_bind_addr default changed from 0.0.0.0 β†’ 127.0.0.1; validation added so invalid IP strings are rejected at config-parse time
  • Monitor dashboard bind address hardcoded to 127.0.0.1 (no auth on that endpoint); documented with an explanatory comment and a TODO.md follow-up for monitor_bind_addr
  • CAP_SYS_ADMIN and /dev/fuse removed from all containers β€” AppImages already use --appimage-extract-and-run so FUSE is not needed
  • Custom Docker image pulls now emit a warn! trust notice only on a genuine 404 (not found locally), distinguishing that case from other Docker errors which now propagate as hard failures
  • format_host_port() utility added so IPv6 bind addresses are rendered with correct bracket notation ([::1]:5900) in log output and the monitor dashboard
  • TODO.md added with a prioritised security roadmap covering remaining audit findings (path traversal, SSRF, resource limits, prompt injection, etc.)
  • IDEA.md deleted as its content is superseded by README.md and CLAUDE.md

Confidence Score: 4/5

  • Safe to merge β€” all changes are defensive hardening with no breaking functional changes and 403 passing tests.
  • The changes are narrowly scoped, well-tested (6 new unit tests for the new validation and formatting logic), and the PR description accurately describes every behaviour change. The one residual fragility is the bollard version-specific DockerResponseServerError { status_code: 404 } pattern match β€” if bollard changes its error representation in a future version bump, the pull path will silently become unreachable and fall into the hard-error arm, blocking image pulls. This is well-documented in a code comment and is the safe failure mode, but it does warrant attention when bumping the bollard dependency. Everything else is straightforward.
  • src/docker/mod.rs β€” the bollard 404 error-variant match should be re-verified after any bollard version bump.

Important Files Changed

Filename Overview
src/config.rs Hardens VNC default bind address to 127.0.0.1, adds IP address validation for vnc_bind_addr, and introduces format_host_port() for correct IPv6 bracket formatting. Well-tested with 6 new unit tests covering invalid IPs, empty strings, valid IPv4/IPv6, and the formatting utility.
src/docker/mod.rs Removes CAP_SYS_ADMIN and /dev/fuse from all containers, improves inspect_image error handling to distinguish 404 from other Docker errors, adds a trust warning when pulling remote images, and updates VNC log output to use format_host_port for IPv6 correctness. The 404-specific match arm has a noted version dependency on bollard 0.18.x.
src/monitor_server.rs Bind address changed from 0.0.0.0 to 127.0.0.1 with an explanatory comment documenting the intentional localhost-only restriction and absence of a config override. Clean, minimal, correct change.
src/interactive.rs Two VNC display-string calls updated to use format_host_port() for IPv6-correct output. Purely cosmetic/correctness change with no logic impact.
src/orchestration.rs VNC URL construction updated to use format_host_port() so IPv6 addresses are correctly bracketed in the monitor dashboard URL. No logic changes.
TODO.md New security hardening roadmap. Well-structured with prioritized items across high/medium/low categories. Covers the edge cases raised in prior review threads (needs_fuse escape hatch, monitor bind override).
IDEA.md Deleted historical MVP design document. PR description confirms content is superseded by README.md and CLAUDE.md.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[DockerSession::create] --> B{custom_image?}
    B -- Yes --> C[inspect_image]
    C -- Ok --> D[Use local image]
    C -- 404 --> E[warn! trust warning\npull from remote registry]
    E --> F[stream pull with debug logs]
    F --> D
    C -- Other error --> G[AppError::Infra\nfail fast]
    B -- No --> H[ensure_image built-in]
    H --> D
    D --> I[HostConfig::default\nno CAP_SYS_ADMIN\nno /dev/fuse]
    I --> J{vnc_port set?}
    J -- Yes --> K[bind host_ip = vnc_bind_addr\ndefault: 127.0.0.1\nformat_host_port for display]
    J -- No --> L[VNC not exposed]
    K --> M[Create + Start container]
    L --> M

    subgraph Config Validation
        V1[parse_and_validate] --> V2{vnc_bind_addr\nvalid IpAddr?}
        V2 -- No --> V3[AppError::Config]
        V2 -- Yes --> V4[proceed]
    end

    subgraph Network Bindings
        N1[VNC: vnc_bind_addr\ndefault 127.0.0.1\noverridable via config]
        N2[Monitor dashboard: 127.0.0.1\nhardcoded, no override yet]
    end
Loading

Reviews (10): Last reviewed commit: "test: add coverage for format_host_port ..." | Re-trigger Greptile

greptile-apps[bot]

This comment was marked as resolved.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

βœ… Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

Miyamura80 and others added 2 commits March 23, 2026 10:50
- Merge redundant warn! + info! into single warn! for custom image pulls
- Distinguish 404 (image not found) from other Docker errors in inspect_image
  to avoid misleading trust warnings on infrastructure hiccups

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep security-hardened inspect_image error handling (404 match)
with master's formatting style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai

greptile-apps[bot]

This comment was marked as resolved.

Add comment explaining the asymmetry with VNC (which has vnc_bind_addr
override). Add monitor_bind_addr to TODO.md as future work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai

greptile-apps[bot]

This comment was marked as resolved.

Add docstring note on DockerSession::create() explaining that only
appimage containers get SYS_ADMIN+FUSE, and custom images wrapping
AppImages will need a future needs_fuse config field. Added to TODO.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai

AppImages are already launched with --appimage-extract-and-run (see
deploy.rs), which bypasses FUSE entirely. SYS_ADMIN + /dev/fuse were
never exercised at runtime, so remove them from container creation
for all app types. This closes the container escape vector.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai review

- Add comment pinning the bollard error variant match to 0.18.x so
  future maintainers know to re-verify after dependency bumps
- Validate vnc_bind_addr as a legal IP address at config parse time
  instead of letting invalid values surface as cryptic Docker errors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai review

devin-ai-integration[bot]

This comment was marked as resolved.

Remove the !is_empty() guard that let an empty string bypass IP
validation and default to 0.0.0.0 in Docker. Add tests for invalid,
empty, and valid vnc_bind_addr values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai review

- Format IPv6 vnc_bind_addr with brackets in log output (e.g. [::1]:5900)
- Use clearer test value for invalid vnc_bind_addr

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai review

devin-ai-integration[bot]

This comment was marked as resolved.

Extract format_host_port() helper in config.rs and use it in all
four places that format vnc_bind_addr:port strings (docker/mod.rs,
orchestration.rs, interactive.rs x2). Fixes broken dashboard links
for IPv6 users.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai review

Add tests for format_host_port() IPv4/IPv6 paths and IPv6
vnc_bind_addr acceptance in config validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Miyamura80
Copy link
Contributor Author

@greptileai review

@Miyamura80 Miyamura80 merged commit d16bc40 into master Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant