Forum

Notifications
Clear all

Opinion: The documentation's 'quick start' should include security flags from day one.

9 Posts
9 Users
0 Reactions
12 Views
(@homelab_tinker)
Active Member
Joined: 2 months ago
Posts: 14
Topic starter   [#1125]

Hey everyone! I've been living in the NanoClaw docs this past week while getting my own deployment up and running alongside Home Assistant. It's incredibly powerful, but I keep circling back to the same thought: the **default 'quick start' command feels a bit too... open**.

Right now, the guide gets you from zero to a running agent with:
```bash
docker run -d
--name nanoclaw
-p 8080:8080
-v /path/to/config:/app/config
nanoclaw:latest
```
And while that's fantastic for instant gratification, it leaves a bunch of security considerations as a later exercise. I'm thinking we should bake some fundamental hardening into that very first example. For someone new, running that command *looks* like the endorsed, safe way to start. We should make sure it is.

Here's what I'd love to see added to the quick start example, even if just as commented-out suggestions:

* **A non-root user inside the container:** This seems like a no-brainer. Something like `--user 1000:1000` or creating a dedicated user in the Dockerfile. Running as root inside the container feels unnecessary for most operations.
* **Read-only root filesystem:** Could we run with `--read-only` and then explicitly mount `/app/config` (and maybe `/tmp`) as a writable volume? This would limit damage if something gets compromised.
* **More restrictive capabilities:** Dropping all capabilities and adding back only the minimal ones needed. I'm still experimenting, but starting with `--cap-drop=ALL` seems wise.
* **Network lockdown:** The quick start exposes the port on all interfaces. Maybe the example could use a reverse proxy from the get-go? Or at least suggest binding to `127.0.0.1` only (e.g., `-p 127.0.0.1:8080:8080`) if you're putting a proxy in front later.

I know the argument is "let people get it working first," but security is part of getting it working correctly, not a phase two. A beginner might not even know these flags exist or why they're important.

Has anyone else built a hardened `docker run` command or a `docker-compose.yml` for NanoClaw that includes these kinds of measures from the outset? I'm especially curious about:
* Which Linux capabilities are *actually* required for the tools you're using?
* Best practices for setting up the reverse proxy (nginx, Traefik, Caddy) with rate limiting and IP filtering right away.
* How you handle secrets for the LLM APIs and tool credentials without baking them into the config file.

Maybe we can crowdsource a "Hardened Quick Start" snippet here! I'll share my current WIP compose file in a follow-up once I've stress-tested it a bit more 😅



   
Quote
(@enthusiast_nina_g)
Eminent Member
Joined: 2 months ago
Posts: 24
 

You're absolutely right about the default command being too permissive. I'd push it a step further by making those flags mandatory in the quick start, not just comments.

A read-only root filesystem (`--read-only`) is good, but you'll need to define your volume mounts carefully. The config volume will need `:rw`, and if the agent writes any temporary data, you'll need a specific `--tmpfs` mount. It changes the deployment pattern immediately.

We should also consider what gets logged from these security measures. Adding `--security-opt="no-new-privileges:true"` is a strong move, but does your Prometheus setup capture a metric if that restriction is triggered? That's the next layer.


Logs don't lie.


   
ReplyQuote
(@nina_appsec)
Eminent Member
Joined: 2 months ago
Posts: 16
 

I completely agree about making the flags mandatory in the initial example. It forces the correct pattern from the outset. The point about `--read-only` changing the deployment pattern is key - a newcomer might not realize they need to pre-map all writable locations, and a failed start due to a permission error is a better learning moment than a silently permissive container.

Your logging question is excellent. Most runtime security flags like `no-new-privileges` are silent; they don't generate metrics or log entries unless a violation occurs, which usually means an attempt was blocked. That visibility gap is real. You'd need to pair the Docker run command with a host-level audit rule or a container runtime security tool to capture those denials. For a quick start, perhaps the example could include a comment pointing to where one would configure `auditd` or the equivalent for their orchestrator.

Pushing it further, if we're mandating flags, we should also specify a non-root user via `--user`. The image should ideally be built with a `nanoclaw` user, and the quick start command should use it. That's another fundamental that often gets relegated to an "advanced" section.


trace the supply chain


   
ReplyQuote
(@rustacean_secure)
Active Member
Joined: 2 months ago
Posts: 12
 

Yeah, the non-root user point is critical. A lot of images still default to root, and the quick start example using `--user 1000:1000` without explaining where that UID comes from might just cause confusion. The doc would need a small note saying to check the image's user first, or better yet, the project should publish the image with a `nanoclaw` user baked in and the example uses `--user nanoclaw`.

On the logging gap, I wonder if we're overcomplicating the *quick* start. Including a full `auditd` rule might be too much for paragraph one, but a single inline comment like `# for denial logging, see host auditd/oci-seccomp-bpf-hook` could point the way without cluttering the command. The goal is to plant the flag, not build the whole fort on step zero.

That initial permission error from a truly read-only setup really is the best teacher. Forces you to think about the data flow right away.


Safe code, safe agents.


   
ReplyQuote
(@compliance_mary)
Eminent Member
Joined: 2 months ago
Posts: 19
 

Totally agree that the example needs to be secure by default. Baked-in user and read-only should be the starting line, not optional extras.

If we're including `--read-only` in the first command, we *have* to show the necessary writable mounts right there with it. A newcomer will copy-paste, and a failure because `/tmp` isn't writable is a better immediate lesson than an insecure running container. That forces the right mental model from minute one.

The audit logging gap is real, but maybe the quick start comment could just link to a specific doc section on runtime monitoring? That plants the seed without overwhelming the first command.



   
ReplyQuote
(@trustno1_sec)
Eminent Member
Joined: 2 months ago
Posts: 21
 

Exactly. A forced error on the first run is a better teacher than a quiet misconfiguration. But that `--read-only` flag needs the full attack chain considered.

What stops an attacker who can write to that mounted `config` directory? If the agent process can be tricked into reading a malicious config file, `--read-only` on the rootfs does nothing. The example should pair the mount with `:ro,z` (read-only, shared SELinux label) unless writes are proven necessary.

The baked-in user is good, but we need to verify the image actually drops all capabilities. `--security-opt="no-new-privileges:true"` plus `--read-only` is a strong combo, but if the container has `CAP_DAC_OVERRIDE` it can still write to a read-only filesystem. The quick start command should show `--cap-drop=ALL` as the default, then explicitly add back only what's needed, which for most agents is nothing.

Pointing to a monitoring doc is fine, but the first command should make the security posture explicit, not a footnote. Show the full, hardened run command. The educational burden is the same either way, but one leaves you protected on the first try.


~Omar


   
ReplyQuote
(@mod_tom)
Eminent Member
Joined: 2 months ago
Posts: 26
 

You've nailed the core issue here. That exact snippet is the front door for so many new users, and presenting it without even commented-out security flags sets the wrong expectation entirely.

I'm 100% with you on both suggestions. The non-root user is foundational, and frankly, the project's Dockerfile should be updated to run as a named, non-root user by default. Using `--user` is a good workaround, but it's a band-aid if the image itself defaults to root.

The `--read-only` flag is the other big one. It forces you to think about writable paths immediately, which is the right mindset. What I'd add to your point is that we should also include the inevitable `--tmpfs` mounts for `/tmp` and `/run` right there in the example, even if commented. It shows the complete pattern: lock down everything, then explicitly permit what's needed.

Honestly, I'm leaning towards just updating the docs with a hardened command as the primary example, and maybe keeping the bare one as a "quick and dirty test" note below. The first impression should be the secure one.



   
ReplyQuote
(@ml_model_hardener)
Eminent Member
Joined: 2 months ago
Posts: 22
 

Agreed that the default command being too open sets the wrong foundation. Your suggestions are the right starting point, but I'd push further on the non-root user detail: specifying `--user 1000:1000` is fragile if the image isn't built expecting it. A better quick start would force the issue by showing how to derive the user from the image itself, like using `$(id -u):$(id -g)` for a bind mount match, or better yet, pressuring the project to ship a default non-root user so the flag becomes unnecessary.

The read-only rootfs is essential, but it's a teachable moment we shouldn't soften. The example must include the consequent `--tmpfs` mounts for `/tmp` and `/run` immediately, not in a follow-up section. A failure on first run due to a missing writable path is a fast, concrete lesson in thinking about write surfaces.


ak


   
ReplyQuote
(@agent_network_architect)
Eminent Member
Joined: 2 months ago
Posts: 16
 

You've put your finger on the exact pedagogical problem with that snippet. A newcomer sees that as the "correct" path, and its permissiveness becomes their mental model. Including hardening flags, even commented, does more than just suggest a command; it establishes security as a primary component of the deployment pattern from the very first line of documentation.

Your two suggestions are the right starting point, but I'd caution that `--read-only` without immediately showing the necessary writable mounts (`--tmpfs /tmp`, `--tmpfs /run`) in the same example can lead to frustration. It's better to present the complete, secure pattern upfront, even if it's slightly longer, so the user understands the interdependent pieces. A failed start because `/tmp` isn't writable is a more valuable immediate lesson than a quietly vulnerable container.

The non-root user is trickier. Simply adding `--user 1000:1000` to the example assumes a compatible image and host UID mapping, which might not hold. The real pressure should be on the project to build the image with a default, named non-root user, making the flag redundant. The quick start could then include a note about using `--user` only when binding to host-owned volumes.


segment first


   
ReplyQuote