{
  "id": "CB-UBU-FS-006",
  "standard": "ubuntu",
  "version": "0.1.0",
  "url": "https://baselines.cirsec.org/b/ubuntu/v0.1.0/CB-UBU-FS-006",
  "json": "https://baselines.cirsec.org/b/ubuntu/v0.1.0/CB-UBU-FS-006.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/v0.1.0/CB-UBU-FS-006.yml",
  "profiles_effective": [
    "core",
    "server",
    "server-hardened",
    "desktop",
    "desktop-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-FS-006",
    "title": "Mount separate filesystems with nodev, nosuid and noexec where they are not needed",
    "finding": {
      "title": "Separate filesystems are mounted without restrictive options",
      "description": "Device files, setuid programs or executables can be used from filesystems that should only hold data."
    },
    "status": "active",
    "profiles": [
      "core"
    ],
    "risk": {
      "severity": "low"
    },
    "assessment": "automated",
    "tags": [
      "mount",
      "partitions",
      "fstab"
    ],
    "description": "When these directories are separate filesystems, they are mounted with the options listed in\n`{{ mount_options }}` (format `directory:option,option`). By default:\n\n| Directory | Options |\n|---|---|\n| `/home`, `/var`, `/opt`, `/srv` | `nodev,nosuid` |\n| `/var/tmp`, `/var/log`, `/var/log/audit`, `/boot` | `nodev,nosuid,noexec` |\n| `/usr` | `nodev` |\n\nThe hardened profiles also require `noexec` on `/home`. Every other local filesystem backed by a\nblock device, except `/` and the snap images under `/snap`, must be mounted with `nodev`. A directory that is not a separate\nfilesystem is skipped; when there is none besides `/`, the control does not apply.\n",
    "rationale": "Only `/dev` needs device files, and only system directories need setuid programs or executables.\n`nodev` stops a device node created on a data filesystem (or brought on removable media) from\ngiving raw access to disks or memory. `nosuid` stops setuid programs planted there from granting\nprivileges, and `noexec` stops tools dropped there from being run directly.\n",
    "impact": "`noexec` on `/home` (hardened profiles) prevents users from running their own scripts and\nprograms directly, including tools installed under `~/.local/bin`. `noexec` on `/boot` and\n`/var/log` has no known effect on Ubuntu. Snaps run from their own read-only squashfs\nmounts under `/snap`, which snapd mounts with `nodev`: the check skips them, and these options\ndo not affect them.\n",
    "notes": "ANSSI also recommends `noexec` on `/var`. It is left out: dpkg runs package maintainer scripts\nfrom `/var/lib/dpkg/info/`, so package installation and upgrades fail with it. Container engines\nthat store images under `/var/lib` need `exec` as well, and so does snapd (`/var/lib/snapd`).\n\nUbuntu 24.04 and 26.04 cloud images have a separate `/boot` partition mounted with `defaults`,\nand every release mounts `/boot/efi` without `nodev`: the check fails on a fresh image until\nthe options are added in `/etc/fstab`.\n",
    "setting": {
      "location": "/etc/fstab",
      "default_value": "Options chosen at installation (usually defaults)",
      "recommended_value": "{{ mount_options }}; nodev on every other local filesystem except /"
    },
    "parameters": [
      {
        "name": "mount_options",
        "type": "list",
        "description": "Required options per mount point, as directory:option,option.",
        "default": [
          "/home:nodev,nosuid",
          "/var:nodev,nosuid",
          "/var/tmp:nodev,nosuid,noexec",
          "/var/log:nodev,nosuid,noexec",
          "/var/log/audit:nodev,nosuid,noexec",
          "/boot:nodev,nosuid,noexec",
          "/opt:nodev,nosuid",
          "/srv:nodev,nosuid",
          "/usr:nodev"
        ],
        "profile_defaults": {
          "server-hardened": [
            "/home:nodev,nosuid,noexec",
            "/var:nodev,nosuid",
            "/var/tmp:nodev,nosuid,noexec",
            "/var/log:nodev,nosuid,noexec",
            "/var/log/audit:nodev,nosuid,noexec",
            "/boot:nodev,nosuid,noexec",
            "/opt:nodev,nosuid",
            "/srv:nodev,nosuid",
            "/usr:nodev"
          ],
          "desktop-hardened": [
            "/home:nodev,nosuid,noexec",
            "/var:nodev,nosuid",
            "/var/tmp:nodev,nosuid,noexec",
            "/var/log:nodev,nosuid,noexec",
            "/var/log/audit:nodev,nosuid,noexec",
            "/boot:nodev,nosuid,noexec",
            "/opt:nodev,nosuid",
            "/srv:nodev,nosuid",
            "/usr:nodev"
          ]
        }
      }
    ],
    "audit": {
      "manual": "1. Run `findmnt -kn -o TARGET,SOURCE,OPTIONS`.\n2. For each mounted directory of `{{ mount_options }}`, the options must contain the listed ones.\n3. Every other line whose source is a `/dev/` device, except `/` and `/snap/...`, must\n   contain `nodev`.\n",
      "checks": [
        {
          "id": "options",
          "engine": "shell",
          "description": "Mount options of the listed directories and nodev on other local block-device filesystems.",
          "spec": {
            "script": "rc=0 seen=\nfor e in $CB_MOUNT_OPTIONS; do\n  p=${e%%:*}\n  o=$(findmnt -kn -o OPTIONS --mountpoint \"$p\" | tail -n 1)\n  [ -n \"$o\" ] || continue\n  seen=\"$seen $p \"\n  miss=\n  for opt in $(printf '%s' \"${e#*:}\" | tr , ' '); do\n    case ,$o, in *,$opt,*) ;; *) miss=\"$miss $opt\" ;; esac\n  done\n  if [ -z \"$miss\" ]; then echo \"$p: ok\"; else echo \"$p: missing$miss\"; rc=1; fi\ndone\nother=$(findmnt -knr -o TARGET,SOURCE,OPTIONS | awk '$2 ~ \"^/dev/\" && $1 != \"/\" && $1 !~ \"^/snap/\"')\nwhile read -r t _ o; do\n  [ -n \"$t\" ] || continue\n  case $seen in *\" $t \"*) continue ;; esac\n  seen=\"$seen $t \"\n  case ,$o, in *,nodev,*) echo \"$t: ok\" ;; *) echo \"$t: missing nodev\"; rc=1 ;; esac\ndone <<EOF\n$other\nEOF\n[ -n \"$seen\" ] || { echo \"no separate filesystem besides /\"; exit 2; }\nexit $rc\n"
          }
        }
      ]
    },
    "remediation": {
      "manual": "For each reported directory:\n1. Add the missing options to the fourth field of its line in `/etc/fstab` (for a mount made by\n   a systemd `.mount` unit, to its `Options=`).\n2. Run `systemctl daemon-reload` and `mount -o remount <directory>`.\n3. Check with `findmnt -kn <directory>`, and reboot at the next maintenance window to confirm.\n\nTest `noexec` and `nosuid` first on systems that run software from `/opt`, `/srv` or `/home`.\n"
    },
    "references": [
      {
        "title": "mount(8), Ubuntu manual pages (filesystem-independent mount options)",
        "url": "https://manpages.ubuntu.com/manpages/noble/man8/mount.8.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "fstab(5), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man5/fstab.5.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R28)",
        "url": "https://messervices.cyber.gouv.fr/documents-guides/fr_np_linux_configuration-v2.0.pdf",
        "publisher": "ANSSI",
        "kind": "guide",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "anssi-linux",
        "framework_version": "2.0",
        "id": "R28",
        "relation": "partial",
        "note": "Ours leaves out noexec on /var (breaks dpkg) and noexec on /home outside the hardened profiles."
      },
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "CM-7",
        "relation": "related"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1548.001"
      },
      {
        "framework": "cwe",
        "id": "CWE-732"
      }
    ],
    "related_controls": [
      {
        "id": "CB-UBU-FS-005",
        "type": "related"
      },
      {
        "id": "CB-UBU-FS-003",
        "type": "related"
      }
    ],
    "authors": [
      {
        "name": "Alice",
        "github": "Sn0wAlice"
      }
    ],
    "verified": [
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "20.04"
        ],
        "environment": "Proxmox VM 2520, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic",
        "notes": "Fails on fresh install (/boot/efi without nodev); snap mounts skipped; loop-mounted /var/tmp fails without options and passes after remount with nodev,nosuid,noexec."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM 2522, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic",
        "notes": "Fails on fresh install (/boot/efi without nodev); snap mounts skipped; loop-mounted /var/tmp fails without options and passes after remount with nodev,nosuid,noexec."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM 2524, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic",
        "notes": "Fails on fresh install (/boot and /boot/efi without the options); passes once they are added in fstab and remounted, update-initramfs and update-grub still work; loop-mounted /var/tmp fails without options and passes after remount with nodev,nosuid,noexec."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM 2526, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic",
        "notes": "Fails on fresh install (/boot and /boot/efi without the options); passes once they are added in fstab and remounted, update-initramfs and update-grub still work; loop-mounted /var/tmp fails without options and passes after remount with nodev,nosuid,noexec."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}