Cloudron makes it easy to run web apps like WordPress, Nextcloud, GitLab on your server. Find out more or install now.


Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Bookmarks
  • Search
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo

Cloudron Forum

Offical apps | Community apps | Demo | Docs | Install
  1. Cloudron Forum
  2. Discuss
  3. Running AI agents on Cloudron: infiltrating other containers and admin services

Running AI agents on Cloudron: infiltrating other containers and admin services

Scheduled Pinned Locked Moved Discuss
6 Posts 3 Posters 95 Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • nostrdevN
    nostrdevN
    nostrdev
    wrote last edited by
    #1

    The below is the result of some back and forth with LLMs following our comment on this thread: https://forum.cloudron.io/topic/15616/hermes-agent/28?_=1788437686460

    The analysis does not change our position - whilst 99.99% of the time it may be safe to run agents in a cloudron container, the relative ease by which (entirely preexisting) vulnerabilities may be exposed, ought to necessitate the consideration of perhaps running a separate cloudron instance for non-business critical services.

    If your cloudron does not serve your company email, vpn, nextcloud, btcpay server etc - you can probably ignore the below.

    If readers who are more technical, or well-versed in cloudron architecture and background can correct this analysis, and set things straight, we would appreciate and welcome the feedback.

    -- nostr dev --

    Generated 2026-09-05. Grounded in the Cloudron platform source (box 10.0.4, release tarball from releases.cloudron.io) and the docs' own App isolation claims.

    This is a follow-up to the agent-isolation discussion in the Hermes Agent thread. It is a single-purpose, source-audited writeup: what the Cloudron platform actually puts between containers, what an administrator and a package maintainer can add, what remains, and a verdict for boxes that carry business-critical apps.

    Threat model: the agent container is compromised (the realistic vector is prompt injection — the agent runs code that follows instructions found in web content). This document asks exactly one question: what can that compromised container reach on the box — other app containers, and the platform's admin services? Data exfiltration and harm to the agent itself are out of scope.

    TL;DR: if the agent container falls, everything else on the box is network-reachable — every co-tenant's app port (by design: that is how the platform's nginx reaches apps), the shared database instances, the platform admin services, and the dashboard. Authentication is the only door, and part of the doors' hygiene is outside your control. Each claim below is verified against the 10.0.4 source with file references so it can be checked, corrected, or argued with.

    Evidence base

    This section lists the platform facts the analysis rests on, each verified in the Cloudron 10.0.4 source or docs.

    • Normal model: every Cloudron app is a single-app container, and a typical box hosts many of them side by side on one user-defined bridge cloudron (172.18.0.0/16) (src/docker.js, src/platform.js). The network is created with a subnet and gateway 172.18.0.1 — it is NOT --internal, and nothing filters container-to-container traffic (setup/start/cloudron-firewall.sh hooks INPUT only). A MASQUERADE rule is added so containers can also reach each other via the host's public IP.
    • Platform nginx reaches each app by its container IP: proxy_pass http://<ip>:<port> (src/nginxconfig.ejs). Consequence: an app's primary HTTP port MUST bind all interfaces (0.0.0.0) to be proxied at all — and is therefore reachable from every other container on the bridge, by design.
    • setup/start/cloudron-firewall.sh line 119: unconditional ACCEPT from 172.18.0.0/16 to the bridge IP 172.18.0.1 on 3002 (ldap), 3003 (dockerproxy), 3006 (app-bridge), 3007 (scim). DNS (53) to unbound is allowed. The INPUT rules for 22/80/202/443 have no source restriction either, so the dashboard on 443 is reachable from containers — and every app gets an /etc/hosts entry pinning the dashboard FQDN to 172.18.0.1 (src/docker.js), so it is always resolvable by name from inside containers.
    • SSO/proxyAuth (src/nginxconfig.ejs auth_request, src/reverseproxy.js) is enforced by the platform's nginx on the PUBLIC route only. A direct connection to the container's bridge IP does not pass through it — the auth you face on that path is whatever the app itself implements.
    • docs security.md: apps connect to addons using authentication only; one app cannot access another app's database or files. Shared addon instances have static IPs (MySQL 172.18.30.1, PostgreSQL 172.18.30.2, MongoDB 172.18.30.3) and rate limits (5000 conns/sec per app).
    • src/dockerproxy.js: the Docker API proxy on 3003 authorizes by source IP plus manifest.addons.docker; without the addon it returns 401, and it strips Privileged/CapAdd/Devices/SecurityOpt on create.
    • The LDAP service on 3002 authenticates binds — the system bind checks a real per-box secret (src/directoryserver.js), and the ldap addon is what hands that secret to the app.
    • setup/start/docker-cloudron-app.apparmor: "Keep in sync with Docker's docker-default" — grants network, capability and file broadly; denies mount and /proc, /sys writes. No custom seccomp, no user-namespace remap, no per-app runtime choice anywhere in src/ or setup/.
    • NET_RAW is dropped unless the manifest asks for "ping" (src/docker.js) — raw-packet spoofing is blocked, but ordinary TCP connect scanning of the bridge is fully possible.

    The 3 key platform protections

    This section describes what the platform itself puts between a compromised container and the other containers / admin services. These are what make ordinary multi-app co-tenancy safe.

    1. Per-app credentials and authentication-only access to shared services. The platform creates unique credentials for each app on every addon (databases, mail, LDAP) and rotates them itself; every shared service accepts only authenticated access. What it stops: a compromised container can open TCP to any co-tenant and to the shared MySQL/PostgreSQL/Mongo instances, but it holds no valid credentials for them — "one app cannot access another app's database or files". On the flat, unfiltered bridge, authentication is the only inter-app barrier, and it is a real one. Its limits: it protects data, not reachability — and it only covers services that authenticate. The ldap addon hands over a valid directory secret, the docker addon unlocks the dockerproxy; and an app's own HTTP port has only whatever auth that app implements (see protection 3 for what SSO does and does not cover).

    2. The container confinement stack applied to every container. Every app container is created read-only rootfs, runs as a non-root user, has NET_RAW dropped unless the manifest asks for it, and runs under the docker-cloudron-app AppArmor profile that denies mount and /proc, /sys writes. What it stops: tampering with the shared kernel interfaces and the common container-escape primitives — i.e. it protects the HOST, which is the admin service of last resort. Its limits, verified in source: the profile is deliberately a copy of Docker's docker-default (baseline, not hardened), and it does nothing to restrict network reach into other containers — a full TCP connect scan of the bridge is trivially possible from a compromised app.

    3. Authenticated TLS proxy as the only public ingress. Each app is served on its own subdomain behind the platform's nginx with TLS; the whole container network (172.18.0.0/16) is unreachable from outside the server, no container port is directly exposed to the internet, and origin isolation means XSS in one app cannot steal another app's session. What it stops: infiltration of any app or admin surface from the public internet — attempts must originate from inside the box. Its limits: this is exactly the position a compromised agent container is in — inside the box, on the bridge, with every co-tenant and admin service routable. And SSO/proxyAuth are enforced on this public route only; they do not apply to a direct bridge connection to the container IP, where the only barrier is the app's own auth.

    Hardening from the Cloudron admin's seat

    This section is what the box administrator can actually do, with the controls Cloudron gives them, to shrink the infiltration surface.

    • Enable SSO/proxyAuth on every app that supports it — but understand what it buys: it guards the front door (public route through nginx). It does nothing for a direct container-to-container connection, which lands on the app's own auth. Treat it as defense for the internet path, not the bridge path.
    • Audit what each app exposes on the bridge. From each app's web terminal, run ss -ltnp and check listeners: you should see the app's httpPort on 0.0.0.0 (normal, required) and anything else that app bundles — a Redis, an internal DB, an admin panel — on 127.0.0.1 only. Anything else on 0.0.0.0 is a bridge-reachable door with only the app's own auth in front of it.
    • Harden the dashboard. It is resolvable by name and reachable on 443 from every container. Single operator account, strong password, 2FA, and restrict dashboard access to admin IPs. Watch the eventlog: ANY failed login originating from a 172.18.x.x address is a container trying the admin door — on a normal box that should be zero, ever.
    • Keep the box patched, urgently. The confinement stack is deliberately baseline (docker-default AppArmor copy, stock seccomp, no userns-remap, runc only — Sysbox and Podman were both declined by Cloudron upstream), so the host is defended mainly by patching speed. Verify unattended-upgrades is active; treat kernel/runc/dockerd CVEs on a box that runs agents as urgent, not routine.
    • Tune detection to the canaries that matter. Alert on container-originated logins to the dashboard, on container-to-172.18.0.1 flows beyond DNS/dashboard (platform services 3002/3003/3006/3007 are always reachable), and on auth-failure spikes at shared addons (brute-force from the bridge). None of this traffic is filtered by the platform, so only your monitoring sees it.
    • Keep tested, encrypted backups. If infiltration succeeds anywhere, rebuild the affected app from backup rather than forensically clean it.

    Hardening from the app maintainer's seat

    This section is what a package maintainer controls in their own app's container — written for the agent package, but the same rules apply to every package.

    • Ship zero addons unless the feature is essential. localstorage only. Each addon mints credentials that are VALID against a shared or admin service: ldap hands the app a real directory secret, docker unlocks container lifecycle via the dockerproxy, sendmail/recvmail authenticates against the mail container, sql mints a database user. No addons = the container holds zero keys that open any admin door. Treat any addon addition on an agent package as a security decision, not a convenience.
    • Bind bundled daemons to 127.0.0.1, never 0.0.0.0. Yes, 0.0.0.0 binds are fully possible in a Cloudron container — and required for the primary httpPort (platform nginx proxies to the container IP). But sidecar services the platform never needs to reach — the agent's internal DB, dashboards, worker APIs — must bind loopback. Scheduler tasks and backup containers share the app's network namespace, so loopback does not break them. Loopback binding is the difference between a bundled daemon being reachable only by its own app and by every co-tenant on the bridge.
    • Declare no capabilities. No net_admin (adds NET_ADMIN + NET_RAW), no ping (keeps NET_RAW dropped). Non-root user throughout.
    • Make the app's own HTTP auth real. The httpPort must listen on 0.0.0.0 and is therefore reachable by co-tenants directly, bypassing any platform proxy auth. Whatever the app serves on that port needs its own authentication — for an agent package, the gateway API key/session the package already generates. Never ship the primary port unauthenticated.
    • Health check and limits. A real health-check path (not an SPA fallback that always returns 200), and memory/CPU limits so a runaway process cannot starve the box.

    Residual risks after all hardening

    This section is what remains attackable — from the compromised agent container into other containers and admin services — even with the platform protections and both hardening seats applied.

    1. Every co-tenant's primary port is reachable, by design, with only the app's own auth in front. The platform's nginx reaches apps by container IP; therefore every app's HTTP port binds all interfaces and every other container can connect to it directly. SSO/proxyAuth do not apply on that path. So the inter-app barrier for the typical web app reduces to the app's own login form — plus per-app credentials where the app talks to addons. An agent container can enumerate co-tenants (Docker DNS or a /16 sweep) and methodically try every front page and login. Detection: container-to-container flow alerts on the host. Mitigation: the audits above (know which of your apps have weak or default auth on their primary port); SSO wherever the app supports it, for the front door at least.

    2. Platform admin services are permanently reachable from every container. Any compromised container can open TCP to 172.18.0.1:3002 (ldap), 3003 (dockerproxy), 3006 (app-bridge), 3007 (scim), DNS, and the dashboard on 443 — unconditionally allowed by the firewall. Without addons these reject the agent's requests (dockerproxy 401s, ldap demands the bind secret), but the attack surface never goes away: the dashboard login accepts attempts from container IPs (rate-limited to 10/s, not blocked), and an unauthenticated bug or info leak in any of these services would be reachable by exactly the workload most likely to look for it. Detection: the 172.18.x failed-login canary; container-to-172.18.0.1 flow alerts. Mitigation: 2FA and IP restriction on the dashboard; accept the network path as a platform constant and monitor it.

    3. Kernel/container escape = the host = every admin service at once. The confinement stack blocks the common primitives, but it is deliberately baseline. Operator hardening does not change escape probability. A public runc/kernel escape lands on the host, which means: every container's /app/data, the platform database (all app credentials, user directory), the docker daemon itself, and the operator's SSH surface. This is the maximum-severity outcome of the infiltration question. Detection: host-side auditd on mount/nsenter/setns; file-integrity monitoring; alert on unexpected privileged processes. Mitigation: urgent patch posture; no SSH keys or VPN credentials stored on the box; snapshot for fast rebuild.

    4. Every addon is a key into an admin or shared service. The "zero addons" rule is load-bearing and fragile: the moment the agent's manifest gains an addon for a feature (ldap for directory sync, docker for builds, sendmail for notifications), the platform mints credentials valid against an admin or shared service — and those live in the container env, readable by the injected process. Addon creep is the quiet way the infiltration surface regrows. Detection: review the manifest diff at every package update. Mitigation: treat every addon request on an agent package as a security decision.

    5. The operator's session is the softest admin door. The agent has a web surface the operator logs into (dashboard, kanban, chat history). A prompt-injected agent renders attacker-influenced content in that surface — crafted links, file names, session summaries — aimed at the one admin who trusts it. The operator's browser session is a realistic path from "agent compromised" to "box admin compromised" that no network control addresses. Mitigation: keep the dashboard in a separate browser profile from the agent UI; 2FA everywhere; treat links and downloads coming out of the agent's UI as untrusted input.

    Bottom line

    This section is the reachability takeaway, then the verdict for business-critical boxes.

    Cloudron's platform protections make ordinary multi-app co-tenancy safe: other containers' data is behind per-app credentials, the host is behind a baseline-but-real confinement stack, and everything admin-facing sits behind an authenticated TLS proxy on the public side. But for a compromised agent container the question is reachability, and the reachability facts are: the bridge is flat and unfiltered; every co-tenant's primary port is reachable by design (that is how the platform's nginx works) with only the app's own auth in front, since SSO/proxyAuth guard the public route alone; the platform admin services and the dashboard are unconditionally reachable from every container; and an addon minted for convenience is a key into an admin door. The practical posture: zero addons on the agent package, loopback for everything the platform does not need to reach, real auth on the primary port, a 2FA-hardened dashboard, urgent kernel patching, and detection tuned to the canaries that matter — container-originated admin logins, container-to-container and container-to-platform flows. The honest caveat from the source: the confinement stack is deliberately baseline, so the host itself remains defended mainly by patching speed.

    Is the risk real if you run your business on the box? Yes — but it decomposes.

    • What is genuinely new with an agent: the probability of the first container falling. A browsing agent converts any webpage it reads into an attack surface — prompt injection is routinely demonstrated, not theoretical, and an agent that browses daily for months will eventually ingest attacker-controlled content. Most business apps are only attackable through their own public interface; the agent moves the entry point to "anything the agent will read". Second, the operator-session vector: the agent renders links, filenames, and summaries that the one admin who logs into everything trusts by default — a path from "agent pwned" to "box admin pwned" that no network control addresses, with mail (the master key to the business's identity infrastructure) sitting on the same box. Third, the crown jewels of a business box — mail, files, password manager — are exactly the co-tenants whose only inter-app barrier is their own login form on a bridge-reachable port.
    • What is honestly not new: the post-compromise topology. If any PHP app on your box is popped today, it can already TCP-scan every co-tenant and try their login forms; businesses have quietly accepted that trade for years. The agent changes the likelihood of the first container falling — not the map of what is reachable after it falls.
    • Practical verdict: a business box hosting mail, files, or password credentials alongside an always-on browsing agent is an unfavorable bet — injection probability multiplied by the value of mail — and we would not run it that way. The same box with only public-facing web apps, plus a zero-addon, loopback-bound agent behind SSO/2FA and the canary alerts above, is defensible — with eyes open that the host boundary is baseline confinement plus patch speed.

    This writeup is the evidence base behind the position we stated in the Hermes Agent thread: the platform's protections assume the threat is outside the box, and an agent is a workload that reliably puts the threat inside it. That is why we run agent workloads off Cloudron (Firecracker microVMs on dedicated hardware, currently under evaluation). For everyone who does want agents on Cloudron, the two hardening sections above are the practical checklist — and if any of the file-cited facts is wrong, the source is public and we would genuinely like to know.

    timconsidineT 1 Reply Last reply
    3
    • nostrdevN nostrdev referenced this topic
    • robiR
      robiR
      robi
      wrote last edited by
      #2

      There is a way to harden containers by changing out the runc runtime for something like sysbox, which makes it more VM like.

      Single parameter change while starting a container which would need platform support to specify per app. (otherwise manual starts with alternate runtime)

      There may be other options if more runtimes are made available for such specific use cases.

      Conscious tech

      1 Reply Last reply
      1
      • nostrdevN nostrdev

        The below is the result of some back and forth with LLMs following our comment on this thread: https://forum.cloudron.io/topic/15616/hermes-agent/28?_=1788437686460

        The analysis does not change our position - whilst 99.99% of the time it may be safe to run agents in a cloudron container, the relative ease by which (entirely preexisting) vulnerabilities may be exposed, ought to necessitate the consideration of perhaps running a separate cloudron instance for non-business critical services.

        If your cloudron does not serve your company email, vpn, nextcloud, btcpay server etc - you can probably ignore the below.

        If readers who are more technical, or well-versed in cloudron architecture and background can correct this analysis, and set things straight, we would appreciate and welcome the feedback.

        -- nostr dev --

        Generated 2026-09-05. Grounded in the Cloudron platform source (box 10.0.4, release tarball from releases.cloudron.io) and the docs' own App isolation claims.

        This is a follow-up to the agent-isolation discussion in the Hermes Agent thread. It is a single-purpose, source-audited writeup: what the Cloudron platform actually puts between containers, what an administrator and a package maintainer can add, what remains, and a verdict for boxes that carry business-critical apps.

        Threat model: the agent container is compromised (the realistic vector is prompt injection — the agent runs code that follows instructions found in web content). This document asks exactly one question: what can that compromised container reach on the box — other app containers, and the platform's admin services? Data exfiltration and harm to the agent itself are out of scope.

        TL;DR: if the agent container falls, everything else on the box is network-reachable — every co-tenant's app port (by design: that is how the platform's nginx reaches apps), the shared database instances, the platform admin services, and the dashboard. Authentication is the only door, and part of the doors' hygiene is outside your control. Each claim below is verified against the 10.0.4 source with file references so it can be checked, corrected, or argued with.

        Evidence base

        This section lists the platform facts the analysis rests on, each verified in the Cloudron 10.0.4 source or docs.

        • Normal model: every Cloudron app is a single-app container, and a typical box hosts many of them side by side on one user-defined bridge cloudron (172.18.0.0/16) (src/docker.js, src/platform.js). The network is created with a subnet and gateway 172.18.0.1 — it is NOT --internal, and nothing filters container-to-container traffic (setup/start/cloudron-firewall.sh hooks INPUT only). A MASQUERADE rule is added so containers can also reach each other via the host's public IP.
        • Platform nginx reaches each app by its container IP: proxy_pass http://<ip>:<port> (src/nginxconfig.ejs). Consequence: an app's primary HTTP port MUST bind all interfaces (0.0.0.0) to be proxied at all — and is therefore reachable from every other container on the bridge, by design.
        • setup/start/cloudron-firewall.sh line 119: unconditional ACCEPT from 172.18.0.0/16 to the bridge IP 172.18.0.1 on 3002 (ldap), 3003 (dockerproxy), 3006 (app-bridge), 3007 (scim). DNS (53) to unbound is allowed. The INPUT rules for 22/80/202/443 have no source restriction either, so the dashboard on 443 is reachable from containers — and every app gets an /etc/hosts entry pinning the dashboard FQDN to 172.18.0.1 (src/docker.js), so it is always resolvable by name from inside containers.
        • SSO/proxyAuth (src/nginxconfig.ejs auth_request, src/reverseproxy.js) is enforced by the platform's nginx on the PUBLIC route only. A direct connection to the container's bridge IP does not pass through it — the auth you face on that path is whatever the app itself implements.
        • docs security.md: apps connect to addons using authentication only; one app cannot access another app's database or files. Shared addon instances have static IPs (MySQL 172.18.30.1, PostgreSQL 172.18.30.2, MongoDB 172.18.30.3) and rate limits (5000 conns/sec per app).
        • src/dockerproxy.js: the Docker API proxy on 3003 authorizes by source IP plus manifest.addons.docker; without the addon it returns 401, and it strips Privileged/CapAdd/Devices/SecurityOpt on create.
        • The LDAP service on 3002 authenticates binds — the system bind checks a real per-box secret (src/directoryserver.js), and the ldap addon is what hands that secret to the app.
        • setup/start/docker-cloudron-app.apparmor: "Keep in sync with Docker's docker-default" — grants network, capability and file broadly; denies mount and /proc, /sys writes. No custom seccomp, no user-namespace remap, no per-app runtime choice anywhere in src/ or setup/.
        • NET_RAW is dropped unless the manifest asks for "ping" (src/docker.js) — raw-packet spoofing is blocked, but ordinary TCP connect scanning of the bridge is fully possible.

        The 3 key platform protections

        This section describes what the platform itself puts between a compromised container and the other containers / admin services. These are what make ordinary multi-app co-tenancy safe.

        1. Per-app credentials and authentication-only access to shared services. The platform creates unique credentials for each app on every addon (databases, mail, LDAP) and rotates them itself; every shared service accepts only authenticated access. What it stops: a compromised container can open TCP to any co-tenant and to the shared MySQL/PostgreSQL/Mongo instances, but it holds no valid credentials for them — "one app cannot access another app's database or files". On the flat, unfiltered bridge, authentication is the only inter-app barrier, and it is a real one. Its limits: it protects data, not reachability — and it only covers services that authenticate. The ldap addon hands over a valid directory secret, the docker addon unlocks the dockerproxy; and an app's own HTTP port has only whatever auth that app implements (see protection 3 for what SSO does and does not cover).

        2. The container confinement stack applied to every container. Every app container is created read-only rootfs, runs as a non-root user, has NET_RAW dropped unless the manifest asks for it, and runs under the docker-cloudron-app AppArmor profile that denies mount and /proc, /sys writes. What it stops: tampering with the shared kernel interfaces and the common container-escape primitives — i.e. it protects the HOST, which is the admin service of last resort. Its limits, verified in source: the profile is deliberately a copy of Docker's docker-default (baseline, not hardened), and it does nothing to restrict network reach into other containers — a full TCP connect scan of the bridge is trivially possible from a compromised app.

        3. Authenticated TLS proxy as the only public ingress. Each app is served on its own subdomain behind the platform's nginx with TLS; the whole container network (172.18.0.0/16) is unreachable from outside the server, no container port is directly exposed to the internet, and origin isolation means XSS in one app cannot steal another app's session. What it stops: infiltration of any app or admin surface from the public internet — attempts must originate from inside the box. Its limits: this is exactly the position a compromised agent container is in — inside the box, on the bridge, with every co-tenant and admin service routable. And SSO/proxyAuth are enforced on this public route only; they do not apply to a direct bridge connection to the container IP, where the only barrier is the app's own auth.

        Hardening from the Cloudron admin's seat

        This section is what the box administrator can actually do, with the controls Cloudron gives them, to shrink the infiltration surface.

        • Enable SSO/proxyAuth on every app that supports it — but understand what it buys: it guards the front door (public route through nginx). It does nothing for a direct container-to-container connection, which lands on the app's own auth. Treat it as defense for the internet path, not the bridge path.
        • Audit what each app exposes on the bridge. From each app's web terminal, run ss -ltnp and check listeners: you should see the app's httpPort on 0.0.0.0 (normal, required) and anything else that app bundles — a Redis, an internal DB, an admin panel — on 127.0.0.1 only. Anything else on 0.0.0.0 is a bridge-reachable door with only the app's own auth in front of it.
        • Harden the dashboard. It is resolvable by name and reachable on 443 from every container. Single operator account, strong password, 2FA, and restrict dashboard access to admin IPs. Watch the eventlog: ANY failed login originating from a 172.18.x.x address is a container trying the admin door — on a normal box that should be zero, ever.
        • Keep the box patched, urgently. The confinement stack is deliberately baseline (docker-default AppArmor copy, stock seccomp, no userns-remap, runc only — Sysbox and Podman were both declined by Cloudron upstream), so the host is defended mainly by patching speed. Verify unattended-upgrades is active; treat kernel/runc/dockerd CVEs on a box that runs agents as urgent, not routine.
        • Tune detection to the canaries that matter. Alert on container-originated logins to the dashboard, on container-to-172.18.0.1 flows beyond DNS/dashboard (platform services 3002/3003/3006/3007 are always reachable), and on auth-failure spikes at shared addons (brute-force from the bridge). None of this traffic is filtered by the platform, so only your monitoring sees it.
        • Keep tested, encrypted backups. If infiltration succeeds anywhere, rebuild the affected app from backup rather than forensically clean it.

        Hardening from the app maintainer's seat

        This section is what a package maintainer controls in their own app's container — written for the agent package, but the same rules apply to every package.

        • Ship zero addons unless the feature is essential. localstorage only. Each addon mints credentials that are VALID against a shared or admin service: ldap hands the app a real directory secret, docker unlocks container lifecycle via the dockerproxy, sendmail/recvmail authenticates against the mail container, sql mints a database user. No addons = the container holds zero keys that open any admin door. Treat any addon addition on an agent package as a security decision, not a convenience.
        • Bind bundled daemons to 127.0.0.1, never 0.0.0.0. Yes, 0.0.0.0 binds are fully possible in a Cloudron container — and required for the primary httpPort (platform nginx proxies to the container IP). But sidecar services the platform never needs to reach — the agent's internal DB, dashboards, worker APIs — must bind loopback. Scheduler tasks and backup containers share the app's network namespace, so loopback does not break them. Loopback binding is the difference between a bundled daemon being reachable only by its own app and by every co-tenant on the bridge.
        • Declare no capabilities. No net_admin (adds NET_ADMIN + NET_RAW), no ping (keeps NET_RAW dropped). Non-root user throughout.
        • Make the app's own HTTP auth real. The httpPort must listen on 0.0.0.0 and is therefore reachable by co-tenants directly, bypassing any platform proxy auth. Whatever the app serves on that port needs its own authentication — for an agent package, the gateway API key/session the package already generates. Never ship the primary port unauthenticated.
        • Health check and limits. A real health-check path (not an SPA fallback that always returns 200), and memory/CPU limits so a runaway process cannot starve the box.

        Residual risks after all hardening

        This section is what remains attackable — from the compromised agent container into other containers and admin services — even with the platform protections and both hardening seats applied.

        1. Every co-tenant's primary port is reachable, by design, with only the app's own auth in front. The platform's nginx reaches apps by container IP; therefore every app's HTTP port binds all interfaces and every other container can connect to it directly. SSO/proxyAuth do not apply on that path. So the inter-app barrier for the typical web app reduces to the app's own login form — plus per-app credentials where the app talks to addons. An agent container can enumerate co-tenants (Docker DNS or a /16 sweep) and methodically try every front page and login. Detection: container-to-container flow alerts on the host. Mitigation: the audits above (know which of your apps have weak or default auth on their primary port); SSO wherever the app supports it, for the front door at least.

        2. Platform admin services are permanently reachable from every container. Any compromised container can open TCP to 172.18.0.1:3002 (ldap), 3003 (dockerproxy), 3006 (app-bridge), 3007 (scim), DNS, and the dashboard on 443 — unconditionally allowed by the firewall. Without addons these reject the agent's requests (dockerproxy 401s, ldap demands the bind secret), but the attack surface never goes away: the dashboard login accepts attempts from container IPs (rate-limited to 10/s, not blocked), and an unauthenticated bug or info leak in any of these services would be reachable by exactly the workload most likely to look for it. Detection: the 172.18.x failed-login canary; container-to-172.18.0.1 flow alerts. Mitigation: 2FA and IP restriction on the dashboard; accept the network path as a platform constant and monitor it.

        3. Kernel/container escape = the host = every admin service at once. The confinement stack blocks the common primitives, but it is deliberately baseline. Operator hardening does not change escape probability. A public runc/kernel escape lands on the host, which means: every container's /app/data, the platform database (all app credentials, user directory), the docker daemon itself, and the operator's SSH surface. This is the maximum-severity outcome of the infiltration question. Detection: host-side auditd on mount/nsenter/setns; file-integrity monitoring; alert on unexpected privileged processes. Mitigation: urgent patch posture; no SSH keys or VPN credentials stored on the box; snapshot for fast rebuild.

        4. Every addon is a key into an admin or shared service. The "zero addons" rule is load-bearing and fragile: the moment the agent's manifest gains an addon for a feature (ldap for directory sync, docker for builds, sendmail for notifications), the platform mints credentials valid against an admin or shared service — and those live in the container env, readable by the injected process. Addon creep is the quiet way the infiltration surface regrows. Detection: review the manifest diff at every package update. Mitigation: treat every addon request on an agent package as a security decision.

        5. The operator's session is the softest admin door. The agent has a web surface the operator logs into (dashboard, kanban, chat history). A prompt-injected agent renders attacker-influenced content in that surface — crafted links, file names, session summaries — aimed at the one admin who trusts it. The operator's browser session is a realistic path from "agent compromised" to "box admin compromised" that no network control addresses. Mitigation: keep the dashboard in a separate browser profile from the agent UI; 2FA everywhere; treat links and downloads coming out of the agent's UI as untrusted input.

        Bottom line

        This section is the reachability takeaway, then the verdict for business-critical boxes.

        Cloudron's platform protections make ordinary multi-app co-tenancy safe: other containers' data is behind per-app credentials, the host is behind a baseline-but-real confinement stack, and everything admin-facing sits behind an authenticated TLS proxy on the public side. But for a compromised agent container the question is reachability, and the reachability facts are: the bridge is flat and unfiltered; every co-tenant's primary port is reachable by design (that is how the platform's nginx works) with only the app's own auth in front, since SSO/proxyAuth guard the public route alone; the platform admin services and the dashboard are unconditionally reachable from every container; and an addon minted for convenience is a key into an admin door. The practical posture: zero addons on the agent package, loopback for everything the platform does not need to reach, real auth on the primary port, a 2FA-hardened dashboard, urgent kernel patching, and detection tuned to the canaries that matter — container-originated admin logins, container-to-container and container-to-platform flows. The honest caveat from the source: the confinement stack is deliberately baseline, so the host itself remains defended mainly by patching speed.

        Is the risk real if you run your business on the box? Yes — but it decomposes.

        • What is genuinely new with an agent: the probability of the first container falling. A browsing agent converts any webpage it reads into an attack surface — prompt injection is routinely demonstrated, not theoretical, and an agent that browses daily for months will eventually ingest attacker-controlled content. Most business apps are only attackable through their own public interface; the agent moves the entry point to "anything the agent will read". Second, the operator-session vector: the agent renders links, filenames, and summaries that the one admin who logs into everything trusts by default — a path from "agent pwned" to "box admin pwned" that no network control addresses, with mail (the master key to the business's identity infrastructure) sitting on the same box. Third, the crown jewels of a business box — mail, files, password manager — are exactly the co-tenants whose only inter-app barrier is their own login form on a bridge-reachable port.
        • What is honestly not new: the post-compromise topology. If any PHP app on your box is popped today, it can already TCP-scan every co-tenant and try their login forms; businesses have quietly accepted that trade for years. The agent changes the likelihood of the first container falling — not the map of what is reachable after it falls.
        • Practical verdict: a business box hosting mail, files, or password credentials alongside an always-on browsing agent is an unfavorable bet — injection probability multiplied by the value of mail — and we would not run it that way. The same box with only public-facing web apps, plus a zero-addon, loopback-bound agent behind SSO/2FA and the canary alerts above, is defensible — with eyes open that the host boundary is baseline confinement plus patch speed.

        This writeup is the evidence base behind the position we stated in the Hermes Agent thread: the platform's protections assume the threat is outside the box, and an agent is a workload that reliably puts the threat inside it. That is why we run agent workloads off Cloudron (Firecracker microVMs on dedicated hardware, currently under evaluation). For everyone who does want agents on Cloudron, the two hardening sections above are the practical checklist — and if any of the file-cited facts is wrong, the source is public and we would genuinely like to know.

        timconsidineT
        timconsidineT
        timconsidine
        App Dev
        wrote last edited by timconsidine
        #3

        @nostrdev thank you so much for this detailed analysis. Very valuable and helpful.

        Like a wide-eyed newbie, I've previously relied on containerisation as my comfort blanket, and this rabbit-hole has shaken me awake. Upstream vulnerabilities in a packaged Hermes (or Dify or AgentZero or whatever) are a real risk.

        necessitate the consideration of perhaps running a separate cloudron instance for non-business critical services.

        This is also the direction my AI-driven research led me. Different and not itself a solution, more of a workaround or security-methodology in the absence of any Cloudron-supported changes/practices. Basically off-load risky activities to controlled environment :

        • agent brain/control-plane (Hermes, whatever) CAN be on a standard Cloudron BUT configured or locked down to not run agent tasks itself on Cloudron
        • sub-tasks created by "brain" on a separate box (non-Cloudron, not needed to be), possibly small single-node Kubernetes, which fires up disposable tasks in Kata container (Firecracker or QEMU-vm), genuinely isolated VM, locked down, controlled ingress (instructions & task input) and egress (LLM calls, resources), output returned to brain, auditable activity with canaries.

        I'm tempted to do a proof-of-concept on this, purely out of interest. But I'm conscious it doesn't solve what most people actually want : single app on their single Cloudron box which does wonderful magical things. So why spend any time doing PoC.

        The 2nd box seems to be the answer, but also the blocker. No-one really wants cost/admin of a 2nd box, unless they re-imagine it as actually a cheap approach to good security.

        An alternative maybe is to use Fly.io ("sprites" or "machines", not sure) : delivers 2nd box functionality but it's an external service (so extra cost, dependency) and maybe contravenes an "entirely selfhosted" principle. But we regularly use cost-bearing external services (LLMs themselves) so why not.

        Personally I find my AI's suggested 2nd box suggestion practical but not very helpful, and rather primitive or unsophisticated in the amazing new world we live in.

        "Blue-sky thinking" might include :

        • best : platform changes for more container defences (probably unlikely as Cloudron team are always inundated, e.g. @robi long-standing sysbox discussion)
        • compromise : "Defender" module, maybe installable app but probably platform-based, which vets all intra-container activity (I have absolutely no idea whether or how this could be done) (I have started work on a project for this)
        • fall-back : agreed framework for community app packagers which enforces security-sensitive implementation of Hermes-type apps, packaged apps to declare, document, and verify compliance.

        Indie app dev, huge fan of Cloudron PaaS, scratching my itches : communityapps.appx.uk

        1 Reply Last reply
        1
        • timconsidineT
          timconsidineT
          timconsidine
          App Dev
          wrote last edited by
          #4

          Following up on my earlier post: I've been looking at a new class of agent systems - DeepSeek Harness (dsh) being the most prominent open-source example - and maybe it offers a more sophisticated decomposition we need.

          dsh is built on an "everything-is-a-plugin" architecture (Cordis) where the agent's filesystem, subprocess, and sandbox capabilities are swappable seams behind typed interfaces. A single provider configuration - pointing the sandbox backend at a remote host - moves all code execution, shell access, and file writes off the agent process without forking a single tool. The agent loop, session log, and UI stay put; the model-facing tools keep working because they talk to the seam, not the host.

          Yes, it's still a 2nd box - but the decomposition is smarter. The 2nd box holds only the effectors (shell, filesystem, subprocess); the brain (control plane, session log, tool registry, UI) stays on Cloudron. This changes what the Cloudron container is: not "an agent that might get compromised and act," but a control plane that can only dispatch decisions to a remote endpoint and talk to an LLM API.

          What this means for the threat model:

          • The Cloudron container never executes untrusted code. Prompt injection compromises the model's decisions, but decisions are dispatched to a remote sandbox.
          • No addon creep. The workspace lives in the remote sandbox; the manifest stays localstorage only, permanently.
          • Blast radius is one disposable remote container. The sandbox is network-isolated by design - no co-tenants, no admin services.

          The practical shape: a Cloudron-hosted dsh package, zero addons, remote sandbox configured via environment variables. Session log, tool schemas, prompt assembly, and UI benefit from Cloudron's TLS, SSO, and backups. Every bash call, file write, and subprocess spawn crosses a network boundary to a sandbox that has no knowledge of Cloudron, its bridge, or its admin services.

          This might be a direction for a solution - not a primitive "run the whole agent on a 2nd box," but a clean separation where the container on Cloudron is architecturally incapable of doing anything the platform needs to worry about.

          Indie app dev, huge fan of Cloudron PaaS, scratching my itches : communityapps.appx.uk

          1 Reply Last reply
          1
          • timconsidineT
            timconsidineT
            timconsidine
            App Dev
            wrote last edited by
            #5

            About sysbox :

            I don't think sysbox helps here. It hardens the container-to-host boundary but doesn't touch the network-layer issues — the flat bridge, co-tenant reachability, admin services — which is where the actual problem lives. And it makes the kernel-attack surface worse, not better: by giving every container a user namespace, it opens up syscalls that standard Docker blocks for good reason. A harder wall with a bigger window, facing the wrong direction.

            Indie app dev, huge fan of Cloudron PaaS, scratching my itches : communityapps.appx.uk

            1 Reply Last reply
            0
            • timconsidineT
              timconsidineT
              timconsidine
              App Dev
              wrote last edited by timconsidine
              #6

              For some reason, this topic really interests me.
              So for transparency :

              • I have started work on a project 'AgentDefender'.
              • later I will work on a 'harness' strategy

              I will publish the work so it can be critiqued and improved.

              Indie app dev, huge fan of Cloudron PaaS, scratching my itches : communityapps.appx.uk

              1 Reply Last reply
              0

              Hello! It looks like you're interested in this conversation, but you don't have an account yet.

              Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

              With your input, this post could be even better 💗

              Register Login
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              • Login

              • Don't have an account? Register

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Bookmarks
              • Search