Back to the course
Test yourself

Linux hardening

Final exam · 53 questions · answers explained as you pick
Start from a default server
6 questions
01Two Ubuntu 26.04 servers were built from the same image on the same day. On one, apt-get -s upgrade says "The following upgrades have been deferred due to phasing" for rust-coreutils, and apt-cache policy lists the candidate as "0.10.0-1ubuntu2~26.04.1 500 (phased 75%)". The other server already runs that version. Why do they differ?
Incorrect — The candidate line shows the new version is available to this machine; apt held it back on purpose.
Correct — The share is computed from /etc/machine-id, the source package name and the version, so identical builds can fall on either side of 75% for a few days.
Incorrect — Security updates are never phased. Phasing applies to bug-fix updates in -updates, like this one.
Incorrect — Automatic-Reboot only decides about rebooting after an upgrade. It has no say in which packages are installed.
02A libexpat1 security update broke an application, so you ran sudo apt-get install --allow-downgrades libexpat1=2.7.4-1 and sudo apt-mark hold libexpat1. The application works again. What risk does the hold now carry?
Incorrect — A hold persists. The dry run keeps printing "No packages found that can be upgraded unattended" until someone removes it.
Incorrect — A hold affects only the package it names; other updates install as usual.
Incorrect — needrestart acts after an apt run changes libraries. A hold changes nothing on disk.
Correct — The nightly job skips the package quietly. Record each hold with an owner and a date, and run apt-mark unhold as soon as a fixed version ships.
03On an Ubuntu 26.04 server, sudo needrestart -b prints matching NEEDRESTART-KCUR and NEEDRESTART-KEXP values and "NEEDRESTART-KSTA: 1", and pro status ends with "This machine is not attached to an Ubuntu Pro subscription." What should monitoring conclude?
Correct — KSTA 1 means the running kernel is the newest one installed. Livepatch shows as available, but without Pro attached it patches nothing.
Incorrect — KSTA 2 or 3 would mean a newer kernel is waiting; here the two kernel values match. An unattached machine gets no live patches.
Incorrect — Updates to libc6 or dbus also create /run/reboot-required. The kernel is only one of the reasons for a reboot.
Incorrect — The "ABI upgrades are not detected" note is a caveat about one kind of update. KSTA 1 is a real verdict: the newest installed kernel is running.
04On the lab's Ubuntu 26.04 machine, sudo ss -tulpn shows systemd-resolve listening on UDP 0.0.0.0:5353 and [::]:5353, although Ubuntu ships 00-disable-mdns.conf with MulticastDNS=no. How do you close the port?
Incorrect — Ubuntu's own drop-in already turns it off. The main file is read before the drop-ins, so a line there would lose.
Incorrect — The setting works once the right file wins. Masking the resolver breaks name resolution for the whole host.
Correct — systemd-analyze cat-config shows 00-lima-enable-mdns.conf after Ubuntu's file. A 99- drop-in wins, and ss then prints only its header.
Incorrect — chronyd listens on 323 on loopback. ss names systemd-resolve as the owner of 5353.
05apt-get -s install aide on a default Ubuntu 26.04 server lists eight packages, postfix among them; with --no-install-recommends it lists one. What does that option change, and why does it matter on a server?
Correct — Recommended packages are installed by default and can include daemons. Add the ones you really want by name.
Incorrect — Recommends are separate packages. aide's own files still install with it.
Incorrect — The option skips the package completely; it never reaches the disk.
Incorrect — Hard dependencies still install. The option only concerns Recommends.
06To remove clear-text clients, a colleague runs apt-get purge ftp tnftp on Ubuntu 26.04 and sees apt plan to install ftp-ssl in their place. They add ubuntu-standard to the purge to stop that. What should they check before confirming?
Incorrect — SSH is not among the packages the simulation lists. The risk lies in the "no longer required" list.
Incorrect — ubuntu-standard is an ordinary metapackage; the simulation shows apt willing to remove it.
Incorrect — The metapackage is what keeps its dependencies installed. Removing it makes them removable.
Correct — Mark the tools you use with apt-mark manual first, or keep ubuntu-standard and accept the FTP client.
6 questions · explanations appear as you answer
Accounts and access
9 questions
01A review of a new Ubuntu cloud server shows getent group sudo admin printing "sudo:x:27:deploy" and "admin:x:107:", and /etc/sudoers.d/90-cloud-init-users holding "lima ALL=(ALL) NOPASSWD:ALL". Which accounts can become root?
Incorrect — Files in /etc/sudoers.d grant rights to named users directly, whatever groups they are in.
Incorrect — NOPASSWD:ALL means no prompt at all. Whoever holds lima's SSH key is root.
Correct — Group rules and per-user files both grant sudo. The empty admin group would also make anyone added to it an administrator, so review all three.
Incorrect — sudo checks the caller's credentials, not root's. A locked root password does not stop sudo.
02You create a daemon account with sudo useradd --system --shell /usr/sbin/nologin --home-dir /nonexistent hard-acct-svc, and sudo passwd -S prints "hard-acct-svc L 2026-09-27 -1 -1 -1 -1". What do the L and the -1 values mean?
Incorrect — passwd -S reports the password field in /etc/shadow. faillock keeps its tallies in /run/faillock.
Correct — L means locked (no usable password) and -1 means unset, so the account never ages out.
Incorrect — --system picks a UID below 1000 on purpose, and -1 means "unset", not a deletion marker.
Incorrect — A forced change reads "password must be changed" in chage -l. The date is the last password change.
03After sudo pam-auth-update --enable faillock faillock_notify on Ubuntu, common-auth starts with pam_faillock.so preauth under the requisite control, then pam_unix.so under [success=2 default=ignore], then pam_faillock.so authfail under [default=die]. A locked user types the correct password. Which line decides the result?
Incorrect — preauth runs first and is requisite, so the stack fails before pam_unix reads anything.
Incorrect — authfail runs only after a wrong password (pam_unix jumps over it on success), and it records failures instead of clearing them.
Incorrect — pam-auth-update computed the jump: it skips exactly the authfail and pam_deny lines.
Correct — That is why the lab's sixth attempt, with the right password, got "The account is locked due to 5 failed logins."
04On Ubuntu you add minlen = 15 in /etc/security/pwquality.conf.d/50-secopslog.conf but leave out enforce_for_root. An administrator runs sudo passwd alice and types a 10-character password twice. What happens?
Correct — Without enforce_for_root, root only sees the BAD PASSWORD message. The lesson's drop-in adds the option so root is held to the same rules.
Incorrect — That is what happens with enforce_for_root. Without it, root is warned and not refused.
Incorrect — pam_pwquality runs and prints its result. It is only the refusal that root is spared.
Incorrect — The module reads its drop-in directory at every password change; there is nothing to rebuild.
05An operator's rule reads "hard-sudo-ops ALL=(root) NOPASSWD: NOEXEC: /usr/bin/tar -czf /var/backups/hard-sudo.tgz /etc/hard-sudo". Running exactly that command prints "tar (child): gzip: Cannot exec: Permission denied". What is happening?
Incorrect — The error is about executing gzip, and /var/backups is on the root filesystem. The failed write follows from the failed child.
Incorrect — tar itself ran. sudo-rs implements NOEXEC with a seccomp filter, which blocked only the child's exec.
Correct — That is the cost of the rule: with tar -cf (no compression) the backup works, and a tool that must start helpers should not get NOEXEC.
Incorrect — The error says "Permission denied", not "not found". NOEXEC refused the exec itself.
06Configuration management installs an operator's rules as /etc/sudoers.d/50-hard-sudo-ops.conf, mode 0440, owned by root. sudo visudo -c reports no problem, yet sudo -l -U hard-sudo-ops says the user is not allowed to run sudo. Why?
Incorrect — The lab's working file was installed with 0440. The name is the problem.
Correct — Rename it to 50-hard-sudo-ops; sudo -l -U then lists the rules. Names ending in ~ are skipped the same way.
Incorrect — sudo-rs reads /etc/sudoers and its includes. /etc/sudoers-rs takes over only if that file exists.
Incorrect — sudo reads its policy on every run; no new login is needed.
07On RHEL 10 you add "Ciphers aes256-gcm@openssh.com" to 00-secopslog.conf for a customer, and sudo sshd -T prints "ciphers aes256-gcm@openssh.com". Months later the fleet moves to update-crypto-policies --set FUTURE. What happens to this server's ciphers?
Correct — 40-redhat-crypto-policies.conf is read after your file, and sshd keeps the first value for each keyword, so no later policy change reaches these ciphers.
Incorrect — The policy reaches sshd as a drop-in file, not through the kernel, and that file loses to an earlier one.
Incorrect — The lab loaded the line and sshd -T showed a single cipher. There is no conflict check.
Incorrect — sshd keeps one value per keyword; the first line read is the whole list.
08Your sshd drop-in says RevokedKeys /etc/ssh/revoked_keys, and configuration management ships that setting to a new server before it ships the file. What happens to logins there?
Incorrect — sshd_config(5) warns the opposite: when the file cannot be read, public key authentication is refused.
Incorrect — RevokedKeys covers every account, root included.
Incorrect — authorized_keys options do not exempt a key from the revocation check.
Correct — Create the list first (ssh-keygen -k -f) and ship it together with the setting.
09An automation key's authorized_keys line starts with restrict,from="127.0.0.1",command="/usr/bin/df -h /". From 127.0.0.1 a job runs ssh -i ci_key host 'cat /etc/shadow'. What does the job get back?
Incorrect — The key is accepted. command= replaces what was asked instead of refusing it.
Correct — The requested command is passed in SSH_ORIGINAL_COMMAND for a forced program that wants to read it; df ignores it.
Incorrect — restrict turns off forwarding, terminals and ~/.ssh/rc, and command= fixes the program; both apply.
Incorrect — The forced command is the one thing that runs.
9 questions · explanations appear as you answer
Logging and audit
6 questions
01After adding SystemMaxUse=1G and SystemKeepFree=2G in /etc/systemd/journald.conf.d/90-secopslog.conf and restarting journald, its status line ends "is 141.3M, max 1G, 882.6M free." What does "882.6M free" mean?
Incorrect — Free disk space is SystemKeepFree's business. This number is measured against the journal's own limit.
Incorrect — SystemKeepFree=2G is still in force; the smaller of the two limits wins.
Incorrect — journald does not preallocate its limit. It deletes old archives as it approaches it.
Correct — "max 1G" proves the drop-in was read, and "free" is what remains under that cap.
02rsyslog forwards with StreamDriverMode="1", StreamDriverAuthMode="x509/name" and StreamDriverPermittedPeers="loghost.example.test". An impostor takes over 203.0.113.2 with a valid certificate for a different name from the same CA. What does rsyslog do?
Correct — x509/name checks the name as well as the CA. Answering on the right address is not enough.
Incorrect — Mode 1 means TLS only. The name check is what x509/name adds, and it fails here.
Incorrect — rsyslog has no such fallback. The action keeps retrying and the messages wait in the queue.
Incorrect — A retry count of -1 means retry forever; the messages stay in the disk-assisted queue.
03An Ubuntu 26.04 server synchronises over NTS with Canonical's servers. You add "server 203.0.113.2 iburst", a plain NTP internal time server, and chronyc -n selectdata lists it in state P with Auth N, while the NTS sources show the effective options "-PTR-". The security team now plans to block outbound TCP 4460 and rely on the internal server. What will happen?
Incorrect — P explains today's order. The require flag (R) on the NTS sources is what decides the outcome once they are gone.
Incorrect — The NTS key exchange runs over TCP 4460 before any NTP packet; without it chrony gets no cookies and the sources become unusable.
Correct — The NTS sources carry require and trust, so an unauthenticated server is used only when they agree; with them unreachable it waits (state W in chronyc(1)). Give the internal server NTS, or keep TCP 4460 and UDP 123 open.
Incorrect — chrony never changes a source's mode. The server line needs the nts option, and the server must offer NTS with a certificate chrony trusts.
04Your rules file has "-a always,exit -F arch=b64 -F path=/etc/passwd -F perm=wa -F key=identity" and a priv rule with "-F auid!=unset". After sudo augenrules --load, sudo auditctl -l prints the identity rule with a long -S list (setxattr, openat, renameat and more) and the priv rule with "-F auid!=-1". What does that mean?
Incorrect — augenrules writes the merged /etc/audit/audit.rules and leaves your file as it was. The expansion is how the kernel holds the rule.
Correct — audit 4 expands path and dir rules into system call lists, and auditctl -l shows what the kernel really enforces.
Incorrect — The rules loaded and fired: ausearch -k identity found the useradd events.
Incorrect — -1 is how auditctl prints unset, so the filter is unchanged.
05On RHEL 10 you change max_log_file in /etc/audit/auditd.conf and run sudo systemctl restart auditd, which fails with "Operation refused, unit auditd.service may be requested by dependency only". How do you apply the change?
Incorrect — No such rule exists. The unit sets RefuseManualStop=yes and systemctl honours it.
Incorrect — auditd rereads its configuration when it gets a reload signal; a reboot is not needed.
Incorrect — That weakens a protection that keeps auditd from being stopped casually, and a supported path exists.
Correct — The reload applies auditd.conf and logs who did it. service auditd restart also works, through the package's legacy-actions script.
06ausearch -k sudoers -i shows a SYSCALL record with "syscall=openat success=yes", "auid=deploy uid=root" and "exe=/usr/lib/cargo/bin/coreutils/install", and a PATH record for /etc/sudoers.d/hard-audit-demo with "nametype=CREATE". What happened?
Incorrect — uid is the identity at the moment of the call. auid keeps the account that logged in.
Incorrect — nametype=CREATE (and O_CREAT in the flags) marks a new file, not an edit.
Correct — auid names the person who logged in, uid the effective identity, and the PATH record marks the file as created.
Incorrect — tty=(none) only means there was no terminal, as in the lab. auid=deploy ties the event to a login.
6 questions · explanations appear as you answer
Files and storage
6 questions
01A permissions audit runs find with -type f -perm /6000 on every local filesystem and nothing else. Which privilege-granting files does it miss?
Correct — Run getcap over the same local filesystems, as the lesson's perms-snapshot does. A capability can be as strong as SUID root.
Incorrect — With the slash, /6000 matches either bit, so the SGID shadow and crontab files are listed.
Incorrect — -perm tests mode bits whatever the owner is.
Incorrect — A link carries no mode bits of its own; the program it points to is a regular file and is listed.
02/etc/shadow is mode 640 root:shadow on Ubuntu 26.04 and mode 0 root:root on RHEL 10. What should an audit do about the difference?
Incorrect — Ubuntu's SGID shadow unix_chkpwd needs that group read to check passwords; mode 0 breaks it.
Correct — Ubuntu uses SGID shadow, RHEL a SUID root unix_chkpwd. The question is whether either has moved from its platform's shipped state.
Incorrect — RHEL keeps hashes in /etc/shadow like Ubuntu; root reads it through its capabilities.
Incorrect — MAC adds a check on top of file modes; it does not replace them.
03On a test filesystem remounted with nosuid, ls -l still shows "-rwsr-xr-x" for a root-owned copy of id, but running it prints no "euid=0". A colleague says the remount stripped the SUID bit. What is right?
Incorrect — ls reads the inode, which still has the bit. Nothing was cleared.
Incorrect — No size rule exists. The same file ran with euid=0 before the remount.
Incorrect — The option never changes the file.
Correct — If root copies it with cp -p to a mount without nosuid, the promotion returns, which is why the option belongs on mounts ordinary users can write.
04You append a line with the options loop,nosuid,nodev,noexce,nofail (a typo) to a copy of /etc/fstab, and sudo findmnt --verify --tab-file on the copy prints "0 parse errors, 0 errors, 1 warning". Is the line ready to install?
Incorrect — It checks structure and sources. Filesystem options are the kernel's business, so the typo passed.
Incorrect — nofail makes the mount wanted instead of required. The kernel still rejects the unknown option.
Correct — The test mount failed and the kernel logged "ext4: Unknown parameter 'noexce'". Fix it, test again, back up fstab, then install.
Incorrect — The warning was only that the source is a regular file, normal for a loop mount. The real problem was not reported at all.
05A year ago you saved a LUKS2 header with cryptsetup luksHeaderBackup. Last week a passphrase leaked, so you added a new one and killed the leaked passphrase's keyslot on the disk. What is the old header backup now?
Correct — The backup holds the old keyslot, which wraps the same volume key. Store header backups as carefully as the disk.
Incorrect — Killing a slot leaves the volume key unchanged; only a full cryptsetup reencrypt replaces it.
Incorrect — A header backup is an ordinary file; cryptsetup cannot reach it to invalidate it.
Incorrect — A header backup contains every keyslot as it was on the day it was taken.
06On the lab VM, cryptsetup luksDump shows argon2id keyslots with a memory cost of 257170 KiB in slot 0 and 184274 KiB in slot 2, which was added later on the same volume. What explains the two values, and what do they protect against?
Incorrect — Nothing is damaged. Each keyslot records the costs chosen when it was made, and they differ by design.
Incorrect — The memory cost is what the key derivation spends on each attempt; it says nothing about where the volume key lives.
Correct — It picks costs for about two seconds per opening on that machine at that moment, with memory between 64 MiB and 1 GiB, so a stolen header is expensive to brute-force.
Incorrect — An attacker can pick the cheaper slot, but both costs are high; the values vary with load, not with any choice of strength.
6 questions · explanations appear as you answer
Services
6 questions
01With DynamicUser=yes, StateDirectory=hard-sandbox, ProtectSystem=strict and ProtectHome=yes in its drop-in, the demo service answers "FAILED /etc/hard-sandbox-probe: Read-only file system" and a Permission denied for /home, but writes /var/lib/hard-sandbox/marker. Which directive makes that last write possible?
Incorrect — strict makes the whole tree read-only; nothing under /var/lib is writable unless a directive carves it out.
Correct — systemd creates /var/lib/hard-sandbox, owned by the dynamic user, and leaves the rest read-only.
Incorrect — A dynamic user has no home. The writable path comes from StateDirectory=.
Incorrect — PrivateTmp gives the service its own /tmp and /var/tmp and nothing more.
02A packaged service scores "9.6 UNSAFE" in systemd-analyze security. After you add SystemCallFilter=@system-service to its drop-in, it starts failing, and journalctl -u shows the process was killed. The service starts its own converter program for each request. What is the sensible next step?
Incorrect — Filters apply to root as well. Running as root gives up the other protections without fixing this one.
Incorrect — A service that no longer works is worse than one that scores 5. The goal is restrictions that fit.
Incorrect — Revert is the rollback, but the other directives may fit well. Loosen the one that broke it.
Correct — Read the failure in journalctl -u, loosen the directive behind it, and test that the service still does its job.
03As an ordinary user with no sudo, you run systemctl show -p Environment hard-secrets-env and get "Environment=API_TOKEN=LAB-ONLY-not-a-real-token-1111". What does that tell you?
Correct — systemd.exec(5) says environment variables are not suitable for secrets; move the token into a credential.
Incorrect — No group is involved; systemctl show answers local users in general.
Incorrect — systemctl reads the property from systemd, which publishes it whatever the unit file's mode.
Incorrect — The process copy is better protected, yet every child process inherits it and may log it.
04A sync tool runs as hard-secrets-app with --password=... on its command line, and pgrep -a -f run by deploy shows the password. Which change keeps other local accounts from reading it without breaking monitoring?
Incorrect — It hides the process, but it also breaks monitoring tools that expect to see every process.
Correct — Arguments sit in /proc/PID/cmdline for local accounts to read; a file with the right owner and mode, or standard input, does not.
Incorrect — HISTCONTROL affects shell history. The running process's command line stays visible.
Incorrect — ProtectProc hides other processes from the service; it does not hide the service's own command line from other accounts.
05A root job in /etc/cron.d calls tar by its bare name. On Ubuntu 26.04 the job logs "start: user=root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin", and /usr/local/bin is writable by an operators' group. What is the risk, and the fix?
Incorrect — On 26.04 cron starts with -P and its PAM session loads /etc/environment, which is exactly the PATH in the log.
Incorrect — System jobs search the same PATH, and this one runs as root.
Correct — Call programs by absolute path in the script too, so the search order cannot redirect them.
Incorrect — cron checks the modes of its own files (INSECURE MODE), not the directories on a job's PATH.
06To follow the CIS RHEL 10 wording on an Ubuntu 26.04 server, you make /etc/cron.allow root:root 0640 and list the admins. Now an admin gets "/etc/cron.allow: Permission denied" and "You (deploy) are not allowed to use this program (crontab)". Why?
Correct — On Ubuntu the file needs group crontab (root:crontab 0640). The CIS mode fits RHEL's cronie.
Incorrect — Names are right; deploy was allowed while the group was crontab.
Incorrect — When cron.allow exists, cron.deny is not consulted, and this host has no cron.deny.
Incorrect — crontab reads the file at each run. The first error line shows it tried and could not read it.
6 questions · explanations appear as you answer
Network
6 questions
01On a default Ubuntu 26.04 install, systemctl is-active ufw.service prints active, while sudo ufw status verbose prints "Status: inactive". How do both hold at once?
Incorrect — There is no such mode. ENABLED=no means no ufw ruleset is loaded.
Incorrect — ufw status reports the live state, and nothing is loaded.
Incorrect — nftables.service is disabled on a default install.
Correct — A fresh Ubuntu server has no host firewall until you add rules and run ufw enable.
02On a Rocky 10.2 host running firewalld with the default public zone, a client's nc -zv to port 22 connects, and to port 8080 prints "Ncat: No route to host." A test web server does listen on 8080. What does that message tell you?
Incorrect — A reset makes the client report the connection as refused. The server does listen on 8080.
Incorrect — A silent drop makes the client wait until its timeout; ncat's message came back at once.
Correct — The public zone's default target rejects what it does not allow, a faster and clearer failure than a timeout.
Incorrect — SELinux confines processes on the server; it does not send ICMP errors to clients.
03After loading the lesson's ruleset, two nc attempts from the outside host (to ports 22 and 9100) both end "timed out: Operation now in progress", and the policy counter at the end of the input chain reads "counter packets 6 bytes 360". Why six packets?
Correct — A dropped packet gets no answer, so the client retries until -w 3 runs out: three packets for each of the two ports.
Incorrect — The log rule has no counter. Only the final rule counts, once per packet.
Incorrect — The target was an IPv4 address, so nc sent IPv4 packets only.
Incorrect — ct state established,related accepts those near the top of the chain.
04A runbook written for another distribution arms a firewall rollback on Ubuntu 26.04 with echo 'flush ruleset' | at now + 5 minutes before a risky change. Why will it not save you?
Incorrect — Root can use at too. The job text is the problem long before permissions matter.
Incorrect — flush ruleset leaves no tables, so everything is accepted. That is not the failure here.
Incorrect — Timing is not the problem; the job would do nothing useful at any time.
Correct — Use systemd-run --on-active=5min nft -f /etc/nftables.conf to reload the known-good file, and stop the timer once the change works.
05A server with two uplinks runs with net.ipv4.conf.all.rp_filter=1 and every interface at 1. Replies to one partner network legitimately leave through eth1 while its packets arrive on eth0, and those connections now fail without any error. How do you fix eth0 and keep strict mode on the rest?
Incorrect — The kernel uses the higher of the two values, so all = 2 makes every interface loose.
Correct — With all at 1, the 2 on eth0 makes loose mode apply to that interface alone.
Incorrect — For rp_filter the maximum wins, so eth0 = 0 with all = 1 still filters strictly.
Incorrect — Logging only records the drops; reverse-path filtering does the dropping.
06Your sysctl file sets log_martians = 1 in all and default and does not name eth0. Afterwards sysctl reads back net.ipv4.conf.eth0.log_martians = 0. Is martian logging active for packets arriving on eth0?
Incorrect — The combining rule differs per key, and log_martians uses OR.
Incorrect — default does affect new interfaces only, but here the value in all is what counts.
Correct — That is why the lesson's file sets log_martians in all and default alone. OR is enough to switch something on, never to switch it off.
Incorrect — This is an IPv4 key, and the lab's martian lines were IPv4.
6 questions · explanations appear as you answer
Kernel and mandatory access control
8 questions
01A colleague wants kernel.unprivileged_bpf_disabled = 1 in the fleet baseline to close unprivileged BPF on Ubuntu 26.04 and RHEL 10 hosts. What would that line change?
Correct — Both kernels start at 2 (CONFIG_BPF_UNPRIV_DEFAULT_OFF). 1 refuses the same calls and is a one-way latch.
Incorrect — Both already refuse unprivileged bpf() at 2; bpftool reports it as restricted to privileged users.
Incorrect — 1 and 2 both concern unprivileged callers. Root keeps BPF either way.
Incorrect — A lower number is not looser here: 1 refuses the same calls and simply locks the setting.
02sysctl shows fs.protected_regular = 2 on Ubuntu 26.04 but 1 on Rocky 10.2, each set in /usr/lib/sysctl.d/50-default.conf. If a shared sysctl.d file pins 1 everywhere, what happens to the Ubuntu servers?
Incorrect — The values differ: 2 covers more directories than 1.
Incorrect — It is not a latch; root can change it back at any time.
Incorrect — The key is about opening an existing file owned by someone else when a new one was meant, not about creating files.
Correct — 2 also protects group-writable sticky directories; pinning 1 on Ubuntu loosens it.
03The CIS RHEL 10 profile lists dccp among the modules to disable. On Ubuntu 26.04, modinfo -F filename dccp prints "modinfo: ERROR: Module dccp not found." What should your modprobe.d file do about DCCP?
Incorrect — There is nothing to autoload: DCCP was removed from the kernel in Linux 6.16.
Correct — Write rules for modules that exist on the kernel and that the host does not need, such as can or sctp on Ubuntu.
Incorrect — The initramfs is built from the installed modules, and there is no dccp among them.
Incorrect — modinfo would print (builtin) for compiled-in code. "not found" means the module does not exist.
04The lesson's rule file uses install can /bin/false, and sudo modprobe can then prints "Error running install command". Why /bin/false and not /bin/true?
Incorrect — Both commands replace the insertion; neither lets the module load.
Incorrect — The CIS RHEL 10 check accepts /bin/true as well.
Correct — With /bin/false, modprobe exits non-zero with an error, so an attempt to load the module is visible.
Incorrect — install rules are read by modprobe; insmod reads no configuration.
05You load and enforce a new profile for an internal API that has been running for days. aa-status counts the API among processes that are unconfined but have a profile defined. Why, and what do you do?
Correct — A running process keeps the confinement it started with, and the new profile applies from its next start.
Incorrect — Complain-mode processes are counted in their own line. This count is for processes that never attached.
Incorrect — AppArmor applies to root too; the user ID is not the reason.
Incorrect — aa-status would then report the module missing; here it lists loaded profiles and enforced processes.
06On a default Ubuntu 26.04 install, you enforce a profile for a small tool, and it fails with "Permission denied". sudo ausearch finds nothing because auditd is not installed. Where is the denial recorded?
Incorrect — That file exists once auditd is installed, which a default Ubuntu Server is not.
Correct — Without auditd, AppArmor's records reach the kernel log as audit type=1400 lines.
Incorrect — aa-status reports profile and process modes; it keeps no log of denials.
Incorrect — Implicit denials are logged in enforce mode; without auditd they go to the kernel log.
07During a review of a Rocky 10.2 server, sestatus reports the current mode as permissive, while the mode from the config file is enforcing. What does that tell you?
Incorrect — A failed load shows in the policy fields. A live mode that differs from the config points to setenforce.
Incorrect — The config file says enforcing, so the next boot enforces. The live mode is the odd one out.
Incorrect — A permissive domain leaves getenforce at Enforcing; it affects that one domain.
Correct — Denials are only logged now, for every service on the host. Raise it as a finding and find out who switched it and why.
08Content for Apache lives in a new directory, /srv/hard-selinux, labelled var_t. Why is semanage fcontext -a followed by restorecon -Rv preferred over chcon -t httpd_sys_content_t?
Incorrect — chcon can set the type with -t. The problem is what happens to its change later.
Incorrect — restorecon labels files. Process domains come from the policy's transitions.
Correct — semanage fcontext -l -C lists it as a local customisation, so every future relabel agrees with it.
Incorrect — chcon works in enforcing mode. The lesson allows it for quick tests.
8 questions · explanations appear as you answer
Integrity and compliance
6 questions
01An AIDE check on Ubuntu reports the line "f >.... mc..H.. . : /usr/local/bin/hard-integrity-report". What changed about the file?
Incorrect — H is also present, so the content hashes changed.
Correct — > in the size position means larger, m and c are the modification and change times, and H the content hashes.
Incorrect — An inode change shows as i, and unchanged contents would not show H.
Incorrect — The first character is the file type and > is in the size position. Mode and owner changes show as p, u or g.
02Ubuntu's dailyaidecheck job runs with COMMAND=update and COPYNEWDB=no. A planned package install happened yesterday and was reported last night. What will tonight's report show?
Correct — aide.db.new is rewritten each day, while aide.db stays the baseline until a person copies the new file into place.
Incorrect — update writes aide.db.new. With COPYNEWDB=no it does not replace aide.db.
Incorrect — Each run compares with aide.db, which has not moved.
Incorrect — The job overwrites aide.db.new every day, as the lab's run did.
03A Rocky 10.2 host is suspected of running a kernel-level rootkit. aide --check on the host lists only the RPM database and linker cache changes you expected from a patch window. How much does that result tell you?
Incorrect — The hashes are sound. The question is whether the bytes AIDE read were the real ones.
Incorrect — AIDE sees files as the running kernel presents them, and a rootkit can hide its own.
Incorrect — NORMAL covers /boot as the host's kernel shows it, and that kernel is the suspect.
Correct — Run the comparison from another system or trusted boot media, with a database and an aide binary kept off the host.
04The CIS Level 1 Server scan of the Rocky lab server counts "125 fail", "32 notapplicable" and "167 pass", and results.xml gives a score of 69.209465. A manager asks whether 69% of the rules passed. What do you answer?
Incorrect — 167 of 292 applicable rules is about 57%, so the score is not that ratio.
Incorrect — 167 of 324 is about 52%. Neither ratio gives 69.
Correct — Compare scores for the same profile over time, and read the failing rules rather than the number.
Incorrect — The score describes results, not which fixes exist.
05Before running the generated remediation for sshd_disable_root_login on Rocky, you read it. Besides writing PermitRootLogin no to 00-complianceascode-hardening.conf, what does it do that could cause trouble?
Correct — Files owned by configuration management or cloud-init get rewritten on their next run, and the two start to fight.
Incorrect — The snippet writes server files under sshd_config.d; the client configuration is untouched.
Incorrect — It uses 00-, which sorts first and wins under sshd's first-value rule.
Incorrect — The fix edits files; it does not touch the service.
06How does the lesson describe the difference between the CIS Level 1 Server and Level 2 Server profiles?
Incorrect — Both levels have server and workstation profiles; cis_server_l1 is Level 1 Server.
Correct — The lab scanned cis_server_l1; the profile named plain cis is Level 2 Server.
Incorrect — Both levels are CIS's. Red Hat's security hardening guide is separate vendor guidance.
Incorrect — Level 2 is the stricter profile, not a subset.
6 questions · explanations appear as you answer