Latest / Linux Server Admin with Fexingo: Sysadmin, Bash, and Server Engineering / How to Use Linux Namespaces for User Isolation
Transcript
- Lucas: Alright Luna, let's talk about a feature that's been in the Linux kernel for over a decade but still feels underutilized in a lot of server rooms: user namespaces. Luna: User namespaces — is that the 'I can be root inside a container but not on the host' trick? Lucas: Exactly. It's one of the most powerful isolation mechanisms in Linux. The core idea is that a process can have a completely different set of UIDs and GIDs inside its namespace than outside. So you can run a process that thinks it's UID 0 — root — but on the host, it's actually an unprivileged user like UID 1000. Luna: And that's huge for containers, right? Because historically, if a process escaped the container, it would have root on the host. Lucas: Right. Without user namespaces, a container running as root is a huge risk. With user namespaces, even if an attacker breaks out, they're just an unprivileged user on the host. It's one of the key reasons Docker and Podman can run rootless containers. Luna: So how do you actually set one up? I've seen the unshare command mentioned but never used it. Lucas: Let's do a quick example. On any modern Linux system, you can run 'unshare --user' and it drops you into a new user namespace. Inside, you're UID 0. Outside, you're still your original user. But there's a catch — by default, you have no UID mappings. You're root, but you can't do much because the kernel maps no UIDs for you. Luna: Right, you need to set up a mapping from the namespace UIDs to host UIDs. That's where /etc/subuid comes in, isn't it? Lucas: Exactly. The system administrator can configure ranges of subordinate UIDs for each user in /etc/subuid. For example, my user might have a range of 65536 UIDs starting at 100000. Then, inside a user namespace, you can map the namespace's UID 0 to host UID 100000, UID 1 to 100001, and so on. Luna: And that mapping is done with the newuidmap and newgidmap tools, right? Or you can write directly to the /proc/PID/uid_map file. Lucas: That's right. The kernel is strict: you can only write to uid_map once, and the mapping must be a subset of your subordinate UIDs. For unprivileged users, newuidmap is the way to go — it's setuid and handles the write securely. Luna: So a practical scenario: I have an old web application that insists on running as root, but I don't trust it. Can I just wrap it in a user namespace? Lucas: Absolutely. That's a perfect use case. You can create a script that launches the app inside a user namespace with unshare, map a single UID — say namespace root to your own host UID — and also set up a network namespace to isolate its network traffic. The app thinks it's root, but on the host, it's running as you, with your permissions. Luna: And you can combine user namespaces with other namespaces too? Like mount or PID? Lucas: Yes. That's what container runtimes do. They create a full set of namespaces — user, mount, PID, network, IPC, UTS. But you can pick and choose. For example, you might only need user and mount if you're just isolating a legacy binary. Luna: One thing I've heard is that user namespaces can be a vector for privilege escalation themselves if not configured correctly. Is that true? Lucas: It is, and it's important to understand. If a user is allowed to create user namespaces and has access to setuid binaries or capabilities inside the namespace, they might be able to exploit kernel bugs to gain elevated privileges on the host. For example, older kernels had a bug where a process inside a user namespace could trigger a use-after-free in the overlay filesystem and escape. Luna: So there's a trade-off. You get isolation, but you're also granting access to certain kernel interfaces that might be risky. Lucas: Exactly. That's why some organizations disable user namespaces for unprivileged users entirely by setting kernel.unprivileged_userns_clone to 0. But that also breaks rootless containers. The better approach is to keep the kernel updated and limit which users can create namespaces via PAM or systemd. Luna: Can you walk through a concrete example? Like, I want to create a user namespace, map my own UID, and run a shell as root inside. Lucas: Sure. First, make sure you have subordinate UIDs configured. Check /etc/subuid — you should see something like 'luna:100000:65536'. If not, you can add it with usermod or manually. Then run: 'unshare --user --map current user' actually that's a shorthand. But let's do it step by step. Luna: Okay, I'm following. Lucas: Open a terminal. Run 'unshare --user bash'. Now you're in a new user namespace as root. But try 'id' — it'll show uid=0. However, run 'cat /proc/self/uid_map' — it should show '0 1000 1' if your host UID is 1000. That means namespace UID 0 is mapped to host UID 1000. Luna: Wait, that worked automatically? I thought I needed newuidmap. Lucas: If you run unshare as root or with the --map current user option, it can set up a simple mapping. But for a more controlled setup, you'd use --map-user and --map-group. For example, 'unshare --map-user=0 --map-group=0' maps both UID and GID 0 inside to your current UID/GID outside. Luna: Got it. So now I'm root inside, but on the host, I'm still me. Can I do something like mount a filesystem? Lucas: You can try, but you'll likely get 'Operation not permitted' because you don't have the CAP_SYS_ADMIN capability in the initial user namespace. Even though you're root inside, the kernel checks capabilities against the host namespace. So you'd need to also create a mount namespace with the appropriate flags, and even then, unprivileged mounts are restricted. Luna: So user namespaces give you a sense of root, but actual privileged operations are still gated. Lucas: Exactly. The kernel has been hardening this over the years. For example, to mount a filesystem, you need the 'user namespace' capability combined with mount flags like 'nodev' or 'noexec'. And some filesystems are completely off-limits for unprivileged users. Luna: What about using user namespaces for multi-tenant environments? Like a shared development server where each developer gets a user namespace. Lucas: That's a great use case. You can give each developer a set of subordinate UIDs and GIDs, and they can create their own namespaces to run containers or test services. They can install packages, run daemons, even simulate a full system — all without affecting each other or the host. Luna: But you mentioned the privilege escalation risk. How do you mitigate that in a multi-tenant setup? Lucas: First, keep the kernel updated. Second, use Seccomp profiles to limit syscalls. Third, restrict capabilities inside the namespace. And fourth, monitor for suspicious activity — like attempts to mount filesystems or access kernel modules. There's also a kernel feature called 'user namespace lockdown' that can be enabled via sysctl. Luna: Is there a way to list all active user namespaces on a system? Like to see who's using them? Lucas: Not a direct command, but you can look at /proc/*/uid_map. If a process is in a user namespace, that file will show a mapping. You can also use 'lsns' from the util-linux package — it lists all namespaces. For user namespaces, run 'lsns -t user'. Luna: Nice. So if I see an unexpected user namespace, I might have a container escape or a malicious process. Lucas: Potentially. But also, some system services run in their own user namespaces by design — like systemd-logind or some VPN clients. So you need to know your baseline. Luna: Let's talk about the interaction with other security mechanisms. For example, SELinux or AppArmor. Do they work inside user namespaces? Lucas: They do, but with caveats. SELinux labels are still enforced, but the kernel may not allow SELinux transitions inside a user namespace for security reasons. AppArmor profiles also apply, but some operations are restricted. In general, user namespaces add another layer on top of MAC systems. Luna: So the ideal setup is user namespace + AppArmor + Seccomp? Lucas: That's a solid defense-in-depth approach. Plus read-only root filesystem, no-new-privileges flag, and dropping capabilities. That's pretty much what modern container runtimes like Podman do by default. Luna: Alright, so user namespaces are a powerful tool, but they need careful configuration. Any final tips for listeners who want to start using them? Lucas: Start simple. Use unshare to explore how it feels. Then set up subordinate UIDs for your user. Then try launching a simple container manually — like running busybox in a user+mount namespace. Once you're comfortable, you can integrate it into your service management. And always keep an eye on kernel security patches. Luna: Great advice. And speaking of practical tools, if this deep dive on namespaces gave you a new technique to harden your servers, that’s exactly the kind of thing we love to hear about. If you find this show useful and want to keep it ad-free, listener support makes that possible. You can find us at buy me a coffee dot com slash fexingo. Lucas: Yeah, it really helps us keep the focus on content like this. No ads, no sponsors — just solid server admin knowledge. So if you're able, we appreciate it. Luna: So to wrap up — user namespaces are a game-changer for isolation. They're not a silver bullet, but when combined with other security measures, they drastically reduce the blast radius of a container breakout. Lucas: Exactly. And they're available on any modern Linux distribution — no extra packages needed. So next time you're running an untrusted application, consider wrapping it in a user namespace. It might save you a headache. Luna: Thanks, Lucas. And thanks, listeners, for joining us. See you next time. Lucas: See you.