Does /tmp have a split personality?

1 minute read

Today I had my first real encounter with the new Linux world, with namespaces, cgroups and systemd. As it turns out, old wisdoms like "an absolute file path is an absolute file path" don't hold any more :-)

But let's start from the beginning. I just recently updated a machine to the latest openSUSE 13.1, which uses systemd unit files for a lot of services. I use this machine to develop a web application in PHP, and for  debugging I wrote something to /tmp, like that:

<?php
file_put_contents('/tmp/some-file', 'some-content');
?>

The script always worked fine and it still does work fine. But not really the way I expected it to ... The file /tmp/some-file does not exist after the script ran, even though I could read it back from PHP! Strange... the file simply seems to disappear!

After scratching my head for some time, I finally found out what's going on:

$> cat /usr/lib/systemd/system/apache2.service
 [Unit]
 Description=The Apache Webserver
 Wants=network.target nss-lookup.target
 After=network.target nss-lookup.target
 Before=getty@tty1.service
[Service]
 Type=notify
 PrivateTmp=true
 EnvironmentFile=/etc/sysconfig/apache2
 ExecStart=/usr/sbin/start_apache2 -D SYSTEMD -DFOREGROUND -k start
 ExecReload=/usr/sbin/start_apache2 -D SYSTEMD -DFOREGROUND -t -k graceful
 ExecStop=/usr/sbin/start_apache2 -D SYSTEMD -DFOREGROUND -k graceful-stop
[Install]
WantedBy=multi-user.target

I already highlighted the offending line in the unit file. What does it? Let's have a look at the man page (man systemd.exec)

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. This is useful to
  secure access to temporary files of the process, but makes sharing between
  processes via /tmp or /var/tmp impossible. All temporary data created by service
  will be removed after service is stopped. Defaults to false.

So yes, that's the solution. Apache (and thus PHP, which runs as Apache module) sees a different /tmp as I do. The solution is easy obviously: either use a different directory for storing your files or disable PrivateTmp. The latter is quite easy as well, thanks to the configuration overrides in systemd (you may need to change the directory of the override, it's the name of the unit + ".d", see the announcement for details)

#> mkdir /etc/systemd/system/apache2.service.d
#> echo -e "[Service]\nPrivateTmp=no" > /etc/systemd/system/apache2.service.d/privatetmp.conf
#> systemctl daemon-reload
#> systemctl restart apache2
#> systemctl show apache | grep PrivateTmp
PrivateTmp=no