While the Linux kernel is fairly secure by default, there are steps we can take to make it even safer.

Kernel hardening can reduce the risk of certain network attacks and information leaks, making it harder for attackers to plan their attacks.

This guide will walk you through adjusting key kernel parameters to strengthen the security of your Linux server.

I assume you're working on a properly set-up Ubuntu server. If not, check out my guide on preparing Ubuntu servers to get started.

Author's Note

When you search the internet for guides on kernel hardening, you'll come across various opinions.

I will not try to claim to have the correct information. Instead, I'll share how I harden the Linux kernel on all my servers and my experience with it.

Leaving the default kernel parameters unchanged usually isn't a problem and may not cause security issues. Tweaking some parameters just enhances your server's security.

However, this might cause problems with what you run on your server if they rely on specific values. That's why I suggest testing these tweaks in a separate environment before applying them to your main server.

Lastly, I just want to mention that I've never encountered any problems with the tweaks I'll be covering on all my Linux servers.

The /proc Directory

Before we dive into adjusting kernel parameters, let me first explain the /proc directory so you can understand where these parameters come from.

If you look inside the /proc directory, you'll find a bunch of numbered directories, and some regular files and directories.

The numbered directories correspond to process IDs (PIDs) of running processes.

Inside a numbered directory, you'll find files and subdirectories filled with details about the running process. However, not all files within these directories are easily understandable unless you're familiar with OS programming.

Instead, we rely on commands like ps and top which extract process information from the /proc directory and present it in an easy-to-read format.

The remaining files and directories with regular names contain details about the kernel's activity. For example, the sys directory contains the kernel parameters that can be adjusted to harden the kernel.

If you enter the /proc/sys/net/ipv4/ directory, you'll find various parameters related to IPv4 networking. Each file in this directory represents a parameter and holds its value.

Now that we know the basics of the /proc directory, it's time to begin hardening the Linux kernel.

The sysctl Utility

The old method of changing parameter values involves echoing the new value to the parameter's file.

This approach is outdated and doesn't work with sudo, thus requiring the use of the bash -c command to enforce execution, as illustrated by the following example:

sudo bash -c "echo '1' > /proc/sys/net/ipv4/icmp_echo_ignore_all"

However, this isn't the recommended approach. Instead, we should use the sysctl utility, which offers a more modern solution.

The following command will list all parameters along with their values:

sudo sysctl -a

Additionally, you can also check a value for a certain parameter like this:

sudo sysctl net.ipv4.icmp_echo_ignore_all

You can use these two commands to verify if the new values have taken effect after you make changes.

If you want to change a parameter's value temporarily, you could use the -w option to set the new value like this:

sudo sysctl -w net.ipv4.icmp_echo_ignore_all=1

It will last until you reboot the server.

Sometimes this is useful, but for the purpose of this guide, we want changes to be permanent.

Hardening the Linux Kernel

In the following, we'll tweak kernel parameters by adding them to the end of the /etc/sysctl.conf file along with their new values, ensuring that the changes become permanent.

After adding them, simply reboot the server for the new values to take effect.

Reverse Path Filtering

A spoofing attack occurs when an attacker pretends to be someone else or a trustworthy entity.

This can occur at various levels, including the network level, such as IP address spoofing, where the attacker sends network packets with spoofed IP addresses.

Such tactics could be used for DoS attacks or to trick access controls.

These two parameters that help us prevent spoofing attacks:

net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1

They control reverse path filtering (RP filter), a security feature that helps protect against IP address spoofing. When enabled, the server checks if it can reach the source address of incoming packets. If it can’t, the packets are dropped.

By default, these parameters are set to 2, which enables them in loose mode. Setting them to 1 enforces strict mode for reverse path filtering, providing better security.

SYN Flood Protection

One form of DoS attacks involves sending a huge number of SYN packets to a server without completing the three-way handshake, also known as SYN flood attacks.

This results in our server having numerous half-open connections, consuming significant resources and preventing it from accepting new legitimate connections.

In severe cases, it can cause the server to crash, leaving it unresponsive and inaccessible.

TCP SYN cookies help mitigate SYN flood attacks by managing half-open connections without consuming too many server resources.

The following parameter enables the use of TCP SYN cookies:

net.ipv4.tcp_syncookies = 1

While it doesn’t provide much protection, I still recommend keeping it enabled.

There are two additional parameters we can add to optimize how our server handles new connections:

net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 3

The first parameter controls the size of the queue for incoming SYN requests. By default, it’s set to 256 on most systems, which is generally too low. Increasing the backlog allows the server to handle more half-open connections, helping it absorb a higher volume of incoming requests.

For most systems, a range between 4096 and 16384 is recommended unless the system has substantial memory resources.

The second parameter determines how many times the server will resend the SYN-ACK packet if no ACK response is received from the client, as happens in a SYN flood attack.

By default, it’s set to 5, meaning the server waits longer and keeps resources tied up on unresponsive clients. For better SYN flood resistance, reducing this value to 2 or 3 helps free up resources more quickly by limiting the number of retry attempts.

👉
Check out my guide on preventing SYN flood attacks using firewall rules and Fail2ban.

Disable ICMP Redirects

ICMP redirects are messages sent by a router to inform the server that it should use a different, better route for reaching a specific destination.

For example, if the server sends traffic to a router, and the router knows of a more efficient path to the destination, it can send an ICMP redirect message to the server to update its routing table.

However, ICMP packets, including ICMP redirects, are extremely easy to fake, and it would be rather simple for an attacker to forge ICMP redirect packets. This makes it a potential security risk, as attackers could use fake redirects to mislead the server into sending traffic through malicious or incorrect routes.

These eight parameters will disable the sending or accepting of ICMP redirects:

net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

In most secure environments, ICMP redirects should be disabled, especially on systems not acting as routers.

Disable Source Routing

Source routing is a feature where the sender of a packet specifies the route that the packet should take through the network, rather than allowing routers to decide the best path.

This can be useful for specific use cases but can also pose significant security risks, as attackers can exploit it to send packets along a malicious or unexpected path. It can also be used to bypass security measures.

These four parameters disable the acceptance of source routes packets:

net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

Disabling source routing is a common security practice, particularly on servers where network traffic should follow trusted paths.

Disable Packet Forwarding

Packet forwarding enables a system to transmit network packets from one network interface to another.

Unless your server is functioning as a router or VPN, packet forwarding should be disabled.

These parameters disable packet forwarding:

net.ipv4.ip_forward = 0
net.ipv4.conf.all.forwarding = 0
net.ipv6.conf.all.forwarding = 0
net.ipv4.conf.default.forwarding = 0
net.ipv6.conf.default.forwarding = 0

Disabling packet forwarding helps prevent your server from being used as a gateway for unauthorized network traffic.

Protect TCP Connections (TIME-WAIT State)

When a TCP connection is closed, it goes into a TIME-WAIT state to prevent old or duplicate packets from interfering with new connections.

This parameter helps protect against TIME-WAIT Assassination or TCP TIME-WAIT attacks:

net.ipv4.tcp_rfc1337 = 1

When enabled, the server follows a safer behavior for closing TCP connections, ensuring that connections in the TIME-WAIT state cannot be hijacked.

This prevents potential data loss or connection issues caused by attackers exploiting this vulnerability.

However, please note that the kernel documentation has confused some people about this parameter, and there is an ongoing debate about whether one should enable it or not.

Therefore, it's advisable to test this parameter in a testing environment first.

Harden the BPF JIT Compiler

Berkeley Packet Filter (BPF) is a framework in the Linux kernel used for network packet filtering, tracing, and other performance monitoring tasks.

The Just-In-Time (JIT) compiler improves the performance of BPF programs by compiling them into machine code at runtime, instead of interpreting them line-by-line.

This parameter enables additional hardening features for the BPF JIT compiler:

net.core.bpf_jit_harden = 2

When enabled, it mitigates JIT Spraying attacks by obfuscating sensitive constants in BPF programs, such as fixed values or memory addresses.

JIT Spraying is an attack where hackers manipulate the JIT compiler to inject malicious code into memory, allowing them to control machine code execution.

By obfuscating these constants, it makes them harder for attackers to predict or exploit.

Next, add this parameter:

kernel.unprivileged_bpf_disabled = 1 

It prevents unauthorized users from loading and using BPF programs and restricts the BPF JIT compiler to root-only access.

Restrict Core Dumps

A core dump is a file the Linux kernel creates when a program crashes. It shows what was in the program's memory, registers, and call stack at the time of the crash.

While useful for debugging, core dumps can expose sensitive data like passwords or encryption keys.

By default, core dumps are saved in /var/lib/systemd/coredump and compressed with zstd. They’re triggered by errors like segmentation faults or illegal instructions and can be analyzed using tools like gdb.

However, core dumps are a security risk, especially for programs with elevated privileges. If an attacker gets access, they might extract sensitive information.

That’s why it’s important to disable core dumps unless absolutely needed for debugging (and honestly, I’ve never needed them).

Two parameters can help:

kernel.core_pattern = |/bin/false
fs.suid_dumpable = 0

kernel.core_pattern = |/bin/false redirects any attempt to create a core dump to the false command, effectively discarding it. While it prevents core dumps globally, some privileged processes might still bypass this restriction.

Adding fs.suid_dumpable = 0 ensures that no core dumps are created for processes with elevated privileges, adding extra protection.

Disable Magic Keys

Magic Keys are special key combinations that allow you to perform debugging and system control actions, even when the server becomes unresponsive due to issues like a kernel panic.

To disable Magic Keys, add this parameter:

kernel.sysrq = 0

Disabling Magic Keys prevents potential misuse or exploitation of these debugging features.

Restrict Access to Kernel Logs

By default, any user on the server can run the dmesg command to view kernel logs, which may contain sensitive information.

To restrict access to this command, add the following parameter:

kernel.dmesg_restrict = 1

This ensures that only users with root privileges can view kernel logs.

Restrict ptrace Access

The ptrace() system call allows one process (tracer) to observe and control the execution of another (tracee).

This functionality can be exploited by attackers if an application is compromised, allowing them to attach to and manipulate other processes, such as SSH sessions.

To mitigate this risk, the kernel.yama.ptrace_scope parameter can be set to restrict ptrace usage. Here’s what each value means:

  • 0: Classic ptrace permissions – a process can attach to any other process running under the same user.
  • 1: Restricted ptrace – processes can only attach to their descendants, or specific allowed processes declared by prctl.
  • 2: Admin-only attach – only processes with CAP_SYS_PTRACE privileges can use ptrace.
  • 3: No attach – no process can use ptrace to attach to another process.

For full security, setting this parameter to 3 is recommended, as it completely prevents any process from using ptrace to attach to others.

For most Linux servers, this is a good precaution unless ptrace is explicitly needed for debugging or administration.

Use the following parameter to disable ptrace unless required:

kernel.yama.ptrace_scope = 3

This ensures that no process can manipulate or inspect another process.

Restrict User Namespaces

User namespaces are a kernel feature that enhances security by creating separate sandboxes for different users.

While available for unprivileged users, this feature could expose the kernel to privilege escalation.

This parameter restricts this feature to users with root privileges only:

kernel.unprivileged_userns_clone = 0

This prevents unprivileged users from creating user namespaces.

Control Swapping

Similar to core dumps, swapping involves copying parts of the memory to the disk, potentially containing sensitive information.

To reduce this risk, we can instruct the kernel to swap only when absolutely necessary by setting the following parameter:

vm.swappiness = 1

This ensures that the server will only swap data to disk when there are no other options.

File Creation Restrictions

To enhance security, we can restrict the creation of certain types of files in potentially insecure directories.

First, we prevent the creation of FIFOs (First In, First Out, also known as named pipes) and regular files in risky locations by adding the following parameters:

fs.protected_regular = 2
fs.protected_fifos = 2

These help reduce the risk of attackers exploiting these file types in insecure directories.

Next, we protect hard links and symbolic links (symlinks) by adding the following parameters:

fs.protected_hardlinks = 1
fs.protected_symlinks = 1

The first parameter ensures that users without proper access cannot create hard links to files, while the second ensures symlinks are handled safely, helping to prevent TOCTOU (time-of-check, time-of-use) race conditions.

Together, these add an extra layer of protection against certain file system vulnerabilities.

Address Space Layout Randomization (ASLR)

Address Space Layout Randomization (ASLR) is a security technique that randomizes the memory layout of key areas in a process's address space.

This makes it difficult for attackers to predict the location of critical data areas, such as buffers, heap, and stack, which are commonly targeted in memory-based exploits.

By randomly placing memory regions, ASLR makes it much harder for attackers to successfully execute memory corruption exploits. Even if an attacker can write to memory, they won’t be able to predict where to target, as the memory layout changes consistently.

To enable ASLR, add the following parameter:

kernel.randomize_va_space = 2

This ensures that the kernel randomizes the memory layout of all processes.

Conclusion and Final Thoughts

Congratulations on reaching the end!

In this guide, you've learned how to harden the Linux kernel to enhance your server's security.

👉
For more comprehensive Linux server security resources, be sure to check out the full collection of detailed guides here.

If you found value in this guide or have any questions or feedback, please don't hesitate to share your thoughts in the discussion section.

Your input is greatly appreciated, and you can also contact me directly if you prefer.