Wednesday 14 December 2016

Basics: Locking down a Linux system

(See baseline also)

Below is a quick must-do (or baseline if you will) list I have compiled that should be applied to any server running linux:

1. Remove all unnecessary services - the smaller attack surface you have the better! If you can help it try to install 'minimal' or 'core' versions of the OS - both CentOS and Debian provide them!

2. Harden SSH configuration in /etc/ssh/sshd_config:

- Disable root logins
PermitRootLogin no

- Disable plain-text logins (i.e. private key auth only)
PasswordAuthentication no

- Only listen on necessary interfaces

- Ensure only SSH v2 is enabled (enabled by default on most modern versions of OpenSSH)
Protocol 2

- Ensure UsePrivilegeSeparation is set to 'sandbox' mode
UsePrivilegeSeparation sandbox

- Ensure logging is enabled
SyslogFacility AUTHPRIV
LogLevel INFO

- Set login grace time (can help mitigate DDoS attacks)
LoginGraceTime 1m

- Ensure strict mode is enabled (ensures SSH keys etc. are setup correctly for users)
StrictMode yes

- Public key authentication:
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

- Ensure user can't login with empty passwords:
PermitEmptyPasswords no

- Enable / disable challenge response if needed (e.g. Google Authenticator, two-factor auth etc.):
ChallengeResponseAuthentication no

- Disable weak ciphers (source:
Ciphers aes256-ctr,aes192-ctr,aes128-ctr

- Unless absolutley needed - disable X11 forwarding:
X11Forwarding no

and in /etc/ssh/ssh_config:

- Unless absolutely needed - disable X11 forwarding:
ForwardAgent no
ForwardX11 no
ForwardX11Trusted no

3. Kernel tweaks

# Disable IPv6 if not needed
echo 1 > /proc/sys/net/ipv6/conf/<interface-name>/disable_ipv6
Reboot and verify - if all OK make the changes permanent with:
echo 'net.ipv6.conf.all.disable_ipv6 = 1' > /etc/sysctl.conf

# Disable IP forwarding (i.e. if not using anything that requires routing e.g. vpn server etc.)
net.ipv4.ip_forward = 0

# log any malformed packets
net.ipv4.conf.all.log_martians = 1

# disable source routing
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_source_route = 0

# disable icmp re-directs / icmp dos mitigation
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0

# enable syn cookies for synflood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1280

# enable reverse-path forwarding filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# disable tcp timstamping
net.ipv4.tcp_timestamps = 0

Now make the changes permanent with:

sysctl -p

4. Ensure selinux is enabled:


If it is not turned on we modify:


and ensure 'SELINUXTYPE=targeted'

5. Setup IPTables properly

Ensure that a default-accept rule is in place on all of the default chains:

sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

and then flush the chains as well as any non-standard chains:

sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -F
sudo iptables -X

Now we will start by allowing traffic to freely flow out and in from our loopback interface:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT

We will also want to ensure that already established connections can get back to the server - i.e. allow stateful connections.

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

We will likely also want to allow all outbound traffic from connections that are currently established:

sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT

We will also likely wish to allow SSH access from a specific host network:

sudo iptables -A INPUT -p tcp -s --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

and allow the incoming SSH connection outbound back to the SSH initiator:

sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT

We might also wish to accept ICMP echo requests:

sudo iptables -A INPUT -p icmp -j ACCEPT

Allow outbound requests for DNS and HTTP/S:

sudo iptables -A OUTPUT -p udp --dport 53 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 53 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

And also log and then drop any other packets:

sudo iptables -N LOGGING
sudo iptables -A INPUT -j LOGGING
sudo iptables -A FORWARD -j LOGGING
sudo iptables -A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
sudo iptables -A LOGGING -j DROP

Finally we should remove our default-allow rules we inserted prior:

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT DROP

I also wrote an article here: that might be of interest.

6. Ensure ntp / datetime information is configured correctly.

7. Ensure you have brute-force detection / prevention system in place e.g. fail2ban or sshguard.

8. Utilize tcpwrappers

9. Setup syslog forwarding to a centralized server.

10. Harden other services - dependent on intended usage e.g. web server (apache, nginx etc.)


Post a Comment