Forum

Notifications
Clear all

Error: 'Permission denied' when trying to write to a tmpfs volume I mounted.

7 Posts
7 Users
0 Reactions
33 Views
(@hobbyist_hardener_max)
Eminent Member
Joined: 3 months ago
Posts: 22
Topic starter   [#1643]

Hey everyone,

Ran into a classic one while hardening a NanoClaw deployment on IronClaw 4.2. I've mounted a `tmpfs` volume for a container's temporary data, aiming to isolate it from the host filesystem. The mount itself works, but the application inside the container throws a `Permission denied` when trying to write to it.

Here's the relevant snippet from my Ansible task for the mount:

```yaml
- name: Mount tmpfs for secure scratch space
ansible.posix.mount:
path: /opt/nanoclaw/secure_tmp
src: tmpfs
fstype: tmpfs
opts: "nosuid,nodev,noexec,size=256M"
state: mounted
```

The directory `/opt/nanoclaw/secure_tmp` is created by the playbook with `0750` permissions, owned by `root:root`. The container runtime (we're using `containerd` with `crun`) is configured to map the container's internal user (`appuser`, UID 1001) to the host's same UID.

My hypothesis: It's a classic mismatch between the host directory ownership and the container user's UID, even though they're numerically the same. The `noexec` and `nodev` are fine, but the kernel might be checking the host-side ownership before the container user is even considered.

**What I've tried:**
* Confirmed UID 1001 exists on the host (it's a system user for the service).
* Changed the mount point's ownership to `1001:1001` on the host → writes work, but this feels wrong from a host hardening perspective.
* Tried adding `uid=1001,gid=1001` to the `opts` of the tmpfs mount → this actually solved it! But I'm not sure if it's the *correct* hardening approach.

My main question: **What's the most secure way to allow a specific container user to write to a host-mounted `tmpfs`?**

Should we:
1. Set the `uid/gid` in the tmpfs mount options (seems clean, confines it to the mount)?
2. Create a dedicated user namespace for the container and map UIDs accordingly (more complex, but more isolated)?
3. Something else with `podman`/`crun` security flags I'm missing?

I want to keep the host's `root` ownership of the mount point directory itself for audit trails. Sharing config diffs and `ls -laZ` output below.

- Max


Hardening is a hobby, not a job.


   
Quote
(@api_warden_cora)
Eminent Member
Joined: 3 months ago
Posts: 16
 

You're on the right track with the UID mapping, but you're missing the mount's implicit ownership. When you mount tmpfs with default options, it's owned by root:root. The container user doesn't have rights to that root-owned mount, even if the underlying directory had different permissions.

You need to set the `uid` and `gid` mount options explicitly. Try adding `uid=1001,gid=1001` to your opts string. That'll create the filesystem with ownership matching your container user.

Also, check your container spec's mount propagation. If it's `rprivate`, the container might not see the ownership changes from the host mount.


Authz > Authn.


   
ReplyQuote
(@mod_tech_asia)
Eminent Member
Joined: 3 months ago
Posts: 26
 

Good catch on the mount options. Setting the uid and gid directly in `opts` is definitely the right move.

I'd just add that you'll want to verify the container's effective UID after any user namespace remapping. The numeric ID in the mount options needs to match the final UID seen by the process inside the container, not necessarily the UID defined in the container image. If your runtime is doing any additional ID shifting, that's where the mismatch could still pop up.

Also, consider if the directory needs to be empty before the mount. If there are leftover files from a previous run with different ownership, that could block writes even with the correct mount ownership.


- Asia (mod)


   
ReplyQuote
(@homelab_policy_maker)
Eminent Member
Joined: 3 months ago
Posts: 22
 

Setting the uid/gid at mount is the right fix, but you're creating a static policy problem.

If someone later changes the container's runtime user, they'll break the mount silently. That's worse than the original permission error because it looks like it works.

Better to bind-mount a host directory you control, or use a dynamic runtime feature. Relying on a host mount option that must match a container detail is brittle.


no default passwords


   
ReplyQuote
(@model_ctrl)
Eminent Member
Joined: 3 months ago
Posts: 25
 

Absolutely, and that namespace remapping point is crucial. I've seen this exact mismatch happen with Podman's rootless mode, where the host's `/usr/bin/newuidmap` shifts the container UID you think you're using. The mount's static `uid=` option can't possibly track that.

One workaround I've used in a similar spot is to bind-mount a world-writable subdirectory *inside* the tmpfs mount, created by a privileged initContainer. The initContainer runs as root, creates the subdir with `0777`, and then the main user can write there regardless of its final numeric ID. It's a bit of a hack, but it decouples the mount ownership from the runtime user.



   
ReplyQuote
(@lena_dev)
Eminent Member
Joined: 3 months ago
Posts: 19
 

Yeah, the UID/GID mismatch is almost definitely it. The container sees the same numeric UID (1001), but the *mount point* itself is owned by root because you didn't set ownership in the `opts`. The kernel's permission check happens on the mounted filesystem's root inode, not the host directory it covers.

Adding `uid=1001,gid=1001` to your opts string should fix it. Just be careful if you ever run this playbook where UID 1001 on the host is a different user - you're essentially granting that host user access to that tmpfs.

Also, since you're using containerd, double-check the pod spec's `securityContext.runAsUser`. Sometimes that overrides the image user and you'd need to match that ID instead.


-- lena


   
ReplyQuote
(@selftaught_sec)
Eminent Member
Joined: 3 months ago
Posts: 18
 

Yeah, the kernel's checking the inode ownership, not the directory underneath. So your numeric match doesn't matter because the mounted filesystem's root is owned by root. The others are right about the `uid=` option, but I'm curious why you're managing this tmpfs on the host at all, instead of as a volume in the container spec. That seems like it's mixing orchestration layers and asking for these exact mapping headaches. Couldn't you define it as a tmpfs volume in your Kubernetes pod spec or container runtime config, letting the runtime handle the user mapping? That way it stays with the container's lifecycle and user namespace.



   
ReplyQuote