The thing that blew my mind with Plan9 is that the bindings are process-specific. So if you need a very specific process to have a very private /tmp, you can do that, and it changes nothing for all the other processes.
What I don’t get with that… all usages of /tmp I’ve had to deal with are with inter-process communication stuff, so if /tmp is isolated to a process then its utility seems hindered?
Unless people are thinking of a sort of layered file system based off your process tree…. but that gets you into a whole world of pain without the right abstraction
I am, indeed, talking about layering of FS namespaces: that is how Plan9 primarily operates. As for uses of /tmp, you should really use /run instead in most cases.
In Plan 9, you would set up the namespace of your process, e.g. for logins or some system service. You could use ramfs(4) on /tmp (https://9p.io/magic/man2html/4/ramfs), or bind or mount something disk/network-backed. You wouldn’t need a variable to find the temp dir: anyone can put anything at the fixed path /tmp.
With per-user /tmp most reasonable sharing of files/sockets will keep working, without most of the risk.
You wouldn’t need a variable to find the temp dir: anyone can put anything at the fixed path /tmp.
Right, but the /tmp process A sees is not the /tmp process B sees, to my understanding.
With per-user /tmp most reasonable sharing of files/sockets will keep working, without most of the risk.
This is a reasonable stop-gap solution for that problem, but using /tmp for files/sockets is inappropriate in the first place IMO. /tmp is not a great way to share files and in general there are better places for things like named pipes/sockets/etc. and tying their existence to the lifetime of the process group and simplifies the application code because it no longer needs something like an atexit to clean up after itself.
Oh, interesting! The multics one sounds like it’s being used for the kinds of things that are usually mmap(MAP_ANON) on unix. But I suppose if the process directory isn’t anonymous it can be used for file-based or shared memory IPC?
Multics had single-level storage, so all those files were considered memory mapped anyways. AFAIK (I haven’t used Multics much), there wasn’t much distinction between files and heap in memory; I guess the best analogy is if all files were mmap’d on Unix - making the MAP_ANON analogy as well work.
I’m more familar with i, which is also SLS, but persistent memory and single address space as well. On i, you can could just pass other processes pointers to things you have in QTEMP (or anywhere else, but objects in QTEMP are known by name only to that process), which can be not just files (which are also SQL tables), but also other things like data queues and user spaces (basically opaque byte arrays).
I added that section after several people mentioned it, as indicated by the “edited to add” note. It wasn’t in the article when ~abhinav wrote that comment,
Of course, we can’t go back in time to get rid of /tmp, so efforts continue to make it safer by making things more complicated.
immediately preceding it seems very dismissive of the concept of PrivateTmp. How is it more complicated? It’s better than per-user $TMPDIR because “runs as same user” is not a valid security boundary in the day and age of per-application sandboxing, and it allows applications to not need to think about this at all while behaving all in a consistent way to the user, unlike your proposed suggestion that each application should just figure out their own per-user $TMPDIR.
Personally, I don’t think a BIND9 developer should lean too far out of the window when it comes to implying other things are too complicated to be secure, considering your software’s penchant to being misconfigured is the only reason why DNS reflection attacks exist.
I think that a shared, user-writable filesystem really optimizes for an edge case at this point and at the great cost of simplicity and safety. It’s an extreme pain in the ass to have a program just say “I want my own place to live with no one fucking with my stuff”. Even per-user isn’t far enough, very few programs need or want a shared directory with other programs, even within a single user. But yeah, per-user would be a sane option to support at least.
On the other hand, look at the state of Android. My music player can’t write M3U playlists to sync with my desktop anymore, because the filesystem permissions model is so needlessly restrictive.
I think that having a shared, user-writeable filesystem is critical. It just doesn’t need to be the only filesystem there is.
IMO, a good solution is similar to what iOS does with permissions. There’s a shared photo library that any app/program can access, but the kernel+runtime require user consent. Something similar could be done with access to folders; Restrict programs from reading/writing to any folder without explicit permission to said folder.
On the other hand, look at the state of Android. My music player can’t write M3U playlists to sync with my desktop anymore, because the filesystem permissions model is so needlessly restrictive.
But apps are allowed to ask the system to show a dialog to choose a directory for them to read and write in, which gives them permission. I have Syncthing set up to sync all kinds of different folders from and to my phone.
Maybe on a locked down iPhone, or for some back-end services, but in general I strongly disagree, especially for desktops and workstations.
Even basic tasks like making a simple “Hello World” program, or downloading an image and viewing it, watching a movie outside of a browser, downloading an MP3, sharing a document, etc. would become more difficult and needlessly complicated.
My feeling is that OSX is moving in that direction, and that’s part of the reason I stopped using it.
I suppose the solution there is to have shared and private paths. It should be possible to share the file system, it’s just that right now you can’t unshare it easily.
Lots to agree with here. There are plenty such edge cases in Unix (and even more in Linux and MacOS and all the “real world” Unixes).
The simplest single way to mostly avoid them is to treat each machine as a single-user single-application appliance, and ignore the internal security boundaries. This doesn’t entirely solve the problem, because some applications (DBMS engines, for example) implement their own multi-tenancy, but it does help avoid the problem for the vast majority of server-side deployments. Designing the large security approach of a system so you don’t have to worry about local privesc at all is extremely freeing.
The client side is more complex. Unless you’re going to go full Qubes, it’s not really possible to avoid multi-application, and isolation between applications can be very important. Most of those environments don’t need to be multi-user, but some of them do. So the client side is harder, and these internal mechanisms are still valuable (even if they aren’t the internal mechanisms that would be most useful for actually isolating applications from each other).
yeah, nothing much to say here other than it’s very agreeable. in shell scripts I’ve sometimes for using XDG_RUNTIME_DIR as a “I don’t want to worry about mktemp stuff right now, just get me something I know is safe enough” temporary directory
You can do that, but be aware that on many distros, this directory has both a very small size limit and a very low inode limit. The XDG spec says “Applications should use this directory for communication and synchronization purposes and should not place larger files in it, since it might reside in runtime memory and cannot necessarily be swapped out to disk.”
huh. I’m aware of that part of the spec, but I didn’t realize in practice distros usually make it super low. mine’s like the same size as normal /tmp. gonna have to rethink like, one script of mine I suppose
So the article mentions systemd’s PrivateTmp which comprehensively solves this issue, then proceeds to ignore it and conclude that it might be an unsolveable issue…
I don’t why you think “/tmp should not exist” is solved by /tmp existing. The issue is all the complications and scar tissue. An overlay doesn’t solve the problem, it adds to it!
I don’t think there’s any way to get rid of the complications I’ve described in this post, as long as a /tmp directory exists – which will be forever. Unix programs have to assume /tmp is unsafe, so the security mechanisms in mkstemp() have to stay. We can’t remove sticky bit support from the kernel as long as there’s a global /tmp, even if it is sometimes hidden by an overlay.
‘/tmp should not exist’ is not a problem statement, it’s a proposed solution to the security leakage issues caused by /tmp existing. The solution proposed by systemd is comprehensive, realistic, and proven in production…as long as you use it, of course.
I’m not proposing any solutions, I’m explaining the mess. I am complaining about unnecessary complexity.
I said in the article that PrivateTmp is safer, so I don’t know why you accused me of ignoring it. And it’s wrong to call it a “comprehensive” solution: a comprehensive solution would ensure /tmp is always safe, but systemd left it unsafe on my systems.
OK so…after you mentioned PrivateTmp, you said there’s probably no way to get rid of /tmp, which, while a valid point, is I think besides the point. The concern for people is that /tmp is a security issue, not that it merely exists. If systemd solves the security issue, then the existence of /tmp is not really a problem per se.
OK, in that case I believe that is incorrect if we are in a context where PrivateTmp exists, because that’s exactly what it does, it gets rid of the need to have the complicated workarounds. Your app uses /tmp just like normal and doesn’t need to worry about security issues.
The complications still exist even if some non-portable programs can skip some of them in some special circumstances. Your suggestion is unsafe unless the app verifies that /tmp is not world writable – yet another complication!
This is like arguing that HTTP servers are still insecure if they don’t verify that they only accept HTTPS connections even when we use a reverse proxy to do TLS termination. Security can work in layers. We layer on /tmp protection without having to add complications to the program itself just like we do for HTTPS support.
You are saying that it’s simpler to write a program that does not call mkstemp() and instead has a hard dependency on a specific configuration of systemd.
If we are trying to make a security recommendation, then yes using systemd PrivateTmp is probably the simplest possible way. If we are just saying that computer things are complicated, then yes but like, what else is new?
I suppose /tmp security bugs were common in the 1990s when I was learning Unix, but they are pretty rare now so I can see why less grizzled hackers might not be familiar with the problems.
Anyway, for the absence of doubt (in case anyone else is still reading), I think programs should always use mkstemp() or mkdtemp() for temporary files and not skip it as ~yawaramin suggested – it’s better to learn from other people’s mistakes, not repeat them.
And since ~yawaramin is no longer claiming I said things that I did not, I can stop commenting.
If this issue is as rare as you say now, it might not be worth the hassle of boiling the oceans to turn every call of every library or subprocess you use to make it use mkstemp/mkdtemp; it might be good enough to just use systemd or some other containerization mechanism (which many people are doing now anyway) to plug this leak.
Some OSes solvled this by having process-local temporary directories instead. Multics had them, as does IBM i (QTEMP).
Plan9 probably did too, considering the whole architecture was built around
mount(1)
andbind(1)
.I disagree with the author somewhat. I don’t think a per-user /tmp would be an ideal fix. I think a per process group /tmp would be ideal
Correct. Plan 9 binds
$user/tmp
to/tmp
by default. If you want finer grained isolation, you can bind any other dir over/tmp
as you desire.The thing that blew my mind with Plan9 is that the bindings are process-specific. So if you need a very specific process to have a very private /tmp, you can do that, and it changes nothing for all the other processes.
What I don’t get with that… all usages of
/tmp
I’ve had to deal with are with inter-process communication stuff, so if/tmp
is isolated to a process then its utility seems hindered?Unless people are thinking of a sort of layered file system based off your process tree…. but that gets you into a whole world of pain without the right abstraction
I am, indeed, talking about layering of FS namespaces: that is how Plan9 primarily operates. As for uses of
/tmp
, you should really use/run
instead in most cases.In Plan 9, you would set up the namespace of your process, e.g. for logins or some system service. You could use ramfs(4) on /tmp (https://9p.io/magic/man2html/4/ramfs), or bind or mount something disk/network-backed. You wouldn’t need a variable to find the temp dir: anyone can put anything at the fixed path /tmp.
With per-user /tmp most reasonable sharing of files/sockets will keep working, without most of the risk.
Right, but the /tmp process A sees is not the /tmp process B sees, to my understanding.
This is a reasonable stop-gap solution for that problem, but using
/tmp
for files/sockets is inappropriate in the first place IMO. /tmp is not a great way to share files and in general there are better places for things like named pipes/sockets/etc. and tying their existence to the lifetime of the process group and simplifies the application code because it no longer needs something like an atexit to clean up after itself.Maybe. But if
/tmp
never existed and everything used$TMPDIR
instead, you could slice and dice to your heart’s content!You might need to find another place for per-login state, though, if named pipes and suchlike can’t go in
$HOME
.Debian (and others) have libpam-tmpdir, that “sets
$TMPDIR
and$TMP
for PAM sessions and sets the permissions quite tight.”Oh, interesting! The multics one sounds like it’s being used for the kinds of things that are usually
mmap(MAP_ANON)
on unix. But I suppose if the process directory isn’t anonymous it can be used for file-based or shared memory IPC?Multics had single-level storage, so all those files were considered memory mapped anyways. AFAIK (I haven’t used Multics much), there wasn’t much distinction between files and heap in memory; I guess the best analogy is if all files were mmap’d on Unix - making the
MAP_ANON
analogy as well work.I’m more familar with i, which is also SLS, but persistent memory and single address space as well. On i, you can could just pass other processes pointers to things you have in QTEMP (or anywhere else, but objects in QTEMP are known by name only to that process), which can be not just files (which are also SQL tables), but also other things like data queues and user spaces (basically opaque byte arrays).
There’s this: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#PrivateTmp=
I didn’t know about it either.
It’s mentioned in the article.
I added that section after several people mentioned it, as indicated by the “edited to add” note. It wasn’t in the article when ~abhinav wrote that comment,
Your sentence:
immediately preceding it seems very dismissive of the concept of PrivateTmp. How is it more complicated? It’s better than per-user $TMPDIR because “runs as same user” is not a valid security boundary in the day and age of per-application sandboxing, and it allows applications to not need to think about this at all while behaving all in a consistent way to the user, unlike your proposed suggestion that each application should just figure out their own per-user $TMPDIR.
Personally, I don’t think a BIND9 developer should lean too far out of the window when it comes to implying other things are too complicated to be secure, considering your software’s penchant to being misconfigured is the only reason why DNS reflection attacks exist.
It wasn’t there in the original version. The author edited in the suggested solutions with a note.
I think that a shared, user-writable filesystem really optimizes for an edge case at this point and at the great cost of simplicity and safety. It’s an extreme pain in the ass to have a program just say “I want my own place to live with no one fucking with my stuff”. Even per-user isn’t far enough, very few programs need or want a shared directory with other programs, even within a single user. But yeah, per-user would be a sane option to support at least.
On the other hand, look at the state of Android. My music player can’t write M3U playlists to sync with my desktop anymore, because the filesystem permissions model is so needlessly restrictive.
I think that having a shared, user-writeable filesystem is critical. It just doesn’t need to be the only filesystem there is.
IMO, a good solution is similar to what iOS does with permissions. There’s a shared photo library that any app/program can access, but the kernel+runtime require user consent. Something similar could be done with access to folders; Restrict programs from reading/writing to any folder without explicit permission to said folder.
But apps are allowed to ask the system to show a dialog to choose a directory for them to read and write in, which gives them permission. I have Syncthing set up to sync all kinds of different folders from and to my phone.
Maybe on a locked down iPhone, or for some back-end services, but in general I strongly disagree, especially for desktops and workstations.
Even basic tasks like making a simple “Hello World” program, or downloading an image and viewing it, watching a movie outside of a browser, downloading an MP3, sharing a document, etc. would become more difficult and needlessly complicated.
My feeling is that OSX is moving in that direction, and that’s part of the reason I stopped using it.
I suppose the solution there is to have shared and private paths. It should be possible to share the file system, it’s just that right now you can’t unshare it easily.
Lots to agree with here. There are plenty such edge cases in Unix (and even more in Linux and MacOS and all the “real world” Unixes).
The simplest single way to mostly avoid them is to treat each machine as a single-user single-application appliance, and ignore the internal security boundaries. This doesn’t entirely solve the problem, because some applications (DBMS engines, for example) implement their own multi-tenancy, but it does help avoid the problem for the vast majority of server-side deployments. Designing the large security approach of a system so you don’t have to worry about local privesc at all is extremely freeing.
The client side is more complex. Unless you’re going to go full Qubes, it’s not really possible to avoid multi-application, and isolation between applications can be very important. Most of those environments don’t need to be multi-user, but some of them do. So the client side is harder, and these internal mechanisms are still valuable (even if they aren’t the internal mechanisms that would be most useful for actually isolating applications from each other).
yeah, nothing much to say here other than it’s very agreeable. in shell scripts I’ve sometimes for using XDG_RUNTIME_DIR as a “I don’t want to worry about mktemp stuff right now, just get me something I know is safe enough” temporary directory
You can do that, but be aware that on many distros, this directory has both a very small size limit and a very low inode limit. The XDG spec says “Applications should use this directory for communication and synchronization purposes and should not place larger files in it, since it might reside in runtime memory and cannot necessarily be swapped out to disk.”
yeah nix tends to fill up XDG_RUNTIME_DIR if you forget to override, very annoying to run out of space when you have space
This is finally fixed with https://github.com/NixOS/nix/pull/10883!
oh nice, a papercut gone :-D
huh. I’m aware of that part of the spec, but I didn’t realize in practice distros usually make it super low. mine’s like the same size as normal /tmp. gonna have to rethink like, one script of mine I suppose
So the article mentions systemd’s
PrivateTmp
which comprehensively solves this issue, then proceeds to ignore it and conclude that it might be an unsolveable issue…I don’t why you think “
/tmp
should not exist” is solved by/tmp
existing. The issue is all the complications and scar tissue. An overlay doesn’t solve the problem, it adds to it!‘
/tmp
should not exist’ is not a problem statement, it’s a proposed solution to the security leakage issues caused by/tmp
existing. The solution proposed by systemd is comprehensive, realistic, and proven in production…as long as you use it, of course.I’m not proposing any solutions, I’m explaining the mess. I am complaining about unnecessary complexity.
I said in the article that PrivateTmp is safer, so I don’t know why you accused me of ignoring it. And it’s wrong to call it a “comprehensive” solution: a comprehensive solution would ensure /tmp is always safe, but systemd left it unsafe on my systems.
OK so…after you mentioned
PrivateTmp
, you said there’s probably no way to get rid of/tmp
, which, while a valid point, is I think besides the point. The concern for people is that/tmp
is a security issue, not that it merely exists. If systemd solves the security issue, then the existence of/tmp
is not really a problem per se.No, I said “I don’t think there’s any way to get rid of the complications”.
OK, in that case I believe that is incorrect if we are in a context where
PrivateTmp
exists, because that’s exactly what it does, it gets rid of the need to have the complicated workarounds. Your app uses/tmp
just like normal and doesn’t need to worry about security issues.The complications still exist even if some non-portable programs can skip some of them in some special circumstances. Your suggestion is unsafe unless the app verifies that /tmp is not world writable – yet another complication!
This is like arguing that HTTP servers are still insecure if they don’t verify that they only accept HTTPS connections even when we use a reverse proxy to do TLS termination. Security can work in layers. We layer on
/tmp
protection without having to add complications to the program itself just like we do for HTTPS support.You are saying that it’s simpler to write a program that does not call mkstemp() and instead has a hard dependency on a specific configuration of systemd.
I am saying that this is all a horrible mess.
If we are trying to make a security recommendation, then yes using systemd
PrivateTmp
is probably the simplest possible way. If we are just saying that computer things are complicated, then yes but like, what else is new?It’s new to some people! As I wrote in the intro,
Anyway, for the absence of doubt (in case anyone else is still reading), I think programs should always use mkstemp() or mkdtemp() for temporary files and not skip it as ~yawaramin suggested – it’s better to learn from other people’s mistakes, not repeat them.
And since ~yawaramin is no longer claiming I said things that I did not, I can stop commenting.
If this issue is as rare as you say now, it might not be worth the hassle of boiling the oceans to turn every call of every library or subprocess you use to make it use
mkstemp
/mkdtemp
; it might be good enough to just use systemd or some other containerization mechanism (which many people are doing now anyway) to plug this leak.That section (which mentions PrivateTmp) was edited to add it later. The author also mentions that.
PrivateTmp
sounds great, but as a FreeBSD user I’m not going to be able to use it in the foreseeable future. I would need some other solution.If you use FreeBSD for production workloads then you should really familiarize yourself with jails.