Enterprise Linux security is not a checklist you run through once and file away. It is an ongoing architectural decision that shapes every process your system runs, every cryptographic handshake it performs, and every audit trail it generates. AlmaLinux occupies a genuinely interesting position in 2026: it is free, openly governed, Application Binary Interface (ABI) compatible with Red Hat Enterprise Linux (RHEL), and increasingly the platform of choice for organizations that want enterprise-grade security without a subscription fee. A clarification worth making here, since it is a source of confusion in the broader ecosystem: AlmaLinux shifted its stated goal in mid-2023 from being a "1:1 downstream rebuild" of RHEL to maintaining ABI compatibility with RHEL. This means that software applications and kernel modules built for RHEL will run on AlmaLinux -- if they do not, AlmaLinux considers it a bug. The distinction matters because some online discussions still describe AlmaLinux as a "RHEL clone" or a "drop-in rebuild," which is no longer precisely accurate. The compatibility guarantee is at the binary interface level, not at the source package level. But "free" does not mean "automatically secure," and the gap between a default AlmaLinux installation and a hardened, compliance-ready one is measured in dozens of deliberate configuration decisions.

This article walks through three interlocking layers of that hardening work: SELinux (Security-Enhanced Linux), FIPS 140-3 mode, and CIS Benchmark compliance. Understanding all three is essential because each operates at a different stratum of the system. SELinux is a kernel-level mandatory access control (MAC) mechanism. FIPS mode constrains the cryptographic primitives the operating system is permitted to use. CIS Benchmarks address the hundreds of configuration settings that neither SELinux nor FIPS covers on their own. Together they create defense-in-depth that can satisfy federal mandates, industry frameworks like SOC 2 and ISO 27001, and the simply prudent demands of any organization that takes data protection seriously.

SELinux -- What It Is, Why It Exists, and How It Works Under the Hood

The Problem SELinux Solves

Traditional Linux access control is Discretionary Access Control (DAC). A file is owned by a user and a group, permissions are set with chmod, and a process running as root can, absent other controls, do nearly anything. DAC worked well enough when systems were single-user workstations, but it creates a catastrophic failure mode on multi-tenant servers and networked services: if an attacker compromises a process running as root, or exploits a privilege escalation vulnerability to reach root, they have effectively unlimited access to the system.

SELinux, originally developed by the NSA in collaboration with partners including NAI Labs, Secure Computing Corporation, and MITRE Corporation, was released as open source on December 22, 2000. It was subsequently merged into the mainline Linux kernel 2.6.0-test3 on August 8, 2003, through the Linux Security Modules (LSM) framework. Red Hat has been a central contributor to SELinux since its early kernel integration and remains so today. SELinux implements Mandatory Access Control. The word "mandatory" is critical here. With DAC, the owner of a resource can grant or revoke access at will. With MAC, the kernel enforces a policy written by an administrator, and neither the resource owner nor the process itself can override it. A web server process, no matter what user it runs as, cannot read /etc/shadow. A compromised SSH daemon cannot write to /var/www/html. Policy violations are denied at the kernel security framework level, not by file permissions.

SELinux provides a strong, flexible, mandatory access control architecture into the major subsystems of the kernel. It provides an enhanced mechanism to enforce the separation of information based on confidentiality and integrity requirements.

-- NSA Security-Enhanced Linux Team, SELinux documentation

There is a broader point worth lingering on here. The NSA releasing SELinux as open source in 2000 was not a philanthropic gesture -- it was itself a security-architecture decision. Closed-source security mechanisms cannot be independently audited, and security that depends on obscurity fails when the obscurity evaporates. By releasing the code publicly, the NSA invited adversarial review from the global development community, which is precisely the process that produces trustworthy security software. The same principle underlies AlmaLinux's position: open governance and public source code mean that the security mechanisms can be verified by anyone, which is a stronger foundation than trust-by-vendor-assertion.

Interactive: Access Control Decision Simulator click a scenario

A compromised Apache process (running as root) attempts to read /etc/shadow. What happens under each access control model?

Select a model above to see the access decision path.

The Security Context: The Atomic Unit of SELinux

Every object on a SELinux system -- files, processes, sockets, pipes, devices -- carries a security context. This context is a four-field label structured as:

SELinux security context format
user:role:type:level

For example, the Apache web server process runs with the context system_u:system_r:httpd_t:s0, while a file it is allowed to serve might be labeled system_u:object_r:httpd_sys_content_t:s0. The type fields (httpd_t and httpd_sys_content_t) are what matter in the targeted policy. SELinux contains rules that explicitly allow the httpd_t domain to read files with httpd_sys_content_t. If a file does not have that type, the web server cannot read it regardless of POSIX permissions.

Interactive: SELinux Context Decoder

Click any field in the context label below to see what it means and why it matters.

: : :
Click a field above to decode it.

You can inspect the security context of any file or process:

terminal
# File context
$ ls -Z /var/www/html/index.html
# unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html

# Process context
$ ps -eZ | grep httpd
# system_u:system_r:httpd_t:s0 ... httpd

Policy Types: Targeted, MLS, and MCS

AlmaLinux ships with two primary policy options, configured in /etc/selinux/config:

Targeted policy confines a specific set of high-risk processes -- network-facing daemons, privileged services, and processes likely to be attack targets -- while leaving user processes unconfined. The rationale is pragmatic: full confinement is operationally disruptive, but the attack surface that matters in many environments is the relatively small set of processes that accept network connections. Targeted is the default and is appropriate for the vast majority of enterprise deployments.

MLS (Multi-Level Security) policy implements the Bell-LaPadula model, a formal security model from the 1970s developed to enforce classified information handling rules. In MLS, subjects (processes) and objects (files, sockets) are assigned a sensitivity level ranging from s0 (unclassified) to s15 (top secret), combined with compartment categories (c0 through c255). A process can only read data at or below its sensitivity level (no-read-up), and can only write to data at or above its sensitivity level (no-write-down). This prevents, in theory, a process from leaking classified data to a lower-classification channel.

MLS is operationally complex and is typically reserved for classified government environments. For nearly every commercial enterprise deployment, targeted policy with enforcing mode is the correct choice.

A third policy variant, MCS (Multi-Category Security), is embedded within the targeted policy and is what enables container isolation in tools like Podman and Docker on AlmaLinux. Each container is assigned a unique pair of MCS categories, ensuring that even if a container escape occurs, the escaped process cannot access the filesystem of another container because the kernel's SELinux policy blocks cross-category reads. This is where the Bell-LaPadula model stops being an abstract concept from a 1973 MITRE technical report and becomes something you are running in production, possibly without realizing it. Every time Podman launches a container with a unique s0:c123,c456 label, it is instantiating a simplified version of the same compartmentalized information flow model that Bell and LaPadula designed for classified military systems. The theory is not optional background knowledge -- it is the active mechanism preventing your containerized applications from reading each other's data. As of Linux kernel 6.6, the SELinux codebase removed its original NSA references, reflecting that SELinux has long since transitioned to a broad community of developers and maintainers.

The Three Modes and Why Permissive Exists

SELinux operates in three modes:

/etc/selinux/config
SELINUX=enforcing   # Policy violations are blocked and logged
SELINUX=permissive  # Policy violations are logged but not blocked
SELINUX=disabled    # SELinux is not loaded

Enforcing mode is the operational goal. Permissive mode exists specifically for troubleshooting and for the process of labeling a newly deployed system or application. The audit subsystem records every AVC (Access Vector Cache) denial whether in enforcing or permissive mode, and audit2allow can analyze those denials to generate policy modules. This is the proper workflow for onboarding a new application: run permissive, collect audit logs, use audit2allow to generate a draft policy, review and refine it, then switch to enforcing.

Warning

Disabling SELinux entirely is rarely justified in a production environment. Phoronix benchmarks on Fedora 31 (Linux 5.3 kernel) showed negligible performance impact for typical workloads, and Red Hat engineering has further reduced SELinux overhead by optimizing policy loading (from 1.3 seconds down to 106 milliseconds on Fedora), cutting kernel memory usage from approximately 30 MB to 15 MB, and reducing file creation latency from 55 to 44 microseconds. The security benefit is substantial.

A note on the "just disable SELinux" debate

There is a longstanding split in the Linux community over SELinux. A significant number of administrators -- including experienced ones -- routinely disable it as a first troubleshooting step, and many popular tutorials instruct readers to run setenforce 0 or set SELINUX=disabled in /etc/selinux/config. The reasoning is understandable: SELinux denials can be opaque, the tooling has a steep learning curve, and some third-party applications ship without SELinux policy support (some vendor documentation even instructs users to disable SELinux before installation). We are aware of this debate and take a firm position here: on a production server -- especially one subject to compliance requirements -- disabling SELinux removes a critical security layer that has prevented real-world privilege escalation chains. Red Hat's own support documentation explicitly states that disabling SELinux should only occur when your regulatory framework accepts that a class of security enforcement will be missing. The kernel itself has been progressively removing the ability to disable SELinux at runtime (the /sys/fs/selinux/disable sysfs interface was fully removed in kernel 6.4). The trajectory of the upstream project is clear: SELinux is intended to stay on. If an application breaks under SELinux, the correct response is permissive mode, audit log analysis, and targeted policy adjustment -- not disabling the entire framework.

Practical SELinux Management

Checking status:

terminal
$ sestatus
$ getenforce

Changing modes without reboot (temporary):

terminal
$ setenforce 0   # permissive
$ setenforce 1   # enforcing

Managing file contexts:

The most common SELinux problem in production is that a file is in the wrong location or has the wrong label, and a confined process cannot access it. The restorecon command applies the default label for a file's path:

terminal
# Label a new web root correctly
$ restorecon -Rv /var/www/html/

# Check what the policy says the label should be
$ matchpathcon /etc/nginx/nginx.conf

When you need a custom path that is not in the default policy, use semanage fcontext:

terminal
# Tell SELinux this non-standard path should be labeled like httpd content
$ semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?"
$ restorecon -Rv /srv/web

Managing booleans:

SELinux booleans are policy switches that allow administrators to enable or disable specific behaviors without writing custom policy. For example:

terminal
# Allow Apache to connect to the network (for reverse proxy use)
$ setsebool -P httpd_can_network_connect on

# Allow Apache to read home directories
$ setsebool -P httpd_read_user_content on

# List all booleans and their current values
$ getsebool -a | grep httpd

The -P flag makes the change persistent across reboots by writing to the policy store.

Reading and acting on audit logs:

AVC denials are logged to /var/log/audit/audit.log. The audit2why and ausearch tools parse these logs into human-readable explanations:

terminal
# Show all recent AVC denials
$ ausearch -m avc -ts recent

# Explain what a denial means
$ ausearch -m avc -ts recent | audit2why

# Generate a policy module to allow a set of denials
$ ausearch -m avc -ts recent | audit2allow -M mypolicy
$ semodule -i mypolicy.pp
Pro Tip

This workflow -- observe, understand, allow only what is needed -- is the correct approach to SELinux policy management. The temptation to disable SELinux when something breaks is the wrong reaction. Every denial is a data point about what your system's processes are trying to do, and understanding that is security-relevant information regardless of whether you ultimately allow the behavior.

A word of caution on audit2allow

There is a legitimate debate in the security community about the audit2allow workflow. The tool generates policy modules by analyzing AVC denials and producing allow rules for everything that was denied. The concern: if a denial was triggered by malicious activity -- a compromised process probing for access it should not have -- running audit2allow and loading the resulting module would grant permanent policy permission for that malicious behavior. This is why we emphasize the "review and refine" step. Never blindly pipe ausearch output through audit2allow and install the result on a production system without reading the generated policy. Examine what domains and types are being allowed, consider whether the access pattern is legitimate, and scope the policy as narrowly as possible. Security-conscious organizations generate policies in test environments and review them through a change management process before deploying to production. The tool is valuable, but it is an assistant for writing policy, not a replacement for understanding it.

FIPS 140-3 Mode -- Cryptographic Boundaries and What Changes When You Enable It

What FIPS 140-3 Actually Is

The Federal Information Processing Standard Publication 140, now in its third revision (FIPS 140-3), is a NIST standard that specifies security requirements for cryptographic modules. The standard is mandatory for any system processing U.S. federal government data, and under the Canadian Communications Security Establishment (CSE) for Canadian federal systems. FIPS 140-3 is also a prerequisite for compliance frameworks including CMMC, FedRAMP, HIPAA, and FISMA. The European Union's NIS 2 Directive, which covers network and information security across member states, similarly mandates current cryptographic standards, and as the AlmaLinux OS Foundation has noted, FIPS 140-3 certification is widely treated as evidence of compliance with these requirements.

Note

FIPS certification is not certification of an operating system. It is certification of specific cryptographic modules -- the kernel's crypto API, OpenSSL, GnuTLS, NSS, libgcrypt, and related libraries. Each module undergoes independent evaluation by a NIST-accredited laboratory (atsec information security corporation tested AlmaLinux's modules), and is issued a Cryptographic Module Validation Program (CMVP) certificate. The certificate is tied to a specific version of the module and the tested operational environment. The AlmaLinux 9.2 kernel was the first EL9 distribution to receive a FIPS 140-3 certificate for the kernel (CMVP #4750), and it was the first software implementation to receive a FIPS 140-3 ESV certificate using SHA3-256 as a conditioner. The OpenSSL FIPS Provider received CMVP certificate #4823. The entire FIPS 140-3 validation process for AlmaLinux 9.2 cost nearly $400,000, funded by CloudLinux as a Platinum sponsor of the AlmaLinux OS Foundation.

FIPS 140-3 is a prerequisite for other security regulations and acts of law such as CMMC, FedRAMP, HIPAA and FISMA; the certificates can also be used as evidence for complying with Common Criteria, SOX, ISO27001 and PCI-DSS.

-- Simon John, Security Certification Manager at CloudLinux, AlmaLinux FIPS Validation for AlmaLinux OS

What Enabling FIPS Mode Does to Your System

When you enable FIPS mode, the kernel and userspace cryptographic libraries enter a restricted operational mode with the following consequences:

Algorithm restrictions. Many algorithms considered weak or unvalidated are disabled. MD5 is prohibited for new key derivation. SHA-1 is restricted. RSA keys below 2048 bits are rejected. ChaCha20-Poly1305, while secure, is not FIPS-approved and is disabled. The Edwards-curve algorithms (Ed25519, X25519) are similarly non-approved in many FIPS contexts. You are left with AES (128, 192, 256-bit with approved modes including GCM, CBC, CTR), SHA-2 and SHA-3 hash families, RSA at 2048+ bits, ECDSA on NIST curves (P-256, P-384, P-521), and HMAC. It is worth noting that the status of algorithms like Ed25519 within FIPS is evolving -- NIST has been working toward standardizing EdDSA (FIPS 186-5 includes Ed25519 as an approved signature scheme), and future FIPS module validations may include these curves. But as of the current AlmaLinux 9.2 and 9.6 validated modules, the Edwards-curve algorithms remain outside the FIPS-approved boundary. If you encounter online discussions claiming Ed25519 is "FIPS-approved now," the nuance is that the algorithm is approved in the standard but individual module validations must explicitly include it -- and the AlmaLinux modules, validated against earlier algorithm lists, do not yet.

TLS cipher suite restrictions. With FIPS mode disabled, a standard nmap scan of an AlmaLinux web server shows weaker TLSv1.2 ciphersuites including non-compliant Edwards curves, SHA-1 hashes, and ChaCha20-Poly1305 ciphers. With FIPS mode enabled, only validated ciphersuites remain negotiable. TLSv1.0 and TLSv1.1 are disabled entirely.

Interactive: FIPS 140-3 Algorithm Status click to filter

Self-tests at module load. FIPS 140-3 requires that validated modules run Known-Answer Tests (KATs) when loaded, verifying that each implemented algorithm produces the expected output for a given input. If a KAT fails, the module will not initialize -- it is better to fail loudly than to operate with broken cryptography silently. There is a game-theoretic insight embedded in this design: the cost of a false-negative (a broken algorithm that silently produces wrong output) is catastrophic and undetectable, while the cost of a false-positive (a working algorithm that fails its self-test and refuses to load) is merely operational disruption. The KAT design deliberately biases toward the detectable failure mode. This is the same engineering principle behind circuit breakers in electrical systems: you design the failure to be obvious, not silent.

Integrity verification. The FIPS modules include HMAC-SHA-256 checksums of their own binary code. At load time, the module recomputes this checksum and compares it against the stored expected value. If a module has been tampered with -- patched, corrupted, or replaced -- the integrity check fails and the module refuses to load. For the OpenSSL FIPS Provider, this is stored in the fips_hmac file computed at build time. For other modules, .hmac files adjacent to the cryptographic object files serve the same function.

The Validation Lifecycle Problem

Here is a challenge that organizations frequently underestimate: FIPS certificates expire, and applying security patches to a validated module can invalidate its certificate. This creates a fundamental tension between security patching (which you must do to address CVEs) and FIPS compliance (which requires validated module versions).

A FIPS certificate, once obtained, is valid for five years. TuxCare's approach for AlmaLinux provides FIPS-compliant security patches: many vulnerabilities do not affect the cryptographic modules themselves, allowing organizations to maintain FIPS 140-3 compliance with security patches. If a cryptographic vulnerability arises, a re-certified package is delivered through a fast-track re-certification process that is considerably faster than the general FIPS 140-3 certification process.

The practical implication is that a FIPS-validated AlmaLinux deployment requires a strategy for managing this lifecycle. Upgrading to a new minor release will break your FIPS validation unless the new release has its own validated modules. This is why TuxCare offers Extended Security Updates (ESU) that lock an organization to specific minor releases while continuing to deliver security fixes that preserve the validation. AlmaLinux 9.2 has active FIPS 140-3 certificates (Kernel Crypto API #4750, OpenSSL #4823), with NSS, GnuTLS, and Libgcrypt also validated and on the NIST Active list. AlmaLinux 9.6 reached a significant milestone in October 2025, when four of its cryptographic modules -- Kernel Crypto API, OpenSSL, GnuTLS, and NSS -- were accepted onto the NIST CMVP Modules in Process (MIP) list, confirming that each has passed accredited lab testing. Libgcrypt entered the pipeline as the fifth module shortly afterward, having been re-validated for 9.2 and retested under the 9.6 operational environment. AlmaLinux 9.10 is the next planned validated target. Each ESU release includes at least a one-year overlap with the previous one, giving organizations a stable window to plan and execute upgrades.

TuxCare also offers KernelCare live patching for FIPS-validated AlmaLinux, making it the only FIPS 140-3 validated Linux distribution with rebootless kernel patching. This is significant for organizations with uptime requirements: KernelCare can apply kernel security patches without rebooting into a new kernel, eliminating the gap that typically exists between patch availability and patch application in regulated environments. Live patching is available for AlmaLinux 9.2 and 9.6 ESU releases.

Achieving MIP status for all four modules demonstrates clear progress on the path to delivering FIPS 140-3 certification for AlmaLinux 9.6.

-- Michael Canavan, Chief Revenue Officer at TuxCare, TuxCare FIPS 140-3 Update, October 2025

Installing and Enabling FIPS Mode

The FIPS-validated modules for AlmaLinux 9 come from the TuxCare repository. The community edition provides the kernel and OpenSSL packages at no cost; GnuTLS, NSS, and libgcrypt require a TuxCare ESU subscription for commercial use:

terminal
# Run all commands in this block as root (or prefix with sudo)

# Install the TuxCare FIPS repository
$ dnf -y install https://repo.tuxcare.com/fips/tuxcare-fips-release-latest-9.noarch.rpm

# Set the FIPS repository as the highest priority source for these packages
$ dnf config-manager --save --setopt="tuxcare-fips-community.priority=1"

# Install the validated kernel and OpenSSL packages
# (version numbers vary by AlmaLinux minor release)
# For AlmaLinux 9.6:
$ dnf -y install openssl-3.2.2-6.el9_6.1.tuxcare.6 kernel-5.14.0-570.21.1.el9_6.tuxcare.1

# Enable FIPS mode
$ fips-mode-setup --enable

# Reboot into the new kernel
$ reboot

After reboot, verify:

terminal
$ fips-mode-setup --check
# FIPS mode is enabled.

$ uname -r
# 5.14.0-570.21.1.el9_6.tuxcare.1.x86_64

# Verify the kernel itself reports FIPS enabled (1 = enabled)
$ cat /proc/sys/crypto/fips_enabled
# 1

# Confirm the system-wide crypto policy is FIPS
$ update-crypto-policies --show
# FIPS
Important

FIPS mode ideally should be enabled from the installer boot using the fips=1 kernel parameter, not as a post-installation step. Some configurations, particularly for environments requiring the highest assurance levels, are better achieved by starting the installation in approved mode. Post-installation enablement is supported and widely deployed, but if you are building a system that will process classified data, consult the security policy document for the validated module to understand any restrictions that apply.

A note on post-install FIPS enablement

This is an area where significant confusion exists online, and the upstream direction is moving fast. Red Hat's own RHEL 9 documentation explicitly states that fips-mode-setup does not guarantee compliance with the FIPS 140 standard, because cryptographic keys generated before FIPS mode was enabled may have used non-approved algorithms. Only enabling FIPS mode during installation ensures that all keys are generated with FIPS-approved algorithms and that continuous monitoring tests are in place from the start. For example, LUKS disk encryption chooses the Argon2 key derivation function during non-FIPS installation, but FIPS requires PBKDF2 -- a system encrypted without FIPS mode may be either non-compliant or unbootable after switching to FIPS. RHEL 10 has gone further and completely removed the fips-mode-setup tool, making install-time enablement the only supported path. Fedora 42 is doing the same. For AlmaLinux 9, post-installation enablement with fips-mode-setup --enable remains available and is the documented TuxCare workflow, but be aware that for the strictest compliance interpretations -- particularly if an auditor asks whether all cryptographic material was generated under FIPS -- install-time enablement with fips=1 is the defensible answer. If you are building new systems, the safest approach is to enable FIPS during installation rather than after the fact.

The Kernel Cryptographic Boundary

The FIPS validation for AlmaLinux's kernel module defines a specific cryptographic boundary. According to the NIST-published Security Policy document for certificate #4750:

The cryptographic boundary consists only of those APIs provided by the Kernel Crypto API. If any other API in the Linux kernel is invoked, the user is not interacting with the module specified in this Security Policy.

-- Kernel Cryptography Module for AlmaLinux 9, FIPS 140-3 Non-Proprietary Security Policy (CMVP #4750)

This distinction matters for developers: if you are writing kernel code or kernel modules that perform cryptographic operations, you must use the Kernel Crypto API (crypto_alloc_cipher, crypto_ahash, etc.) and not implement your own cryptographic primitives, even if those primitives would be correct. The FIPS boundary is enforced at the API level, not at the algorithm level.

CIS Benchmarks -- The Configuration Layer That Covers Everything Else

What CIS Benchmarks Are and Are Not

The Center for Internet Security (CIS) publishes benchmarks for hundreds of operating systems, applications, and platforms. These benchmarks are consensus-developed documents that translate security best practices into specific, testable configuration requirements. As described by the AlmaLinux CIS benchmark contributor Simon John:

The Center for Internet Security is a community-driven nonprofit, responsible for the CIS Controls and CIS Benchmarks, globally recognized best practices for securing IT systems and data.

-- Simon John, AlmaLinux CIS Benchmarks Update, November 2023

Many organizations use the CIS benchmarks as their baseline for security hardening, either as a starting point to build on or as a goal to aspire toward, with a percentage pass rate to achieve based on organizational policies.

Important distinctions: CIS Benchmarks are not regulatory requirements in themselves. They are not law. They are recommendations developed by practitioners, reviewed by the vendor community and security professionals, and published as free-to-download PDF documents. However, many regulatory frameworks -- PCI DSS, HIPAA, FedRAMP, CMMC -- explicitly reference or implicitly accept CIS Benchmark compliance as evidence of reasonable security practice. A Level 2 CIS Benchmark audit result is routinely accepted by auditors as documentation of system hardening. In practice, many organizations experience CIS as de facto mandatory because auditors treat non-compliance as a finding, even though the benchmark itself is technically a recommendation. If your organization is subject to third-party audits, treat CIS compliance as a practical requirement rather than an optional guideline, regardless of its formal legal status.

The AlmaLinux OS 9 benchmark is currently at v2.0.0, developed collaboratively with representatives from various distributions, end-users, and security professionals through weekly working group calls with CIS. Simon John, who has contributed to AlmaLinux CIS benchmarks since v1.0.0, noted that the AlmaLinux OS 9 v1.0.0 benchmark (published December 2022) was the upstream of the RHEL 9 v1.0.0 benchmark. In testing against a default AlmaLinux 9.4 installation for the Level 2 Server profile, the CIS Build Kit remediates more than 100 default settings that do not comply with benchmark guidance, and a follow-up scan by CIS-CAT Pro returns a pass result above 94%. That number -- over 100 non-compliant settings on a default installation -- is worth sitting with. A fresh AlmaLinux system, properly installed, is already a reasonable operating system. But the gap between "reasonable" and "hardened to enterprise standards" encompasses more than a hundred configuration decisions.

Level 1 vs. Level 2: Understanding the Profiles

The CIS benchmark defines two profiles:

Level 1 covers configuration recommendations that can be implemented without significant operational impact. These settings reduce attack surface, close unnecessary services, and enforce sensible defaults. They should be universally applicable to any AlmaLinux deployment.

Level 2 adds more restrictive settings appropriate for environments with strict security requirements. Some Level 2 controls trade operational convenience for a meaningfully reduced attack surface. For example, Level 2 requires disabling USB storage, restricting core dumps more aggressively, and implementing more restrictive audit policies.

Profile Scope Operational Impact Typical Use
Level 1 Universal baseline Minimal -- sensible defaults All enterprise AlmaLinux deployments
Level 2 Extended hardening Moderate -- trades convenience for security Environments with strict compliance or threat models
ScopeUniversal baseline
ImpactMinimal -- sensible defaults
UseAll enterprise AlmaLinux deployments
ScopeExtended hardening
ImpactModerate -- trades convenience for security
UseEnvironments with strict compliance or threat models

Key CIS Controls in Practice

Section 1: Filesystem and Partitioning

The CIS benchmark recommends dedicated mount points for /tmp, /var, /var/tmp, /var/log, /var/log/audit, and /home, each with restrictive mount options such as nodev, nosuid, and noexec. This limits blast radius: a filled /tmp cannot take down /var/log, and an attacker who writes a binary to /tmp cannot execute it.

Section 2: Software and Updates

terminal
# Ensure gpgcheck is globally enabled
$ grep ^gpgcheck /etc/dnf/dnf.conf
# gpgcheck=1

# Install and enable automatic security updates
$ dnf install dnf-automatic
$ systemctl enable --now dnf-automatic.timer

The DNF Automatic timer with appropriate configuration handles security updates without manual intervention. The relevant configuration in /etc/dnf/automatic.conf should set upgrade_type = security and apply_updates = yes for environments that want automatic patching, or apply_updates = no with emit_via = email for environments that require change management review before applying patches.

Section 3: Auditing and Logging

This is where the benchmark becomes operationally significant for incident response and forensics. The audit subsystem (auditd) records low-level system calls, file accesses, and authentication events in a format that is both tamper-evident and legally admissible.

Key audit rules required by the CIS benchmark:

/etc/audit/rules.d/cis.rules
# Monitor time-change events (tampering with system time)
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change

# Monitor user/group modifications
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity

# Monitor sudoers
-w /etc/sudoers -p wa -k scope
-w /etc/sudoers.d/ -p wa -k scope

# Monitor privileged command use
-a always,exit -F arch=b64 -F euid=0 -F auid>=1000 -F auid!=-1 -S execve -k privileged

# Ensure audit logs are not modifiable
-e 2

The final line, -e 2, puts the audit system in "immutable" mode: the audit rules cannot be modified without rebooting. This is critical for environments where audit log integrity is a compliance requirement.

Section 4: Access, Authentication, and Authorization

SSH hardening is one of the highest-impact controls in this section. The default sshd configuration in AlmaLinux 9 is reasonable, but the CIS benchmark tightens it further:

/etc/ssh/sshd_config
# /etc/ssh/sshd_config hardening for CIS compliance
LogLevel VERBOSE
MaxAuthTries 4
IgnoreRhosts yes
HostbasedAuthentication no
PermitRootLogin no
PermitEmptyPasswords no
PermitUserEnvironment no
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha256
ClientAliveInterval 300
ClientAliveCountMax 3
LoginGraceTime 60
Banner /etc/issue.net
AllowTcpForwarding no
X11Forwarding no
Note

If you have also enabled FIPS mode, the cipher and KexAlgorithm lists should be verified against what FIPS-mode OpenSSL will permit. curve25519-sha256@libssh.org may not be available in FIPS mode, and the allowed ciphers and key exchange algorithms will be restricted to the FIPS-approved list described in the FIPS section above.

PAM configuration is equally important. Password complexity and aging requirements are set in /etc/security/pwquality.conf and /etc/login.defs:

/etc/security/pwquality.conf
minlen = 14
minclass = 4
maxrepeat = 3
maxclassrepeat = 3
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
/etc/login.defs
PASS_MAX_DAYS   365
PASS_MIN_DAYS   1
PASS_WARN_AGE   7

Account lockout policy is configured via PAM's faillock module:

/etc/security/faillock.conf
deny = 5
fail_interval = 900
unlock_time = 900

Five failed login attempts in a 15-minute window triggers a lockout for 15 minutes. This mitigates brute-force attacks without permanently locking out legitimate users.

Section 5: Network Configuration

Many of the network controls in the CIS benchmark are kernel parameter settings applied via sysctl. A selection of the security-relevant settings:

/etc/sysctl.d/60-cis-hardening.conf
# Disable IP forwarding (unless this is a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Disable accepting ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0

# Enable reverse path filtering (prevent IP spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0

# Enable SYN cookies (mitigate SYN flood attacks)
net.ipv4.tcp_syncookies = 1

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Log martian packets
net.ipv4.conf.all.log_martians = 1

Apply with sysctl --system.

Section 6: System Maintenance

The benchmark requires that file permissions on critical system files are appropriate, that no world-writable files or directories exist without justification, and that SUID/SGID bits are audited:

terminal
# Find world-writable files
$ find / -xdev -type f -perm -002 -ls 2>/dev/null

# Find SUID programs
$ find / -xdev -type f -perm -4000 -ls 2>/dev/null

# Find SGID programs
$ find / -xdev -type f -perm -2000 -ls 2>/dev/null

Review the output of these commands. Every SUID binary is a potential privilege escalation vector. Unnecessary SUID bits should be removed with chmod u-s.

Automating CIS Assessment

Manual compliance assessment is error-prone and labor-intensive. The standard tool for automated CIS assessment is OpenSCAP, which AlmaLinux ships in its repositories:

terminal
$ dnf install openscap-scanner scap-security-guide

# List available profiles
$ oscap info /usr/share/xml/scap/ssg/content/ssg-almalinux9-ds.xml | grep -A2 'Profile'

# Run an assessment against the CIS Level 2 profile
$ oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis_server_l2 \
  --results /var/tmp/cis-results.xml \
  --report /var/tmp/cis-report.html \
  /usr/share/xml/scap/ssg/content/ssg-almalinux9-ds.xml

# Generate a remediation script from the results
$ oscap xccdf generate fix \
  --fix-type bash \
  --result-id "" \
  /var/tmp/cis-results.xml > /var/tmp/cis-remediation.sh
Caution

Review the remediation script before applying it. Automated remediation scripts make assumptions about your environment that may not hold -- particularly around partition layout, network configuration, and SSH access methods. A script that locks SSH configuration more tightly than intended can lock you out of your own server.

The Intersection -- Running All Three Simultaneously

Where SELinux and FIPS Interact

SELinux policy enforcement does not inherently depend on FIPS mode, but there are interactions worth understanding. When FIPS mode is active, SELinux's own policy store and the kernel's enforcement mechanisms continue to operate normally -- SELinux uses kernel data structures and system calls, not the userspace cryptographic libraries. The interaction primarily surfaces in the audit logs: if a FIPS-mode OpenSSL library is called to perform a prohibited operation (for example, an attempt to use MD5 for key derivation), the failure will appear as an application error rather than an SELinux denial. These two systems log to different channels (/var/log/audit/audit.log for SELinux, application logs and /var/log/messages for FIPS-related crypto failures), and you need to correlate both when troubleshooting.

Where FIPS and CIS Interact

The CIS Benchmark has specific controls around cryptographic configuration. If you have enabled FIPS mode, some of those controls are automatically satisfied by the FIPS policy itself, while others require additional verification. The SSH ciphers configured in sshd_config need to be consistent with what FIPS-mode OpenSSL permits. Attempting to configure a non-FIPS cipher in sshd on a FIPS-enabled system will cause sshd to refuse to start, which is the intended behavior -- the system is enforcing its cryptographic policy at the subsystem level.

System-wide crypto policies (update-crypto-policies) coordinate these constraints. Setting the policy to FIPS applies a consistent set of restrictions across OpenSSL, GnuTLS, NSS, and the kernel TLS implementation:

terminal
$ update-crypto-policies --set FIPS
# then verify:
$ update-crypto-policies --show

After installing FIPS mode via fips-mode-setup --enable, the system's crypto policy is also set to FIPS automatically.

A common misconception: crypto policy vs. FIPS validation

A frequent source of confusion -- and you will find conflicting advice on this across forums, vendor documentation, and even some compliance guides -- is the difference between running update-crypto-policies --set FIPS and having an actually FIPS-validated system. Setting the crypto policy to FIPS restricts the algorithms your system will negotiate, which is useful and security-positive. But it is not the same as running FIPS-validated cryptographic modules. As Simon John noted in the original AlmaLinux FIPS blog post: claiming FIPS compliance just by running update-crypto-policies --set FIPS is not enough for regulated industries -- you need formally validated modules with CMVP certificates to show an auditor. Think of it like a university degree: you can claim the knowledge, but an auditor will ask to see the diploma. The TuxCare FIPS packages provide that diploma -- specific versions of the kernel, OpenSSL, GnuTLS, NSS, and libgcrypt that have been tested by an accredited lab and issued NIST certificates.

Where SELinux and CIS Interact

CIS Benchmark control 1.6.1.1 requires SELinux to be installed and enabled. Control 1.6.1.2 requires the targeted or stricter policy type to be active. Control 1.6.1.3 requires enforcing mode. These are not optional. An organization claiming CIS Level 2 compliance on an AlmaLinux system with SELinux disabled is making a false claim.

Beyond those explicit controls, SELinux provides defense-in-depth that compensates for other potential gaps. If a file permission is misconfigured and allows a process broader DAC access than intended, SELinux's type enforcement can still prevent exploitation. The layers reinforce each other.

The DISA STIG -- A Higher Standard

For organizations in or serving the U.S. Department of Defense, the Defense Information Systems Agency (DISA) publishes Security Technical Implementation Guides (STIGs). The AlmaLinux OS 9 STIG was published by DISA in February 2025, with TuxCare contributing over a year of work to its development. The STIG requirements are derived from NIST 800-53, and the current release is v1r5 (updated March 2026). FIPS cryptographic module use is mandatory with a STIG, as are stringent policies including SmartCard authentication, CPU/RAM protection, LUKS full disk encryption, and the use of USBGuard, which prevents unauthorized USB device connections.

A STIG is a set of secure configuration standards for using a product within the US Department of Defence and associated networks. It is probably the highest level of security hardening guidance there is, covering practices like AAA, DLP, physical and logical access control and least-privilege.

-- Simon John, Securing the Future: FIPS 140-3 Validation and the DISA STIG for AlmaLinux OS, March 2025

The STIG can be thought of as CIS Level 2 plus additional controls specific to DoD threat models. STIG findings use three severity categories: a Category I (CAT I) finding could result in loss of life, a CAT II could result in injury or loss of confidentiality, integrity, or availability, and a CAT III could result in disaster recovery delays. Whether the STIG represents the "highest level" of hardening guidance is a point worth addressing. For publicly available, freely downloadable hardening guidance, the STIG is as stringent as it gets. However, classified environments often apply additional restrictions beyond what the STIG specifies -- custom policies, network isolation requirements, and access controls that are not published. The STIG is the highest publicly available standard, not necessarily the most restrictive configuration that exists. That said, for the vast majority of organizations -- including those in defense, healthcare, finance, and government contracting -- the STIG exceeds what is practically needed and represents a ceiling rather than a floor. The STIG is freely available from the Department of Defense Cyber Exchange, and DISA provides SCAP content and Ansible automation for STIG compliance, making automated assessment and remediation feasible even for large fleets.

Conclusion: Security as Architecture

Interactive: Attack Scenario -- How Three Layers Respond

Walk through a realistic attack scenario. At each step, see which security layer activates and what it does. Click each phase to advance.

Click a phase above to walk through the attack scenario. Each phase shows which of the three security layers -- SELinux, FIPS, CIS -- responds and how.

The three layers described in this article are not independent checkboxes. They are mutually reinforcing architectural decisions. SELinux enforces least-privilege process behavior that persists even when applications have bugs or misconfigurations. FIPS mode ensures that every cryptographic operation the system performs uses validated algorithms and implementations, removing an entire class of cryptographic downgrade and implementation vulnerability. CIS Benchmark compliance addresses the surface area that neither SELinux nor FIPS covers: the hundreds of sysctl settings, file permissions, authentication parameters, and service configurations that together determine the system's actual security posture.

There is a philosophical tension embedded in this work that is worth naming directly: every security control makes a system harder to operate. SELinux denials block legitimate operations until policy is adjusted. FIPS mode disables algorithms that applications may depend on. CIS hardening removes conveniences that administrators have relied on for years. The instinct to disable these controls when they cause friction is not irrational -- it is a rational response to the immediate cost of the control weighed against the hypothetical cost of the threat it mitigates. The reason security professionals insist on leaving them enabled is not because the friction is imaginary, but because the cost of a breach is asymmetric: the inconvenience of an SELinux denial is recoverable in minutes; the damage from a privilege escalation that SELinux would have prevented can take months or years to remediate, and may never fully resolve. Mature security operations are not about eliminating friction -- they are about making the friction legible, expected, and manageable.

The deeper insight is this: compliance is a minimum floor, not a ceiling. Running a CIS Level 2 audit that returns 94% pass does not mean your system is secure -- it means your system meets a consensus baseline developed by practitioners for typical threat models. Your specific threat model may require additional controls. Your specific applications may require SELinux policy customization. Your specific regulatory obligations may require a STIG on top of a CIS baseline on top of FIPS mode.

The correct posture is to understand what each of these mechanisms does at the kernel level, in the cryptographic module, and in the configuration file, so that when something breaks -- and something will break -- you can reason about it from first principles rather than running through a troubleshooting checklist without comprehension. An administrator who understands SELinux type enforcement can debug a novel AVC denial in minutes. An administrator who has only ever run setenforce 0 as a fix will disable the system's most important access control mechanism and not understand the consequences.

AlmaLinux provides a free, openly governed, enterprise-grade platform with access to FIPS 140-3 validated cryptographic modules (the first EL9 distribution to achieve FIPS 140-3 kernel certification), mature SELinux tooling inherited from decades of RHEL development, and actively maintained CIS Benchmark and DISA STIG support. The capability is there. The work of making it operational -- and understanding it deeply enough to maintain it -- belongs to the people responsible for these systems.

How to Harden AlmaLinux for Enterprise Security

Step 1: Verify and Enforce SELinux

Run sestatus to confirm SELinux is installed and in enforcing mode with the targeted policy. If the mode is permissive or disabled, edit /etc/selinux/config to set SELINUX=enforcing and SELINUXTYPE=targeted, then reboot. After reboot, use getenforce to confirm enforcing mode is active. Review any AVC denials in /var/log/audit/audit.log using ausearch and audit2why before deploying applications.

Step 2: Enable FIPS 140-3 Mode

Install the TuxCare FIPS repository with dnf, set its priority to 1, and install the validated kernel and OpenSSL packages for your AlmaLinux minor release. Run fips-mode-setup --enable and reboot into the new kernel. After reboot, verify with fips-mode-setup --check, confirm the kernel reports FIPS enforcement via cat /proc/sys/crypto/fips_enabled (should return 1), and confirm the system-wide crypto policy is set to FIPS with update-crypto-policies --show.

Step 3: Run a CIS Benchmark Assessment with OpenSCAP

Install openscap-scanner and scap-security-guide from the AlmaLinux repositories. Run oscap xccdf eval with the CIS Level 2 Server profile against the ssg-almalinux9 datastream to generate an HTML report. Review the report, generate a remediation script with oscap xccdf generate fix, and review the script before applying it to avoid locking yourself out of SSH or breaking partition assumptions.

Step 4: Validate the Intersection of All Three Layers

After enabling all three layers, verify that SSH ciphers in sshd_config are consistent with FIPS-mode restrictions. Confirm SELinux is required by CIS controls 1.6.1.1 through 1.6.1.3. Run a second OpenSCAP assessment to verify your pass rate, and correlate both audit.log (SELinux denials) and application logs (FIPS crypto failures) to ensure no conflicts exist between the layers.

Frequently Asked Questions

What is the difference between SELinux targeted and MLS policy on AlmaLinux?

Targeted policy confines a specific set of high-risk, network-facing processes while leaving user processes unconfined. MLS (Multi-Level Security) policy implements the Bell-LaPadula model with formal sensitivity levels and compartment categories, enforcing strict no-read-up and no-write-down rules. Targeted policy is appropriate for the vast majority of enterprise deployments, while MLS is typically reserved for classified government environments.

Does enabling FIPS mode on AlmaLinux affect SELinux enforcement?

SELinux policy enforcement does not depend on FIPS mode. SELinux uses kernel data structures and system calls rather than the userspace cryptographic libraries that FIPS mode constrains. The interaction primarily surfaces in logging: FIPS-related cryptographic failures appear in application logs and /var/log/messages, while SELinux denials appear in /var/log/audit/audit.log. You need to correlate both channels when troubleshooting.

How do I verify FIPS 140-3 mode is active after enabling it on AlmaLinux?

After rebooting, run fips-mode-setup --check, which should report that FIPS mode is enabled. Confirm the kernel itself is reporting FIPS enforcement with cat /proc/sys/crypto/fips_enabled, which should return 1. You can also verify the kernel version with uname -r to confirm you booted into the TuxCare FIPS-validated kernel. Additionally, update-crypto-policies --show should return FIPS, confirming that the system-wide cryptographic policy is correctly set.

What percentage of CIS Benchmark controls does a default AlmaLinux installation pass?

A default AlmaLinux 9.4 installation has more than 100 settings that do not comply with the CIS Level 2 Server profile. After remediation with the CIS Build Kit, a follow-up scan by CIS-CAT Pro returns a pass result above 94 percent. The gap between a default installation and a hardened one encompasses configuration decisions across partitioning, auditing, SSH, PAM, network parameters, and file permissions.

Sources and References

Technical details in this guide are drawn from official documentation and verified sources.