Security
A fresh installation prioritizes functionality: every service the distribution ships starts automatically, default firewall policies pass all traffic, and SSH accepts password authentication from any source. This is reasonable during setup. It becomes a liability the moment the system connects to an untrusted network.
Hardening reduces a system’s attack surface by removing or disabling what you don’t need, restricting what you do need, and verifying that nothing remains open by accident. The underlying principle is least privilege: every service, user, and process should have exactly the access it needs and no more.
Linux defaults leave several categories of exposure:
- Network exposure: The default iptables and nftables policy is ACCEPT. All inbound and outbound traffic passes without restriction until you write rules.
- Service exposure: Package managers install software with its service enabled. A web server install starts Apache or Nginx immediately. Each running service is a potential entry point.
- Authentication exposure: OpenSSH allows password authentication and root login by default on many distributions. Password authentication is vulnerable to brute force. A successful attack against root grants immediate full control.
- Kernel exposure: Several kernel parameters ship permissive. ICMP redirects, source routing acceptance, and SYN flood protections are often not configured to their most restrictive values.
This section covers the common hardening steps for Linux hosts: disabling unnecessary services, configuring the firewall to default-deny, restricting SSH, and tuning kernel parameters that affect network security.
CIS Critical Security Controls
The CIS Critical Security Controls are a prioritized set of 18 defensive actions published by the Center for Internet Security. They address the most common attack techniques and are organized by implementation groups (IGs) that reflect organizational maturity and resources:
- IG1: Essential cyber hygiene for any organization. Covers the safeguards every organization should implement regardless of size or resources.
- IG2: For organizations with dedicated IT staff and moderate risk exposure. Builds on IG1 with additional controls for managing sensitive data and more sophisticated attacks.
- IG3: For mature organizations with security expertise and high-risk profiles. Includes all 18 controls and their full safeguard sets.
The groups are cumulative: IG2 includes everything in IG1, and IG3 includes everything in IG2.
| # | Control | What it covers |
|---|---|---|
| 1 | Inventory and control of enterprise assets | Track all hardware connected to the network. You can’t protect what you don’t know exists. |
| 2 | Inventory and control of software assets | Track all authorized software. Unauthorized programs are a primary malware vector. |
| 3 | Data protection | Classify, handle, retain, and dispose of data assets securely. |
| 4 | Secure configuration of enterprise assets and software | Harden default configurations on all devices, OSes, and applications. |
| 5 | Account management | Manage credentials and authorization for user and service accounts. |
| 6 | Access control management | Create, assign, manage, and revoke access privileges across the environment. |
| 7 | Continuous vulnerability management | Assess and remediate vulnerabilities continuously to reduce attacker opportunity. |
| 8 | Audit log management | Collect, alert on, review, and retain logs that support detection and recovery. |
| 9 | Email and web browser protections | Defend against phishing and drive-by attacks targeting users. |
| 10 | Malware defenses | Prevent installation, spread, and execution of malicious code. |
| 11 | Data recovery | Maintain tested backups that restore systems to a known-good state. |
| 12 | Network infrastructure management | Track and harden routers, switches, and other network devices. |
| 13 | Network monitoring and defense | Monitor traffic for threats and respond to anomalies. |
| 14 | Security awareness and skills training | Train users to recognize and respond to security threats. |
| 15 | Service provider management | Assess third-party vendors that handle sensitive data or systems. |
| 16 | Application software security | Manage security across the lifecycle of developed and acquired software. |
| 17 | Incident response management | Maintain documented plans, roles, and procedures for responding to incidents. |
| 18 | Penetration testing | Test defenses by exploiting weaknesses before attackers do. |
Benchmarks
CIS also publishes CIS Benchmarks: prescriptive configuration guides for specific operating systems, cloud platforms, network devices, and server software. Each benchmark maps its recommendations to the CIS Controls and provides step-by-step hardening instructions for that product. Benchmarks are available for Linux distributions, Windows, macOS, AWS, Azure, Kubernetes, Cisco devices, and more than 100 other products.
Each guide lists every configuration setting you should check, the recommended value, and the rationale. Benchmarks are organized by IG level so you can work through IG1 items first and add IG2 and IG3 settings as your program matures.
To use a benchmark:
- Download the PDF for your OS or platform from cisecurity.org/cis-benchmarks.
- Work through each recommendation, noting current state and required changes.
- Automate assessment with CIS-CAT Pro (commercial) or open-source tools such as
oscapfrom the OpenSCAP project.
Hardware inventory
Before hardening a system, document what you’re working with. These commands read from the kernel’s virtual filesystem and require no additional tools.
cat /proc/cpuinfo # CPU model, core count, clock speed, and supported feature flags
cat /proc/meminfo # total and available RAM, swap usage, and kernel memory statistics
cat /etc/issue # distribution name and version (the pre-login banner)
cat /proc/version # kernel version and the compiler used to build it
uname -v # kernel version string with build date and time
uname -n # hostname of the system
sudo fdisk -l # disk devices, partition tables, and sizes
sudo dmesg # kernel ring buffer — hardware detection and driver messages at boot
sudo dmidecode # BIOS/UEFI firmware table: motherboard, memory slots, chassis, and serial numbers
Inventory script
This script collects hardware, OS, network, CPU, memory, and disk information in a single pass and prints each category between separator lines. Run it as root so dmidecode and fdisk can access firmware and partition data.
#!/bin/bash
echo -n "Basic Inventory for Hostname: "
uname -n
#
echo ============================================
dmidecode | sed -n '/System Information/,+2p' | sed 's/\x09//'
dmesg | grep Hypervisor
dmidecode | grep "Serial Number" | grep -v "Not Specified" |
grep -v None
#
echo ============================================
echo "OS Information: "
uname -o -r
if [ -f /etc/redhat-release ]; then
echo -n " "
cat /etc/redhat-release
fi
if [ -f /etc/issue ]; then
cat /etc/issue
fi
#
echo ============================================
echo "IP information: "
ip ad | grep inet | grep -v "127.0.0.1" | grep -v "::1/128" | tr -s " " | cut -d " " -f 3
echo ============================================
echo "CPU Information: "
cat /proc/cpuinfo | grep "model name\|MH\|vendor_id" | sort -r | uniq
echo -n "Socket Count: "
cat /proc/cpuinfo | grep processor | wc -l
echo -n "Core Count (Total): "
cat /proc/cpuinfo | grep cores | cut -d ":" -f 2 | awk '{sum+=$1} END {print sum}'
echo ============================================
echo "Memory Information: "
grep MemTotal /proc/meminfo | awk '{print $2,$3}'
echo ============================================
echo "Disk Information: "
fdisk -l | grep Disk | grep dev
Several parts use techniques worth understanding:
echo -ncombined withuname -n: The-nflag suppressesecho’s trailing newline souname -nprints on the same line as the label.sed -n '/System Information/,+2p': The,+2range prints the matching line plus the next two lines. The secondsedstrips\x09(the hex escape for tab) to remove the indentationdmidecodeadds to its output.dmesg | grep Hypervisor: The kernel logs a line such asHypervisor detected: KVMat boot when running in a VM. This grep returns the hypervisor type or nothing on bare metal.- Chained
grep -vfor serial numbers:dmidecodeusesNot SpecifiedandNoneas placeholders when no serial number is set, which is common on VMs. The twogrep -vcalls strip these non-values so only real serial numbers appear. tr -s " " | cut -d " " -f 3:ip addruses variable whitespace.tr -s " "squeezes runs of spaces into one socut -f 3reliably extracts the IP/prefix field regardless of indentation.grep processor | wc -lvs. thecoresawk block: The first counts logical processors including hyperthreads. Theawk '{sum+=$1}'block sums physical cores per socket across all CPU packages, giving the true core count.fdisk -l | grep Disk | grep dev:fdisk -lmixes device lines, partition entries, and disk label info. The secondgrep devisolates physical device lines (/dev/sda,/dev/nvme0n1) and drops everything else.
Software inventory
Knowing what software is installed is the first step toward removing what you don’t need. This maps to CIS Control 2. On Debian and Ubuntu systems, dpkg is the authoritative source for installed package data.
sudo apt list --installed | wc -l # count of installed packages (includes one header line)
dpkg -l # all installed packages with version, architecture, and status
dpkg -L <package-name> # every file installed by a specific package
On Red Hat family systems, use rpm instead:
rpm -qa # all installed packages
rpm -qi <package-name> # detailed info for a specific package: version, size, description, and install date
osquery
osquery exposes the operating system as a relational database you can query with standard SQL. It ships three components:
- osqueryi: An interactive SQL shell for ad-hoc queries. Results print to the terminal. No daemon required.
- osqueryd: A daemon that runs scheduled queries defined in a configuration file and writes results to a log for collection by a SIEM or monitoring tool.
- osqueryctl: A helper script that wraps
systemctlto start, stop, and check the status ofosqueryd.
Launch the interactive shell:
osqueryi
Shell commands
These commands control the osqueryi session rather than querying data:
.help # list all available dot commands and options
.tables # list every table available in the osquery schema
OS version
SELECT * FROM os_version;
Returns the OS name, version string, major, minor, and patch numbers, build identifier, platform family, and codename.
Network interfaces
SELECT interface, address, mask
FROM interface_addresses
WHERE interface NOT LIKE '%lo%';
Returns all non-loopback network interfaces with their IP address and subnet mask. The NOT LIKE '%lo%' filter excludes loopback addresses.
ARP cache
SELECT * FROM arp_cache;
Returns the local ARP table: each IP address mapped to its MAC address and the interface that learned it. Useful for spotting unexpected hosts or duplicate IPs on a segment.
Installed packages
SELECT * FROM deb_packages LIMIT 2;
Returns installed Debian package records including name, version, architecture, and install size. Use rpm_packages on Red Hat systems.
Recently started processes
SELECT pid, name
FROM processes
ORDER BY start_time DESC
LIMIT 10;
Returns the 10 most recently started processes by PID and name. Useful for spotting processes launched after a known-good baseline.
Process hashes and owners
SELECT DISTINCT h.sha256, p.name, u.username
FROM processes AS p
INNER JOIN hash AS h ON h.path = p.path
INNER JOIN users AS u ON u.uid = p.uid
ORDER BY start_time DESC
LIMIT 5;
Joins three tables to return the SHA256 hash of each running executable, the process name, and the username it runs as. The hash lets you verify binaries against known-good values. Processes running as unexpected users or with unrecognized hashes are worth investigating.
Linux Security Modules (LSMs)
Linux file permissions are discretionary access control (DAC): the owner of a file decides who can read, write, or execute it. DAC works well for normal use but fails as a security boundary — a misconfigured application or compromised process running as root can access anything. Mandatory access control (MAC) enforces policy at the kernel level, independent of file ownership. Even root cannot bypass MAC policy.
Linux implements MAC through the Linux Security Modules (LSM) framework. Two systems dominate: SELinux and AppArmor. Both confine processes to only the resources their policy explicitly permits, but they take fundamentally different approaches.
| SELinux | AppArmor | |
|---|---|---|
| Model | Label-based | Path-based |
| Default on | RHEL, CentOS, Fedora | Ubuntu, Debian |
| Policy complexity | High | Low |
| Granularity | Very fine | Moderate |
| Configuration style | Central policy database | Per-application profile files |
SELinux
SELinux (Security-Enhanced Linux) was developed by the NSA and merged into the kernel in 2003. It assigns a security context — a label in the form user:role:type:level — to every process, file, socket, and port on the system. Policy rules define which types are allowed to interact. A web server process labeled httpd_t can read files labeled httpd_sys_content_t but cannot read files labeled shadow_t or sshd_t, regardless of Unix permissions.
This label-based model makes SELinux extremely granular. It also makes it harder to configure. A mislabeled file silently breaks an application, and diagnosing why requires reading audit logs and understanding type enforcement rules.
SELinux runs in one of three modes:
- Enforcing: policy violations are blocked and logged.
- Permissive: violations are logged but not blocked. Use this to test an application before enforcing policy. Run the application through its normal workflows, then check
/var/log/audit/audit.logto see which operations SELinux would have denied. This lets you identify and resolve policy gaps before switching to enforcing mode. - Disabled: SELinux is off. Requires a reboot to re-enable.
Install and enable
SELinux is installed by default on RHEL, CentOS, and Fedora. On Debian and Ubuntu it requires manual installation.
# Debian/Ubuntu
apt install selinux-basics selinux-policy-default auditd
Configure the bootloader and schedule a filesystem relabel:
selinux-activate
# RHEL/CentOS (if removed or missing)
dnf install selinux-policy selinux-policy-targeted
Enable, disable, and check status by editing /etc/selinux/config. Changes require a reboot:
vim /etc/selinux/config
# SELINUX=enforcing ← enable and enforce policy
# SELINUX=permissive ← enable but log only, do not block
# SELINUX=disabled ← turn off entirely (reboot required to re-enable)
Usage
To switch modes without rebooting, or to manage policy modules:
reboot # required after any change to /etc/selinux/config
setenforce 0 # temporarily switch to permissive without rebooting
setenforce 1 # temporarily switch to enforcing without rebooting
semodule -l # list all loaded policy modules
semodule -e <module> # enable a disabled module
semodule -d <module> # disable a module without removing it
To check status, inspect contexts, and troubleshoot denials:
getenforce # print current mode: Enforcing, Permissive, or Disabled
sestatus # detailed status including policy name and loaded modules
ls -Z /var/www/html # show SELinux context on files
ps -eZ | grep httpd # show context of running processes
restorecon -Rv /var/www/html # restore default file contexts after a move or mislabel
chcon -t httpd_sys_content_t file # manually set a file context (not persistent across relabel)
ausearch -m avc -ts recent # search audit log for recent SELinux denials
audit2allow -a # suggest policy rules to allow recent denials
sealert -a /var/log/audit/audit.log # human-readable analysis of SELinux denials (requires setroubleshoot)
Recommended settings
Set /etc/selinux/config to:
SELINUX=enforcing
SELINUXTYPE=targeted
- Use
targetedpolicy. It confines high-risk services (Apache, SSH, DNS) without restricting every process on the system. - Keep
auditdrunning. SELinux writes all denials to/var/log/audit/audit.log. Without auditd, you lose the AVC records you need to diagnose problems. - Use
semanage fcontextfor permanent file context changes, notchcon. Changes made withchconare overwritten the next time SELinux relabels the filesystem. - Run
restorecon -Rvafter moving files into a service directory to restore the correct context. - Don’t disable SELinux to fix a broken application. Switch to permissive mode, reproduce the problem, then use
audit2allowandsealertto understand the denial before re-enabling enforcing.
AppArmor
AppArmor was developed by Novell and is the default MAC system on Ubuntu and Debian. Rather than labeling every object on the filesystem, AppArmor confines individual programs using profiles: text files that list the filesystem paths, capabilities, and network access a program is allowed. A profile for Nginx permits it to read /etc/nginx/** and /var/www/html/** but nothing outside those paths.
The path-based model is simpler to understand and write than SELinux type enforcement. The tradeoff is granularity: a hard link or bind mount can bypass path-based policy in ways that label-based systems prevent.
Each profile runs in one of two modes:
- Enforce: violations are blocked and logged.
- Complain: violations are logged but not blocked. Use complain mode as a learning mode: run the application through its normal workflows and AppArmor records every access it makes. Review the logs with
aa-logprofto generate a profile that reflects actual behavior before switching to enforce mode.
Dependencies
AppArmor is installed by default on Ubuntu and Debian. Install the utilities package to get the profile management tools:
apt install apparmor apparmor-utils
Usage
To manage the service:
systemctl enable apparmor # enable at boot
systemctl start apparmor # start immediately
systemctl stop apparmor # stop immediately
systemctl disable apparmor # disable at boot
To load, reload, and remove profiles:
apparmor_parser -a /etc/apparmor.d/profile # load a new profile into the kernel
apparmor_parser -r /etc/apparmor.d/profile # reload a profile after editing
apparmor_parser -R /etc/apparmor.d/profile # remove a profile from the kernel
systemctl reload apparmor # reload all profiles
To manage profile modes and troubleshoot violations:
aa-status # list all profiles and their current mode
aa-enforce /etc/apparmor.d/usr.sbin.nginx # set a profile to enforce mode
aa-complain /etc/apparmor.d/usr.sbin.nginx # set a profile to complain mode
aa-complain /etc/apparmor.d/* # set all profiles to complain mode at once
aa-disable /etc/apparmor.d/usr.sbin.nginx # disable a profile
aa-genprof /usr/sbin/myapp # interactively generate a new profile
aa-logprof reads /var/log/syslog or the audit log and presents each unconfined access interactively. For each event, you choose to allow, deny, or ignore it. When you exit, aa-logprof writes your decisions into the profile file. Run it after exercising the application in complain mode to build a profile from real behavior rather than writing rules by hand.
aa-logprof # interactively update profiles from logged violations
journalctl -xe | grep apparmor # view AppArmor denial messages in the system journal
Recommended settings
- Keep
apparmorenabled and started at boot. - Install
apparmor-utils. Theaa-*management commands depend on it. - Verify that all distro-shipped profiles run in enforce mode with
aa-status. Any profile in complain mode is not blocking violations. - For custom applications, start in complain mode, run the application through its normal workflows, then use
aa-logprofto build the profile from real behavior before switching to enforce. - Store custom profiles in
/etc/apparmor.d/. AppArmor loads every file in that directory automatically. - After editing a profile, reload with
systemctl reload apparmor. Restarting the service is not necessary and disrupts active confinement.