Skip to Content
OpenVAPTAlpha
Pre-release. Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. Free for your own use, 100% at your own risk. This website collects no data. Read MoreRead Less
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
LowHOST-KRN-022Secure ConfigurationServer

Unprivileged User Namespaces Allowed

Checks that normal users cannot create user namespaces, unless the server runs containers that need them.

Why It Matters
User namespaces give an unprivileged process a fake root inside a namespace, which exposes kernel code paths that were written for root only; many local kernel exploits start by unsharing a user namespace.
How It Detects
Reads /proc/sys/kernel/unprivileged_userns_clone (Debian and Ubuntu kernels) and, when that does not exist, /proc/sys/user/max_user_namespaces. The first is hardened at 0; the second at 0. Kernels with neither parameter are skipped. Passes with a note when /proc/sys/kernel/apparmor_restrict_unprivileged_userns is 1 (Ubuntu 23.10 and later), because AppArmor then denies the namespace capabilities to unconfined programs. Container hosts that need rootless namespaces should suppress this check deliberately.
How to Fix
Create /etc/sysctl.d/60-openvapt-hardening.conf containing kernel.unprivileged_userns_clone = 0 (Debian family) or user.max_user_namespaces = 0 (other kernels), then apply with: sysctl --system. Rootless Docker, Podman, Flatpak and some browsers need user namespaces, so test first.
How to Verify
sysctl kernel.unprivileged_userns_clone user.max_user_namespaces
CVSS v3.1 Vector
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Control Category
Secure Configuration: CIS Controls v8 4; NIST CSF 2.0 PR.PS
Fix Effort
Low
CIS Controls v8 Safeguard
4.1
NIST CSF 2.0 Subcategory
PR.PS-01
CWE
CWE-749
NIST SP 800-53 Rev. 5
CM-6, CM-7
Benchmark Topic
Kernel parameters: user namespaces (CIS Controls v8 4.1) (topic only, no benchmark text)
References
user_namespaces(7), Linux man-pages
Kernel Self Protection Project, Recommended Settings
Learn the theory behind this check: P1 Linux Server Hardening, P17 Kernel, Filesystem and Boot Hardening.

Source Code

The complete file that defines this check. Run it with openvapt scan --only HOST-KRN-022, or read it offline with openvapt explain HOST-KRN-022.

internal/checks/host/kernel_depth.go · Lines 1-123View on GitHub at This Commit
1package host
2
3import (
4 "context"
5 "errors"
6 "io/fs"
7 "strings"
8
9 "github.com/openvapt/openvapt/internal/check"
10 "github.com/openvapt/openvapt/internal/checks/collect"
11 "github.com/openvapt/openvapt/internal/sys"
12)
13
14// Kernel parameter depth: the second table of sysctl rules. Every row is a
15// parameter that kernel.go does not already cover (the first table holds
16// ASLR, kptr_restrict, dmesg_restrict, suid_dumpable, ptrace_scope,
17// unprivileged_bpf_disabled, protected_symlinks and hardlinks, rp_filter,
18// log_martians, send_redirects, the ICMP and source-route settings and
19// perf_event_paranoid). Values come from /proc/sys; no command is run.
20//
21// Sources: Linux kernel documentation (docs.kernel.org), the Kernel
22// Self Protection Project recommended settings, NIST SP 800-123 and SP
23// 800-53 CM-6 and CM-7, CIS Controls v8 safeguard 4.1 by id.
24
25var (
26 refKSPP = check.Ref{Title: "Kernel Self Protection Project, Recommended Settings", URL: "https://kspp.github.io/Recommended_Settings"}
27 refKernelFS = check.Ref{Title: "Linux kernel documentation, /proc/sys/fs", URL: "https://docs.kernel.org/admin-guide/sysctl/fs.html"}
28 refKernelVM = check.Ref{Title: "Linux kernel documentation, /proc/sys/vm", URL: "https://docs.kernel.org/admin-guide/sysctl/vm.html"}
29 refKernelUNS = check.Ref{Title: "user_namespaces(7), Linux man-pages", URL: "https://man7.org/linux/man-pages/man7/user_namespaces.7.html"}
30 refSysRq = check.Ref{Title: "Linux kernel documentation, Linux Magic System Request Key Hacks", URL: "https://docs.kernel.org/admin-guide/sysrq.html"}
31)
32
33var sysctlDepthRules = []sysctlRule{
34 {"HOST-KRN-019", "Magic SysRq Key Enabled", "kernel.sysrq", "0",
35 "Checks that the magic SysRq key combination is disabled or limited to keyboard synchronisation.",
36 "With SysRq enabled, anyone at a console or with access to /proc/sysrq-trigger can reboot the machine, kill every process or dump memory without logging in.",
37 check.Low, "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L", refSysRq, "CWE-749", []string{"4"}},
38 {"HOST-KRN-020", "FIFO Protection in Shared Directories Disabled", "fs.protected_fifos", "1",
39 "Checks that the kernel refuses to open FIFOs in world-writable sticky directories that another user owns.",
40 "A FIFO planted in /tmp can make a privileged program block or write into an attacker-controlled pipe, a classic step in local attacks.",
41 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKernelFS, "CWE-59", []string{"2"}},
42 {"HOST-KRN-021", "Regular File Protection in Shared Directories Disabled", "fs.protected_regular", "1",
43 "Checks that the kernel refuses to open, with O_CREAT, regular files in world-writable sticky directories that another user owns.",
44 "Without it, a user can pre-create a file that a root-run script later writes to in /tmp, redirecting privileged output into a file they control.",
45 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKernelFS, "CWE-59", []string{"2"}},
46 {"HOST-KRN-023", "IPv6 Source-Routed Packets Accepted", "net.ipv6.conf.all.accept_source_route", "0",
47 "Checks that the kernel drops IPv6 packets that dictate their own route.",
48 "IPv6 routing headers can be used to bypass filters and spoof trusted addresses, as source routing could in IPv4.",
49 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", nil},
50 {"HOST-KRN-024", "Secure ICMP Redirects Accepted", "net.ipv4.conf.all.secure_redirects", "0",
51 "Checks that the kernel also ignores ICMP redirects that come from its configured gateways.",
52 "An attacker who spoofs the gateway address can still reroute traffic unless secure redirects are off as well; a server has no reason to accept redirects at all.",
53 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", nil},
54 {"HOST-KRN-025", "IPv6 Router Advertisements Accepted", "net.ipv6.conf.all.accept_ra", "0",
55 "Checks that the server does not reconfigure its IPv6 addresses and routes from router advertisements.",
56 "A rogue router advertisement on the local network can add a default route through an attacker's machine or assign addresses that bypass firewall rules; servers use static addressing.",
57 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", nil},
58 {"HOST-KRN-026", "Bogus ICMP Error Responses Logged", "net.ipv4.icmp_ignore_bogus_error_responses", "1",
59 "Checks that the kernel silently ignores malformed ICMP error responses instead of logging each one.",
60 "Logging every bogus ICMP error lets a remote sender fill the kernel log and disk at will, hiding real events among noise.",
61 check.Low, "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L", refKernelNet, "CWE-779", nil},
62 {"HOST-KRN-027", "Kexec Kernel Replacement Allowed", "kernel.kexec_load_disabled", "1",
63 "Checks that a running kernel cannot be replaced at run time with kexec.",
64 "kexec lets root boot a different kernel without going through the firmware, which bypasses Secure Boot and module signing and is used to load unsigned kernels after a compromise.",
65 check.Low, "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:L", refKSPP, "CWE-749", nil},
66 {"HOST-KRN-028", "Unprivileged userfaultfd Allowed", "vm.unprivileged_userfaultfd", "0",
67 "Checks that only privileged processes can use userfaultfd.",
68 "userfaultfd lets a process pause the kernel at a chosen moment, which has turned many kernel race conditions into reliable local exploits.",
69 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKernelVM, "CWE-749", nil},
70 {"HOST-KRN-029", "TTY Line Discipline Autoloading Enabled", "dev.tty.ldisc_autoload", "0",
71 "Checks that unprivileged users cannot make the kernel load terminal line discipline modules.",
72 "Automatically loaded line disciplines have carried several exploitable kernel bugs; disabling autoload shrinks the kernel attack surface reachable from a normal shell.",
73 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKSPP, "CWE-1188", nil},
74 {"HOST-KRN-030", "BPF JIT Hardening Off", "net.core.bpf_jit_harden", "2",
75 "Checks that the BPF just-in-time compiler blinds constants in generated code for every user.",
76 "Without hardening, attacker-chosen constants in BPF programs land in executable kernel memory, a known technique for building kernel exploit payloads.",
77 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKSPP, "CWE-1188", []string{"1"}},
78 {"HOST-KRN-031", "io_uring Open to Unprivileged Users", "kernel.io_uring_disabled", "2",
79 "Checks that io_uring is disabled, or limited to privileged processes, on kernels that offer the setting.",
80 "io_uring has produced a long run of local privilege-escalation bugs; several large operators disable it for untrusted workloads.",
81 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKSPP, "CWE-749", []string{"1"}},
82}
83
84func init() {
85 for _, r := range sysctlDepthRules {
86 registerSysctl(r)
87 }
88
89 check.Register(&check.Check{
90 ID: "HOST-KRN-022", Title: "Unprivileged User Namespaces Allowed", Domain: check.DomainHost,
91 Severity: check.Low, Category: check.CatConfig, Effort: check.EffortLow,
92 CVSS: "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
93 Summary: "Checks that normal users cannot create user namespaces, unless the server runs containers that need them.",
94 Rationale: "User namespaces give an unprivileged process a fake root inside a namespace, which exposes kernel code paths that were written for root only; many local kernel exploits start by unsharing a user namespace.",
95 Detection: "Reads /proc/sys/kernel/unprivileged_userns_clone (Debian and Ubuntu kernels) and, when that does not exist, /proc/sys/user/max_user_namespaces. The first is hardened at 0; the second at 0. Kernels with neither parameter are skipped. Passes with a note when /proc/sys/kernel/apparmor_restrict_unprivileged_userns is 1 (Ubuntu 23.10 and later), because AppArmor then denies the namespace capabilities to unconfined programs. Container hosts that need rootless namespaces should suppress this check deliberately.",
96 Fix: "Create /etc/sysctl.d/60-openvapt-hardening.conf containing kernel.unprivileged_userns_clone = 0 (Debian family) or user.max_user_namespaces = 0 (other kernels), then apply with: sysctl --system. Rootless Docker, Podman, Flatpak and some browsers need user namespaces, so test first.",
97 Verify: "sysctl kernel.unprivileged_userns_clone user.max_user_namespaces",
98 Refs: []check.Ref{refKernelUNS, refKSPP},
99 Standards: check.Standards{CWE: []string{"CWE-749"}, NIST: []string{"CM-6", "CM-7"}, CISTopic: "Kernel parameters: user namespaces (CIS Controls v8 4.1)"},
100 Run: func(_ context.Context, s sys.System) []check.Result {
101 // Ubuntu 23.10 and later: AppArmor confines unprivileged user
102 // namespaces by default, which removes the exposed code paths.
103 if v, err := collect.Sysctl(s, "kernel.apparmor_restrict_unprivileged_userns"); err == nil && strings.TrimSpace(v) == "1" {
104 return []check.Result{check.PassR("kernel.apparmor_restrict_unprivileged_userns", "Unprivileged user namespaces are restricted by AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1, the Ubuntu 23.10 and later default): only profiled programs get full namespace rights", "kernel.apparmor_restrict_unprivileged_userns = 1")}
105 }
106 for _, k := range []string{"kernel.unprivileged_userns_clone", "user.max_user_namespaces"} {
107 v, err := collect.Sysctl(s, k)
108 if err != nil {
109 if errors.Is(err, fs.ErrNotExist) {
110 continue
111 }
112 return []check.Result{check.IncompleteR(k, "Parameter could not be read")}
113 }
114 v = strings.TrimSpace(v)
115 if v == "0" {
116 return []check.Result{check.PassR(k, k+" = 0")}
117 }
118 return []check.Result{check.FailR(k, k+" = "+v+" (hardened value: 0)")}
119 }
120 return []check.Result{check.SkipR("kernel.unprivileged_userns_clone", "Parameter not available on this kernel")}
121 },
122 })
123}

Also available as plain Markdown for notes, scripts and AI tools. Same facts, same source, no styling.

Cite This Page

If this page helped your work, please cite it. The version and web address are filled in; the access date is today's date in your browser. Every other style, and when credit is required, is on How to Cite OpenVAPT.

APA 7
OpenVAPT. (2026). HOST-KRN-022 Unprivileged User Namespaces Allowed (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/HOST-KRN-022.html

In text: (OpenVAPT, 2026). Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

BibTeX
@misc{openvapt_host_krn_022,
  author       = {{OpenVAPT}},
  title        = {{HOST-KRN-022 Unprivileged User Namespaces Allowed}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/HOST-KRN-022.html}},
  note         = {OpenVAPT Check Catalogue, version 0.1.0-alpha.6. Accessed 16 October 2026}
}

Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

Citing OpenVAPT does not mean OpenVAPT endorses, approves or has reviewed your work.