Hacker News new | past | comments | ask | show | jobs | submit login
Against /tmp (dotat.at)
257 points by todsacerdoti 4 months ago | hide | past | favorite | 159 comments



I'm amazed that polyinstantiation of directories via pam_namespace.so[1] is so unheard of. Setting this up fixes almost all of the qualms mentioned in the article by giving each user their own mount namespace with an isolated /tmp directory (and others if configured). Still though, this wouldn't prevent poorly written applications using /tmp from clashing with others that are running under the same user.

It's relatively easy to set up[2] and provides a pretty huge defense mitigation against abusing /tmp.

[1] https://www.man7.org/linux/man-pages/man8/pam_namespace.8.ht...

[2] https://docs.redhat.com/en/documentation/red_hat_enterprise_...


Is there an easy way to duplicate a specific process' namespace? My biggest issue with all these new features is that privatize state is how much harder it is to reproduce a state.

Back when it was just environment variables, I could pipe /proc/PID/environ to xargs and get basically the same state. Given that things like unix domain sockets may end up in $TMPDIR, I can be left unable to do certain things.


    nsenter --all --target $PID
or something like that?

https://man7.org/linux/man-pages/man1/nsenter.1.html


/proc/PID/root is a view of that process’s mount namespace.

Also you can use nsenter(8) to run a command (or even a shell) under another process’s mount, pid, network, etc namespace.


mount namespace and root directory are bit different things though.

/proc/$PID/ns is the place to look for namespaces


Thanks!

It's exactly what I was looking for, and the world can now continue to improve without breaking any of my workflows :)


I don't think there is, or should be, a way to do that. Granular copying of per-process resource state seems like a need that would be better served at either a closer-to-the-program layer (i.e. debug hooks in code you control that provide information on how to reconstruct its state) or much further away (e.g. via CRIU/whole-machine snapshots or scary tricks like SIGSTP or ptrace-injecting calls to fork(2)).

> I can be left unable to do certain things

Most of what I can imagine of "certain things" falls into two categories: debugging (for which much better tools exist), or concerns that would be better served by a program providing an API of some kind rather than "go muck with state in $TMPDIR".


Here's a recent one; I needed to play some sound via an ssh login session. A Wayland/pipewire session was already open. I was able to do this just by copying a running processes environment. With enough containerization &c. I'll need to do more things to do that, if it's at all possible.

Also, /proc/ is (among other things) a debug interface.


There's also https://www.freedesktop.org/software/systemd/man/latest/syst... to do the same for services if you use systemd.


If you're not using PAM then you don't get these.

For example, Kubernetes doesn't use PAM in the pods it creates to run your containers.

You might think "who cares", but I've written code that is agnostic as to whether it's running in a logged-in user's session or something else. https://news.ycombinator.com/item?id=41916623


Lovely, another layer of complexity to increase the safety of a fundamental mistake that we can no longer fix!


> There should be per-user temporary directories. In fact, on modern systems there are per-user temporary directories!

On Linux+systemd, I think this is referring to /run/user/$UID. $XDG_RUNTIME_DIR is set to this path in a session by default. There's a spec for that environment variable at <https://specifications.freedesktop.org/basedir-spec/latest/>. I assume there's also some systemd doc talking about this.

On macOS, I see that $TMPDIR points to a path like /var/folders/jd/d94zfh8d1p3bv_q56wmlxn6w0000gq/T/ that appears to be per-user also.

What do FreeBSD/OpenBSD/NetBSD do?


Unfortunately /run/user/$UID/ is NOT universally available.

On Linux it's typically created by a PAM, so if you're not using PAM then it doesn't exist. This means that on Kubernetes pods/containers... it doesn't exist!

Yes, /tmp/ is a security nightmare on multi-user systems, but those are a rarity nowadays.

Lots of things want to write things into /tmp, like Kerberos, but not only. I recently implemented a token file-based cache for JWT that... is a lot like a Kerberos ticket cache. I needed it because the tokens all have specific aud (audience) values. Now where to keep that cache?? The only reasonable place turned out to be /tmp/ precisely because /run/user/$UID/ is not universally available, not even on Linux.


> Yes, /tmp/ is a security nightmare on multi-user systems, but those are a rarity nowadays.

What's not a rarity though is apps (or code in general) that you don't fully trust, and that you don't want to give a chance to exfiltrate all your data for example.

Sadly, the POSIX permission model is entirely ill-suited for that, precisely because it tries to solve the multi-user problem, wherein all code belonging to a single user is effectively treated omnipotent within that user's domain (i.e. the files the user owns). That's why iOS and macOS (the non-POSIX parts) has a container model with strong sandboxing, entitlements, etc.


> What's not a rarity though is apps (or code in general) that you don't fully trust, and that you don't want to give a chance to exfiltrate all your data for example.

How many of these do you have? I have 1 and I have it installed via a flatpak with sandboxing (that has no access to /tmp).

Flatpak's are an implementation of that container model for software on Linux.


As an obvious example: You should not trust your browser to have access to all of your file system(s) without explicitly allowing at the time of access. The only thing it should (usually) have access to is your "Downloads" folder... and that's about it.

Browser security would be a lot less time-sensitive if that were the case.

The same logic applies to games, etc. etc. I do NOT trust the developers of these things to get things right 100% of the time, so why even take the risk of allowing their programs unfettered access to all of my files? As a dev, I don't even trust myself to be perfect and I'd like to be able (in my program) to state up from "my code will never touch anything outside Downloads/" or whatever.

ETA: The point is minimal trust for any given program to do its thing. I'd like to be even more pithy with something about "trust but verify", but that doesn't quite fit, alas.


On my phone? Tons. My phone has a boatload of data, and I readily install apps. I don't want one rogue update of a rarely used app (supply chain attacks are very real as we know) to get access to all my text messages and email, or to whatever my banking apps keep as state.

On my Mac? Less, but it happens. But text messages, photos, and the banking apps installed there etc. are still inaccessible by anything except the thing's I've explicitly given access.


Sure, POSIX is basically Unix as standardized over several decades. Unix is 54 years old. Containers are a very recent development. POSIX is behind. But you do have the option to use containers/jails/zones, it's just not POSIX. What does any of that have to do with TFA?


I just wanted to point out that some unixoid systems (even some who are actually UNIX certified, but that's not really relevant to the issue) employ protection of temporary files stronger than what was suggested in TFA. The containers/jails/zones that you mention are an example of this.


Does k8s guarantee that /tmp is available either?


Well, I suppose that depends on the images you choose to run, and how you choose to build them, but I've never seen one that didn't have a /tmp. Have you?


Sure, all `FROM scratch` images for starters


FreeBSD doesn't create user tmp when setting up a new user using the automatic tool. I don't recall if that's an option or not. There is, of course, a /tmp


iOS and macOS go further and separate their (native) apps almost entirely, including temporary files. That way, if you download "Super Free VPN Pro!!", it at least doesn't get access to, say, photos, temporary data or not.


Windows seemed like a mess to me but it’s actually not too bad. Local/roaming per-app data can be separated and the per-user temp would always then be in the local part.


I think the idea of a shared filesystem in general is bad. It’s not the 80s anymore where we’re logging on to a shared mainframe. Applications should be completely sandboxed from each other by default and only allowed to see what they need to see. Real sandboxing by default (not like systemd’s opt in sandboxing, which is an absolute mess) would eliminate entire classes of vulnerabilities.


This has been iOS's (and macOS's, for native apps) model for a long time, yeah. "Multiuser" computers are not common anymore, but computers with a bunch of apps/code that you put different levels of trust in (especially on a level on what you want a given app to have access to), now are.

It's very different from 20-30 years ago.


macOS sandboxing for native apps has a long way to go to catch up with iOS. As one example, any application can read from your clipboard at any time. Web apps are, I believe, much better sandboxed.


> It’s not the 80s anymore where we’re logging on to a shared mainframe

It's even worse than that: We're all using the same shared applications on some cloud.


> It’s not the 80s anymore where we’re logging on to a shared mainframe.

Modern high performance clusters still follow that logic, and are found in almost all large universities or companies doing research on heavy computational topics (artificial intelligence, comp. chemistry, comp. biology, comp. engineering, and so on)


Oh man, this sort of thing is part of what I love love love about systemd. It bakes in so many great isolation/sandboxing/privacy measures for units! From the article:

> There should be per-user temporary directories. In fact, on modern systems there are per-user temporary directories! But this solution came several decades too late.

> If you have per-user $TMPDIR then temporary filenames can safely be created using the simple mechanisms described in the mktemp(1) rationale or used by the old deprecated C functions. There’s no need to defend against an attacker who doesn’t have sufficient access to mount an attack! There’s no need for sticky directories because there aren’t any world-writable directories.

May I introduce you to PrivateTMP= ?

> PrivateTmp=¶

> Takes a boolean argument. If true, sets up a new file system namespace for the executed processes and mounts private /tmp/ and /var/tmp/ directories inside it that are not shared by processes outside of the namespace

https://www.freedesktop.org/software/systemd/man/latest/syst...

Notably you don't even need to change how programs work (no $TMPDIR necessary)! It creates a filesystem namespace for your process, such-that you see the normal fs, but with your own /tmp ! That way your program behaves regularly/as convention goes everywhere else, and existing programs you run can also benefit without re-writing!

I cannot emphasize enough how many excellent well integrated kick ass security features systemd gives you totally for free. DynamicUser= turns on PrivateTmp= by default and is an easy way to insure isolation, to prevent needing to hand-code & safely manage uid/gids yourself; I'd start there if you can.

There's so so so many great isolation features in this man page.


That is very cool, thank you for sharing it.

I wonder if Fedora does this by default?


Happy debugging, yeah. FastCGI examples usually create sockets in /tmp, but nginx doesn't see them, go figure.


Fix: JoinsNamespaceOf=


A more canonical means would be to use the runtime directory. Explicitly setting a Runtime directory= for each would be appropriate.

I get your point. Yeah as a newbie flipping on random options listed under "sandbox" may be bad for you. But this hardly seems like a good dig against a well integrated unit process that has lots on tap to do the job very very well, in a succint manner.


I like /tmp in RAM myself, it's truly temporary that way

EDIT: I do this more for avoiding certain disk reads/writes than security actually


It's also the default on most distros these days since it means /tmp is always wiped on every reboot. (For a programming side, this also means that if you write a file to /tmp, it'll probably have the fastest read/write speed you can find on the OS, which can be desirable.)


I prefer the explicit /dev/shm for this.


You'd need to pin pages in physical memory to guarantee it stays in physical memory. What happens if an 'attacker' (or accidental user) exceeds available physical memory? OOM Kill other applications? Just don't accept temp data, leading to failures in operations requested by the user or system?

Pages in physical memory are not typically zero'ed out upon disuse. Yes, they're temporary... but only guaranteed temporary if you turn the system off and the DRAM cells bleed out their voltage.


By default a tmpfs has a really low RAM priority so the OS will try to move it in swapspace if memory gets low. tmpfs size is specified on creation of the tmpfs (and cant be larger than the total memory available, which is swap + RAM) but it's only "occupied" when files begin to fill the tmpfs.

If it gets too full for regular OS operations, you get the fun of the OOM Killer shutting down services (tmpfs is never targeted by the OOM Killer) until the entire OS just deadlocks if you somehow manage to fill the tmpfs up entirely.


> OS will try to move it in swapspace if memory gets low

That defeats the idea GP presented.


Only if memory gets low, otherwise it'll stay in RAM and give the benefit GGP intended. IIRC tmpfs data shouldn't be evicted to swap just to allow more room for cache, or if an app requests a large chunk of memory but doesn't use it, just to allow more room for application pages that are actively in use.

Normal case: tmpfs data stays in RAM

Worst case: it is pushed to swap partitions/files, which is no worse than it being in a filesystem on physical media to start with (depending on access patters and how swap space is arranged it may still be a little more efficient).

It isn't quite the same as /tmp being on disk anyway but under normal loads in cache, because the data will usually get written to disk even if only ever read from cache and the cached data from disk will be evicted to make room for caching other data where tmpfs data is less likely to.


True overall, but I think it makes a lot of sense to evict rarely used data in tmpfs to swap, so that the DRAM it occupied can be used for valuable caches instead of holding some obscure temporary data that will be rarely or ever accessed.

Rarely used data that got evicted then behaves more or less like a normal /tmp filesystem when it does eventually get accessed, i.e. it gets read in from disk, while other data still gets all the benefits from tmpfs (e.g. ephemerality).

(If you take the thought experiment to its logical conclusion, you'll anyway end up in transparent hierarchical storage a la AS/400, where all data is just addressed by a single pointer in a very very large address space and the OS decides where that currently points to, but let's stay within the confines of what we're mostly used to...)


Use of /tmp on regular file system has almost the same behavior because the kernel has a file system cache… if you’re using the file, it will remain available in RAM. There’s some subtle differences, but I’ve seen enough benchmarks around this to have realized that tmpfs doesn’t really have an impact.


Yeah, that's why I think the prime feature of tmpfs is more ephemerality than anything else.


That depends on how you view swapspace; on most devices, swapspace is either created as a separate partition on the disk or as a file living somewhere on the filesystem.

For practical reasons, swapspace isn't really the same thing as keeping it in an actual storage folder - the OS treats swapspace as essentially being empty data on each reboot. (You'd probably be able to extract data from swapspace with disk recovery tools though.)

On a literal level it's not the same as "keep it in RAM", but practically speaking swapspace is treated as a seamless (but slower) extension of installed RAM.


> On a literal level it's not the same as "keep it in RAM"

I read the GP as 'literal level' in-RAM. If I interpreted that incorrectly, apologies to GP.


It may or may not be what the OP was talking about, depending on your threat model.


> exceeds available physical memory?

shm and memory mounts use half the available system memory by default. so this is not typically possible.

> are not typically zero'ed out upon disuse

They're zeroed when they're reallocated.

> and the DRAM cells bleed out their voltage.

This occurs in less than a second in almost every room temperature environment.


Well I guess you could tell Linux to not use some memory addresses using the BadRAM feature, then setup an `mtd` device to those memory addresses and create a RAM-based block device, then use `cryptsetup` to encrypt it. If your Linux box is headless and you have a GPU with RAM there mostly sitting unused then you could use the VRAM.


I use this with a size of a few GB: https://wiki.archlinux.org/title/Tmpfs


This is fine until something uses more memory than is available, such as sox insisting on routing a huge audio file though a too-small /tmp, or the MATLAB installer likewise only using /tmp. With sox you could in theory recompile to get it to use some other path (iirc none of the TMP or TMPDIR environment variables did anything), but I instead gave up on the /tmp in memory (it complicated the OpenBSD desktop setup). I forget exactly how I worked around the MATLAB installer issue, probably something horrible involving LD_PRELOAD or time wasted reconfiguring and rebooting and reconfiguring and rebooting one node to do the install on, plus more time wasted running the massive and bloated installer process(es) under strace to see exactly what file paths were in play.

So, not really a fan of /tmp in memory. (And I don't much run massive and bloated browsers that may murder your SSD lifetime with excessive file writes better diverted to an in-memory /tmp.)


Ideally you have swap, so that stuff that's not actually in use ends up in swap instead of occupying physical memory.

That's why you still usually see machines with unreasonable amounts of GB of RAM having swap partitions: Instead of having data that's rarely, if ever, used occupy precious DRAM, it's much better to have that data in swap so that the DRAM can contain, say, filesystem caches.


The /tmp in RAM does not address any of the vulnerabilities unfortunately.


At least the 'tmp cleanup' related ones from the article


No not really. These are there for long running jobs. Reboot clean up of tmp is not an issue.


I thought that was the standard in Linux, at least. I'm not sure how they do it in the BSDs.


Ok, but that's not relevant or responsive to TFA.


There is (fortunately) no rule that replies have to be explicit responses to anything in the article. Tangents are welcome in my mind.


I guess the general gist is shared spaces between users causes security issues.

I recall using 'shared hosting' where instead of using your default IP address for fetching anything from the network, you could do some funky stuff in the shared environment to discover many more IPs that could be used. Useful for scraping and such. Generally any shared hosting that used cpanel would expose all their network interfaces, often a /24 or two.


Any shared resource seems to give rise to security issues. Extracting data through side channels in the hardware's architecture is what woke me up to this.


That's true of physical reality itself. Everything that happens constantly leaks information to the surrounding, spreading outward at the speed of light.

Point being, there always are side channels.


I recently had to copy a secret which was available in a CI-job to a new repository, but the system was smart enough to filter it if echoed literally.

So "echo $API_TOKEN" failed, but getting the output of the complete environment was as easy as "env | base64".


One has to question the premise of such "smartness" in the system in the first place.


I think of it as a form of politeness, basically. It's only a security feature in the sense that it's a tool you can use to make good logging hygiene a little easier for your CI system, not in the sense of helping form any kind of security boundary.

I assume (hope?) that's the intention, that nobody is advertising this as a way to prevent exfiltration of secrets.


I remember digging into this 10-15 years ago. 'shared hosting' per provider had some arbitrary resource restrictions, but you could still find out via a cron job or some such. Like `cat`ting /etc/network stuff. Basically a sieve.


In which people forget that computers have other purposes beyond being boxes for CTF competitions. Shared mutable global state isn't always bad.


If your machine is on the Internet in any way, you're taking part in the big ongoing global CTF.


True as it may be, it's not the reason my machine is on the Internet.


> Shared mutable global state isn't always bad.

I agree, but I think that shared mutable global state is a bad default. I think it'd be better to be opt-in (eg, you get a `/tmp/${USER}` and your user can `chmod o+rw` during setup if it needs to be globally mutable.


There are very few always in such matters, but I view this one as an 'except for rare circumstances'. Even when true, it should be modeled as "contained state where the container includes everyone".

The problem is that Unices use access control, rather than capabilities, so ensuring state is shared only by those who need it is quite a bit more difficult than just punting, and declaring that 'those who need it' is 'everyone'.

Nor has the design problem of a user-friendly capabilities architecture truly been solved, IHMO. Nonetheless, we shouldn't confuse convenience with correctness.


I serve 1Gb of /dev/null to every scraper that tries known CVE endpoints. Fuck 'em.


How much is 1 GB*0 bytes? Better serve 1GB of /dev/zero instead! (Or even better, /dev/urandom, because zeroes compress very well and are easy to spot.)


> The fix, way back when, should have been for login(8) to create a per-user temporary directory in a sensible place before it drops privilege, and set $TMPDIR so the user’s shell and child processes can find it.

Something like

    tmpdir := "/tmp/${USERNAME}"
    loop:
        rmdir(tmpdir, recurse=true)
        while not mkdir(tmpdir, 0o700, must-create=true)
    chown(tmpdir, user=$USERNAME, group=$USERGROUP)
    export("TMPDIR", tmpdir)
with /tmp having root:root owner with 0o775 permissions on it? Yeah, would've been nice.


MacOS does something like this. Not by username, but through /private, which is a private mount, and then /tmp is linked to /private/tmp, as are /var and /etc.


You're right that macOS has per-user temp (and cache) dirs under /private/var/folders/ (since 10.5), but it still has traditional shared /tmp (via the /private/tmp symlink) since not everything respects the per-user temp dir.

https://magnusviri.com/what-is-var-folders.html

That's not the reason for /private though. Rather, /private is a holdover from NeXTSTEP days which could mount the OS via NFS (NetBoot), and where /private was local to the machine:

"Each NetBoot client will share the server's root file system, but there are several administrative files (such as the NetInfo database, log files, and the swapfile) that must be unique to each client. The server must have a separate directory tree for each client, which the client mounts on its own /private directory during startup. This lets a client keep its own files separate from those of other clients."

https://www.nextcomputers.org/files/manuals/nsa/13_NetBoot.h...


Thanks for that first link, explained some stuff I've been curious about


Why not both, like Windows?

$HOME/.tmp for user operations and /tmp for system operations?

EDIT: I see from other posters it can be done. Why the heck isn't this the default?!


IMO even a home-level, per-user tmp directory isn't ideal (though it is better). In a single-user environment, where malware is the biggest concern in current times, what difference does it make if it's a process running under a different user or one that is running under your current user that is attacking you?

In other words, for many systems, a home-level temp directory is virtually the same as /tmp anyway since other than system daemons, all applications are being started as a single user anyway.

And that might be a security regression. For servers you're spinning up most services at bootup and those should either be running fully sandboxed from each other (containerization) or at least as separate system users.

But malware doesn't necessarily need root, or a daemon process user id to inflict harm if it's running as the human user's id and all temp files are in $HOME/.tmp.

What you really want is transient application-specific disk storage that is isolated to the running process and protected, so that any malware that tries to attack another running application's temp files can't since they don't have permission even when both processes are running under the same user id.

At that point malware requires privilege escalation to root first to be able to attack temp files. And again, if we're talking about a server, you're better off running your services in sandboxes when you can because then even root privilege escalation limits the blast radius.


> In a single-user environment, where malware is the biggest concern in current times, what difference does it make if it's a process running under a different user or one that is running under your current user that is attacking you?

In these systems, the responsibility passes to EDRs or similar. But neither a $HOME/.tmp or /tmp matter in these scenarios. _Shared_ systems are where the concept of $HOME/.tmp might be more interesting.


> In a single-user environment, where malware is the biggest concern in current times, what difference does it make if it's a process running under a different user or one that is running under your current user that is attacking you?

Very true, and this is a real weakness of the UNIX (and Windows, even worse!) style security model in the modern environment. Android/iOS do a lot better.


> Android/iOS do a lot better.

They would if they were designed with the user's security in mind, instead of Google's/Apple's control.

But I disagree, they don't do better at all. Any software that wants to get access to everything just needs to insist.


Check pledge/unveil under OpenBSD. You get isolated software yet with freedoms.


I've recently packed some Linux software in flatpak. It's surprisingly good.

Not as good as a real capability-based access control, but quite good compared to the other things that are usable on Linux.


Why are capabilities restrictions not the norm when the concept is so old and seemingly so sound?


Linux doesn't have a good capability system.

And no good system makes it into Linux because it has a huge, well supported one, and some 3 other candidates pushing to get there already.


So something crummy but usable-enough for experts (SELinux?) worse-is-better'd its way onto the Linux scene, and now it has matured enough that on the one hand it can't be displaced but on the other its model is ossified and can't be untangled or simplified. Makes sense.

I love Linux and many of the fruits of its messy evolution, but such fruits are certainly not all equally delicious. :(


They're really annoying to use.

Also the "UNIX ideal" is composable tools, which doesn't combine very well with any kind of sandboxing.


The thing about capabilities is that they compose very well.


"Modern" Windows apps actually have a sandbox (which includes a private temporary folder), and require permissions to access pretty much anything outside. This is all implemented in terms of the existing Win32 security model, fundamentally.

In principle, there's nothing precluding e.g. having a separate user per app on Linux, either...


I'm guessing, but I would think that the idea is to have all the junk in one place so that it can be safely cleared at startup and excluded from backups.

If the user tmp files were placed in /tmp/${USER}/ then that would achieve the same goal.


What system operations exist that need temp storage shouldn't have a separate user anyhow?


I see where you're going with your question, but like Windows' Services/scheduled tasks, most of those 'users' don't have a $HOME folder.

Not to say they couldn't have one!


Services on windows have home folder e.g. in \Windows\ServiceProfiles\LocalService


/tmp is 01777

Anything that requires login(8) or PAM to make it happen is insufficient. This has to happen in environments like Kubernetes too.


It doesn't have to be, does it? Drop the S_IWOTH from it.


But you'll have to have all users be members of the group then. That gets you nothing.


No you don't have to?

    joker@e2509h:~/test_tmp$ ll
    total 12K
    drwxr-xr-x  3 joker joker 4.0K Oct 22 22:12 ./
    drwxr-x--- 11 joker joker 4.0K Oct 22 22:12 ../
    drwxr-xr-x  3 root  root  4.0K Oct 22 22:13 tmp/
    joker@e2509h:~/test_tmp$ cd tmp
    joker@e2509h:~/test_tmp/tmp$ ll
    total 12K
    drwxr-xr-x 3 root  root  4.0K Oct 22 22:13 ./
    drwxr-xr-x 3 joker joker 4.0K Oct 22 22:12 ../
    drwxr-xr-x 2 joker joker 4.0K Oct 22 22:13 joker/
    -rw-r--r-- 1 root  root     0 Oct 22 22:15 z
    joker@e2509h:~/test_tmp/tmp$ touch x
    touch: cannot touch 'x': Permission denied
    joker@e2509h:~/test_tmp/tmp$ rm z
    rm: remove write-protected regular empty file 'z'? y
    rm: cannot remove 'z': Permission denied
    joker@e2509h:~/test_tmp/tmp$ touch joker/x
    joker@e2509h:~/test_tmp/tmp$ ll joker
    total 8.0K
    drwxr-xr-x 2 joker joker 4.0K Oct 22 22:13 ./
    drwxr-xr-x 3 root  root  4.0K Oct 22 22:15 ../
    -rw-r--r-- 1 joker joker    0 Oct 22 22:13 x
    joker@e2509h:~/test_tmp/tmp$ rm joker/x
    joker@e2509h:~/test_tmp/tmp$
    
Looks like it works just fine.


The only thing I know about /tmp is that it's accessible by all users, privileged or not, which is quite helpful when you are escalating your user privilege on a box


:)

Don't put . in your PATH, that's for sure.


To hide `/tmp` from other processes and users, I sometimes use `bwrap --dev-bind / / --tmpfs /tmp <command>`.

Unfortunately Ubuntu 24.04 has put restrictions on unprivileged user namespaces, so that it no longer works out of the box :(


What I do is all users get TMPDIR set to /tmp/$USER and /etc/rc.d/rc.local will create these as 700 on boot. Plus a script in /etc/profile.d/ will set TMPDIR on login for the users. With this you get /tmp cleanup on boot.

As as someone said, you can mount /tmp as tmpfs if you can spare the memory.


Back in the day I used libpam-tmpdir for that purpose - it covered SSH and other services that used PAM.


The right answer is to use /run/user/${UID}/. Unfortunately that's not universally available, not even on Linux. If you don't use PAM in the process of starting the user processes in question, then you won't have /run/user/${UID}/. That's because on Linux /run/user/${UID}/ is made by a PAM. Kubernetes does not use PAM, naturally, so you don't get this on Kubernetes.

This is supremely annoying. /run/user/${UID}/ needs to exist universally. Ugh.


I really think there are quite a few reforms and new ideas that could help Unixes. Also it's not only about introducing features/new ways, but also the right culture and instruction around the new ways.

For example, the Android(/iOS?) permission based model at kernel level where apps (that could be processes in general?) can only access some private storage (which presumably has its own isolated tmp/ directory) really should be default, and permissions should be opt-in (of course, there should be a 'legacy permission' that makes things work as before).

(I believe most of permission functionality is technically possible through SELinux (??), or you could use containers, but is not easy to use or default)

I think containers arose partially to provide some of this isolation? But they have their own overhead and redundancy issues.

---

It seems some of this work is being done in SELinux project? Is it going to be enough? (and easy enough to use by default?)

https://wiki.archlinux.org/title/SELinux

I think a simple permission model might have been more elegant than the SELinux model ?


> For example, the Android(/iOS?) permission based model at kernel level where apps (that could be processes in general?) can only access some private storage

They have access to _all_ storage. Permissions on Android are at the DOS level, all or nothing.


Pledge/unveil it's intristic, per software, under OpenBSD.


That does not expand on the whole TOCTOU-style family of bugs, which permeates all APIs, and the only solution is to manipulate everything by file descriptor; Linux has many syscalls for that: openat, mkdirat, renameat(2), unlinkat, execveat, (new)fstatat, symlinkat, faccessat, fchmodat, fchownat, linkat, mknodat, pidfd_*, etc.

Arguably, many are not relevant to /tmp, but it's good to keep in mind.


So here we have again how "Worse is better" works in practice, and how we got here regarding /tmp in 2024.


As if C:\TEMP or %tmpdir% were any better.

Or using letters as drivers.

Worse is better? A lot of tech in Win32 is built as if it were for DOS 1.0 or CP/M. See AUX, PRN, COM and so.


Backward compatibility, innit.

Microsoft have tried to get people to use the newer, more heavily sandboxed APIs like UWP, but only very weakly, and they haven't committed to transitioning their own apps over as dogfood. Nearest they've got is actually migrating a lot of office to the cloud as Office365.

Sunsetting Win32, or even having significant backwards-compatibility breaks, would upset so many corporate customers who would then refuse to upgrade.


> Sunsetting Win32, or even having significant backwards-compatibility breaks, would upset so many corporate customers who would then refuse to upgrade.

Creating solutions in search of a problem doesn't work. I have to do my job and my computer shall halp me not get in the way. We are at the end of 2024 and i need admin access to install and use an USB to serial converter (or any other HW device) in Windows.


Yeah, nice jab attempt, except no one sells MS-DOS decisions as some grandiose OS architecture design.

Its origins are quite clear, QDOS, Quick and Dirty Operating System.

https://en.m.wikipedia.org/wiki/86-DOS


> As if C:\TEMP or %tmpdir% were any better.

I mean, yes? %tmp% is in the user's directory, not accessible by the world.


TMPDIR can be trivially set at $HOME/tmp too with just a file at /etc/profile


But how many things on Unix hardcode /tmp though?

On DOS it wasn't a thing because it didn't have a standard temp directory to begin with, so when the need arose, it was bolted on in an adhoc way (I remember the apps couldn't even agree on whether it should be %TEMP% or %TMP% or something else). Windows introduced it as a proper first party concept, but then moved it around so much that you pretty much had to use the API to retrieve the actual value if you wanted it to work. And then NT made that API return a path to a per-user directory by default.


Most things in Unix are libre software. Recompiling them to use another path it woudn't be rocket science.


Is it possible to configure every user to see /tmp as $USER/.tmp via some Linux isolation method (namespaces)?


This exact thing is possible with pam_namespace.so!


Very nice, thank you. I will look into this. This may be my break into PAM that I've ignored thus far.

I'm wondering if there's programs that will break with such a change. One example would be if multiple users in a group need access to the same file under /tmp.


pam_namespace normally isolates /tmp by user or SELinux context, so your example might require a couple tweaks. I haven't tried any of these but I'm thinking any of:

1) You could modify the namespace init script used by pam_namespace to also mount a shared directory under each user's /tmp, and do this only for the users who need it.

2) Rely on a different shared directory for the users who need it.

3) Configure namespace.conf to isolate by SELinux context and put each user who needs a shared /tmp into the same SELinux role.


What occurs to me now is that with a proper SELinux configuration you do not even need per-user /tmp, you can use the old /tmp for all. It is still motivating to look into PAM, but perhaps also motivating to learn more about SELinux that I've also put off.


For a janky way of doing it, create a new mount namespace, then bind-mount $HOME/.tmp over /tmp. In practice better ways (ex. sibling comment) exist and you should use those.


> It’s a bad idea because it’s shared global mutable state that crosses security boundaries.

I think there is a use for such a thing (I take advantage of these features somewhat regularly), but having it also be the default $TMPDIR is definitely a bad idea.


I think this is an instance where, to crib a phrase from the golang world, "share memory by communicating" (i.e. programs that need to support this kind of intervention should provide some form of API) is more appropriate than "communicate by sharing memory" (mucking about with programs' runtime state in tmpfs).

I replied to your similar comment upthread as well.


Once shells have easy-to-use support for sending and receiving data between two sessions running as two users, then maybe we can get rid of shared filesystem directories.

I think /tmp is a poor solution even if we are going to use the filesystem for this (some sort of per-user spool makes far more sense), but its value is in its ubiquity.


Things goes along with the author's "tmp cleanup" section, but I have lost valuable work on a number of occasions from hacking on random files that I created in /tmp under the assumption that they were throwaway junk files, only to reboot my machine a few hours later and have them automatically deleted by the OS. It's much safer and just as easy to use a "$HOME/tmp" dir as a junk drawer and then manually clean it up from time to time.


Pro* tip: don't put your mSQL database file on /tmp on a Solaris box.

Because at some point a few months later people will say "where did the database go?" and you'll have a lot of explaining and reconstruction to do.

(mid 90s)


I think this one was in the "Unix admin horror stories"


Like those users who start with Linux, and later move to Solaris only to learn "killall" does something different there..

In my defense I only did it the once.


Wish I'd read that before I did it then.


> valuable work

> created ... under the assumption that they were throwaway junk files

Don't leave your valuables in the office trash bins (they get cleaned every 3 hours), what else can I say?


It's best to assume that any directory which holds temporary files for applications will be cleaned up at any time. The whole point of those directories is to hold things that need to exist somewhere but are ephemeral.


You can use /var/tmp for this purpose

It is not cleared between reboots


Do note that systemd does have a tmpfiles.d (terrible name nowadays, but that is besides the point) drop-in with an extra service that will clean out /var/tmp/ of unused files when they haven't been accessed within 30 days (all of a/m/ctime are checked). Same applies to /tmp/ but with 10 days. I don't know if the service is enable by default on any distros though, so assume it is unless otherwise ensured :)


Depends on the OS. In OpenBSD, /var/tmp is a symlink to ../tmp and is so is cleaned on reboots and periodically.



The title is slightly misleading, as tmp storage is still of need but rather as per the author to be at a per-user level:

> So where should temporary files have gone, if not in /tmp?

> There should have been per-user temporary directories in different per-user locations. In fact, on some modern systems there are per-user temporary directories! But this solution came several decades too late.


> Probably the main reason was path-dependence

Nice pun. :)


I get that /tmp as a shared world readable location is a security issue, but in a day of easy-to-provision VMs, serverless architectures, are there many true multiuser (not multi tenant) systems out there, in the sense of multiple users logging into the same logical system to complete compute tasks?


If you're opening with O_CREAT|O_EXCL, why does it matter whether the filename is predictable?


As u/fanf says, denial of service. The canonical example here would be `/tmp/krb5cc_$UID`, which is where Kerberos libraries keep your Kerberos credentials. If you create that for some other user's UID before they login, they won't be able to use Kerberos (unless they have a clue), causing support calls. This isn't really an issue, but still.

And of course those libraries' code that uses those files had to be written very carefully.

Sure, the more modern thing is to have a daemon called `kcm` that does that and which has an AF_LOCAL socket in... /var/run/, but it's a multi-user-capable daemon, so it doesn't need /var/run/user/${UID}, which as I've noted elsewhere here, is not universally available (for the same reasons that /run/user/${UID} is not either).


Denial of service, the next point in that list.


Because other processes can periodically check whether a predictable filename is in use, and guess things you'd rather keep private?


Why would you have processes you do not trust, or why not use firejail for those that may pose a security risk?

> Firejail is a SUID sandbox program that reduces the risk of security breaches by restricting the running environment of untrusted applications using Linux namespaces, seccomp-bpf and Linux capabilities. It allows a process and all its descendants to have their own private view of the globally shared kernel resources, such as the network stack, process table, mount table. Firejail can work in a SELinux or AppArmor environment, and it is integrated with Linux Control Groups.

It supports "--private" (mounts new /root and /home/user directories in temporary filesystems), along with "--private-{bin,cache,cwd,dev,etc,home,lib,opt,srv,tmp} (plus "noexec /tmp")". It also supports "keep-config-pulse", "keep-dev-shm", and so forth, meaning you can have shared files between process if you so wish (for DBus, etc.).


But you wouldn't need to guess names to do that, since the names of files in /tmp are publicly listable.


Shouldn't XDG_CACHE_DIR handle most use cases for /tmp? I can't see any reasons why we need to keep /tmp around since we have so many better options for ipc and for storing ephemeral data.


and XDG_RUNTIME_DIR for sockets and such. Typically set to /run/user/<id>/


I think if one is going to post something like this one should post statistics on security breaches of the risk in question.

How many security breaches have there actually been because of /tmp?


We use /tmp all the time with a one writer / many readers rule. If we ever encounter an issue, then we move that data object outside of /tmp.


Whenever I find myself needing /tmp, it's usually as a form of IPC shared memory, in which case I use /dev/shm directly.


On the other hand /tmp is useful for symlinking ~/Downloads to on MacOS since then you never have to clean up your Downloads!


Same for the sysadmins/scripts that create C:\Temp on Windows, there is nothing temporary about that directory.


I use a tmp dir in my home folder on my machines too, I like the idea of tmp.


I wonder how much this matters for containerized apps.


The actual answer is to use O_TMPFILE.


Which is not portable. But also if you wanted to find your stuff again in other processes that you can't arrange to inherit shared memory and/or open FDs from the one creating it, then O_TMPFILE is not a relevant answer.


> Against /Tmp

This is like a pope talking about celibacy.

Did he try to remove /tmp entirely and see how it goes ?


That makes little sense; the author's complaint amounts to "programs should not use /tmp", so removing it before fixing all programs would be putting the cart before the horse.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: