CoreOS CIS Hardening with Ignition Configuration
This guide provides a comprehensive Ignition configuration for hardening Fedora CoreOS (FCOS) according to the CIS Distribution Independent Linux Benchmark. The configuration implements security controls across filesystem partitioning, kernel parameters, network settings, SSH hardening, and system security policies.
Overview
Fedora CoreOS is an automatically updating, minimal operating system for running containerized workloads. While it provides excellent security defaults, additional hardening according to CIS (Center for Internet Security) benchmarks ensures compliance with industry security standards.
This Ignition configuration implements:
- Filesystem partitioning and mount options
- Kernel security parameters
- Network security settings
- SSH server hardening
- Password policies and authentication controls
- Firewall configuration with nftables
- System security policies
Prerequisites
- Fedora CoreOS installation media or cloud image
- Understanding of Ignition configuration format
- Access to modify boot parameters or cloud-init data
- Basic knowledge of Linux security concepts
Complete Ignition Configuration
Here’s the comprehensive Ignition configuration implementing CIS controls:
variant: fcosversion: 1.4.0storage: disks: - device: /dev/sda wipe_table: true partitions: # 1.1.6 Ensure separate partition exists for /var - label: VAR number: 1 size_mib: 4096 # 1.1.11 Ensure separate partition exists for /var/log - label: LOG number: 2 size_mib: 4096 # 1.1.12 Ensure separate partition exists for /var/log/audit - label: AUDIT number: 3 size_mib: 4096 # 1.1.13 Ensure separate partition exists for /home - label: HOME number: 4 size_mib: 4096
filesystems: # 1.1.6 Ensure separate partition exists for /var - path: /var device: /dev/disk/by-partlabel/VAR format: ext4 label: VAR wipe_filesystem: true
# 1.1.11 Ensure separate partition exists for /var/log - path: /var/log device: /dev/disk/by-partlabel/LOG format: ext4 label: LOG wipe_filesystem: true
# 1.1.12 Ensure separate partition exists for /var/log/audit - path: /var/log/audit device: /dev/disk/by-partlabel/AUDIT format: ext4 label: AUDIT wipe_filesystem: true
# 1.1.13 Ensure separate partition exists for /home - path: /home device: /dev/disk/by-partlabel/HOME format: ext4 label: HOME wipe_filesystem: true
files: # 1.1.17 Ensure noexec option set on /dev/shm partition - path: /etc/fstab mode: 0644 overwrite: true contents: inline: | tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0
# 1.6.1.2 Ensure the SELinux state is enforcing - path: /etc/selinux/config mode: 0644 overwrite: true contents: inline: | SELINUX=enforcing SELINUXTYPE=targeted
# 1.7.1.2 Ensure local login warning banner is configured properly - path: /etc/issue mode: 0644 overwrite: true contents: inline: | Authorized uses only. All activity may be monitored and reported.
# 1.7.1.3 Ensure remote login warning banner is configured properly - path: /etc/issue.net mode: 0644 overwrite: true contents: inline: | Authorized uses only. All activity may be monitored and reported.
# Kernel module blacklisting for security - path: /etc/modprobe.d/cis.conf mode: 0600 overwrite: true contents: inline: | install cramfs /bin/true install freevxfs /bin/true install jffs2 /bin/true install hfs /bin/true install hfsplus /bin/true install squashfs /bin/true install udf /bin/true install dccp /bin/true install sctp /bin/true install rds /bin/true install tipc /bin/true
# Network security settings - path: /etc/sysctl.d/cis.conf mode: 0600 overwrite: true contents: inline: | # IPv4 settings net.ipv4.ip_forward = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.all.secure_redirects = 0 net.ipv4.conf.default.secure_redirects = 0 net.ipv4.conf.all.log_martians = 1 net.ipv4.conf.default.log_martians = 1 # Disable source routing net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 # IPv6 settings net.ipv6.conf.all.accept_ra = 0 net.ipv6.conf.default.accept_ra = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv6.conf.default.accept_redirects = 0 net.ipv6.conf.all.accept_source_route = 0 net.ipv6.conf.default.accept_source_route = 0 # Additional recommended settings kernel.randomize_va_space = 2
# 5.6 Ensure access to the su command is restricted - path: /etc/pam.d/su mode: 0644 overwrite: true contents: inline: | auth sufficient pam_rootok.so auth required pam_wheel.so debug use_uid auth required pam_unix.so account required pam_unix.so session required pam_unix.so
# Password quality and aging settings - path: /etc/pam.d/system-auth mode: 0644 overwrite: true contents: inline: | auth required pam_env.so auth sufficient pam_unix.so try_first_pass likeauth nullok auth sufficient pam_sss.so use_first_pass auth required pam_deny.so account required pam_unix.so account required pam_sss.so ignore_unknown_user ignore_authinfo_unavail account optional pam_permit.so password required pam_pwhistory.so remember=5 password required pam_pwquality.so retry=3 minlen=14 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1 password sufficient pam_unix.so use_authtok try_first_pass nullok sha512 shadow password sufficient pam_sss.so use_authtok password required pam_deny.so session required pam_limits.so session required pam_env.so session required pam_unix.so session optional pam_permit.so session optional pam_sss.so -session optional pam_systemd.so
# CIS hardening script - path: /usr/local/bin/cis-hardener.sh mode: 0700 overwrite: true contents: inline: | #!/bin/bash # CIS Distribution Independent Linux Benchmark hardening script
echo "Running CIS hardening script..."
# 6.2.8 Ensure users' home directories permissions are 750 or more restrictive echo "Setting secure home directory permissions..." find /home -type d -exec chmod 750 {} \;
# 5.4.4 Ensure default user umask is 027 or more restrictive echo "Setting secure umask defaults..." sed -i '/^umask/c\# CIS 5.4.4 - Secure umask\numask 027' /etc/profile sed -i '/^UMASK/c\# CIS 5.4.4 - Secure umask\nUMASK 027' /etc/login.defs
# 5.4.1.4 Ensure inactive password lock is 30 days or less echo "Setting password aging controls..." useradd -D -f 30 sed -i '/^PASS_MAX_DAYS/c\# CIS 5.4.1.1\nPASS_MAX_DAYS 90' /etc/login.defs sed -i '/^PASS_MIN_DAYS/c\# CIS 5.4.1.2\nPASS_MIN_DAYS 7' /etc/login.defs sed -i '/^PASS_WARN_AGE/c\# CIS 5.4.1.3\nPASS_WARN_AGE 7' /etc/login.defs
# Apply password settings to existing users for user in $(awk -F: '($3 >= 1000) && ($7 != "/sbin/nologin") {print $1}' /etc/passwd); do echo "Updating password aging for user: $user" chage --inactive 30 --maxdays 90 --mindays 7 --warndays 7 "$user" done
# 5.2 SSH Server Configuration echo "Hardening SSH configuration..."
# Ensure we have a backup of the original config if [ ! -f /etc/ssh/sshd_config.orig ]; then cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig fi
# Set hardened SSH configuration cat > /etc/ssh/sshd_config << 'EOF' # SSH configuration hardened according to CIS Distribution Independent Linux Benchmark
# 5.2.1 Permissions handled separately # 5.2.2 Ensure SSH Protocol is set to 2 Protocol 2
# Basic SSH settings Port 22 AddressFamily any ListenAddress 0.0.0.0
# 5.2.3 Ensure SSH LogLevel is set to INFO LogLevel INFO
# 5.2.4 Ensure SSH MaxAuthTries is set to 4 or less MaxAuthTries 4
# 5.2.5 Ensure SSH IgnoreRhosts is enabled IgnoreRhosts yes
# 5.2.6 Ensure SSH HostbasedAuthentication is disabled HostbasedAuthentication no
# 5.2.7 Ensure SSH root login is disabled PermitRootLogin no
# 5.2.8 Ensure SSH PermitEmptyPasswords is disabled PermitEmptyPasswords no
# 5.2.9 Ensure SSH PermitUserEnvironment is disabled PermitUserEnvironment no
# 5.2.10 Ensure only strong ciphers are used Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# Ensure only strong MACs are used MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
# Ensure only strong key exchange algorithms are used KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
# 5.2.11 Ensure Idle Timeout Interval is configured ClientAliveInterval 300 ClientAliveCountMax 0
# 5.2.12 Ensure SSH LoginGraceTime is set to one minute or less LoginGraceTime 60
# 5.2.15 Ensure SSH warning banner is configured Banner /etc/issue.net
# Additional security settings X11Forwarding no UsePAM yes PrintMotd no
# Allow specific users only - to be customized based on environment # AllowUsers core
# Subsystem for SFTP Subsystem sftp internal-sftp EOF
# Fix permissions on SSH config chmod 600 /etc/ssh/sshd_config
# 3.6 Firewall configuration with nftables (modern replacement for iptables) echo "Configuring firewall with nftables..."
# Create nftables config file cat > /etc/nftables.conf << 'EOF' #!/usr/sbin/nft -f
flush ruleset
table inet filter { # Base chain for incoming packets chain input { type filter hook input priority 0; policy drop;
# Accept established/related connections ct state established,related accept
# Accept loopback traffic iif lo accept
# Drop invalid packets ct state invalid drop
# Accept ICMP and IGMP ip protocol icmp accept ip6 nexthdr icmpv6 accept
# Accept SSH on port 22 tcp dport 22 ct state new accept
# Log and drop all other traffic log prefix "NFT-INPUT-DROP: " limit rate 5/minute drop }
# Base chain for outgoing packets chain output { type filter hook output priority 0; policy drop;
# Accept established/related connections ct state established,related accept
# Accept loopback traffic oif lo accept
# Allow DNS queries udp dport 53 ct state new accept tcp dport 53 ct state new accept
# Allow outbound HTTP/HTTPS tcp dport { 80, 443 } ct state new accept
# Allow NTP udp dport 123 ct state new accept
# Log and drop all other traffic log prefix "NFT-OUTPUT-DROP: " limit rate 5/minute drop }
# Base chain for forwarded packets chain forward { type filter hook forward priority 0; policy drop;
# Accept established/related connections ct state established,related accept
# Log and drop all other traffic log prefix "NFT-FORWARD-DROP: " limit rate 5/minute drop } } EOF
# Secure nftables config file chmod 600 /etc/nftables.conf
# Enable nftables service if [ -x "$(command -v systemctl)" ]; then systemctl enable nftables fi
echo "CIS hardening complete" exit 0
systemd: units: # 1.1.5 Ensure noexec option set on /tmp partition - name: tmp.mount enabled: true contents: | [Unit] Description=Temporary Directory (/tmp) Documentation=man:hier(7) Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target
[Mount] What=tmpfs Where=/tmp Type=tmpfs Options=mode=1777,strictatime,nosuid,nodev,noexec
[Install] WantedBy=local-fs.target
# 1.1.7 Ensure separate partition exists for /var/tmp with security options - name: var-tmp.mount enabled: true contents: | [Unit] Description=Temporary Directory (/var/tmp) Documentation=man:hier(7) DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target After=swap.target
[Mount] What=tmpfs Where=/var/tmp Type=tmpfs Options=mode=1777,strictatime,nosuid,nodev,noexec
[Install] WantedBy=local-fs.target
# 1.1.6 Ensure separate partition exists for /var - name: var.mount enabled: true contents: | [Unit] Description=/var Directory DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target
[Mount] What=/dev/disk/by-label/VAR Where=/var Type=ext4 Options=defaults
[Install] WantedBy=local-fs.target
# 1.1.11 Ensure separate partition exists for /var/log - name: var-log.mount enabled: true contents: | [Unit] Description=/var/log Directory DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target After=var.mount
[Mount] What=/dev/disk/by-label/LOG Where=/var/log Type=ext4 Options=defaults
[Install] WantedBy=local-fs.target
# 1.1.12 Ensure separate partition exists for /var/log/audit - name: var-log-audit.mount enabled: true contents: | [Unit] Description=/var/log/audit Directory DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target After=var-log.mount
[Mount] What=/dev/disk/by-label/AUDIT Where=/var/log/audit Type=ext4 Options=defaults
[Install] WantedBy=local-fs.target
# 1.1.13 Ensure separate partition exists for /home # 1.1.14 Ensure nodev option set on /home partition - name: home.mount enabled: true contents: | [Unit] Description=/home Directory DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target
[Mount] What=/dev/disk/by-label/HOME Where=/home Type=ext4 Options=defaults,nodev
[Install] WantedBy=local-fs.target
# Run the hardener script at first boot - name: cis-hardener.service enabled: true contents: | [Unit] Description=CIS Hardening Service ConditionFirstBoot=yes After=network.target
[Service] Type=oneshot ExecStart=/usr/local/bin/cis-hardener.sh RemainAfterExit=yes
[Install] WantedBy=multi-user.target
# Enable and start firewall - name: nftables.service enabled: true contents: | [Unit] Description=nftables firewall service Documentation=man:nft(8) Wants=network-pre.target Before=network-pre.target
[Service] Type=oneshot ExecStart=/usr/sbin/nft -f /etc/nftables.conf ExecReload=/usr/sbin/nft -f /etc/nftables.conf ExecStop=/usr/sbin/nft flush ruleset RemainAfterExit=yes
[Install] WantedBy=multi-user.target
passwd: # 1.4.3 Ensure authentication required for single user mode # IMPORTANT: Change this default password in production environments! users: - name: root password_hash: "$6$rounds=4096$J86aZz4zInvxG$YZ8j2Kh.Z/7rCF1Kj9tFQawixQySGcUnZi7mlkKRJbZ9Pu4pJLrSWfFsRgPGu0qKgjLFJW2r7w0n6XUggXVDT1" home_dir: /root no_create_home: true shell: /bin/bash groups: - wheel - sudo
Key Security Controls Implemented
1. Filesystem Partitioning (CIS Section 1.1)
The configuration creates separate partitions for:
/var
- Variable data (4GB)/var/log
- Log files (4GB)/var/log/audit
- Audit logs (4GB)/home
- User home directories (4GB)
Mount options enforce:
noexec
on/tmp
and/var/tmp
- Prevents execution from temporary directoriesnodev
on/home
- Prevents device files in user directoriesnosuid
on temporary filesystems - Prevents setuid binaries
2. Kernel Security Parameters (CIS Section 3.2)
Network security hardening includes:
- Disabled IP forwarding
- Blocked ICMP redirects
- Enabled martian packet logging
- Disabled source routing
- IPv6 security hardening
Additional kernel parameters:
kernel.randomize_va_space = 2
- Full ASLR randomization
3. Module Blacklisting (CIS Section 1.1.1)
Disabled unnecessary filesystems:
- cramfs, freevxfs, jffs2, hfs, hfsplus
- squashfs, udf
Disabled unnecessary network protocols:
- dccp, sctp, rds, tipc
4. SSH Hardening (CIS Section 5.2)
Comprehensive SSH security:
- Protocol 2 only
- Root login disabled
- Empty passwords disabled
- Strong ciphers only (ChaCha20, AES-GCM)
- Strong MACs (HMAC-SHA2)
- Strong key exchange (Curve25519, DH Group 16/18)
- Idle timeout (5 minutes)
- Login grace time (60 seconds)
- Warning banners configured
5. Password Policies (CIS Section 5.4)
Strong password requirements:
- Minimum length: 14 characters
- Complexity: At least one digit, uppercase, lowercase, special character
- History: Remember last 5 passwords
- Aging: 90 days maximum, 7 days minimum
- Inactive lock: 30 days
6. Access Controls (CIS Section 5.6)
su
command restricted to wheel group- SELinux enforcing mode
- Secure umask (027)
- Home directory permissions (750)
7. Firewall Configuration (CIS Section 3.6)
nftables rules implement:
- Default deny policy
- Stateful connection tracking
- Minimal allowed services (SSH only inbound)
- Outbound restrictions (DNS, HTTP/S, NTP only)
- Logging of dropped packets
Deployment Instructions
1. Convert to Ignition Format
Save the YAML configuration as coreos-hardened.yml
and convert:
# Install Butane if not already installedpodman pull quay.io/coreos/butane:release
# Convert YAML to Ignitionpodman run --rm -v .:/data:z quay.io/coreos/butane:release \ --pretty --strict /data/coreos-hardened.yml > coreos-hardened.ign
2. Deploy with Ignition
For bare metal:
sudo coreos-installer install /dev/sda \ --ignition-file coreos-hardened.ign
For cloud deployments, provide the Ignition config via user-data.
3. Verify Hardening
After deployment, verify the hardening:
# Check partitionsdf -h
# Verify mount optionsmount | grep -E "(tmp|home|var)"
# Check SSH configurationsshd -T | grep -E "(permit|protocol|ciphers)"
# Verify kernel parameterssysctl -a | grep -E "(forward|redirect|martian)"
# Check firewall rulesnft list ruleset
Customization Considerations
1. Partition Sizes
Adjust partition sizes based on your needs:
partitions: - label: VAR size_mib: 8192 # 8GB for larger deployments
2. Network Access
Modify firewall rules for your services:
# Add HTTPS servicetcp dport 443 ct state new accept
# Add Kubernetes APItcp dport 6443 ct state new accept
3. User Management
Add regular users instead of using root:
passwd: users: - name: admin ssh_authorized_keys: - "ssh-rsa AAAAB3..." groups: - wheel - systemd-journal
4. Additional Services
For container workloads, add:
# Allow container registry accesstcp dport 5000 ct state new accept
# Allow Kubernetes pod networkip saddr 10.0.0.0/8 accept
Monitoring and Compliance
1. Audit Configuration
Enable comprehensive auditing:
- path: /etc/audit/rules.d/cis.rules mode: 0640 contents: inline: | -w /etc/passwd -p wa -k identity -w /etc/group -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/sudoers -p wa -k scope
2. Log Monitoring
Configure log forwarding:
- path: /etc/rsyslog.d/50-default.conf contents: inline: | *.* @@remote-syslog-server:514
3. Compliance Scanning
Use tools like OpenSCAP:
# Install OpenSCAPrpm-ostree install openscap-scanner
# Run CIS scanoscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis \ /usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml
Security Maintenance
1. Regular Updates
CoreOS automatically updates, but monitor:
# Check update statusrpm-ostree status
# View staged updatesrpm-ostree upgrade --preview
2. Security Reviews
Regularly review:
- User accounts and permissions
- Firewall rules
- SSH access logs
- Failed authentication attempts
3. Incident Response
Prepare for security incidents:
- Enable audit logging
- Configure centralized log collection
- Document emergency procedures
- Test recovery processes
Conclusion
This Ignition configuration provides a solid security foundation for Fedora CoreOS deployments following CIS benchmark recommendations. The layered security approach addresses:
- Filesystem security through partitioning and mount options
- Network security via kernel parameters and firewall rules
- Access control through SSH hardening and authentication policies
- System security with SELinux and module restrictions
Regular monitoring, updates, and security reviews ensure ongoing compliance and protection against evolving threats. Customize the configuration based on your specific requirements while maintaining the security principles established by the CIS benchmarks.