System Hardening
- 🐧 Linux Command Line
Ansible Playbook
Core
SSH
ATTENTION: Make sure ssh port is open in the firewall before applying these changes.
ATTENTION: Make sure you have another user with sudo privileges before disabling root login.
/etc/ssh/sshd_config
Port <ssh-port>
Protocol 2
MaxAuthTries 3
X11Forwarding no
AllowAgentForwarding no
LogLevel VERBOSE
MaxSessions 2
PermitRootLogin no
Restart the SSH service to apply the changes.
systemctl restart sshd
File Permissions
chmod 700 /usr/bin/gcc
chmod 700 /usr/bin/g++
chmod 700 /usr/bin/make
chmod 700 /usr/bin/as
chmod 700 /usr/bin/ld
chmod 700 /usr/bin/cc
chmod 640 /etc/at.deny
chmod 640 /etc/cron.deny
chmod 600 /etc/crontab
chmod 700 /etc/cron.d
chmod 700 /etc/cron.daily
chmod 700 /etc/cron.hourly
chmod 700 /etc/cron.weekly
chmod 700 /etc/cron.monthly
rm -rf /etc/at.deny
touch /etc/at.allow
chown root:root /etc/at.allow
chmod 600 /etc/at.allow
rm -rf /etc/cron.deny
touch /etc/cron.allow
chown root:root /etc/cron.allow
chmod 600 /etc/cron.allow
Kernel Hardening
/etc/sysctl.conf
vm.swappiness=1
net.ipv4.ip_forward=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.tcp_syncookies=1
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
dev.tty.ldisc_autoload = 0
fs.protected_fifos = 2
fs.protected_regular = 2
fs.suid_dumpable = 0
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.sysrq = 0
kernel.unprivileged_bpf_disabled = 1
kernel.yama.ptrace_scope = 2
net.core.bpf_jit_harden = 2
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.log_martians = 1
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
Apply the changes.
sysctl -p
Disable core dumps
/etc/security/limits.conf
* hard core 0
Set UMASK to restrict permissions
/etc/login.defs
UMASK 027
Maps loopback address to hostname
/etc/hosts
127.0.1.1 <hostname> <FQDN>
# e.g.: 127.0.1.1 webserver1 webserver1.rectitude.dev
Prevent unnecessary kernel modules
/etc/modprobe.d/blacklist.conf
blacklist usb-storage
options usb-storage modeset=0
install usb-storage /bin/true
blacklist firewire-core
options firewire-core modeset=0
install firewire-core /bin/true
blacklist dccp
options dccp modeset=0
install dccp /bin/true
blacklist sctp
options sctp modeset=0
install sctp /bin/true
blacklist rds
options rds modeset=0
install rds /bin/true
blacklist tipc
options tipc modeset=0
install tipc /bin/true
Apply the changes.
dracut -f
Add Linux Audit Rules
/etc/audit/rules.d/audit.rules
-a always,exit -F arch=b64 -F euid=0 -S execve -k root_audit
-a always,exit -F arch=b64 -F path=/etc/passwd -F perm=wa -k passwd_changes
-a always,exit -F arch=b64 -F path=/etc/shadow -F perm=wa -k shadow_changes
-a always,exit -F arch=b64 -F path=/etc/group -F perm=wa -k group_changes
-a always,exit -F arch=b64 -F path=/etc/gshadow -F perm=wa -k gshadow_changes
-a always,exit -F arch=b64 -F path=/var/log/secure -F perm=wa -k secure_logs
-a always,exit -F arch=b64 -F path=/usr/sbin/useradd -F perm=x -k user_management
-a always,exit -F arch=b64 -F path=/usr/sbin/usermod -F perm=x -k user_management
-a always,exit -F arch=b64 -F path=/usr/sbin/userdel -F perm=x -k user_management
-a always,exit -F arch=b64 -F path=/usr/sbin/groupadd -F perm=x -k group_management
-a always,exit -F arch=b64 -F path=/usr/sbin/groupmod -F perm=x -k group_management
-a always,exit -F arch=b64 -F path=/usr/sbin/groupdel -F perm=x -k group_management
# Load the new rules
auditctl -R /etc/audit/rules.d/audit.rules
# List the loaded rules
auditctl -l
# Query the audit logs
ausearch -k root_audit
# Query the audit logs: ausearch -k user_management -i | grep usermod
Stronger hashing for authentication
# view the current profile
authselect current
# create a custom profile based on the sssd profile
authselect create-profile custom-sssd --base-on=sssd
/etc/authselect/custom/custom-sssd/system-auth
password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok rounds=50000
authselect select custom/custom-sssd
# change the current password to apply the new hashing
passwd
Optional
Disable promiscuous mode
Promiscuous mode allows a network device to intercept and read each network packet that arrives in its entirety. Disabling it helps prevent unauthorized network traffic monitoring.
/etc/sysconfig/network-scripts/ifcfg-eth0
PROMISC=no
Disable Unused Service
# Review active services
systemctl list-units | grep service
# Disable the service
systemctl disable <service-name>
Extended
Explanations of SSH Configuration Options
# Use a more secure SSH protocol
Protocol 2
# Disconnect clients after a set number of incorrect password attempts
MaxAuthTries 3
# Disable X11 forwarding to reduce the attack surface if GUI features are not needed
X11Forwarding no
# Disallow SSH agent forwarding to remote hosts, which may restrict some automated operations
AllowAgentForwarding no
# Record more detailed login activities
LogLevel VERBOSE
# Limit the number of simultaneous sessions per SSH connection
MaxSessions 2
# Prohibit root user login
PermitRootLogin no
# Disallow accounts with empty passwords from logging in
PermitEmptyPasswords no
# Change the default SSH port
Port 22222
Other Useful SSH Configuration Options
# Disable TCP forwarding, preventing remote access to databases and internal services
AllowTcpForwarding no
# Disable password authentication
PasswordAuthentication no
Tenable audit policies
system-hardening.yml
- name: System Configuration
hosts: localhost
remote_user: root
vars:
ssh_port: "<ssh-port>"
hostname: "<hostname>"
fqdn: "<FQDN>"
tasks:
- name: Configure SSH
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^{{ item.key }} "
line: "{{ item.key }} {{ item.value }}"
create: true
mode: "0600"
loop:
- {key: "Port", value: "{{ ssh_port }}"}
- {key: "Protocol", value: "2"}
- {key: "MaxAuthTries", value: "3"}
- {key: "X11Forwarding", value: "no"}
- {key: "AllowAgentForwarding", value: "no"}
- {key: "LogLevel", value: "VERBOSE"}
- {key: "MaxSessions", value: "2"}
- {key: "PermitRootLogin", value: "no"}
- name: Restart SSH service
systemd:
name: sshd
state: restarted
- name: Set file permissions
file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
loop:
- {path: "/usr/bin/gcc", mode: "0700"}
- {path: "/usr/bin/g++", mode: "0700"}
- {path: "/usr/bin/make", mode: "0700"}
- {path: "/usr/bin/as", mode: "0700"}
- {path: "/usr/bin/ld", mode: "0700"}
- {path: "/usr/bin/cc", mode: "0700"}
- {path: "/etc/at.deny", mode: "0640"}
- {path: "/etc/cron.deny", mode: "0640"}
- {path: "/etc/crontab", mode: "0600"}
- {path: "/etc/cron.d", mode: "0700"}
- {path: "/etc/cron.daily", mode: "0700"}
- {path: "/etc/cron.hourly", mode: "0700"}
- {path: "/etc/cron.weekly", mode: "0700"}
- {path: "/etc/cron.monthly", mode: "0700"}
- name: Configure at.allow and cron.allow
file:
path: "{{ item.path }}"
state: touch
owner: root
group: root
mode: "0600"
loop:
- {path: "/etc/at.allow"}
- {path: "/etc/cron.allow"}
- name: Remove deny files
file:
path: "{{ item }}"
state: absent
loop:
- /etc/at.deny
- /etc/cron.deny
- name: Configure sysctl
lineinfile:
path: /etc/sysctl.conf
regexp: "^{{ item.key }}="
line: "{{ item.key }}={{ item.value }}"
create: true
mode: "0600"
loop:
- {key: "vm.swappiness", value: "1"}
- {key: "net.ipv4.ip_forward", value: "0"}
- {key: "net.ipv4.conf.all.accept_redirects", value: "0"}
- {key: "net.ipv4.conf.default.accept_redirects", value: "0"}
- {key: "net.ipv4.tcp_syncookies", value: "1"}
- {key: "net.ipv4.conf.all.accept_source_route", value: "0"}
- {key: "net.ipv4.conf.default.accept_source_route", value: "0"}
- {key: "dev.tty.ldisc_autoload", value: "0"}
- {key: "fs.protected_fifos", value: "2"}
- {key: "fs.protected_regular", value: "2"}
- {key: "fs.suid_dumpable", value: "0"}
- {key: "kernel.dmesg_restrict", value: "1"}
- {key: "kernel.kptr_restrict", value: "2"}
- {key: "kernel.sysrq", value: "0"}
- {key: "kernel.unprivileged_bpf_disabled", value: "1"}
- {key: "kernel.yama.ptrace_scope", value: "2"}
- {key: "net.core.bpf_jit_harden", value: "2"}
- {key: "net.ipv4.conf.all.log_martians", value: "1"}
- {key: "net.ipv4.conf.all.rp_filter", value: "1"}
- {key: "net.ipv4.conf.all.send_redirects", value: "0"}
- {key: "net.ipv4.conf.default.log_martians", value: "1"}
- {key: "net.ipv6.conf.all.accept_redirects", value: "0"}
- {key: "net.ipv6.conf.default.accept_redirects", value: "0"}
- name: Apply sysctl changes
command: sysctl -p
register: sysctl_output
changed_when: sysctl_output.rc != 0
- name: Disable core dumps
lineinfile:
path: /etc/security/limits.conf
regexp: "^\\* hard core"
line: "* hard core 0"
create: true
mode: "0644"
- name: Set UMASK
lineinfile:
path: /etc/login.defs
regexp: "^UMASK"
line: "UMASK 027"
create: true
mode: "0644"
- name: Configure /etc/hosts
lineinfile:
path: /etc/hosts
regexp: "^127.0.1.1"
line: "127.0.1.1 {{ hostname }} {{ fqdn }}"
create: true
mode: "0644"
- name: Prevent unnecessary kernel modules
copy:
dest: /etc/modprobe.d/blacklist.conf
mode: "0644"
content: |
blacklist usb-storage
options usb-storage modeset=0
install usb-storage /bin/true
blacklist firewire-core
options firewire-core modeset=0
install firewire-core /bin/true
blacklist dccp
options dccp modeset=0
install dccp /bin/true
blacklist sctp
options sctp modeset=0
install sctp /bin/true
blacklist rds
options rds modeset=0
install rds /bin/true
blacklist tipc
options tipc modeset=0
install tipc /bin/true
- name: Apply kernel module changes
command: dracut -f
register: dracut_output
changed_when: dracut_output.rc != 0
- name: Ensure audit rules
lineinfile:
path: /etc/audit/rules.d/audit.rules
regexp: "^-a always,exit -F arch=b64 -F path={{ item.path }}"
line: "-a always,exit -F arch=b64 -F path={{ item.path }} -F perm={{ item.perm }} -k {{ item.key }}"
create: true
mode: "0644"
loop:
- {path: "/etc/passwd", perm: "wa", key: "passwd_changes"}
- {path: "/etc/shadow", perm: "wa", key: "shadow_changes"}
- {path: "/etc/group", perm: "wa", key: "group_changes"}
- {path: "/etc/gshadow", perm: "wa", key: "gshadow_changes"}
- {path: "/var/log/secure", perm: "wa", key: "secure_logs"}
- {path: "/usr/sbin/useradd", perm: "x", key: "user_management"}
- {path: "/usr/sbin/usermod", perm: "x", key: "user_management"}
- {path: "/usr/sbin/userdel", perm: "x", key: "user_management"}
- {path: "/usr/sbin/groupadd", perm: "x", key: "group_management"}
- {path: "/usr/sbin/groupmod", perm: "x", key: "group_management"}
- {path: "/usr/sbin/groupdel", perm: "x", key: "group_management"}
- name: Apply audit rules
command: auditctl -R /etc/audit/rules.d/audit.rules
register: audit_rules_output
changed_when: audit_rules_output.rc != 0
- name: Create custom authselect profile
command: authselect create-profile custom-sssd --base-on=sssd
register: authselect_output
changed_when: authselect_output.rc != 0
- name: Update system-auth in custom profile
lineinfile:
path: /etc/authselect/custom/custom-sssd/system-auth
regexp: '^\s*password\s+sufficient\s+pam_unix.so'
line: "password sufficient pam_unix.so sha512 shadow nullok use_authtok rounds=50000"
create: true
backrefs: true
mode: "0644"
- name: Select custom authselect profile
command: authselect select custom/custom-sssd
register: authselect_select_output
changed_when: authselect_select_output.rc != 0
- name: Prompt for password change
debug:
msg: "Please run the 'passwd' command to apply new hashing to the current user."