Adnan Canturk.
Security

Five kernel settings that harden a shared Linux server while you wait for a patch

9 min readTürkçe oku

Last updated:

Five kernel hardening settings for a shared Linux server

On a shared hosting server every customer runs code on the same kernel. A local privilege escalation bug therefore turns any compromised website into a potential root compromise, and in late October 2026 several unpatched kernel bug reports became public before fixes were ready. Patches and live patches are the real answer, but they take days. Configuration is what protects the server in between, and most of it should be in place permanently anyway.

TL;DR

  • Five settings shrink the kernel attack surface on a shared server: user.max_user_namespaces = 0, ptrace_scope = 1, dmesg_restrict = 1, kptr_restrict = 2 and unprivileged_bpf_disabled = 1.
  • Write them to /etc/sysctl.d/99-fleet-hardening.conf so they load after CloudLinux’s defaults and survive a reboot.
  • Check first: record current values and confirm with lsns -t user that no customer uses namespaces.
  • Container hosts are the exception, because rootless containers need user namespaces.
  • On our fleet nine of fourteen servers had namespaces open because the value lived only at runtime; aligning them changed no site.

The five settings

Five settings, one drop-in fileLOADS AFTER 90-CLOUDLINUX.CONF, SURVIVES A REBOOT# /etc/sysctl.d/99-fleet-hardening.confuser.max_user_namespaces= 0DOES MOST OF THE WORKkernel.yama.ptrace_scope= 1kernel.dmesg_restrict= 1kernel.kptr_restrict= 2kernel.unprivileged_bpf_disabled= 1VERIFY: sysctl --system, then unshare -Ur true must fail
The five kernel hardening settings in one sysctl drop-in file
Setting Value What it does
user.max_user_namespaces 0 Stops unprivileged users from creating user namespaces, which removes a large set of kernel interfaces from their reach
kernel.yama.ptrace_scope 1 A process can only be traced by its own parent, so one compromised process cannot inspect another
kernel.dmesg_restrict 1 Only privileged users can read the kernel log, which often contains addresses and driver details
kernel.kptr_restrict 2 Hides kernel pointers in /proc and logs from every user
kernel.unprivileged_bpf_disabled 1 Blocks unprivileged eBPF programs, another frequent entry point for kernel bugs

The first setting carries most of the weight. Many recent kernel privilege escalations need capabilities that an ordinary user only gets inside a user namespace they created. With the limit at zero that step fails, and the bug becomes unreachable for a hosting customer even before it is patched. The other four reduce what an attacker can learn or touch on the way.

Make them survive a reboot

A value set with sysctl -w lives only until the next boot. Put the settings in their own drop-in file, named so that it loads after the vendor’s defaults. On CloudLinux the vendor file is /etc/sysctl.d/90-cloudlinux.conf, so a 99- prefix wins:

# /etc/sysctl.d/99-fleet-hardening.conf
user.max_user_namespaces = 0
kernel.yama.ptrace_scope = 1
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.unprivileged_bpf_disabled = 1

Apply it with sysctl --system and read each value back with sysctl user.max_user_namespaces and so on. To confirm the namespace limit as an ordinary user, unshare -Ur true should now fail with “No space left on device”.

Disable kernel modules you never use

Every loadable module is code an attacker might persuade the kernel to load. A shared web server rarely needs the in-kernel SMB server or old network protocols, so block them in /etc/modprobe.d/:

# /etc/modprobe.d/99-fleet-hardening.conf
install ksmbd /bin/false
install dccp /bin/false
install sctp /bin/false
install rds /bin/false
install tipc /bin/false

Check first with lsmod that none of them is loaded or needed. Do not block modules that something on the server depends on; for example esp4 and esp6 carry IPsec, and blocking them breaks VPN tunnels.

Build the two files for your server:

How do you check a server before changing anything?

Every one of these settings can break something that depends on it, so the first step is read-only. Record the current values with sysctl -a filtered to the five keys, and look for the files that set them today with grep -r over /etc/sysctl.conf and /etc/sysctl.d/. A value that is in force but appears in no file is set only at runtime and will be lost at the next reboot.

Then look for anything that uses user namespaces right now. lsns -t user lists the namespaces that exist; on a shared web server you expect to see only the initial one, owned by root. A namespace owned by a customer account means something on that account depends on it, and that account has to be looked at before the limit goes to zero. Finally, list installed container tools, since a container runtime that is installed but unused is fine, while one that is running is a reason to treat the host differently.

What does each setting cost?

Setting What can stop working Typical impact on a shared web server
user.max_user_namespaces = 0 Rootless containers, Flatpak, some browser sandboxes None, when no tenant uses namespaces
kernel.yama.ptrace_scope = 1 Attaching a debugger to an unrelated process as a normal user None for hosting customers
kernel.dmesg_restrict = 1 Reading the kernel log without privileges None; administrators still read it as root
kernel.kptr_restrict = 2 Kernel addresses in profiling and debugging tools None for web workloads
kernel.unprivileged_bpf_disabled = 1 Unprivileged eBPF tooling None; monitoring agents run privileged

When not to use them

  • Container hosts. Rootless Docker, Podman, LXC and Flatpak need user namespaces. On a server that runs them, keep the limit for tenants by other means or accept the risk on that host only.
  • Debugging tools. ptrace_scope = 1 stops attaching a debugger to an unrelated process; root can still do it.
  • Monitoring agents that use eBPF. They run privileged and keep working, but check them after the change. Note that unprivileged_bpf_disabled = 1 cannot be switched back without a reboot; use 2 if you want to keep that option.

A field note from a hosting fleet

We audited fourteen shared cPanel servers read-only before changing anything. Nine of them had user namespaces open, even though an earlier review had recorded them as hardened: the value had been set at runtime only and disappeared at a reboot. On every server we checked first that no customer was using namespaces, applied the first three settings, user.max_user_namespaces, ptrace_scope and dmesg_restrict, through a 99- drop-in, and compared the control panel and a sample of customer sites before and after. None of them changed. The whole fleet now runs the same values, written to disk; the pointer and eBPF settings are the next step.

Keep the real fix moving

  1. Confirm live patching is automatic. With KernelCare, check AUTO_UPDATE=True in /etc/sysconfig/kcare/kcare.conf; the command-line status can be blank even when it is enabled.
  2. Read “latest patch applied” carefully. It means no newer patch exists for your kernel yet, not that a specific bug is fixed.
  3. Plan reboots for kernels you cannot live patch. Installing a new kernel package does nothing until the server boots into it.
  4. Keep the settings after the patch. They cost almost nothing on a web server and protect against the next bug as well.

How do you make sure the settings stay in place?

The fleet note above is the reason this section exists: a hardening that was recorded as done had quietly undone itself at a reboot. Writing the drop-in fixes the immediate cause, but settings also drift when a package update ships its own file, when someone tunes a value by hand, or when a server is rebuilt from an older template.

Three habits keep them honest. Check the five values after every reboot and every kernel or CloudLinux update, ideally from monitoring rather than by memory, and alert on any difference from the expected value. Keep the drop-in under configuration management or at least in a versioned copy, so a rebuilt server gets the same file. And include the check in any periodic server review, next to firewall and SSH settings, since a value that nobody looks at is a value that nobody will notice changing.

A small script that prints the five keys and compares them with the expected values is enough. Run it from cron and send its output to the same place as your other alerts, so a drifted value is a ticket rather than a surprise.

How do you roll back if something breaks?

Keep the rollback as simple as the change. Before writing the drop-in, save the current values to a file in a working directory. If a service fails afterwards, remove or comment out the line in /etc/sysctl.d/99-fleet-hardening.conf and set the old value back with sysctl -w; most of these settings take effect immediately and need no reboot. The exception is unprivileged_bpf_disabled = 1, which stays in force until the next boot, which is why 2 is the safer first step on a server you are unsure about.

For the runtime side of the same month, see the PHP2Shell patch-day runbook and Node.js, BIND and Java in one week.

The short version

None of these settings is new, and none of them replaces a kernel patch. What they change is the gap between disclosure and fix, which on a shared server is the period that matters most, because every customer’s code shares the same kernel. Put them in place once, write them to disk, watch for drift, and treat a container host as the one deliberate exception. The next time a privilege escalation report becomes public before its fix, the question will be when the patch arrives, not whether the server is already exposed.

FAQ: kernel hardening for a shared Linux server

Does setting user.max_user_namespaces to 0 break cPanel or CloudLinux servers?

Not in our experience. On fourteen shared cPanel servers the control panel and sample customer sites returned the same responses before and after the change. Check first with lsns -t user that no customer account uses namespaces, since that account would be affected.

Why should the sysctl drop-in file start with 99 on a CloudLinux server in 2026?

Because files in /etc/sysctl.d/ load in name order and a later file wins. CloudLinux ships 90-cloudlinux.conf, so a 99- file is applied after it and its values are the ones that remain in force.

How do you verify that user namespaces are really disabled for ordinary users?

Read the value with sysctl user.max_user_namespaces, then run unshare -Ur true as an unprivileged user. With the limit at zero it fails with “No space left on device”; if it succeeds, the setting is not in force.

kernel.yama.ptrace_scope 1 vs 2: which should a shared hosting server use?

Value 1 is the practical default: a process can be traced only by its parent, which stops one compromised process from inspecting another. Value 2 limits tracing to administrators only, which is stricter and can get in the way of legitimate debugging.

kernel.kptr_restrict 1 vs 2: what is the difference on a web server?

With 1, kernel pointers are hidden from users without the right capability; with 2, they are hidden from everyone, root included. A web server rarely needs kernel addresses, so 2 is a reasonable choice.

Can kernel.unprivileged_bpf_disabled be switched back without a reboot?

Value 1 cannot be changed back until the next boot, by design. Value 2 also blocks unprivileged eBPF but can be relaxed at runtime by an administrator, so it is the safer first step if you are not sure what the server runs.

What should a container host do instead of disabling user namespaces in 2026?

Treat it as a separate class of server. Rootless Docker, Podman and LXC need user namespaces, so keep them enabled there, keep that host out of the shared-hosting pool, and rely on prompt kernel patches and live patching to close privilege escalation bugs.

Which kernel modules are safe to block on a shared web server in 2026, and which are not?

Modules for features the server never uses are good candidates, such as the in-kernel SMB server and rarely used network protocols. Never block esp4 or esp6 if any IPsec tunnel exists, and check lsmod before adding any entry.

Does “latest patch applied” in KernelCare mean a specific kernel bug is fixed?

No. It means no newer live patch exists for the running kernel yet. To know whether a specific bug is covered, check KernelCare’s patch list or advisories for that issue, and confirm AUTO_UPDATE=True so the fix applies once it is released.

Should the hardening settings stay in place after the kernel patch is installed?

Yes. They cost almost nothing on a web server and reduce the reach of the next privilege escalation bug as well, before its patch exists. Treat them as baseline configuration, not as a temporary workaround.

Sources