Harden.yml
Ansible playbook for Linux hardening
* Debian (Trixie) * :dragon: Kali * π Raspberry Pi OS * Slackware (>= [15.0](http://www.slackware.com/announce/15.0.php)) The project is written primarily in Jinja, distributed under the MIT License license, first published in 2013. Key topics include: ansible, ansible-playbook, debian, hardening, linux.
harden.yml :lock:
Ansible playbook to harden your Linux system.
Supported distros
- Debian (Trixie)
- :dragon: Kali
- π Raspberry Pi OS
- Slackware (>= 15.0)
:question: Why I made this
- Bastille is obsolete
- Not a member of CIS, so no downloading of the ready made scripts
- Also to go "beyond CIS" with things like:
- Reducing CA certs
- Hardening PAM
- Making smart audit rules that don't spam your logs with unnecessary and useless information
- Properly locking down system accounts
- Other stuff that is not covered by CIS benchmarks
- Also to go "beyond CIS" with things like:
- For learning
- For minimizing the effort needed to tweak fresh installations
- Also for consistency
:question: What does it do?
For a complete list you can run ansible-playbook --list-tasks harden.yml.
Network
- Enables TCP wrappers
- :bulb: Some people consider TCP wrappers as obsolete and unnecessary, because nowadays firewall(s) take care of this kind of network level access. I disagree, because TCP wrappers still provide an additional layer of control in a case where the firewall(s) might fail for any number of reasons (usually misconfiguration). TCP wrappers also work as an network level ACL for the programs that utilize it and is a "native" control for those programs.
- IP stack hardening via sysctl settings
- For the complete list, see network.conf.new
- Creates a basic firewall
- Configures NetworkManager as follows:
- For each existing connection:
- Disables IPv6 (
ipv6.method->disabled)
- Disables IPv6 (
- Configures few defaults to
/etc/NetworkManager/conf.d/:- Sets
dhcp-send-hostnametofalse - Sets
wifi.scan-rand-mac-addresstotrue
- Sets
- For each existing connection:
:wood: Logging
- :calendar: Configure log retention time to be 6 months
- Configures
logrotatetoshredfiles- :information_source: NOTE: Read the fine print in SHRED(1): "CAUTION: shred assumes the file system and hardware overwrite data in place. Although this is common, many platforms operate otherwise."
- Configure
journaldto use Forward Secure Sealing (FSS) and enable auditing (see notes on how to create keys for FSS) - Enables auditing
- Run
ansible-playbook --list-tasks --tags logging harden.ymlfor a full list
:bar_chart: Accounting
- Enables system accounting (sysstat)
- :calendar: Sets it's log retention to 99999 days (the logs are really small, so it doesn't eat up disk space)
- Enables process accounting
- Run
ansible-playbook --list-tasks --tags accounting harden.ymlfor a full list
:peanuts: Kernel
- :no_entry: Disables the use of certain kernel modules via
modprobe(see files/modprobe.d/)- Disable Firewire
- :warning: WARNING: Also disables
usb-storage, which will disable support for USB mass medias
- sysctl settings hardening
- :keyboard: Enables Secure Attention Key (SAK) and disables the other magic SysRq stuff
- :no_entry: Restricts the use of
dmesgby regular users - :no_entry: Enable YAMA (disallow
ptrace) - For the complete list, see sysctl.conf.new
- Run
ansible-playbook --list-tasks --tags kernel harden.ymlfor a full list
:file_folder: Filesystem
- Hardens mount options (creates
/etc/fstab.new) (see fstab.awk) - :house: Sets strict permissions to users home directories
- :no_entry: Limits permissions to various configuration files and directories that might contain sensitive content (see
permissionstag for a complete list) - :do_not_litter: Clean up
/tmpduring boot (see tmp.conf.new) - Removes SUID and/or SGID bits from various binaries (see
ansible-playbook --list-tasks --tags suid,sgid harden.ymlfor details)
Application specific
- Configures basic auditing based on stig.rules if audit is installed (see audit.yml)
- :blowfish: Configures
sshd_configandssh_config(seeansible-playbook --list-tasks --tags ssh harden.ymlfor details)- Removes 2048-bit moduli from
/etc/ssh/moduli - :information_source: Removes SUID bit from
ssh-keysign, so host-based authentication will stop working. Host-based authentication shouldn't be used anyway. - :information_source: Password authentication is not disabled so you wouldn't lock yourself out of your system accidentally
- See ssh_config.j2 and sshd_config.j2
- Removes 2048-bit moduli from
- :sandwich: Configures sudo (see sudoers.j2)
- :warning: WARNING: If there are rules in
/etc/sudoers.d/that match ourbecome: truetasks that do not have explicitEXEC, it can "break"sudoas we defineDefaults noexecin the mainsudoersfile. There is a "Fix generic rules" task insudoers.ymlwhich tries to tackle this problem, but it's not guaranteed to work. - :warning: WARNING: Sudo binary
/usr/bin/sudoischmodded so, that onlysudo_group(seevars.yml) can run it - :wood: You can set the
sudo_iologinvars.ymltotrueto enable I/O logging - You can set the
sudo_idsinvars.ymltotrueto enable "Intrusion Detection" as described in Sudo Mastery chapter 9 (#59) - See also notes
- :warning: WARNING: If there are rules in
- :smiling_imp: ClamAV configuration (see clamav.yml)
- Configures
clamd&freshclamby first generating fresh configurations with clamconf - Configured ClamAV to unarchive with password "infected" (see Passwords for archive files & ClamAV and ZIP File Decryption)
- Downloads YARA Forge rules for ClamAV to use. ClamAV doesn't support all YARA features so a bunch of rules needs to be discarded. This is done with strip_unsupported_yara_rules.py.
- :warning: WARNING: ClamAV consumes a lot of memory, so it might not be suitable for all systems. See Recommended System Requirements.
- :information_source:
harden.ymlskips the ClamAV installation completely if there's less than 3 GiB of memory available
- Configures
- rkhunter configuration (see rkhunter.yml)
- :tiger: Tiger: Configures
tigerrc&tiger.ignore - Lynis configuration (see lynis.yml)
- Configures AIDE (see aide.yml)
- Display managers:
- Disables user lists in GDM3 & LightDM
- :no_entry: Disables guest sessions and VNC in LightDM
- :feather: Minor Apache HTTP server hardening
- Minor PHP (
php.ini) hardening
User accounts / authentication / authorization
- Sets default umask to a more stricter
077(see https://github.com/pyllyukko/harden.yml/wiki/umask) - :timer_clock: Sets console session timeout via
$TMOUT(Bash) - 🎟️ Properly locks down system accounts (0 -
SYS_UID_MAX&& !root)- :no_entry: Lock the user's password
- :shell: Sets shell to
/sbin/nologin - Set
maxloginsto0inlimits.conf(enforced bypam_limits)- :information_source: You can see it in the logs as:
pam_open_session: Permission denied
- :information_source: You can see it in the logs as:
- Expire the account
- :no_entry: Set
RLIMIT_NPROCandRLIMIT_NOFILEto0in pam_limits for those system accounts that don't need to run any processes- :information_source: You can see this in action in the PAM limits test
- :information_source: See
unnecessary_system_accountsinvars.ymlfor the list of accounts - :information_source:
RLIMIT_NOFILEwill causepam_open_session: Too many open fileserrors andRLIMIT_NPROCwill causefork: Resource temporarily unavailableerrors
- :busts_in_silhouette: Makes minor modifications to existing accounts. See
ansible-playbook --list-tasks --tags accounts harden.ymlfor details.
Password policy
-
🎟️ Configures the default password inactivity period
-
Configures the password aging values to
/etc/login.defsPASS_MAX_DAYSPASS_MIN_DAYSPASS_WARN_AGE
-
These settings are also applied to existing user accounts
-
Two password quality backends are supported. The playbook auto-detects which is installed and prefers passwdqc over
libpwquality(see passwdqc.conf.j2 and pwquality.conf.j2) -
Creates a CrackLib dictionary to be used with libpwquality (see
ansible-playbook --list-tasks --tags cracklib harden.ymland the CrackLib related handlers in handlers.yml)- The small dictionary (derived from
cracklib-small) that usually comes bundled with CrackLib packages contains 51526 words and the one we generate (from cracklib-words) contains 1911477 words
- The small dictionary (derived from
-
Creates a
passwdqccuckoo filter file based on RockYou -
Downloads John the Ripper's password.lst to be used with
passwdqc'swordlistoption -
Configures
/etc/security/pwhistory.conf- :information_source:
pam_pwhistoryneeds to be manually enabled withpam-auth-updatein Debian
- :information_source:
-
Run
ansible-playbook --list-tasks --tags passwords harden.ymlto list all password related tasks
NIST SP 800-63
You can set the password_policy variable to nist_sp800_63 in vars.yml for NIST Special Publication (SP) 800-63-4 style password policy.
- :warning: This is an experimental feature and :construction: under construction :construction:
- See New password policies according to NIST SP 800-63 #88
- :information_source: If you opt to use this, it is highly recommended to use
passwdqcinstead oflibpwquality, aspasswdqc'swordlist&filterfeatures are superior compared tolibpwquality's use of CrackLib
The main differences are:
| Property | Traditional | SP 800-63 |
|---|---|---|
| Password maximum age (days) | 365 | 99999 |
| Password minimum length | 14 | 15 |
| Composition/complexity requirements | ✅ | ❌ |
🎟️ Authorization
- Create a strict
securetty - Creates
/etc/ftpusers - Restricts the use of cron and
at - Disallow non-admin users from authenticating as other users in polkit
- Run
ansible-playbook --list-tasks --tags authorizationfor a full list
PAM
- Configures
/etc/security/namespace.conf - 🎟️ Configures
/etc/security/access.confforpam_access(authorization) (see access.conf.j2) - Configures password quality —
/etc/security/pwquality.confor/etc/passwdqc.conf— depending on which is installed (see Password policy section) - 🛞 Require pam_wheel in
/etc/pam.d/su - :no_entry: Creates a secure /etc/pam.d/other
- See also A strong /etc/pam.d/other
- :bulb: Fun fact: As Debian 13 doesn't seem to ship
/etc/pam.d/systemd-run0, this will also block the use of run0
- Configures
/etc/security/limits.confas follows:- Disable core dumps
- Sets maximum amount of processes (or threads, see setrlimit(2))
- :no_entry: Sets
nprocto 0 for system users that don't need to run any processes
- Run
ansible-playbook --list-tasks --tags pam harden.ymlto list all PAM related tasks - You can also run
ansible-playbook --check --diff --tags pam harden.ymlto see details of the changes
Miscellaneous
- :placard: Creates legal banners (see banners.yml)
- Reduce the amount of trusted CAs (see ca-certificates.conf.new)
- You can also run
make /etc/ssl/certs/ca-certificates.crtto update the CAs - :information_source: Do note, that Slackware will overwrite
/etc/ca-certificates.confwhen upgradingca-certificatespackage. See Slackware's ChangeLog entry for Wed Oct 6 00:02:15 UTC 2021. - :bulb: See the CA incidents wiki page for a curated list of historical CA compromises, mis-issuance and other incidents that motivate reducing the default trust store.
- :shell: Restricts the number of available shells (
/etc/shells) - :shell: Creates an option to use a restricted shell (rbash)
- Only available for Debian & Slackware and for the
sshdservice because of the required PAM configuration changes (regardingpam_env& enforcingPATH) - :information_source: See Restricted shell
- :warning: WARNING: Contains plenty of caveats, details and hazards. Make sure you read and understand (at least) everything in the aforementioned wiki page, test it thoroughly and accept the risk that it may contain escapes.
- Only available for Debian & Slackware and for the
- Disable core dumps via
/etc/systemd/coredump.conf - Kerberos hardening via
/etc/krb5.conf
Slackware specific
- Disables unnecessary services
- See
slackware_servicesin vars.yml
- See
- Run
ansible-playbook --list-tasks --tags slackware harden.ymlfor a full list - Make Xorg rootless
- :wood: Makes default log files group
admreadable (as in Debian) - 🛞 Restricts the use of
cronso that only users in the wheel group are able to create cronjobs (as described in /usr/doc/dcron-4.5/README) - Mount /proc with
hidepid=2 - :wood: Make
installpkgstore the MD5 checksums - :bar_chart: Enable process accounting (
acct) - :busts_in_silhouette: Does some housekeeping regarding group memberships (see login_defs-slackware.yml)
- 🎟️ Configures
inittabto useshutdown -a(and/etc/shutdown.allow) - Reconfigured bunch of services (run
ansible-playbook --list-tasks --tags slackware harden.yml | grep '\bservices\b'for a full list) - Configures cgroups (v1, because of too old
libcgroup) into/etc/cg{config,rules}.conf - Enables
bootlogd- :information_source: NOTE: Requires
CONFIG_LEGACY_PTYS(which KSPP recommends to disable)
- :information_source: NOTE: Requires
PAM
- Creates a custom
/etc/pam.d/system-auth, which has the following changes:- :timer_clock: Use
pam_faildelay - 🎟️ Use
pam_faillock - 🎟️ Use
pam_access - :no_entry: Removes
nullokfrompam_unix - Sets crypt rounds for
pam_unix - Change password
minlenfrom 6 to 14 - Enables
pam_pwhistory - See system-auth.j2
- :timer_clock: Use
- The following PAM modules are added to
/etc/pam.d/postlogin:pam_umaskpam_cgrouppam_keyinit
- Add
pam_namespaceto/etc/pam.d/{login,sddm,sshd,xdm} - Removes
auth include postloginfrom several files, aspostloginshould (and has) onlysessionmodule types - :sandwich: Creates
/etc/pam.d/sudo, as that seemed to be missing - 🎟️ Disallows the use of
su(see su.new)- :information_source: Filesystem/permissions hardening will also remove the SUID bit from
su, so it won't even be able to switch privileges
- :information_source: Filesystem/permissions hardening will also remove the SUID bit from
- :no_entry: Block
/etc/pam.d/remote(see /etc/pam.d/remote) - Adds
pam_shellsto/etc/pam.d/chsh(require a valid shell in order to change your shell)
Debian specific
- Disables unnecessary systemd services
- See
debian_servicesin vars.yml
- See
- :shield: Enables AppArmor
- Configure
SUITEindebsecan - Install
debsumsand enable weekly cron job - Installs a bunch of security related packages (see debian_packages.yml)
- Configures
chkrootkitand enables daily checks - Configures APT not to install suggested packages
pam-configs
Creates bunch of pam-configs that are toggleable with pam-auth-update:
| PAM module | Type | Description |
|---|---|---|
| 🛞 pam_wheel<sup>1</sup> | auth | Require wheel group membership (su) |
| 🎟️ pam_succeed_if | auth & account | Require UID >= 1000 && UID <= 60000 (or 0 & login) |
| :no_entry: pam_unix<sup>1</sup> | auth | Remove nullok |
| :timer_clock: pam_faildelay | auth | Delay on authentication failure |
| pam_ssh_agent_auth | auth | SSH agent authentication for sudo<sup>3</sup> |
🎟️ pam_faillock | auth & account | Deter brute-force attacks |
| 🎟️ pam_access | account | Use login ACL (/etc/security/access.conf) |
| 🎟️ pam_time | account | /etc/security/time.conf |
| 🎟️ pam_lastlog | account | Lock out inactive users (no login in 90 days) |
| pam_namespace | session | Polyinstantiated temp directories |
| pam_lastlog | session | Display info about last login and update the lastlog and wtmp files<sup>2</sup> |
| pam_pwhistory | password | Limit password reuse |
- <span id="fn1"/>Not a
pam-config, but a modification to existing/etc/pam.d/files - <span id="fn2"/>For all login methods and not just the console login
- <span id="fn3"/>Disabled by default and requires libpam-ssh-agent-auth package. Needs to have higher priority than
krb5or other password auths.sshdneeds to haveAllowAgentForwarding yes- You need to configure
sudowithDefaults env_keep += "SSH_AUTH_SOCK"
Out of scope
At least the following hardening areas are currently not covered at all:
- Bootloader password enforcement
- Secure Boot / UEFI Hardening
- Network:
- DNS
- DNSSEC
- Defence against ARP cache poisoning
- DNS
- X11/Wayland hardening
- Automatic security updates (beyond installing
unattended-upgradesin Debian) - Wireless interfaces like WiFi and Bluetooth
- Authentication:
- Biometric authentication
- MFA
- Remote logging
- Compiler restrictions (e.g. removing/blocking development tools like
gcc,makeetc.)
Usage
- Edit the
harden.ymland modifyhostsor create a completely new playbook by making a copy of theharden.ymlfile- You can comment out the "task sets" that you don't need
- Check
vars.ymlin case you want to tweak some of the settings - You can check all the tasks before running the playbook by running
ansible-playbook --list-tasks harden.yml - Harden your system by running
ansible-playbook harden.yml
:information_source: Notes
- :busts_in_silhouette: Make sure regular users that should be able to login are members of the
allowed_groupgroup - :sandwich: Sudo hardening:
noexecis on by default, so you need to take this into account in your custom rules- :timer_clock: Interactive shells to
roothave timeout, so usescreenfor those longer administrative tasks
- :arrows_counterclockwise: Rebooting the system after running this is highly recommended
- The AIDE DB creation is made asynchronously and without polling, so let that finish before rebooting
- :bulb: You might want to get additional (unofficial) rules for ClamAV with clamav-unofficial-sigs (although see #425). At least the following rulesets are freely available:
- Sanesecurity
- Porcupine ("The following databases are distributed by Sanesecurity, but produced by Porcupine Signatures")
- bofhland ("The following databases are distributed by Sanesecurity, but produced by bofhland")
- Foxhole
- Linux Malware Detect
- InterServer
- URLhaus
- Sanesecurity
- :warning: WARNING: There is a hazard with immutable
loginuidenabled in auditing in non-systemd systems (Slackware). See longer description of this in the wiki. - :file_folder: Review
/etc/fstab.newmanually and deploy applicable changes to/etc/fstab - :bulb: Consider running a hardened kernel. For Slackware you can check out my other project kspp_confnbuild that has been (mostly) configured according to KSPP's recommendations. You can use kernel-hardening-checker to check your kernel configs.
- :envelope: Make sure your system is able to send e-mails somehow. Many of the tools will be sending alerts about various anomalies.
- Customize the firewall to suit your needs
- :globe_with_meridians: You can use Nftables geoip script to block certain parts of the world (see also GeoIP matching)
- :wood: Logging:
- :eyes: Consider installing and configuring Logwatch
- :key: Run
journalctl --setup-keysto generate a new key pair for Forward Secure Sealing (FSS) - Configure remote logging
- Consider purchasing Openwall passwdqc filter files to check for leaked credentials during password change (see "passwdqc filter" tasks in
pam.yml) - Variable
sudo_groupis also considered as administrator group (see tagpolkit) - Consider setting
PasswordAuthenticationtonoin/etc/ssh/sshd_config - Consider running arpwatch to detect ARP cache poisoning
passwdqc>libpwquality
Tags
Tags that you can use with ansible-playbook --tags:
pkikernelrngnetworkfirewallipv6networkmanager
- :wood:
logging - :file_folder: Filesystem related:
- :no_entry:
permissions fstabsuid&sgid
- :no_entry:
- Specific software:
- :bar_chart:
sysstat - :blowfish:
ssh rkhunterchkrootkitaideaudit(use--skip-tags auditin Slackware if you don't have audit installed)debsecandebsumslynis(to only configure Lynis you can use--tags lynis --skip-tags packages)- :sandwich:
sudo kerberos- :smiling_imp:
clamav(use--skip-tags clamavin Slackware if you don't have clamav installed)yara
- :shield:
apparmor cron(also includes tasks regardingat)php- :feather:
apachehsts
- :clock10:
ntp lightdmgnome- :tiger:
tiger john- 🔄
unattended-upgrades
- :bar_chart:
- :placard:
banners - AAA:
- :bar_chart:
accounting(includessysstat) - 🎟️
authorization passwordscracklib
- :busts_in_silhouette:
accounts pamlimits
- :bar_chart:
cgroup(Slackware)hidepid(Slackware)inittab(Slackware)- :shell:
shells umask- :timer_clock:
timeout polkitsystemd(experimental, enable withharden_systemd_services)
There are also operating system tags for tasks that only apply to specific OS.
You can speed up the hardening by skipping OSs that don't apply. E.g. if you're
hardening a Slackware system you can use --skip-tags debian.
Other tags are just metadata for now. You can list all the tags with
ansible-playbook --list-tags harden.yml.
Other features
- :no_entry: There is a
lock_account.ymlplaybook that you can use to lock user accounts. Just modify thehosts&user. - Limited hardening for FreeBSD (see freebsd.yml)
- :sandwich: Experimental feature: If you enable
sudo_idsinvars.yml, it enables "Sudo Intrusion Detection" as seen in chapter 9 of Sudo Mastery- Only for
SHELLSCmnd_Aliasfor now
- Only for
- Experimental and limited systemd service hardening (see services-systemd.yml)
- Needs to be enabled with
harden_systemd_services
- Needs to be enabled with
Makefile
| Target | Description |
|---|---|
pamcheck | Check (and diff) your Slackware's PAM configs |
:blowfish: /etc/ssh/moduli.new | Create a new SSH moduli |
/etc/audit/rules.d/31-privileged.rules.new | Find privileged binaries to be audited |
/etc/audit/rules.d/40-authorized_keys.rules.new | Find SSH authorized_keys from /home/ to be audited |
crls | Download CRLs |
/etc/ssl/certs/ca-certificates.crt | Create limited/reduced CA list |
virtualenv
A recent version of Ansible is recommended to run harden.yml. If you want to do this with virtualenv, you can install it as follows:
virtualenv venv. venv/bin/activatepip install ansible jmespath
Tests
See tests README
References
Hardening guides
Some of these documents are quite old, but most of the stuff still applies.
- Slackware System Hardening by Jeffrey Denton
- Center for Internet Security:
- CIS Slackware Linux 10.2 Benchmark v1.1.0
- CIS Debian Linux Benchmark
- CIS Distribution Independent Linux
- CIS MIT Kerberos 1.10 Benchmark v1.0.0
- CIS Password Policy Guide
- SlackDocs: Security HOWTOs
- :alien: Alien's Wiki: Security issues
- SlackWiki: Basic Security Fixes
- :bomb: Wikipedia: Fork bomb Prevention
- Configuration recommendations of a gnu/linux system (ANSSI-BP-028)
- How to harden a systemd service unit
Other docs
- Linux Standard Base Core Specification 4.1
- :busts_in_silhouette: Chapter 21. Users & Groups
- :file_folder: Filesystem Hierarchy Standard 2.3
- https://iase.disa.mil/stigs/os/unix-linux/Pages/index.aspx
- :book: PAM Mastery book by Michael W Lucas
- The Linux-PAM System Administrators' Guide
- :book: Sudo Mastery, 2nd Edition
- :book: Linux Firewalls
- :blowfish: Secure Secure Shell
- Securing Debian Manual
- :shield: AppArmor HowToUse
- ArchWiki: limits.conf
- Effectiveness of Linux Rootkit Detection Tools
- How to keep a detailed audit trail of what’s being done on your Linux systems
- Announcing systemd v257:
- The GNU C Library (glibc): Limiting Resource Usage
- Protect your VPN from TunnelVision attacks
Contributors
Showing top 4 contributors by commit count.
