geoffwilliams@home:~$

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

Post comment

Markdown is allowed, HTML is not. All comments are moderated.