Signal9Signal9 — home
online
all lessons

CIS Controls in Config Files: Core Dumps, cron, Modules, sysctl and TLS

Intermediate35 min7 steps
What you'll learn
  • Explain how a CIS control is audited: a persistent setting in a file, plus an effective value you read back
  • Restrict core dumps three ways: a pam_limits hard limit, fs.suid_dumpable = 0 in /etc/sysctl.d, and systemd-coredump Storage=none, and verify the limit in a new login shell
  • Allow-list cron and at with cron.allow / at.allow, know which file wins, and set safe ownership and modes on /etc/crontab and the /etc/cron.* directories
  • Stop unused filesystem modules from loading with install <module> /bin/false and blacklist in /etc/modprobe.d, and verify with modprobe -n -v
  • Persist the CIS network sysctls (redirects, source routing, martians, broadcast echo, rp_filter) in /etc/sysctl.d, predict which file wins, and know when sysctl --system or sysctl -p applies them
  • Set a system-wide OpenSSL floor (MinProtocol, CipherString @SECLEVEL) in openssl.cnf, prove OpenSSL reads it, and recognise RHEL's update-crypto-policies equivalent
  1. How a CIS control is checked

    Separate the persistent setting a scanner reads from the effective value the system uses right now.

    The CIS Benchmarks are consensus hardening checklists: a few hundred numbered controls per distribution, each with an audit (how to prove it) and a remediation (how to fix it). A scanner such as CIS-CAT or OpenSCAP walks that list on your host and marks each control pass or fail.

    For the controls in this lesson the audit reads configuration files, because the file is what survives a reboot. A value you set live with sysctl -w and never wrote down is gone at the next boot, and the next scan fails it again. So every control here has the same two halves the hardening guide taught: the persistent setting (one line in the right file) and the effective value (what the running system does now). Write the first, then prove the second with the tool that reads it back.

    The five families below come from the CIS Ubuntu 22.04 benchmark and its STIG neighbours: core dumps, cron access, unused kernel modules, network kernel parameters and the system-wide TLS policy. Each step shows where the setting lives, the exact line, and how to check it.

    Check yourself

    A colleague runs sysctl -w net.ipv4.conf.all.accept_redirects=0, sees sysctl -n print 0, and closes the CIS finding. The next week's scan fails it again after a reboot. What was missing?

  2. Core dumps: a hard limit, a sysctl and systemd-coredump

    Forbid core dumps for ordinary logins and setuid programs, and verify the limit where it applies.

    A core dump is a copy of a crashed process's memory written to disk. That memory can hold whatever the process was handling: private keys, session tokens, password hashes read from /etc/shadow. CIS closes it in three places, because three different mechanisms produce dumps:

    • *` hard core 0** in /etc/security/limits.conf (or a .conf` file in `/etc/security/limits.d/`, which `pam_limits` also reads). A hard* limit is a ceiling the user can't raise, so ulimit -c unlimited fails. Some scanners only read limits.conf itself, so when the benchmark names that file, put the line there.
    • `fs.suid_dumpable = 0` in a file under /etc/sysctl.d/. It decides whether a process that changed privilege (a setuid program such as passwd) may dump at all: 0 never, 1 always (debugging only), 2 only as a root-readable file.
    • `Storage=none` in the [Coredump] section of /etc/systemd/coredump.conf, for hosts where systemd-coredump catches crashes and stores them under /var/lib/systemd/coredump. ProcessSizeMax=0 stops it from even processing one.
    echo '* hard core 0' >> /etc/security/limits.conf
    su - student -c 'ulimit -c; ulimit -Hc'
    su - student -c 'ulimit -c unlimited'
    0
    0
    -bash: line 1: ulimit: core file size: cannot modify limit: Operation not permitted
    su - student -c
    Start a login session as student (PAM session modules run, so limits apply) and run one command.
    ulimit -c / -Hc
    Print the soft / hard limit on core file size, in 1024-byte blocks.
    pam_limits applies limits when a session opens, so your current shell never sees the new line. Verify in a new login shell instead: su - student runs the PAM session stack exactly like a real login. Both the soft (ulimit -c) and hard (ulimit -Hc) core limits are now 0, and the attempt to raise the limit is refused. Note the wildcard * does not cover root: pam_limits skips wildcard and group lines for uid 0, so add an explicit root hard core 0 line if your policy wants root covered too.
    echo 'fs.suid_dumpable = 0' > /etc/sysctl.d/60-cis-coredump.conf
    printf '[Coredump]\nStorage=none\nProcessSizeMax=0\n' > /etc/systemd/coredump.conf
    grep -H suid_dumpable /etc/sysctl.d/*.conf
    systemd-analyze cat-config systemd/coredump.conf
    /etc/sysctl.d/60-cis-coredump.conf:fs.suid_dumpable = 0
    # /etc/systemd/coredump.conf
    [Coredump]
    Storage=none
    ProcessSizeMax=0
    grep -H
    Prefix every match with its file name, so you see which file sets the key.
    cat-config systemd/coredump.conf
    Show a systemd config file plus its drop-ins, in the order they apply.
    The other two halves, written and read back. The grep over every drop-in proves exactly one file sets the key, so nothing later overrides it. systemd-analyze cat-config prints coredump.conf merged with any drop-ins in /etc/systemd/coredump.conf.d/, in the order systemd-coredump would read them, so it also catches an override a plain cat of one file would miss.

    Example — not runnable on your lab box

    sysctl -p /etc/sysctl.d/60-cis-coredump.conf
    sysctl -n fs.suid_dumpable
    fs.suid_dumpable = 0
    0
    -p FILE
    Load settings from FILE and print each key it set (default file: /etc/sysctl.conf).
    -n
    Print only the value, not the key name.
    On a real host, apply the file now and read the live kernel value back. sysctl -p FILE loads one file and echoes each key it set; sysctl -n prints just the value. On the lab container this write is refused, which is why the file itself is what gets graded there.
  3. cron and at: allow-list who may schedule

    Use cron.allow and at.allow correctly and lock down the cron configuration files.

    Scheduled jobs are a favourite persistence spot, so CIS limits who may create them and protects the files that define them. Access is decided by two pairs of files, read the same way by crontab and by at:

    • If `/etc/cron.allow` exists, only the users listed in it may use crontab. cron.deny is then ignored entirely, so a user listed in both is allowed: the allow file wins.
    • If only `/etc/cron.deny` exists, everyone may use crontab except the users it lists. That is a blocklist, and every account created later is allowed by default.
    • If neither exists, it's a build-time default: Debian and Ubuntu allow everyone, some builds allow only root. (RHEL ships an empty cron.deny, which also allows everyone.)

    at.allow and at.deny work the same way for at, except that with neither file only root may use it. Root can always use both tools. CIS wants the allow-list model: cron.allow and at.allow present, listing only the accounts that need them, and the deny files removed so nobody mistakes them for the control.

    touch /etc/cron.allow
    chown root:crontab /etc/cron.allow && chmod 640 /etc/cron.allow
    su - student -c 'crontab -l'
    echo student >> /etc/cron.allow
    su - student -c 'crontab -l'
    You (student) are not allowed to use this program (crontab)
    See crontab(1) for more information
    no crontab for student
    crontab -l
    List the calling user's crontab. A refusal here is the access check, before any crontab is read.
    An empty cron.allow locks every non-root user out at once. Adding student lets that one account back in: no crontab for student is crontab working normally and finding nothing to list. On Debian and Ubuntu the file is root:crontab mode 640 because crontab runs setgid crontab and must read it.
    chmod 600 /etc/crontab
    chmod 700 /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
    rm -f /etc/cron.deny
    echo root > /etc/at.allow && chown root:daemon /etc/at.allow && chmod 640 /etc/at.allow
    stat -c '%a %U:%G %n' /etc/crontab /etc/cron.d /etc/cron.allow /etc/at.allow
    su - student -c 'atq'
    600 root:root /etc/crontab
    700 root:root /etc/cron.d
    640 root:crontab /etc/cron.allow
    640 root:daemon /etc/at.allow
    You do not have permission to use atq.
    stat -c '%a %U:%G %n'
    Print octal permissions, owner:group and path for each file.
    atq
    List pending at jobs; refused when at.allow exists and doesn't list you.
    The file side of the control. /etc/crontab and the /etc/cron.* directories hold root's jobs, so only root should read or change them: an attacker who can read them learns what runs as root and when, and one who can write them runs code as root. stat -c '%a %U:%G %n' prints the octal mode, owner, group and name: exactly what an audit compares. at.allow lists only root (owned root:daemon, the group at runs as on Ubuntu), so student is refused.

    Check yourself

    /etc/cron.allow lists alice and /etc/cron.deny lists alice too. Can alice use crontab?

  4. Kernel modules: make unused filesystems impossible to load

    Disable a module with install <module> /bin/false plus blacklist, and verify what modprobe would do.

    Linux loads most drivers on demand. Mount a cramfs or udf image, or plug in a USB stick, and the kernel asks modprobe to load the matching module, pulling rarely-used and rarely-audited filesystem code into the kernel because of a file an attacker handed you. CIS lists the filesystems most servers never use (cramfs, freevxfs, hfs, hfsplus, jffs2, udf, and usb-storage for removable media) and wants each one unloadable.

    Two lines in a file under /etc/modprobe.d/ do it, and they do different jobs. The file name must end in .conf: modprobe reads only *.conf files there, so the same lines in cis.conf.bak or cis.txt do nothing, and modprobe -n -v cramfs will tell you so.

    • `install <module> /bin/false` tells modprobe to run /bin/false instead of loading the module. Every load request fails, including an explicit modprobe <module> by root.
    • `blacklist <module>` only stops the module from loading automatically through an alias (the udev and hardware path). An explicit modprobe still loads a module that is only blacklisted.

    CIS wants both: the install line is the actual stop, and the blacklist line is what its audit also looks for.

    printf '%s\n' 'install cramfs /bin/false' 'blacklist cramfs' 'install udf /bin/false' 'blacklist udf' > /etc/modprobe.d/cis-filesystems.conf
    modprobe -n -v cramfs
    modprobe --showconfig | grep -E '^(install|blacklist) (cramfs|udf)'
    install /bin/false 
    blacklist cramfs
    blacklist udf
    install cramfs /bin/false
    install udf /bin/false
    -n
    Dry run: do everything except load or run anything.
    -v
    Verbose: print each insmod or install command modprobe would execute.
    --showconfig
    Print the effective configuration merged from all modprobe.d files.
    modprobe -n -v is a dry run: it resolves the request through every config file and prints what it would do, without touching the kernel. install /bin/false is the pass: loading cramfs now means running /bin/false. A module still enabled would print an insmod /lib/modules/.../cramfs.ko line instead (or, on this container, which ships no modules, Module cramfs not found). --showconfig dumps the merged configuration from every file in /etc/modprobe.d, so the grep proves your lines are what modprobe actually sees.
    modprobe cramfs 2>&1; echo "exit status: $?"
    modprobe: ERROR: ../libkmod/libkmod-module.c:990 command_do() Error running install command '/bin/false' for module cramfs: retcode 1
    modprobe: ERROR: could not insert 'cramfs': Invalid argument
    exit status: 1
    A real load attempt, which is safe here because the install line replaces the load with /bin/false. modprobe ran the install command, saw it fail, and refused. That is the behaviour every request for cramfs now gets, whether it comes from root, a mount or udev.
  5. Network sysctls: redirects, source routes and spoofed sources

    Persist the CIS network kernel parameters and predict which file's value wins.

    CIS section 3.3 tunes how the kernel treats packets that try to change its routing. Most keys come in all and default pairs. net.ipv4.conf.all.<key> covers every interface that exists now (for most of these keys the kernel combines it with the per-interface value), and net.ipv4.conf.default.<key> is the template copied to interfaces created later. Set both.

    | Key | CIS value | Why |

    |---|---|---|

    | accept_redirects | 0 | An ICMP redirect rewrites your route table. A forged one sends your traffic through the attacker. |

    | secure_redirects | 0 | The same, for redirects claiming to come from a listed gateway. |

    | send_redirects | 0 | Only routers send redirects. A server that does is misconfigured. |

    | accept_source_route | 0 | Source-routed packets choose their own path, which bypasses your network's filtering. |

    | log_martians | 1 | Log packets with impossible source addresses, an early sign of spoofing. |

    | rp_filter | 1 | Strict reverse-path filtering: drop a packet whose source isn't reachable back through the interface it arrived on. 2 is the looser mode. |

    | icmp_echo_ignore_broadcasts | 1 | Ignore pings sent to a broadcast address (smurf amplification). It has no all/default pair. |

    Write them to one drop-in. The file name matters, as the next block shows.

    cat > /etc/sysctl.d/60-cis-net.conf <<'EOF'
    net.ipv4.conf.all.accept_redirects = 0
    net.ipv4.conf.default.accept_redirects = 0
    net.ipv4.conf.all.send_redirects = 0
    net.ipv4.conf.default.send_redirects = 0
    net.ipv4.conf.all.rp_filter = 1
    net.ipv4.conf.default.rp_filter = 1
    EOF
    systemd-analyze cat-config sysctl.d | grep -E '^net\.ipv4\.conf\.(all|\*)\.rp_filter'
    net.ipv4.conf.all.rp_filter=2
    net.ipv4.conf.*.rp_filter = 2
    net.ipv4.conf.all.rp_filter = 1
    cat-config sysctl.d
    Concatenate every sysctl.d file in the order they are applied, each headed by its path.
    Every *.conf file in /etc/sysctl.d, /run/sysctl.d and /usr/lib/sysctl.d is applied in file-name order (a file in /etc replaces one with the same name in /usr/lib), and the last assignment of a key wins. cat-config prints them in that order, so read the result bottom-up. Ubuntu ships rp_filter=2 twice, in 10-network-security.conf and systemd's 50-default.conf. Your 60- file sorts after both, so its 1 is the value the box boots with. Name the same file 10-cis.conf and it is silently overridden, and the scan fails although the line is right there.

    Example — not runnable on your lab box

    sysctl -p /etc/sysctl.d/60-cis-net.conf
    sysctl -n net.ipv4.conf.all.accept_redirects net.ipv4.conf.all.rp_filter
    net.ipv4.conf.all.accept_redirects = 0
    net.ipv4.conf.default.accept_redirects = 0
    net.ipv4.conf.all.send_redirects = 0
    net.ipv4.conf.default.send_redirects = 0
    net.ipv4.conf.all.rp_filter = 1
    net.ipv4.conf.default.rp_filter = 1
    0
    1
    -p FILE
    Load settings from FILE and print each key it set (default file: /etc/sysctl.conf).
    -n
    Print only the value, not the key name.
    On a real host, apply and read back. sysctl -p FILE applies one file and ignores precedence, so it can show your value even when a later file will override it at boot. sysctl --system re-applies every file in boot order, which makes it the honest check that the boot result is what you want. sysctl -p with no file reads only /etc/sysctl.conf. sysctl -n KEY... prints the live values.

    Check yourself

    /usr/lib/sysctl.d/50-default.conf sets net.ipv4.conf.all.rp_filter = 2 and you add /etc/sysctl.d/40-cis.conf with rp_filter = 1. What value does the host boot with?

  6. TLS: a system-wide OpenSSL floor

    Wire a system_default section into openssl.cnf, set MinProtocol and SECLEVEL, and prove OpenSSL reads it.

    Every program linked against OpenSSL (web servers, mail, curl, scripts) reads openssl.cnf when it starts. A system default section in that file sets a floor all of them inherit unless the program overrides it. Two directives carry the policy:

    • `MinProtocol = TLSv1.2` refuses TLS 1.0 and 1.1.
    • `CipherString = DEFAULT@SECLEVEL=2` sets the security level. Level 2 means at least 112-bit security: RSA and DH keys of 2048 bits or more, no SHA-1 certificate signatures, no export or NULL ciphers. Level 3 raises RSA to 3072 bits.

    The section is reached through a chain of names, and every link has to be there:

    1. openssl_conf = <init> in the global section, meaning above the first [header]. Anything below a header belongs to that section, and OpenSSL never looks for openssl_conf there.

    2. [<init>] with ssl_conf = <ssl>.

    3. [<ssl>] with system_default = <policy>.

    4. [<policy>] with the directives.

    The section names are yours to choose. Ubuntu 22.04 already ships the chain with SECLEVEL=2 but no MinProtocol. A trimmed or hand-written file may have no chain at all.

    openssl version -d
    cnf="$(openssl version -d | cut -d'"' -f2)/openssl.cnf"
    grep -nE '^(openssl_conf|ssl_conf|system_default|CipherString|MinProtocol) *=|^\[ *(openssl_init|ssl_sect|system_default_sect) *\]' "$cnf"
    OPENSSLDIR: "/usr/lib/ssl"
    17:openssl_conf = openssl_init
    53:[openssl_init]
    55:ssl_conf = ssl_sect
    393:[ssl_sect]
    394:system_default = system_default_sect
    396:[system_default_sect]
    397:CipherString = DEFAULT:@SECLEVEL=2
    version -d
    Print OPENSSLDIR, where OpenSSL looks for openssl.cnf.
    grep -n
    Show line numbers, so you can see openssl_conf sits above the first [section].
    openssl version -d names the directory OpenSSL reads openssl.cnf from, and the second line builds the file's path from it, so you grep the file OpenSSL really uses. On Ubuntu /usr/lib/ssl/openssl.cnf is a symlink to /etc/ssl/openssl.cnf; on RHEL the file is /etc/pki/tls/openssl.cnf. The grep walks the chain on a stock Ubuntu file: openssl_conf on line 17, above every header, then each link down to [system_default_sect]. To add the floor here, put MinProtocol = TLSv1.2 under that last header. If the grep finds no openssl_conf line, you build all four links yourself, as in the next block.
    cat > /root/policy.cnf <<'EOF'
    openssl_conf = default_conf
    
    [default_conf]
    ssl_conf = ssl_sect
    
    [ssl_sect]
    system_default = system_default_sect
    
    [system_default_sect]
    MinProtocol = TLSv1.2
    CipherString = DEFAULT@SECLEVEL=2
    EOF
    key=/root/tls-demo.key crt=/root/tls-demo.crt
    openssl req -x509 -newkey rsa:2048 -nodes -keyout "$key" -out "$crt" -subj "/CN=localhost" -days 1 2>/dev/null
    sed 's/SECLEVEL=2/SECLEVEL=3/' /root/policy.cnf > /root/probe.cnf
    OPENSSL_CONF=/root/probe.cnf timeout 3 openssl s_server -accept 127.0.0.1:8443 -cert "$crt" -key "$key" </dev/null 2>&1 | grep -o 'ee key too small'
    ee key too small
    OPENSSL_CONF=FILE
    Environment variable: read this config file instead of OPENSSLDIR/openssl.cnf.
    req -x509 -newkey rsa:2048 -nodes
    Make a throwaway self-signed certificate and an unencrypted 2048-bit RSA key.
    s_server -cert -key
    Start a test TLS server. It checks the key against the security level before it listens.
    A complete chain, and a test that proves OpenSSL reads it. OPENSSL_CONF points any OpenSSL program at a file of your choice, so you can try a policy before touching the system one. Ubuntu's built-in default is already level 2, so a level-2 policy behaves the same whether or not it is read. The probe raises the level to 3 instead: a 2048-bit RSA certificate passes level 2 but not level 3, so s_server refuses to load it with ee key too small only if your [system_default_sect] was applied. If the server starts listening instead (ACCEPT), the chain is broken, and the usual cause is openssl_conf sitting below a [header]. Run the same probe without OPENSSL_CONF after editing /etc/ssl/openssl.cnf.

    Example — not runnable on your lab box

    update-crypto-policies --show
    grep -oE '^TLS\.MinProtocol = .*|@SECLEVEL=[0-9]' /etc/crypto-policies/back-ends/opensslcnf.config
    update-crypto-policies --set FUTURE
    DEFAULT
    @SECLEVEL=2
    TLS.MinProtocol = TLSv1.2
    Setting system policy to FUTURE
    Note: System-wide crypto policies are applied on application start-up.
    It is recommended to restart the system for the change of policies
    to fully take place.
    --show
    Print the system-wide policy in force.
    --set POLICY
    Regenerate every back-end file for POLICY.
    RHEL, Rocky and Fedora don't have you edit openssl.cnf by hand. Their openssl.cnf includes a generated back-end file, and update-crypto-policies writes that file, plus the matching ones for GnuTLS, NSS, Java and OpenSSH, from one named policy: LEGACY, DEFAULT, FUTURE or FIPS, optionally with modifiers such as DEFAULT:NO-SHA1. --show prints the active policy, and the back-end file shows what it means for OpenSSL. FUTURE raises OpenSSL to @SECLEVEL=3. Programs pick up the change when they restart. Output from Rocky 9; the Ubuntu lab box doesn't have this tool.

    Check yourself

    openssl.cnf starts with a [ req ] section, and you append openssl_conf = default_conf plus the three policy sections at the end of the file. Does the policy apply?

  7. Now do it: audit these controls on the sandbox

    Run the read-only checks a CIS scan makes and say pass or fail for each.

    Run these in your lab terminal as a mini CIS audit. Each one only reads. For every command, say the verdict out loud the way a findings report would: control, expected value, observed value, pass or fail. The labs cis-core-dumps, cis-cron-hardening, cis-filesystem-modules, cis-network-params and security-tls-crypto-policy each start from a failing host and grade the files exactly the way these checks read them.

    grep -E '^[[:space:]]*\*[[:space:]]+hard[[:space:]]+core' /etc/security/limits.conf; su - student -c 'ulimit -Hc'; grep -rh suid_dumpable /etc/sysctl.d/ /etc/sysctl.conf
    Core dumps: the hard-limit line, its effect in a new login, and every place fs.suid_dumpable is set. Pass is * hard core 0, 0, and a single fs.suid_dumpable = 0.

    Runnable in the cis-cron-hardening room — that room’s setup plants the files it reads

    stat -c '%a %U:%G %n' /etc/crontab /etc/cron.d; ls -l /etc/cron.allow /etc/cron.deny /etc/at.allow 2>&1
    cron: modes on the config, then the access files. Pass is 600 and 700, cron.allow and at.allow present, cron.deny absent.
    for m in cramfs freevxfs udf usb-storage; do printf '%s: ' "$m"; modprobe -n -v "$m" 2>&1; done
    Modules: a dry run for each CIS filesystem module. Pass is install /bin/false (or /bin/true). On this container, not found means no install line exists yet.
    systemd-analyze cat-config sysctl.d | grep -E '^-?net\.ipv4\.(conf\.(all|default|\*)\.(accept_redirects|send_redirects|accept_source_route|log_martians|rp_filter)|icmp_echo_ignore_broadcasts)'
    Network sysctls in apply order. For each key the last line printed is the value the host boots with.

    Runnable in the security-tls-crypto-policy room — that room’s setup plants the files it reads

    grep -m1 -n '^ *\[' /etc/ssl/openssl.cnf; grep -nE '^ *(openssl_conf|ssl_conf|system_default|MinProtocol|CipherString) *=' /etc/ssl/openssl.cnf
    TLS: the first line prints the first [header]; openssl_conf must have a smaller line number. Then follow the chain and check the policy section sets MinProtocol to TLSv1.2 or higher and a SECLEVEL of 2 or more.
Recap
  • A CIS control has two halves: the persistent line in the right file (what the scanner reads and what survives a reboot) and the effective value you read back with the tool that applies it.
  • Core dumps: * hard core 0 in limits.conf (checked with ulimit -c in a NEW login shell; * skips root), fs.suid_dumpable = 0 in /etc/sysctl.d, and Storage=none in /etc/systemd/coredump.conf.
  • cron and at: if cron.allow exists it alone decides and cron.deny is ignored; with only a deny file it's a blocklist. Keep cron.allow / at.allow, delete the deny files, chmod 600 /etc/crontab and 700 the /etc/cron.* directories. On Ubuntu, cron.allow is root:crontab 640.
  • Modules: install <mod> /bin/false stops every load; blacklist <mod> only stops alias autoloading. Verify with modprobe -n -v <mod> and modprobe --showconfig.
  • sysctl.d files apply in file-name order and the last assignment wins, so name the CIS drop-in to sort late (60- beats Ubuntu's 10- and 50- files). sysctl --system re-applies everything in boot order; sysctl -p FILE applies one file.
  • OpenSSL reads its policy through openssl_conf (global section only) -> ssl_conf -> system_default -> MinProtocol / CipherString @SECLEVEL. Prove it with OPENSSL_CONF and a stricter probe; on RHEL use update-crypto-policies.
Where each control lives and how to read it back
ControlSettingFileVerify
Core dumps (limit)* hard core 0/etc/security/limits.confsu - user -c 'ulimit -Hc'
Core dumps (setuid)fs.suid_dumpable = 0/etc/sysctl.d/*.confsysctl -n fs.suid_dumpable
Core dumps (systemd)Storage=none/etc/systemd/coredump.confsystemd-analyze cat-config systemd/coredump.conf
cron / at accessallow file present, deny file removed/etc/cron.allow, /etc/at.allowsu - user -c 'crontab -l'
cron config600 / 700, root-owned/etc/crontab, /etc/cron.*stat -c '%a %U:%G %n'
Filesystem modulesinstall <mod> /bin/false + blacklist <mod>/etc/modprobe.d/*.confmodprobe -n -v <mod>
Network parametersredirects 0, source route 0, martians 1, rp_filter 1/etc/sysctl.d/*.confsystemd-analyze cat-config sysctl.d, sysctl -n
TLS floorMinProtocol = TLSv1.2, @SECLEVEL=2/etc/ssl/openssl.cnfOPENSSL_CONF probe; update-crypto-policies --show on RHEL