noexec /tmp in 2024
TLDR;
/tmp
is controlled by systemd. Create a /etc/systemd/system/tmp.mount.d/override.conf
with content:
[Mount]
Options=noexec
Then enable and reboot:
systemctl enable tmp.mount
reboot
Long Version
Want to set noexec
on /tmp
for security/hardening? You used to be able to do this by editing /etc/fstab
but systemd has now taken over this job and there is no mention of tmp
in this fstab
any more.
The following are my notes from a RHEL8 system that required noexec
to be enabled for testing.
systemd tmp support
- systemd calls management of tempfiles
tmpfiles
- tempfile functionality is in the core
systemd
package on RHEL - Poke around for interesting files shipped in systemd that deal with tempfiles:
rpm -ql systemd|grep tmpf
This reveals the main control for /tmp
is in /usr/lib/tmpfiles.d/tmp.conf
man 5 tmpfiles.d
Has some good information. /etc/tmpfiles.d/
doesn’t directly support setting mount options like noexec
, instead, you can create a mount unit or override the existing tmp.mount
unit.
Editing a systemd mount
When the mount being edited already exists, systemctl edit
will create an override file. To save the hassle of remembering how systemd munges paths, just systemctl edit tmp.mount
and add #findme
as part of the edit, then search the filesystem for #findme
. This proves the file we should override tmp.mount
with is /etc/systemd/system/tmp.mount.d/override.conf
.
After editing, the mount needs to be enabled (that’s right, we are overriding and enabling) and we need to reboot:
systemctl enable tmp.mount
reboot
Testing
It’s very easy to miss steps and not properly activate the settings, especially when you know what your doing, so be sure to test each time:
- Create an executable script on
/tmp
and try to run it. You must see message:Permission denied
. - Mount must also show
noexec
option, as below:
# mount|grep tmp
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=7980172k,nr_inodes=1995043,mode=755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
tmpfs on /tmp type tmpfs (rw,noexec,relatime,seclabel)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=1599608k,mode=700,uid=1000,gid=1000)
Automation
If everything works, you probably want to automate setting this option with some ansible:
- name: "create systemd tmp.mount override dir"
ansible.builtin.file:
path: /etc/systemd/system/tmp.mount.d
state: directory
mode: '0755'
owner: "root"
group: "root"
- name: systemd override for /tmp
ansible.builtin.copy:
dest: /etc/systemd/system/tmp.mount.d/override.conf
owner: root
group: root
mode: 0644
content: |
[Mount]
Options=noexec
register: tmp_mount_override
- name: Enable tmp.mount
ansible.builtin.systemd_service:
name: tmp.mount
enabled: true
register: tmp_mount_service
- ansible.builtin.reboot:
when: tmp_mount_override.changed or tmp_mount_service.changed
Why are we doing this?
Security:
- Normally, anyone can write to
/tmp
- If you need to break into a system and create and run an executable payload, dumping it in
/tmp
somewhere is the traditional approach noexec
on/tmp
stops this completely.- Now an attacker would have to put in more effort to find a directory that he can both write and execute