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        - sudoKey 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:
noexecon/tmpand/var/tmp- Prevents execution from temporary directoriesnodevon/home- Prevents device files in user directoriesnosuidon 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)
sucommand 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.ign2. Deploy with Ignition
For bare metal:
sudo coreos-installer install /dev/sda \  --ignition-file coreos-hardened.ignFor 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 rulesetCustomization Considerations
1. Partition Sizes
Adjust partition sizes based on your needs:
partitions:  - label: VAR    size_mib: 8192 # 8GB for larger deployments2. 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 accept3. User Management
Add regular users instead of using root:
passwd:  users:    - name: admin      ssh_authorized_keys:        - "ssh-rsa AAAAB3..."      groups:        - wheel        - systemd-journal4. 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 acceptMonitoring 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 scope2. Log Monitoring
Configure log forwarding:
- path: /etc/rsyslog.d/50-default.conf  contents:    inline: |      *.* @@remote-syslog-server:5143. 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.xmlSecurity Maintenance
1. Regular Updates
CoreOS automatically updates, but monitor:
# Check update statusrpm-ostree status
# View staged updatesrpm-ostree upgrade --preview2. 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.