Skip to content

6. Server Hardening

Setting the correct timezone on your server is important for accurate logging, scheduled tasks, and ensuring that timestamps in your applications are consistent with your local time.

First, check your current timezone configuration:

# Display the current timezone
sudo timedatectl

To find and set the appropriate timezone:

# List all available timezones
sudo timedatectl list-timezones
# Search for a timezone related to a specific city
sudo timedatectl list-timezones | grep city
# Example: Search for Paris timezone
sudo timedatectl list-timezones | grep Paris
# Set the timezone to Europe/Paris
sudo timedatectl set-timezone Europe/Paris
# Verify the timezone change
sudo timedatectl

Example output after setting the timezone:

Local time: Sun 2025-03-02 14:29:40 CET
Universal time: Sun 2025-03-02 13:29:40 UTC
RTC time: Sun 2025-03-02 13:29:40
Time zone: Europe/Paris (CET, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no

Swap space is a portion of the hard drive that the system can use as virtual memory when physical RAM is fully utilized. Properly configured swap space can prevent system crashes during high memory usage.

The following table provides recommendations for swap size based on your server’s RAM:

RAMNo HibernationWith HibernationMaximum
256MB256MB512MB512MB
512MB512MB1024MB1024MB
1024MB1024MB2048MB2048MB
2GB1GB3GB4GB
4GB2GB6GB8GB
8GB3GB11GB16GB
16GB4GB20GB32GB
32GB6GB38GB64GB

Source: Ubuntu SwapFaq

Before creating a new swap file, check if swap is already enabled:

# Verify current swap status
sudo swapon -s
# If no output appears, swap is not enabled
# You can also check with htop, which will show:
# Swp[ 0K/0K] when swap is not enabled

If you need to change an existing swap file:

# Deactivate the swap file
sudo swapoff /swapfile
# Backup fstab before editing
sudo cp /etc/fstab /etc/fstab.bak
# Edit fstab to remove the swap entry
sudo nano /etc/fstab
# Delete the swap file
sudo rm /swapfile

Choose the appropriate size for your server based on the table above:

# Create a 2 GiB swap file
sudo dd if=/dev/zero of=/swapfile bs=1024 count=2097152
# For 4 GiB swap file
sudo dd if=/dev/zero of=/swapfile bs=1024 count=4194304
# For 6 GiB swap file
sudo dd if=/dev/zero of=/swapfile bs=1024 count=6291456
# For 8 GiB swap file
sudo dd if=/dev/zero of=/swapfile bs=1024 count=8388608

Example output for a 4 GiB swap file:

4194304+0 records in
4194304+0 records out
4294967296 bytes (4.3 GB, 4.0 GiB) copied, 8.81315 s, 487 MB/s
  • sudo: Runs the command with administrative privileges
  • dd: A low-level command to copy and convert data
  • if=/dev/zero: Uses /dev/zero as input, which provides unlimited zero bytes
  • of=/swapfile: Sets the output file to /swapfile
  • bs=1024: Sets the block size to 1024 bytes (1 KB)
  • count=4194304: Specifies the number of blocks to write (4194304 blocks × 1024 bytes = 4 GiB)

After creating the swap file, configure it for use:

# Navigate to root directory
cd /
# Set the correct permissions for the swap file (prevents other users from reading it)
sudo chmod 600 /swapfile
# Set up the file as a Linux swap area
sudo mkswap /swapfile
# Activate the new swap file
sudo swapon /swapfile
# Verify swap status
sudo swapon -s
# Make swap permanent after reboot by editing fstab
sudo vim /etc/fstab
# Add this line to the fstab file:
/swapfile swap swap defaults 0 0

To improve system performance, you can adjust how aggressively the system uses swap:

# Check current swappiness and cache pressure settings
sudo sysctl -a | grep -e vm.swappiness -e vm.vfs_cache_pressure
# Navigate to sysctl.d directory
cd /etc/sysctl.d/
# Create or edit custom_overrides.conf
sudo vim custom_overrides.conf

Add the following configuration:

# SWAP Customization
vm.swappiness = 1
vm.vfs_cache_pressure = 50

These settings:

  • vm.swappiness = 1: Minimizes swap usage by prioritizing RAM, using swap only as a last resort
  • vm.vfs_cache_pressure = 50: Balances memory use by keeping cached file metadata longer, improving file access speed

Apply the changes:

# Reboot to apply changes
sudo reboot
# After reboot, verify the settings
sudo sysctl -a | grep -e vm.swappiness -e vm.vfs_cache_pressure

Expected output:

vm.swappiness = 1
vm.vfs_cache_pressure = 50

Shared memory (/dev/shm) is a temporary filesystem used for inter-process communication. By default, it may have security vulnerabilities that could be exploited by attackers.

6.3.1. Checking Current Shared Memory Settings

Section titled “6.3.1. Checking Current Shared Memory Settings”

First, check the current configuration of your shared memory:

# Confirm current settings
mount | grep shm
# Example output:
# tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,inode64)

6.3.2. Hardening Shared Memory Configuration

Section titled “6.3.2. Hardening Shared Memory Configuration”

To secure shared memory, you need to modify the /etc/fstab file:

# Back up the fstab file before making changes
cd /etc
sudo cp fstab fstab.bak
# Edit the fstab file
sudo vim /etc/fstab

Add the following line to the end of the file:

# HARDEN SHARED MEMORY
none /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0

This configuration:

  • noexec: Prevents execution of binaries in the shared memory area
  • nosuid: Blocks privilege escalation via setuid/setgid bits
  • nodev: Disallows creation of device files in shared memory

After saving the changes, you can either reboot or remount the shared memory to apply the changes:

# Remount /dev/shm with the new settings
sudo mount -o remount /dev/shm
# Verify the new settings
mount | grep /dev/shm

Expected output:

tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,inode64)

The presence of noexec, nosuid, and nodev in the output confirms that the hardening has been successfully applied.

IPv6 is the next-generation Internet Protocol, but if you’re not using it, disabling IPv6 can reduce your server’s attack surface and simplify your network configuration.

For systems using GRUB bootloader, you can disable IPv6 at the kernel level:

# Open the GRUB configuration file
sudo nano /etc/default/grub

Modify the GRUB_CMDLINE_LINUX line to include the IPv6 disable parameter:

GRUB_CMDLINE_LINUX="ipv6.disable=1"

If the line already contains other parameters, add the IPv6 parameter with a space separator:

GRUB_CMDLINE_LINUX="existing_parameters ipv6.disable=1"

Update GRUB and reboot to apply the changes:

# Update GRUB configuration
sudo update-grub
# Reboot the system
sudo reboot

6.4.2. Method 2: Disabling IPv6 Using Sysctl

Section titled “6.4.2. Method 2: Disabling IPv6 Using Sysctl”

Alternatively, you can disable IPv6 using the sysctl configuration:

# Edit the sysctl configuration file
sudo vim /etc/sysctl.d/99-sysctl.conf

Add the following lines to disable IPv6:

# Disable IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

Apply the changes without rebooting:

# Reload sysctl configuration
sudo sysctl -p

To confirm that IPv6 has been successfully disabled:

# Check for any IPv6 addresses
ip a | grep inet6

If no output is displayed, IPv6 is successfully disabled. If you still see IPv6 addresses, you may need to reboot the system or check your configuration.

6.5. Hardening and Optimizing the Network Layer

Section titled “6.5. Hardening and Optimizing the Network Layer”

Optimizing your network configuration can improve both security and performance. The following settings help protect against common network attacks and optimize network performance.

6.5.1. Network Security and Performance Settings

Section titled “6.5.1. Network Security and Performance Settings”

Navigate to the sysctl configuration directory:

# Go to sysctl configuration directory
cd /etc/sysctl.d/
# Create or edit the custom overrides file
sudo vim custom_overrides.conf

Add the following network hardening directives:

# IP Spoofing Protection
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
# SYN Flood Protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
# Source Packet Routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
# Increase the Number of Usable Ports
net.ipv4.ip_local_port_range = 1024 65535
# Increase File Handles & Restrict Core Dumps
fs.file-max = 2097152
fs.suid_dumpable = 0
# Number of Incoming Connections/Backlog
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 262144
# Increase Maximum Memory Buffers
net.core.optmem_max = 25165824
# Send/Receive Buffer Sizes
net.core.rmem_default = 31457280
net.core.rmem_max = 67108864
net.core.wmem_default = 31457280
net.core.wmem_max = 67108864

6.5.2. Understanding the Network Hardening Settings

Section titled “6.5.2. Understanding the Network Hardening Settings”

These settings enhance your server’s security and performance:

  • IP Spoofing Protection:

    • rp_filter = 1 enables source validation of packets, preventing IP spoofing attacks where attackers forge the source address.
  • SYN Flood Protection:

    • tcp_syncookies = 1 protects against SYN flood attacks by validating connection requests without using connection queue entries.
    • tcp_max_syn_backlog = 2048 increases the SYN backlog queue to handle more connection requests.
    • tcp_synack_retries = 2 and tcp_syn_retries = 5 optimize TCP connection establishment.
  • Source Packet Routing:

    • Disabling accept_source_route prevents attackers from specifying the route that packets take through the network.
  • Usable Ports:

    • ip_local_port_range = 1024 65535 increases the range of available local ports for outgoing connections.
  • File Handles & Core Dumps:

    • fs.file-max = 2097152 increases the maximum number of file handles.
    • fs.suid_dumpable = 0 prevents setuid programs from dumping core, which could expose sensitive information.
  • Connection Handling:

    • somaxconn = 65535 increases the maximum number of pending connections.
    • netdev_max_backlog = 262144 increases the maximum length of the processor input queue.
  • Memory Buffers and Socket Buffers:

    • The optmem_max, rmem_*, and wmem_* settings optimize memory allocation for network operations.

6.5.3. Applying and Verifying the Settings

Section titled “6.5.3. Applying and Verifying the Settings”

Apply the changes by rebooting the system:

# Reboot to apply all changes
sudo reboot

After rebooting, verify that the settings have been applied:

# Check a specific setting (example)
sudo sysctl -a | grep net.core.optmem_max
# Expected output:
# net.core.optmem_max = 25165824

You can check other settings similarly by replacing net.core.optmem_max with the specific parameter you want to verify.

TCP congestion control algorithms manage network traffic to prevent congestion collapse. Google’s BBR (Bottleneck Bandwidth and RTT) algorithm can significantly improve network performance compared to traditional algorithms.

6.6.1. Checking Current Congestion Control Settings

Section titled “6.6.1. Checking Current Congestion Control Settings”

First, check which congestion control algorithms are available and which one is currently active:

# Check available congestion control algorithms
sudo sysctl net.ipv4.tcp_available_congestion_control
# Check the currently active algorithm
sudo sysctl net.ipv4.tcp_congestion_control

To enable the BBR congestion control algorithm:

# Load the BBR module
sudo modprobe tcp_bbr
# Ensure BBR loads at boot time
sudo bash -c 'echo "tcp_bbr" > /etc/modules-load.d/bbr.conf'
# Verify BBR is now available
sudo sysctl net.ipv4.tcp_available_congestion_control

6.6.3. Setting BBR as the Default Algorithm

Section titled “6.6.3. Setting BBR as the Default Algorithm”

Edit the custom overrides configuration file:

# Edit the sysctl configuration file
sudo vim /etc/sysctl.d/custom_overrides.conf

Add the following lines to set BBR as the default congestion control algorithm:

# Activate BBR Algorithm
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

The fq (Fair Queuing) packet scheduler works well with BBR to reduce bufferbloat and improve latency.

After saving the changes, you can apply them immediately and verify:

# Apply the changes
sudo sysctl -p /etc/sysctl.d/custom_overrides.conf
# Confirm BBR is now the active algorithm
sudo sysctl net.ipv4.tcp_congestion_control

Expected output:

net.ipv4.tcp_congestion_control = bbr

By default, Linux updates the access time (atime) of a file every time it is read, which can cause unnecessary disk I/O operations. Disabling this feature can improve performance, especially for busy servers.

First, check your current filesystem mount options:

# View mounted filesystems with human-readable sizes
df -h
# View detailed mount options
cat /proc/mounts

Example output from /proc/mounts:

/dev/vda1 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0

or

/dev/sda1 / ext4 rw,relatime,discard,errors=remount-ro,commit=30 0 0

Note that most systems use relatime by default, which is a compromise that only updates access times if they’re older than the modify time. While better than the default atime, using noatime provides even better performance.

6.7.2. Modifying the File System Table (fstab)

Section titled “6.7.2. Modifying the File System Table (fstab)”

To disable access time updates permanently, edit the /etc/fstab file:

# Edit the filesystem table
sudo vim /etc/fstab

Find the line for your root filesystem (/) and add the noatime option:

Before (example):

UUID=8ce122bd-f39c-45ae-b129-9650cfc67567 / ext4 errors=remount-ro 0 1

After (example):

UUID=8ce122bd-f39c-45ae-b129-9650cfc67567 / ext4 errors=remount-ro,noatime 0 1

If your fstab uses a different format, ensure you add noatime to the mount options:

/dev/sda1 / ext4 rw,noatime,discard,errors=remount-ro,commit=30 0 0

You can either reboot your system or remount the filesystem to apply the changes:

# Remount the root filesystem with the new options
sudo mount -o remount /
# Verify the changes
cat /proc/mounts

Expected output (note the noatime option is now present):

/dev/vda1 / ext4 rw,noatime,errors=remount-ro,data=ordered 0 0

This confirms that the filesystem is now mounted with the noatime option, which will improve disk I/O performance.

Linux systems have limits on the number of files a process can open simultaneously. For busy servers, especially those running databases or web servers, the default limits may be too restrictive.

First, check your current open file limits:

# Check hard limit (maximum allowed)
ulimit -Hn
# Check soft limit (current setting)
ulimit -Sn

Default values are typically 1024 for soft limits and 4096 for hard limits, which can be insufficient for busy servers.

To increase the limits for all users, create a configuration file in the limits.d directory:

# Navigate to the limits.d directory
cd /etc/security/limits.d/
# Create or edit the custom overrides file
sudo vim custom_overrides.conf

Add the following configuration to increase the limits:

# <domain> <type> <item> <value>
* soft nofile 120000
* hard nofile 120000
root soft nofile 120000
root hard nofile 120000

This configuration:

  • Sets both soft and hard limits to 120,000 open files
  • Applies to all users (*) and specifically to the root user
  • The value 120,000 is sufficient for most server workloads, but can be increased if needed

For the limits to take effect, you need to ensure that the PAM (Pluggable Authentication Modules) system is configured to apply them. Edit the PAM session configuration files:

# Add pam_limits.so to common-session
sudo bash -c 'echo "session required pam_limits.so" >> /etc/pam.d/common-session'
# Add pam_limits.so to common-session-noninteractive
sudo bash -c 'echo "session required pam_limits.so" >> /etc/pam.d/common-session-noninteractive'

Alternatively, you can edit these files directly:

# Edit common-session
sudo vim /etc/pam.d/common-session
# Edit common-session-noninteractive
sudo vim /etc/pam.d/common-session-noninteractive

Add the following line to each file if it’s not already present:

session required pam_limits.so

After making these changes, you’ll need to log out and log back in for them to take effect. Then verify the new limits:

# Check the new hard limit
ulimit -Hn
# Check the new soft limit
ulimit -Sn

The output should show the new limits (120000) that you configured.