🔥 This guide details the planning and the tools involved in creating a secure Linux production systems - work in progress.
Branch: master
Clone or download
trimstray minor fixes
- signed-off-by: trimstray <trimstray@gmail.com>
Latest commit faf2ed1 Feb 17, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
doc/img updated CONTRIBUTING.md; fixed typos Jan 22, 2019
lib init commit Oct 6, 2018
CODE_OF_CONDUCT.md init commit Oct 6, 2018
CONTRIBUTING.md updated CONTRIBUTING.md; fixed typos Jan 22, 2019
LICENSE.md updated CONTRIBUTING.md; fixed typos Jan 22, 2019
README.md minor fixes Feb 17, 2019

README.md

The Practical Linux Hardening Guide


Master


"Did you know all your doors were locked?" - Riddick (The Chronicles of Riddick)


Branch License

Created by trimstray and contributors

All suggestions and pull requests are welcome!


💥 Work in progress, just a moment... First, I update a Table Of Content and chapters.

If you want to support another repository containing hardening recipes, please see: linux-hardening-checklist - it's a simple checklist with the most important hardening rules.


Table Of Contents

Pre install tasks

Hard disk encryption

ℹ️ Introduction

Disk encryption is focused on securing physical access, while relying on other parts of the system to provide things like network security and user-based access control.

Most of the Linux distributions will allow you to encrypt your disks before installation.

If you use an alternative installation method (e.g. from debootstrap) you can create an encrypted disk manually.

Before this you should to answer the following questions:

  • What part of filesystem do you want to encrypt?
    • only user data
    • user data and system data
  • How should swap, /tmp and other be taken care of?
    • disable or mount as ramdisk
    • encrypt (separately of as part of full)
  • How should encrypted parts of the disk be unlocked?
    • passphrase
    • key file
  • When should encrypted parts of the disk be unlocked?
    • before boot process
    • during boot process
    • mixed above or manually

Source

✴️ Encrypt root filesystem

Unlocked during boot, using passphrases or USB stick with keyfiles.

✴️ Encrypt /boot partition

  • encrypting the whole disk without /boot partition but keeping it on a flash drive you carry at all times
  • using a checksum value of the boot sector
  • boot partition to detect it and change you passphrase

This may not completely get rid of the attack vector described in this post as there is still part of the bootloader that isn't encrypted, but at least the grub stage2 and the kernel/ramdisk are encrypted and should make it much harder to attack.

In addition, the /boot partition may be a weak point if you use encryption methods for the rest of the disk.

Historically it has been necessary to leave /boot unencrypted because bootloaders didn't support decrypting block devices. However, there are some dangers to leaving the bootloader and ramdisks unencrypted.

Before this you should to answer the following questions:

  • Where your /boot partition is stored?
    • the same place where stored /
    • separately partition
    • external flash drive

The following recipe should be made after installing the system (however, these steps are included in this section to avoid mixing issues).

Source

Create copy of your /boot
mkdir /mnt/boot
mount --bind / /mnt/boot
rsync -aAXv /boot/ /mnt/boot/
umount /mnt/boot
Removed old /boot partition
umount /boot
sed -i -e '/\/boot/d' /etc/fstab
Regenerate grub configuration
# Debian like distributions
grub-mkconfig > /boot/grub/grub.cfg

# RedHat like distributions
grub2-mkconfig > /boot/grub2/grub.cfg
Enable GRUB_ENABLE_CRYPTODISK param
echo GRUB_ENABLE_CRYPTODISK=y >> /etc/default/grub
Reinstall grub
# Debian like distributions
grub-install /dev/sda

# RedHat like distributions
grub2-install /dev/sda

More details can be found here (Bootloader configuration (grub) section):

✴️ Swap partition

  • swap area is not required to survive a reboot, therefore a new random encryption key can be chosen each time the swap area is activated
  • get the key from /dev/urandom because /dev/random maybe stalling your boot sequence

☑️ Summary checklist

Item True False
Encrypting the whole disk 🔲 🔲
Usage passphrase or key file to disk unlocked 🔲 🔲
Choosing a strong passphrase 🔲 🔲
Encrypting the /boot partition 🔲 🔲
Securing swap partition with /dev/urandom 🔲 🔲
swap or tmp using an automatically generated per-session throwaway key 🔲 🔲

Post install tasks

Bootloader configuration (grub)

ℹ️ Introduction

Protection for the boot loader can prevent unauthorized users who have physical access to systems, e.g. attaining root privileges through single user mode.

Basically when you want to prohibit unauthorized reconfiguring of your system, otherwise anybody could load anything on it.

✴️ Protect bootloader with password

You can set password for the bootloader for prevents users from entering single user mode, changing settings at boot time, access to the bootloader console, reset the root password, if there is no password for GRUB-menu or access to non-secure operating systems.

Generate password hash
# Debian like distributions
grub-mkpasswd-pbkdf2

# RedHat like distributions
grub2-mkpasswd-pbkdf2
Updated grub configuration
cat > /etc/grub.d/01_hash << __EOF__
set superusers="user"
password_pbkdf2 user
grub.pbkdf2.sha512.<hash> # rest of your password hash
__EOF__

And regenerate grub configuration:

# Debian like distributions
grub-mkconfig > /boot/grub/grub.cfg

# RedHat like distributions
grub2-mkconfig > /boot/grub2/grub.cfg

✴️ Protect bootloader config files

Set the owner and group of /etc/grub.conf to the root user:

chown root:root /etc/grub.conf

or

chown -R root:root /etc/grub.d

Set permission on the /etc/grub.conf or /etc/grub.d file to read and write for root only:

chmod og-rwx /etc/grub.conf

or

chmod -R og-rwx /etc/grub.d

☑️ Summary checklist

Item True False
Set password for the bootloader 🔲 🔲

Disk partitions

ℹ️ Introduction

Critical file systems should be separated into different partitions in ways that make your system a better and more secure.

✴️ Separate disk partitions

Make sure the following filesystems are mounted on separate partitions:

  • /boot
  • /tmp
  • /var
  • /var/log

Additionally, depending on the purpose of the server, you should consider separating the following partitions:

  • /usr
  • /home
  • /var/www

You should also consider separating these partitions:

  • /var/tmp
  • /var/log/audit

✴️ Mount options: nodev, nosuid and noexec

For more security-focused situations is as follows:

  • nodev - specifies that the filesystem cannot contain special devices: This is a security precaution. You don't want a user world-accessible filesystem like this to have the potential for the creation of character devices or access to random device hardware
  • nosuid - specifies that the filesystem cannot contain set userid files. Preventing setuid binaries on a world-writable filesystem makes sense because there's a risk of root escalation or other awfulness there
  • noexec - this param might be useful for a partition that contains no binaries, like /var, or contains binaries you do not want to execute on your system (from partitions with noexec), or that cannot even be executed on your system

✴️ Secure /boot directory

The boot directory contains important files related to the Linux kernel, so you need to make sure that this directory is locked down to read-only permissions.

Add ro option and nodev, nosuid and noexec to /etc/fstab for /boot entry:

LABEL=/boot  /boot  ext2  defaults,ro,nodev,nosuid,noexec  1 2

When updating the kernel you will have to move the flag to rw:

mount -o remount,defaults,rw /boot

✴️ Secure /tmp and /var/tmp

On Linux systems, the /tmp and /var/tmp locations are world-writable.

Several daemons/applications use the /tmp or /var/tmp directories to temporarily store data, log information, or to share information between their sub-components. However, due to the shared nature of these directories, several attacks are possible, including:

  • Leaks of confidential data via secrets in file names
  • Race-condition attacks (TOCTOU) on the integrity of processes and data
  • Denial-of-Service (DoS) attacks based on race conditions and pre-allocating file/directory names

As a rule of thumb, malicious applications usually write to /tmp and then attempt to run whatever was written. A way to prevent this is to mount /tmp on a separate partition with the options nodev, nosuid and noexec enabled.

This will deny binary execution from /tmp, disable any binary to be suid root, and disable any block devices from being created.

The first possible scenario is create symlink

mv /var/tmp /var/tmp.old
ln -s /tmp /var/tmp
cp -prf /var/tmp.old/* /tmp && rm -fr /var/tmp.old

and set properly mount params:

UUID=<...>  /tmp  ext4  defaults,nodev,nosuid,noexec  1 2

The second scenario is a bind mount

The storage location /var/tmp should be bind mounted to /tmp, as having multiple locations for temporary storage is not required:

/tmp  /var/tmp  none  rw,nodev,nosuid,noexec,bind  0 0

The third scenario is setting up polyinstantiated directories

Create new directories:

mkdir --mode 000 /tmp-inst
mkdir --mode 000 /var/tmp/tmp-inst

Edit /etc/security/namespace.conf:

 /tmp      /tmp-inst/          level  root,adm
 /var/tmp  /var/tmp/tmp-inst/  level  root,adm

Set correct SELinux context:

setsebool polyinstantiation_enabled=1
chcon --reference=/tmp /tmp-inst
chcon --reference=/var/tmp/ /var/tmp/tmp-inst

And set nodev, nosuid and noexec mount options in /etc/fstab.

Alternative for polyinstantiated directories is PrivateTmp feature available from systemd. For more information please see: New Red Hat Enterprise Linux 7 Security Feature: PrivateTmp.

✴️ Secure /dev/shm

/dev/shm is a temporary file storage filesystem, i.e. tmpfs, that uses RAM for the backing store. One of the major security issue with the /dev/shm is anyone can upload and execute files inside the /dev/shm similar to the /tmp partition. Further the size should be limited to avoid an attacker filling up this mountpoint to the point where applications could be affected. (normally it allows 20% or more of RAM to be used). The sticky bit should be set like for any world writeable directory.

For applies to shared memory /dev/shm mount params:

tmpfs  /dev/shm  tmpfs  rw,nodev,nosuid,noexec,size=1024M,mode=1777 0 0

You can also create a group named 'shm' and put application users for SHM-using applications in there. Then the access can be completely be restricted as such:

tmpfs  /dev/shm  tmpfs  rw,nodev,nosuid,noexec,size=1024M,mode=1770,uid=root,gid=shm 0 0

✴️ Secure /proc filesystem

The proc pseudo-filesystem /proc should be mounted with hidepid. When setting hidepid to 2, directories entries in /proc will hidden.

proc  /proc  proc  defaults,hidepid=2  0 0

Some of the services/programs operate incorrectly when the hidepid parameter is set, e.g. Nagios checks.

✴️ Swap partition

✴️ Disk quotas

☑️ Summary checklist

Item True False
Separate base partition scheme: /boot, /tmp, /var, /var/log 🔲 🔲
Separate /usr partition 🔲 🔲
Separate /home partition 🔲 🔲
Separate /var/www partition 🔲 🔲
Separate /var/tmp partition 🔲 🔲
Separate /var/audit partition 🔲 🔲
Secure /boot directory with ro, nodev, nosuid, noexec options 🔲 🔲
Secure /tmp and /var/tmp directory with nodev, nosuid, noexec options 🔲 🔲
Create symlink for /var/tmp in /tmp 🔲 🔲
Setting up bind-mount /var/tmp to /tmp 🔲 🔲
Setting up polyinstantiated directories for /tmp and /var/tmp 🔲 🔲
Secure /dev/shm directory with nodev, nosuid, noexec options 🔲 🔲
Secure /proc filesystem with hidepid=2 option 🔲 🔲

Services

Disable all unnecessary services

The action in this section provide guidance on some of unwanted applications and services which you might not needed but they are installed by default during OS installation and unknowingly start eating your system resources and also threats to the system security. If unused services is not enabled then it cannot be exploited.

✴️ Common Unix Print System

The Common Unix Print System (CUPS) provides the ability to print to both local and network printers. If the system does not need to accept print jobs from other systems, it's recommended that CUPS be disabled to reduce the potential attack.

Run the following command to verify cups is not enabled:

# systemctl is-enabled cups
disabled

Run the following command to disable cups:

# systemctl disable cups

Source

Web services

Nginx

Nginx is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev. It's used worldwide, and is one of best tools at what it does. Default configuration that comes with it, however, is not very security oriented, and it requires some work to set it up properly. That's what this section aims to help you with.

Source

✴️ Files and directories permissions

Usually setting directories permissions to 0755 and file permissions to 0644 is a good practise. 0755 permissions for directories allows nginx user to access files in the folder, however you don't want to grant same type of permissions to a file, as granting execution permissions to a file is not a good idea, especially on a publicly exposed server.

Script for setting all directories permissions to 0755 (here we assume that webserver directory path is /var/www/html):

find /var/www/html -type d -exec chmod 755 {} \;

Script for setting all files permissions to 0644:

find /var/www/html -type f -exec chmod 644 {} \;

Whatever you do, never grant 0777 permissions to files, nor folders.

✴️ Use HTTPS

In this day and age, with services like Let's Encrypt, there's no excuse not to use HTTPS for your website.

This example configuration also includes stronger cihper suite, ssl session adjustments, HSTS header, stronger DHE parameter, and OSCP Stapling.

Example of a config with HTTP to HTTPS redirection:

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name example.com;

        return 301 https://$host$request_uri;
        server_tokens off;
}

server {
        listen 443 ssl default_server;
        listen [::]:443 ssl;

        server_name example.com;
        server_tokens off;

        ssl     on;
        ssl_certificate /etc/nginx/ssl/ssl-bundle.crt;
        ssl_certificate_key /etc/nginx/ssl/cert.key;
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_session_tickets off;
        ssl_protocols TLSv1.2;
        ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
        ssl_prefer_server_ciphers on;
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_dhparam /etc/nginx/ssl/dhparam-4096.pem;
}

Source

✴️ Enable HTTP/2

HTTP/2 is a replacement for how HTTP is expressed “on the wire.” It is not a ground-up rewrite of the protocol; HTTP methods, status codes and semantics are the same, and it should be possible to use the same APIs as HTTP/1.x (possibly with some small additions) to represent the protocol.

Source

Differences between HTTP/2 and HTTP/1.1:

At a high level, HTTP/2:

  • is binary, instead of textual
  • is fully multiplexed, instead of ordered and blocking
  • can therefore use one connection for parallelism
  • uses header compression to reduce overhead
  • allows servers to “push” responses proactively into client caches

Source

Example config that enables HTTP/2:

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name example.com;

        return 301 https://$host$request_uri;
        server_tokens off;
}

server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2;

        server_name example.com;
        server_tokens off;

        ssl     on;
        ssl_certificate /etc/nginx/ssl/ssl-bundle.crt;
        ssl_certificate_key /etc/nginx/ssl/cert.key;
}

✴️ Separate domains

In case you have more than one website you'd like to serve from your server, nginx allows you to that.

In this example we'll have 2 different websites, with 2 different domains, served from same virtual machine.

Example config that allows you to serve two websites with two different domains:

server {
        listen 80;
        listen [::]:80;
        server_name first-example.com;

        root /var/www/html/website1;
        index index.html;
        server_tokens off;

        location / {
          try_files $uri $uri/ =404;
        }

}

server {
        listen 80;
        listen [::]:80;
        server_name second-example.com;

        root /var/www/html/website2;
        index index.html;
        server_tokens off;

        location / {
          try_files $uri $uri/ =404;
        }
}

✴️ Redirect all unencrypted traffic to HTTPS

This config entry is responsible for permanently redirecting all HTTP traffic to HTTPS. It will redirect all visitors that try to access website through HTTP on port 80, to HTTPS on port 443:

return 301 https://$host$request_uri;

Example config:

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name example.com;

        return 301 https://$host$request_uri;
        server_tokens off;
}

server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2;

        server_name example.com;
        server_tokens off;

        ssl     on;
        ssl_certificate /etc/nginx/ssl/ssl-bundle.crt;
        ssl_certificate_key /etc/nginx/ssl/cert.key;
}

✴️ Enable HTTP Strict Transport Security

What is HSTS?

HTTPS (HTTP encrypted with SSL or TLS) is an essential part of the measures to secure traffic to a website, making it very difficult for an attacker to intercept, modify, or fake traffic between a user and the website.

When a user enters a web domain manually (providing the domain name without the http:// or https:// prefix) or follows a plain http:// link, the first request to the website is sent unencrypted, using plain HTTP. Most secured websites immediately send back a redirect to upgrade the user to an HTTPS connection, but a well‑placed attacker can mount a man‑in‑the‑middle (MITM) attack to intercept the initial HTTP request and can control the user’s session from then on.

Source

Config entry :

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Example config

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name example.com;

        return 301 https://$host$request_uri;
        server_tokens off;
}

server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2;

        server_name example.com;
        server_tokens off;

        ssl     on;
        ssl_certificate /etc/nginx/ssl/ssl-bundle.crt;
        ssl_certificate_key /etc/nginx/ssl/cert.key;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

✴️ Diffie Hellman Ephemeral Parameter

All versions of nginx as of 1.4.4 rely on OpenSSL for input parameters to Diffie-Hellman (DH). Unfortunately, this means that Ephemeral Diffie-Hellman (DHE) will use OpenSSL's defaults, which include a 1024-bit key for the key-exchange. This example aims to generate stronger DHE parameter:

cd /etc/nginx/ssl/
openssl dhparam -out dhparam-4096.pem 4096

Then add it to your nginx config with this config entry:

ssl_dhparam /etc/nginx/ssl/dhparam-4096.pem;

Source

✴️ Security related headers

Cross-site scripting (XSS) protection:

Helps with preventing XSS attacks, it's enabling cross-site scripting filter built into modern browsers.

add_header x-xss-protection "1; mode=block" always;

X-Frame-Options:

Prevents iframe loading from different websites:

add_header x-frame-options "SAMEORIGIN" always;

X-Content-Type-Options:

It helps reducing drive-by downloads:

add_header X-Content-Type-Options "nosniff" always;

HTTP Strict Transport Security (HSTS):

When a browser sees this header from an HTTPS website, it “learns” that this domain must only be accessed using HTTPS (SSL or TLS). It caches this information for the max-age period (typically 31,536,000 seconds, equal to about 1 year).

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Source 1 Source 2