πŸ“– Chapter 0 0 Exam Tasks

RHCSA (EX200) on RHEL 10 β€” Complete Reverse-Learning Guide

Built from your five practice sets (254 actual question-heading occurrences, about 150 unique tasks after de-duplication), re-categorized by exam domain, ordered for learning, and completed with detailed answers. Gaps against the official RHEL 10 EX200 objectives (verified against redhat.com) are filled with added tasks marked [ADDED]. Topics that are not on the RHEL 10 blueprint (Podman, Stratis, vsftpd, RAID, logrotate, rsyslog) are answered in Appendix A so the core chapters stay exam-focused.

Reference basis (checked 2026-09-17): the official Red Hat EX200 exam page and objectives. Red Hat can revise objectives; recheck that page before booking. The mock exams in Appendix E are original blueprint-aligned simulations, not leaked or recalled exam content.

Conventions used throughout

Symbol Meaning
[S3 Q12] Source: Set 3, Question 12 in your original files
[ADDED] Not in your sets; added to cover an official objective or a frequently-seen real-exam task
Traps Things that lose points on the real exam (persistence, SELinux, wrong file, forgetting reboot check)
servera / serverb Red Hat lab convention. Your sets use linux5.lab, linuxA.lab etc.; keep those names in chapter tasks
Passwords redhat unless the task specifies otherwise (e.g. Password123)
🎯 Chapter 1 6 Exam Tasks

Part 0 β€” The Exam Environment and How to Attack It

  • Format: hands-on, 3 hours, no internet, no notes. man, info, /usr/share/doc and --help are available and expected to be used.
  • Machines: lab and practice environments commonly use two systems (servera and serverb), but the number and interface can vary. Tasks may reference another host for NFS or SSH.
  • Grading: configurations must persist after reboot; this requirement is stated on the official objective page. The published passing score is 210/300 (70%). Do not assume individual tasks have equal weight or that partial-credit behavior is fixed.
  • Root recovery: interrupting boot is an official objective and appears repeatedly in these practice sets. If a node is supplied without usable root credentials, recover it first because later work depends on administrative access.
  • Hostnames, IPs, repo URLs are given in the task text (e.g. http://content.example.com/rhel10/x86_64/dvd/BaseOS). Copy them exactly; a typo in a repo URL fails every subsequent package task.
  • Documentation ships with the OS. If you forget a keyword, man 5 fstab, man 5 crontab, man nmcli-examples, man semanage-fcontext, man sshd_config, man 5 sudoers, man lvcreate, man autofs (man 5 auto.master) are the pages that save you.
Phase Time What
1 0:00–0:15 Read every task once. Reset root password on the locked node. Configure repos on both nodes (nearly everything depends on packages). Configure network/hostname if asked.
2 0:15–1:45 Do all "sure" tasks: users/groups/permissions, cron/at/timers, LVM/swap/fstab, SELinux, firewall, httpd, NFS/autofs, scripts.
3 1:45–2:15 Harder tasks: tuned, journald, chrony, autofs home directories, thin LVM, container-like oddities.
4 2:15–2:45 Reboot both nodes. Verify every task survived (Appendix B checklist).
5 2:45–3:00 Fix anything broken. Do not start new risky work in the last 10 minutes.
  1. Persistence is everything. --permanent + --reload for firewalld; /etc/fstab for mounts; /etc/sysctl.d/ for kernel params; systemctl enable --now for services; nmcli con mod (not ip addr add); timedatectl/hostnamectl (not hostname).
  2. SELinux is enforcing and must stay so unless a task says otherwise. New web roots, moved files, and non-standard ports all need semanage fcontext/restorecon or semanage port.
  3. Test the fstab before rebooting: umount /mnt/x && mount -a && findmnt /mnt/x. A broken fstab drops you into emergency mode and costs 10+ minutes.
  4. Reboot before the end. A non-booting system prevents persistent work from being graded reliably. Reboot with 30–45 minutes left so there is time to repair it.
  5. Use exactly the names given (user names, group names, mount points, file names, LV names). Graders match strings.
  6. Sizes: "1 GiB" β†’ 1G; "512 MiB" β†’ 512M; "50 extents of 16 MiB" β†’ -l 50 with vgcreate -s 16M. Read whether it says size or extents.
  7. Scripts must be executable (chmod +x), have a #!/bin/bash shebang, and be placed at the exact path. Test them.
  8. Never delete or reformat existing partitions/LVs unless told. "Non-destructively" is in the objectives on purpose.
  9. If dnf fails, the repo is wrong; fix it before anything else β€” dnf repolist, dnf clean all.
  10. Don't over-engineer. The grader checks results, not method. Prefer the simplest persistent way.
BASH emergency shell (initramfs)
πŸ“ŒOn the locked node: reboot, e -> add rd.break to linux line -> Ctrl-x
mount -o remount,rw /sysroot
chroot /sysroot
passwd root
touch /.autorelabel
exit; exitπŸ’‘system continues booting; relabel takes 1-3 minutes
 
πŸ“ŒOn both nodes, repos (URLs come from the exam text)
cat > /etc/yum.repos.d/rhel.repo <<'EOF'
[BaseOS]
name=BaseOS
baseurl=http://content.example.com/rhel10/x86_64/dvd/BaseOS
enabled=1
gpgcheck=0
 
[AppStream]
name=AppStream
baseurl=http://content.example.com/rhel10/x86_64/dvd/AppStream
enabled=1
gpgcheck=0
EOF
dnf clean all; dnf repolist; dnf -y install vim bash-completion tuned
Area RHEL 9 exam RHEL 10 exam
Containers (Podman) Included Removed (see Appendix A)
Flatpak β€” Added: configure Flatpak repositories, install/remove Flatpak apps
Scheduling at, cron at, cron and systemd timer units
Partitions MBR and GPT GPT only mentioned
Archiving tar, star, gzip, bzip2 tar, gzip, bzip2
dnf modules dnf module selected modular streams RHEL 10 distributes no modular content; use ordinary RPM package names
SSH root login default prohibit-password Same; config via /etc/ssh/sshd_config.d/ drop-ins
Network config keyfiles (ifcfg deprecated) keyfiles only (/etc/NetworkManager/system-connections/)
GRUB grubby / grub2-mkconfig Same; /boot/grub2/grub.cfg for BIOS and UEFI
Objective group Chapter
Understand and use essential tools Ch 3
Manage software (RPM repos, dnf, Flatpak) Ch 2
Create simple shell scripts Ch 11
Operate running systems (boot, targets, root recovery, processes, tuned, journals, scp) Ch 1, Ch 8
Configure local storage Ch 6
Create and configure file systems (ext4/XFS/VFAT, NFS, autofs, extend LV, permissions) Ch 6, Ch 10, Ch 5
Deploy, configure, maintain (at/cron/timers, services, targets, chrony, packages, bootloader) Ch 8, Ch 1, Ch 2
Manage basic networking (IPv4/IPv6, hostname, firewalld) Ch 7
Manage users and groups Ch 4
Manage security (firewalld, umask, SSH keys, SELinux) Ch 5, Ch 9

πŸ”„ Chapter 2 10 Exam Tasks

Chapter 1 β€” Boot Process, GRUB, systemd Targets, Root Recovery

Objectives covered: Interrupt the boot process to gain access to a system Β· Boot systems into different targets manually Β· Configure systems to boot into a specific target automatically Β· Modify the system bootloader Β· Boot, reboot and shut down normally.

Why first: the exam almost always begins with a node whose root password you do not know. Every other task on that node depends on this chapter.


πŸ”₯ 8x in Exam Sets
S1 Q23 S2 Q1 S3 Q1 S3 Q51 S4 Q1 S5 Q35 +2 more
🎯 Exam Objective

Task. You do not know root's password on the RHEL 10 server. Reset it to redhat (or Password123 where specified) and make sure the system boots normally into its default target.

TEXT emergency shell (initramfs)
1.Reboot the VM. When the GRUB menu appears, press any key (e.g. ↑) to stop the countdown.
2.Highlight the default kernel entry and press e to edit.
3.Move to the line beginning with linux (use ↓ / End). Append at the end of that line:
rd.break
(Optional but useful if the line contains them: remove rhgb quiet so you see messages.)
4.Press Ctrl-x to boot with these parameters. You land in an emergency shell inside the initramfs;
the real root filesystem is mounted READ-ONLY on /sysroot.
BASH emergency shell (initramfs)
mount -o remount,rw /sysrootπŸ’‘make the real root writable
chroot /sysrootπŸ’‘switch into the real root filesystem
passwd rootπŸ’‘type new password twice (redhat)
touch /.autorelabelπŸ’‘SELinux: relabel everything on next boot (shadow file got wrong context)
exitπŸ’‘leave chroot
exitπŸ’‘leave initramfs shell -> boot continues

The system continues to boot and relabels the filesystem before reaching normal login. Duration depends on filesystem size; do not interrupt it.

🧠 Why This Works (Concept)

Why.

  • rd.break stops the boot inside the initramfs, before control is handed to systemd on the real root β€” no root password is needed at this point.
  • /sysroot is read-only by default; without remount,rw passwd cannot write /etc/shadow.
  • passwd inside the emergency shell runs without SELinux policy loaded, so /etc/shadow ends up with the wrong context (unlabeled_t). Without the relabel, logins fail in enforcing mode. /.autorelabel triggers fixfiles on boot.
βœ… Verification Steps

Verify.

BASH root@servera
πŸ“Œafter reboot
su -πŸ’‘from a console user, or log in as root at the console
ls -Z /etc/shadowπŸ’‘should show system_u:object_r:shadow_t:s0
getenforceπŸ’‘Enforcing
systemctl get-defaultπŸ’‘multi-user.target or graphical.target (unchanged)
⚠️ Exam Traps & Pitfalls

Traps.

  • Forgetting touch /.autorelabel β†’ login "Permission denied" even with the correct password. If this happens, boot again with rd.break, and this time run restorecon -v /etc/shadow inside the chroot or touch /.autorelabel.
  • Faster alternative that avoids the relabel wait: inside the chroot, run load_policy -i before passwd, then restorecon -v /etc/shadow instead of touching /.autorelabel β€” but only if you know these commands; the autorelabel route is the safe default.
  • Keyboard layout in GRUB is US. On US keyboards = and . are where you expect; on others they may not be. Type slowly.
  • UEFI systems: same procedure; GRUB screen may look different but e / Ctrl-x are identical.
  • If the GRUB menu is hidden (0-second timeout), hold Shift or press Esc repeatedly during firmware POST.
  • RHEL 10 may show a "kernel lockdown"/secure boot notice; rd.break still works on exam VMs.

Alternative method (also accepted): init=/bin/bash

TEXT root@servera
e -> on the linux line replace "rhgb quiet" (or append) with: init=/bin/bash -> Ctrl-x
BASH emergency shell (initramfs)
mount -o remount,rw /
passwd root
touch /.autorelabel
exec /sbin/initπŸ’‘or: /usr/sbin/reboot -f

Here you are already on the real root (no chroot). Use whichever you remember best; rd.break is what Red Hat teaches.


⭐ Official Objective
🎯 Exam Objective

Task. Boot the system once into rescue.target (single-user, filesystems mounted) or emergency.target (minimal, root read-only) without changing the default.

TEXT GRUB Menu Editor
GRUB: e -> end of linux line -> append one of:
systemd.unit=rescue.target
systemd.unit=emergency.target
systemd.unit=multi-user.target
-> Ctrl-x

Rescue mode asks for root's password. In emergency mode: mount -o remount,rw / before editing files such as /etc/fstab.

From a running system (no reboot):

BASH root@servera
systemctl isolate rescue.targetπŸ’‘drop to rescue
systemctl isolate multi-user.targetπŸ’‘back to text multi-user
systemctl isolate graphical.targetπŸ’‘to GUI
systemctl rescueπŸ’‘shortcut to rescue mode
systemctl emergencyπŸ’‘shortcut to emergency mode
βœ… Verification Steps

Verify. systemctl list-units --type=target --state=active shows the active target.

⚠️ Exam Traps & Pitfalls

Traps. systemd.unit= is a one-boot change β€” the default is untouched. If the system drops to emergency mode on its own after a reboot, the cause is almost always a bad /etc/fstab line: journalctl -xb shows it; fix with mount -o remount,rw /, vim /etc/fstab, systemctl daemon-reload, reboot.


πŸ”₯ 4x in Exam Sets
S2 Q12 S4 Q9 S5 Q6 S5 Q39
🎯 Exam Objective

Task. Make the system boot into multi-user.target (text) β€” or into graphical.target (GUI) β€” by default.

BASH root@servera
systemctl get-defaultπŸ’‘see current
systemctl set-default multi-user.targetπŸ’‘text mode
πŸ“Œor
systemctl set-default graphical.targetπŸ’‘GUI mode (needs GNOME installed: dnf group install "Server with GUI")
systemctl isolate multi-user.targetπŸ’‘switch now without reboot (optional)
🧠 Why This Works (Concept)

Why. set-default replaces the symlink /etc/systemd/system/default.target β†’ /usr/lib/systemd/system/<target>. That symlink is read at every boot, so it persists.

βœ… Verification Steps

Verify.

BASH root@servera
systemctl get-default
ls -l /etc/systemd/system/default.target
rebootπŸ’‘and confirm login screen type
⚠️ Exam Traps & Pitfalls

Traps.

  • Setting graphical.target on a machine without a desktop environment boots to text anyway but the objective is still met if the symlink is correct. If the task explicitly says "starts with the GUI", install it: dnf group install -y "Server with GUI" (takes minutes β€” start early).
  • Do not edit /etc/inittab (does not exist) or use telinit numbers. Runlevel 3 β‰ˆ multi-user.target, 5 β‰ˆ graphical.target, 1 β‰ˆ rescue.target.

πŸ”₯ 2x in Exam Sets
S1 Q15 S5 Q31
🎯 Exam Objective

Task. Make the kernel print verbose boot messages on every boot, for every installed kernel.

BASH root@servera
grubby --info=ALL | grep -E '^(index|kernel|args)'πŸ’‘inspect current args
grubby --update-kernel=ALL --remove-args="quiet rhgb"πŸ’‘persistent, all entries

Also fix the template so future kernels do not re-add them:

BASH GRUB Menu Editor
vim /etc/default/grub
πŸ“ŒGRUB_CMDLINE_LINUX="... rhgb quiet" -> delete rhgb quiet
grub2-mkconfig -o /boot/grub2/grub.cfg --update-bls-cmdline
🧠 Why This Works (Concept)

Why. grubby edits the BootLoaderSpec entries in /boot/loader/entries/*.conf, which is what RHEL 8+ actually boots from. /etc/default/grub is the template consumed by grub2-mkconfig; on RHEL 10 with BLS, grub2-mkconfig also propagates GRUB_CMDLINE_LINUX to the entries.

βœ… Verification Steps

Verify.

BASH root@servera
grubby --info=DEFAULT | grep argsπŸ’‘no quiet/rhgb
cat /proc/cmdlineπŸ’‘after reboot
⚠️ Exam Traps & Pitfalls

Traps.

  • To add an argument: grubby --update-kernel=ALL --args="foo=bar".
  • On RHEL 10, /boot/grub2/grub.cfg is the correct output path on both BIOS and UEFI (/boot/efi/EFI/redhat/grub.cfg is now a stub that redirects). Do not overwrite the EFI stub.
  • Removing rhgb disables the graphical boot splash; quiet suppresses kernel messages. The task says "detailed boot messages" β†’ remove both.

πŸ”₯ 2x in Exam Sets
S4 Q34 S4 Q41
🎯 Exam Objective

Task. Explain/perform a persistent GRUB change (e.g., set timeout to 10 s).

Answer.

File Role Edit?
/etc/default/grub Template: GRUB_TIMEOUT, GRUB_CMDLINE_LINUX, GRUB_DEFAULT Yes
/etc/grub.d/* Scripts that generate menu sections Rarely
/boot/grub2/grub.cfg Generated main config (BIOS & UEFI on RHEL 10) Never by hand
/boot/loader/entries/*.conf BLS entries, one per kernel (what grubby edits) Via grubby
/boot/grub2/grubenv Saved default entry / env variables Via grub2-editenv/grubby
BASH root@servera
vim /etc/default/grubπŸ’‘e.g. GRUB_TIMEOUT=10
grub2-mkconfig -o /boot/grub2/grub.cfg
βœ… Verification Steps

Verify. grep timeout /boot/grub2/grub.cfg; reboot and observe the menu.

⚠️ Exam Traps & Pitfalls

Traps. For kernel arguments prefer grubby (persists across kernel updates because BLS entries are what is used). For anything else edit /etc/default/grub then regenerate.


πŸ”₯ 2x in Exam Sets
S2 Q14 S4 Q8
🎯 Exam Objective

Task. Install the latest kernel from the configured repo. After reboot the new kernel must be default; the previous kernel remains bootable.

BASH root@servera
uname -rπŸ’‘current
dnf -y install kernelπŸ’‘NOT "dnf update kernel" β€” install keeps old kernels side by side
πŸ“Œ(dnf update kernel also installs alongside on RHEL; both are fine because kernel is "installonly")
grubby --default-kernelπŸ’‘newest is default automatically
grubby --info=ALL | grep -E 'index|title'
reboot
uname -rπŸ’‘new version
🧠 Why This Works (Concept)

Why. kernel is an installonly package (installonly_limit=3 in /etc/dnf/dnf.conf), so dnf install kernel adds a new version without removing the old. grubby and the BLS system make the newest installed kernel the default unless GRUB_DEFAULT=saved points elsewhere.

βœ… Verification Steps

Verify.

BASH root@servera
rpm -q kernelπŸ’‘lists both
grubby --default-indexπŸ’‘0 = newest
ls /boot/loader/entries/πŸ’‘one file per kernel
⚠️ Exam Traps & Pitfalls

Traps.

  • If the exam provides a kernel RPM by URL: dnf -y install http://server/pub/kernel-6.x.rpm (or rpm -ivh, never rpm -Uvh for kernels β€” -U removes the old one).
  • If the new kernel is not default: grubby --set-default /boot/vmlinuz-<new-version> or grubby --set-default-index=0.

πŸ”₯ 1x in Exam Sets
S2 Q24
🎯 Exam Objective

Task. Make the kernel at GRUB index 2 the default.

BASH root@servera
grubby --info=ALL | grep -E 'index|kernel'πŸ’‘map index -> vmlinuz
grubby --set-default-index=2
grubby --default-indexπŸ’‘2
grubby --default-kernel

Equivalent: grub2-set-default 2 (writes saved_entry to grubenv; requires GRUB_DEFAULT=saved, which is the RHEL default).

βœ… Verification Steps

Verify. grub2-editenv list shows saved_entry=...; reboot and uname -r.

⚠️ Exam Traps & Pitfalls

Traps. Index counting starts at 0 and the newest kernel is 0. The rescue entry usually has the highest index.


πŸ”₯ 1x in Exam Sets
S5 Q43
🎯 Exam Objective

Task. Rebuild the rescue image and initramfs for the running kernel.

BASH root@servera
πŸ“ŒRegenerate the normal initramfs for the running kernel
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)
 
πŸ“ŒRegenerate the rescue image (remove old one first, then let the kernel-install plugin recreate it)
rm -f /boot/vmlinuz-0-rescue-* /boot/initramfs-0-rescue-* /boot/loader/entries/*-0-rescue.conf
kernel-install add $(uname -r) /lib/modules/$(uname -r)/vmlinuz
βœ… Verification Steps

Verify. ls -l /boot/*rescue* shows fresh timestamps; grubby --info=ALL | grep rescue.

⚠️ Exam Traps & Pitfalls

Traps. dracut without -f refuses to overwrite. Low probability on the real exam; know dracut -f at minimum.


⭐ Official Objective
BASH root@servera
systemctl rebootπŸ’‘= reboot
systemctl poweroffπŸ’‘= poweroff ; shutdown -h now
shutdown -r +5 "Rebooting in 5 minutes"πŸ’‘scheduled; cancel with shutdown -c
systemctl halt
⚠️ Exam Traps & Pitfalls

Traps. shutdown with no time argument defaults to +1 minute, not now. Use shutdown -r now or reboot.


  1. Reset root password with rd.break including relabel. (Target: < 5 minutes including boot.)
  2. Set default target to multi-user, verify symlink.
  3. Remove quiet rhgb from all kernels with grubby, confirm in /proc/cmdline after reboot.
  4. Boot once into rescue.target via GRUB, then back.
  5. Install a kernel and confirm both kernels appear in GRUB.

πŸ“¦ Chapter 3 8 Exam Tasks

Chapter 2 β€” Software Management: Repositories, dnf, rpm, Flatpak

Objectives covered: Configure access to RPM repositories Β· Install and remove RPM software packages Β· Install and update software packages from Red Hat CDN, a remote repository, or the local file system Β· Configure access to Flatpak repositories Β· Install and remove Flatpak software packages.

Why second: on the exam, both nodes come with no working repository. Repo configuration unlocks every package-dependent task (httpd, autofs, chrony, tuned, …). Do it right after the root reset.


πŸ”₯ 3x in Exam Sets
S1 Q1 S2 Q33 S5 Q27
🎯 Exam Objective

Task. A RHEL 10 ISO is mounted at /mnt/media (or /repo, /opt). Configure the system to use it as the repository for BaseOS and AppStream, and verify.

BASH root@servera
ls /mnt/mediaπŸ’‘confirm: BaseOS AppStream EFI images media.repo ...
 
cat > /etc/yum.repos.d/local.repo <<'EOF'
[local-baseos]
name=RHEL 10 BaseOS (ISO)
baseurl=file:///mnt/media/BaseOS
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
 
[local-appstream]
name=RHEL 10 AppStream (ISO)
baseurl=file:///mnt/media/AppStream
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
EOF
 
dnf clean all
dnf repolistπŸ’‘both repos listed with package counts
dnf -y install treeπŸ’‘prove it works

Make the ISO mount itself persistent if the task expects it to survive reboot (the ISO file path is usually given):

BASH root@servera
echo '/root/rhel-10.iso /mnt/media iso9660 loop,ro 0 0' >> /etc/fstab
mount -a; findmnt /mnt/media
🧠 Why This Works (Concept)

Why.

  • file:// URLs need three slashes (file:///path).
  • A RHEL ISO ships two repos in two sub-directories; each needs its own [section] because each has its own repodata/.
  • gpgcheck=1 with the shipped key is correct for signed Red Hat media. Use gpgcheck=0 only when the task says so or the supplied lab repository is confirmed unsigned.
βœ… Verification Steps

Verify.

BASH root@servera
dnf repolist -v | grep -E 'Repo-id|Repo-baseurl'
dnf info tree
⚠️ Exam Traps & Pitfalls

Traps.

  • Missing repodata/ directory inside the baseurl path β†’ "Cannot download repomd.xml". Point to the directory that contains repodata/.
  • Leaving enabled=0 or a typo in section name ([repo name] with spaces is invalid).
  • Alternative: dnf config-manager --add-repo file:///mnt/media/BaseOS creates a repo file automatically (dnf-plugins-core installed by default on RHEL 10) β€” you still need to add gpgcheck=0 or the key.

πŸ”₯ 2x in Exam Sets
S3 Q2 S4 Q32
🎯 Exam Objective

Task. A repo server exists at http://192.168.1.200 (typical exam: http://content.example.com/rhel10/x86_64/dvd/BaseOS and .../AppStream). Configure the client to use only this repository, then install httpd.

BASH root@servera
πŸ“Œ1. Disable everything else (rename rather than delete, to be safe)
mkdir -p /etc/yum.repos.d/disabled
mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/disabled/ 2>/dev/null
πŸ“ŒRHEL 10 with subscription-manager: also stop it from re-injecting redhat.repo
subscription-manager config --rhsm.manage_repos=0 2>/dev/null || true
 
πŸ“Œ2. Create the repo file
cat > /etc/yum.repos.d/localrepo.repo <<'EOF'
[localrepo-baseos]
name=localrepo BaseOS
baseurl=http://192.168.1.200/BaseOS
enabled=1
gpgcheck=0
 
[localrepo-appstream]
name=localrepo AppStream
baseurl=http://192.168.1.200/AppStream
enabled=1
gpgcheck=0
EOF
 
πŸ“Œ3. Use it
dnf clean all
dnf repolist
dnf -y install httpd

One-liner alternative for a single repo (dnf config-manager requires the relevant plugin; install dnf-plugins-core if unavailable):

BASH root@servera
dnf config-manager --add-repo http://192.168.1.200/BaseOS
dnf config-manager --set-disabled '*' ; dnf config-manager --set-enabled 192.168.1.200_BaseOS
🧠 Why This Works (Concept)

Why. enabled=0 in other files or moving them away satisfies "disables all other repositories". gpgcheck=0 because a lab HTTP repo is normally unsigned; if the task provides a key URL, use gpgcheck=1 + gpgkey=http://.../RPM-GPG-KEY-redhat-release.

βœ… Verification Steps

Verify.

BASH root@servera
dnf repolist --allπŸ’‘others show "disabled"
dnf repoinfo localrepo-baseos
curl -s http://192.168.1.200/BaseOS/repodata/repomd.xml | head -3πŸ’‘URL sanity
rpm -q httpd
⚠️ Exam Traps & Pitfalls

Traps.

  • If the exam URL includes the repo directory (e.g. .../dvd/BaseOS), do not append /BaseOS again.
  • dnf repolist shows nothing β†’ check enabled=1, section header brackets, and curl the URL.
  • On a registered RHEL box subscription-manager may keep a redhat.repo; disabling manage_repos or setting enabled=0 inside it is acceptable.

πŸ”₯ 1x in Exam Sets
S5 Q37
🎯 Exam Objective

Task. Install the zsh package that exists as zsh.rpm on ftp://server1.example.com/pub/updates.

BASH root@servera
πŸ“Œmake the name resolvable if DNS is not configured (see 7.5)
echo '192.168.0.254 server1.example.com server1' >> /etc/hosts
 
πŸ“ŒOption A: dnf installs directly from a URL (resolves deps from configured repos)
dnf -y install ftp://server1.example.com/pub/updates/zsh.rpm
 
πŸ“ŒOption B: download then install
cd /tmp && curl -O ftp://server1.example.com/pub/updates/zsh.rpmπŸ’‘or: wget URL
dnf -y localinstall /tmp/zsh.rpmπŸ’‘or: dnf install ./zsh.rpm or: rpm -ivh zsh.rpm

If the task says the FTP directory is a repository (has repodata/):

BASH root@servera
cat > /etc/yum.repos.d/updates.repo <<'EOF'
[updates]
name=updates
baseurl=ftp://server1.example.com/pub/updates
enabled=1
gpgcheck=0
EOF
dnf -y install zsh
βœ… Verification Steps

Verify. rpm -q zsh; dnf history info last.

⚠️ Exam Traps & Pitfalls

Traps. rpm -ivh fails on missing dependencies; prefer dnf install <file>, which resolves them. curl -O keeps the remote file name; curl -o name sets it.


πŸ”₯ 7x in Exam Sets
S2 Q48 S3 Q44 S3 Q31 S4 Q42 S4 Q43 S4 Q44 +1 more
BASH root@servera
πŸ“ŒRepositories
dnf repolistπŸ’‘enabled
dnf repolist --allπŸ’‘incl. disabled
dnf repoinfo
 
πŸ“ŒSearch / info
dnf search httpd
dnf search "web server"πŸ’‘searches name+summary
dnf info httpd
dnf provides */semanageπŸ’‘which package has a file
dnf list installed | grep gcc
dnf list available httpd
 
πŸ“ŒInstall / remove / update
dnf -y install gcc httpd
dnf -y remove httpd
dnf -y updateπŸ’‘everything
dnf -y update httpd
dnf -y reinstall httpd
dnf group list; dnf group install -y "Development Tools"
 
πŸ“ŒHistory / undo
dnf history
dnf history info 12
dnf history undo 12
 
πŸ“ŒVerify with rpm
rpm -q gccπŸ’‘installed version
rpm -qi httpdπŸ’‘info
rpm -ql httpdπŸ’‘files
rpm -qc httpdπŸ’‘config files
rpm -qd httpdπŸ’‘docs
rpm -qf /etc/httpd/conf/httpd.confπŸ’‘which package owns a file
rpm -qa | grep -i kernel
rpm -V httpdπŸ’‘verify integrity vs. db
rpm -qpl ./zsh.rpmπŸ’‘inspect an uninstalled rpm file (-p)
rpm -qp --scripts ./zsh.rpm
⚠️ Exam Traps & Pitfalls

Traps.

  • dnf install prompts unless -y. In scripts always -y.
  • RHEL 10 distributes no modular Application Stream content. A compatibility dnf module command may still emit a deprecation warning, but candidates should install ordinary RPM package names. See 2.7.
  • Do not confuse rpm -e (erase) with -E. Never use rpm -Uvh on kernel packages.

πŸ”₯ 3x in Exam Sets ⭐ Official Objective
S1 Q28 S2 Q28 S2 Q29
🎯 Exam Objective

Task. Configure access to a Flatpak repository (remote) and install a Flatpak application from it; also be able to remove it and list what is installed. On the exam the remote URL/.flatpakrepo file is given (e.g. http://content.example.com/flatpak/repo.flatpakrepo).

BASH root@servera
dnf -y install flatpakπŸ’‘usually preinstalled on Workstation, not on Server
 
πŸ“ŒAdd a remote system-wide (name it exactly as the task says)
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
πŸ“Œexam-style local mirror:
flatpak remote-add --if-not-exists --no-gpg-verify examplerepo http://content.example.com/flatpak/repo
πŸ“Œ(--no-gpg-verify only if the repo is unsigned; add --user for a per-user remote)
 
flatpak remotesπŸ’‘list remotes (name, options)
flatpak remote-ls examplerepoπŸ’‘what the remote offers
flatpak search gimpπŸ’‘search across remotes
 
πŸ“ŒInstall (application ID is reverse-DNS)
flatpak install -y examplerepo org.gimp.GIMP
flatpak install -y --user flathub org.gnome.CalculatorπŸ’‘per-user
 
flatpak listπŸ’‘installed apps + runtimes
flatpak list --app
flatpak info org.gimp.GIMP
flatpak run org.gimp.GIMP
 
πŸ“ŒUpdate / remove
flatpak update -y
flatpak uninstall -y org.gimp.GIMP
flatpak uninstall --unused -yπŸ’‘drop orphan runtimes
flatpak remote-delete examplerepo
🧠 Why This Works (Concept)

Why.

  • A Flatpak remote is the equivalent of a dnf repository; the .flatpakrepo file carries URL + GPG key. Adding by plain URL needs --no-gpg-verify unless you also pass --gpg-import=key.gpg.
  • System installs live in /var/lib/flatpak; user installs in ~/.local/share/flatpak. Remote definitions: /var/lib/flatpak/repo/config.
  • Graders typically check flatpak remotes contains the given name and flatpak list contains the app ID.
βœ… Verification Steps

Verify.

BASH root@servera
flatpak remotes -d
flatpak list --app --columns=application,origin
⚠️ Exam Traps & Pitfalls

Traps.

  • Flatpak needs network to reach the remote; check curl first.
  • flatpak install <appid> without a remote name asks interactively; scripts/exam β†’ always give the remote and -y.
  • Removing the last app leaves runtimes installed; --unused cleans them (not usually required).
  • Adding a remote as root without --user = system-wide, which is what "configure the system" means.

πŸ”₯ 1x in Exam Sets
S1 Q27
BASH root@servera
dnf -y install container-toolsπŸ’‘meta-package: podman, buildah, skopeo, ...
πŸ“Œor individually:
dnf -y install podman buildah skopeo
rpm -q podman buildah skopeo

See Appendix A for Podman usage and Quadlet.


πŸ”₯ 1x in Exam Sets
S5 Q49

Answer (what to say/know):

  • RHEL splits content into BaseOS (core OS, 10-year lifecycle, traditional RPMs) and AppStream (userspace applications, languages, databases, web servers, whose versions can move faster than the OS).
  • In RHEL 8/9, AppStream additionally used modules (dnf module list/enable/install) to offer multiple parallel versions (streams) of e.g. nodejs:18 vs nodejs:20.
  • RHEL 10 distributes no modular content. AppStream is a normal RPM repository. Alternative versions are delivered as separately named packages (for example postgresql16 and postgresql17) or through other distribution methods. Use plain dnf install <name>; modularity compatibility commands are deprecated and do not select RHEL 10 application streams.
  • Practical difference vs. "traditional": lifecycle. BaseOS packages get the full 10 years; AppStream content may have shorter, documented lifecycles per package.

  1. From memory, write a two-section repo file for http://content.example.com/rhel10/x86_64/dvd/{BaseOS,AppStream} with gpgcheck=0, then dnf repolist shows both.
  2. dnf -y install httpd; verify with rpm -q and rpm -qc.
  3. Add a Flatpak remote named flathub, install org.gnome.Calculator, list, uninstall.
  4. Install an RPM from a URL with dnf.
  5. rpm -qf /etc/fstab β€” which package? (setup)

πŸ‘€ Chapter 5 10 Exam Tasks

Chapter 4 - Local Users, Groups, Password Policy, and Privileged Access

Objectives covered: Create, delete, and modify local user accounts; change passwords and password aging; create and manage local groups and memberships; configure superuser access; manage account shells, expiration, and lock state.

User and group work is mostly database work. Use the account-management commands instead of editing /etc/passwd, /etc/shadow, /etc/group, or /etc/gshadow directly.


πŸ”₯ 2x in Exam Sets ⭐ Official Objective
S1 Q21 S3 Q43
File Purpose Important fields
/etc/passwd Public account attributes name, UID, primary GID, comment, home, shell
/etc/shadow Password hash and password/account aging last change, minimum, maximum, warning, inactive, expiry
/etc/group Group name, GID, supplementary members name, GID, member list
/etc/gshadow Protected group administration data group administrators and members
BASH root@servera
getent passwd jerry
getent group friends
id jerry
id -u jerry
id -g jerry
id -Gn jerry
grep '^jerry:' /etc/passwd
🧠 Why This Works (Concept)

Why. A UID identifies a user and a GID identifies a group. File ownership is stored numerically; names are only database mappings. A user's primary group is the GID in /etc/passwd. Supplementary memberships are normally listed in /etc/group. getent is preferable to grep because it also works if identity sources are later provided by SSSD or another NSS source.

The normal default shell for a newly created regular user is /bin/bash, unless /etc/default/useradd specifies another shell. [S3 Q43]

βœ… Verification Steps

Verify. getent passwd jerry; id jerry. Check numeric file ownership with ls -ln when a name mapping looks wrong.

⚠️ Exam Traps & Pitfalls

Traps. [S1 Q21] asks you to save Jerry's passwd line to /tmp/users and edit that copy. It does not change Jerry's real shell; that text-processing task is covered in Chapter 3. To change the account, use usermod -s as shown below.


πŸ”₯ 12x in Exam Sets
S1 Q17 S1 Q34 S1 Q35 S1 Q39 S1 Q40 S2 Q19 +6 more

Representative task. Create the friends group, create Jerry and George, and make both supplementary members without removing any existing memberships.

BASH root@servera
getent group friends >/dev/null || groupadd friends
id jerry >/dev/null 2>&1 || useradd -m jerry
id george >/dev/null 2>&1 || useradd -m george
usermod -aG friends jerry
usermod -aG friends george
passwd jerry
passwd george

Apply the same pattern to the duplicate sets:

BASH root@servera
groupadd -f gfandneighbor
for u in elaine kramer; do id "$u" >/dev/null 2>&1 || useradd -m "$u"; usermod -aG gfandneighbor "$u"; done
 
groupadd -f accounting
groupadd -f finance
for u in babu peter; do id "$u" >/dev/null 2>&1 || useradd -m "$u"; usermod -aG accounting "$u"; done
for u in newmen david; do id "$u" >/dev/null 2>&1 || useradd -m "$u"; usermod -aG finance "$u"; done
 
groupadd -f contractors
groupadd -f eastside
groupadd -f admin
groupadd -f monk
groupadd -f reggis
 
πŸ“ŒIdentities needed by the ACL tasks in Chapter 5
for u in cosmo peterman bania kenny; do id "$u" >/dev/null 2>&1 || useradd -m "$u"; done
🧠 Why This Works (Concept)

Why. useradd -m creates the home directory if needed. On RHEL, useradd normally creates a same-named private primary group. usermod -aG appends supplementary groups; -G without -a replaces the entire supplementary group list. groupadd -f succeeds harmlessly when the named group already exists.

For a script such as [S2 Q39] or [S2 Q40], test before changing state:

BASH root@servera
if ! id testuser >/dev/null 2>&1; then
useradd -m -d /home/testuser testuser
passwd testuser
fi
getent group testgroup >/dev/null || groupadd testgroup
usermod -aG testgroup testuser

Use interactive passwd; do not put a cleartext exam password in shell history. If automation is explicitly required, printf '%s\n' 'testuser:password' | chpasswd works but exposes a weak secret to the script author and should not be used in production.

βœ… Verification Steps

Verify.

BASH root@servera
getent group friends
id jerry
id george
su - jerry -c 'id'
⚠️ Exam Traps & Pitfalls

Traps. Existing login sessions retain their old supplementary group list. Log out and back in, use su - user, or use newgrp friends for a new shell. Do not add users by editing the comma-separated member field manually.


πŸ”₯ 8x in Exam Sets
S2 Q18 S2 Q31 S2 Q43 S3 Q7 S3 Q36 S3 Q42 +2 more

Representative tasks. Create Bobca with UID 2000; create a user with UID 1001 and primary GID 1002; create group admin with GID 600.

BASH root@servera
useradd -m -u 2000 bobca
 
groupadd -g 1002 appteam
useradd -m -u 1001 -g appteam appuser
 
groupadd -g 600 admin

Numeric form is accepted for an existing primary group:

BASH root@servera
useradd -m -u 1001 -g 1002 appuser
🧠 Why This Works (Concept)

Why. -u sets UID, groupadd -g sets GID, and useradd -g selects an existing primary group. -G is for supplementary groups and is not interchangeable with -g.

βœ… Verification Steps

Verify.

BASH root@servera
getent passwd bobca
getent passwd appuser
getent group 1002
id appuser
⚠️ Exam Traps & Pitfalls

Traps. Check first: getent passwd 1001; getent group 1002. Reusing a UID or GID causes ambiguous ownership. Avoid -o unless duplicate numeric identities are explicitly required. Creating a user with -g 1002 fails unless that group already exists.


πŸ”₯ 5x in Exam Sets
S2 Q18 S3 Q6 S3 Q43 S4 Q10 S1 Q21
BASH root@servera
πŸ“ŒCreate accounts that cannot obtain an interactive login shell
useradd -m -u 1500 -s /sbin/nologin simpson
useradd -m -u 5000 -s /sbin/nologin sally
 
πŸ“ŒChange an existing user's shell
usermod -s /bin/bash marty
πŸ“Œequivalent interactive command:
chsh -s /bin/bash marty
 
πŸ“ŒDisable shell login without deleting the account
usermod -s /sbin/nologin serviceacct
🧠 Why This Works (Concept)

Why. /sbin/nologin rejects interactive logins and prints a refusal message. It is appropriate for service or file-transfer-only identities. /bin/false also exits immediately, but /sbin/nologin communicates intent clearly and is the usual exam answer.

βœ… Verification Steps

Verify.

BASH root@servera
getent passwd simpson
getent passwd marty
grep -Fx /bin/bash /etc/shells
su - simpson
⚠️ Exam Traps & Pitfalls

Traps. A nologin shell is not the same as a locked password. It may not prevent every non-shell access mechanism. Apply the control the task names. Use a shell listed in /etc/shells for normal users.


πŸ”₯ 3x in Exam Sets ⭐ Official Objective
S3 Q7 S3 Q19 S5 Q7

These controls are different:

Control Command example Effect
Account expiry usermod -e 2026-12-21 peter Account cannot log in on/after expiry date
Password maximum age chage -M 90 peter Password must be changed after 90 days
Force password change chage -d 0 peter Change required at next password login
Inactive after password expiry chage -I 7 peter Disable after 7 days with an expired password
Password lock usermod -L marty Prefixes the password hash so password auth fails

Create the dated accounts from the sets:

BASH root@servera
useradd -m -u 1250 -e 2026-12-21 peter
useradd -m -u 1451 -e 2027-06-06 john

Disable Marty without deleting files:

BASH root@servera
usermod -L marty
πŸ“Œor: passwd -l marty

Unlock when requested:

BASH root@servera
usermod -U marty
πŸ“Œor: passwd -u marty
βœ… Verification Steps

Verify.

BASH root@servera
chage -l peter
passwd -S marty
getent shadow marty
⚠️ Exam Traps & Pitfalls

Traps. Locking the password does not necessarily terminate current sessions, stop the user's processes, or block SSH public-key authentication. If the requirement is to expire the entire account immediately, use usermod -e 1970-01-02 user (and set the requested future date later to restore it). Do not use userdel when files must remain.


πŸ”₯ 2x in Exam Sets
S2 Q16 S3 Q18
BASH root@servera
touch /etc/skel/notepad
printf '%s\n' 'Welcome to this system.' > /etc/skel/WelcomeHome
chown root:root /etc/skel/notepad /etc/skel/WelcomeHome
chmod 644 /etc/skel/notepad /etc/skel/WelcomeHome
 
useradd -m skeltest
ls -la /home/skeltest
🧠 Why This Works (Concept)

Why. useradd -m copies the contents of /etc/skel, including hidden files, into a newly created home and changes copied ownership to the new user. This affects future users only.

βœ… Verification Steps

Verify. stat /home/skeltest/notepad /home/skeltest/WelcomeHome.

⚠️ Exam Traps & Pitfalls

Traps. Files added to /etc/skel are not retroactively copied to existing homes. Keep skeleton files owned by root; do not create them as a regular user and leave unsafe writable modes.


πŸ”₯ 2x in Exam Sets
S1 Q16 S2 Q17
🎯 Exam Objective

Task A: default maximum password age for future local users. Set the value requested by the question in /etc/login.defs:

TEXT root@servera
PASS_MAX_DAYS 90

For [S2 Q17], use PASS_MAX_DAYS 100 instead. This setting is copied into /etc/shadow when a local account is created; it does not update existing users.

Apply aging to an existing user:

BASH root@servera
chage -M 90 jerry
πŸ“ŒS2 variant:
chage -M 100 jerry
chage -l jerry

Useful related defaults in /etc/login.defs are PASS_MIN_DAYS and PASS_WARN_AGE.

Task B: password strength for password changes. Set the requested value after checking every pwquality configuration location:

BASH root@servera
grep -R '^[[:space:]]*minlen' /etc/security/pwquality.conf /etc/security/pwquality.conf.d 2>/dev/null
πŸ“ŒSet one requested value in /etc/security/pwquality.conf and remove/comment conflicting values:
πŸ“ŒS1 Q16:
minlen = 8
πŸ“ŒS2 Q17 uses this instead:
minlen = 9

Only one effective minlen value should remain. Drop-ins are also supported, but inspect the main file because its setting can override a drop-in.

🧠 Why This Works (Concept)

Why. /etc/login.defs supplies account-creation defaults such as maximum password age. pam_pwquality reads /etc/security/pwquality.conf and pwquality.conf.d/*.conf to enforce password composition during password changes. They solve different problems.

βœ… Verification Steps

Verify.

BASH root@servera
grep -E '^PASS_(MAX|MIN)_DAYS|^PASS_WARN_AGE' /etc/login.defs
grep -R '^[[:space:]]*minlen' /etc/security/pwquality.conf /etc/security/pwquality.conf.d 2>/dev/null
chage -l jerry
passwd jerryπŸ’‘test through PAM, then choose a compliant password
⚠️ Exam Traps & Pitfalls

Traps.

  • Changing PASS_MAX_DAYS does not alter existing /etc/shadow entries; use chage -M for each existing account.
  • minlen in /etc/login.defs is not the modern PAM password-quality control. Use pwquality configuration.
  • Root can receive a warning yet be allowed to set a weak password unless enforce_for_root is configured. Add that standalone line only if policy must also reject weak passwords chosen by root.
  • Do not hand-edit /etc/pam.d/system-auth or password-auth; they are authselect-managed and may be overwritten.

πŸ”₯ 4x in Exam Sets ⭐ Official Objective
S2 Q30 S2 Q46 S3 Q36 S5 Q46

Preferred broad administrator access. RHEL's standard sudo policy normally authorizes members of wheel:

BASH root@servera
useradd -m frank
usermod -aG wheel frank
grep -E '^[[:space:]]*%wheel' /etc/sudoers

The expected active rule is commonly:

TEXT root@servera
%wheel ALL=(ALL) ALL

Dedicated drop-in. For a named user, create /etc/sudoers.d/frank using visudo:

BASH root@servera
visudo -f /etc/sudoers.d/frank
chmod 440 /etc/sudoers.d/frank

Content for full password-authenticated sudo:

TEXT root@servera
frank ALL=(ALL) ALL

Use least privilege when the task permits it, for example:

TEXT root@servera
operator ALL=(root) /usr/bin/systemctl restart httpd, /usr/bin/systemctl status httpd
🧠 Why This Works (Concept)

Why. wheel follows the normal RHEL administration model and is easy to audit. A drop-in is better for a user-specific or command-specific rule and avoids editing the main file. ALL=(ALL) ALL permits all commands as any target user, but it does not make the account UID 0.

Safe validation. Keep the current root shell open until a separate test succeeds:

BASH root@servera
visudo -cf /etc/sudoers
visudo -cf /etc/sudoers.d/frank
sudo -l -U frank
su - frank -c 'sudo -n true'πŸ’‘succeeds only for NOPASSWD rules/cached credentials

For a normal password-required rule, start a separate login as Frank and run sudo id; enter Frank's password.

⚠️ Exam Traps & Pitfalls

Traps.

  • Never validate sudo syntax with a plain text editor alone. visudo -c checks the main file and included files.
  • Files in /etc/sudoers.d should be root-owned, mode 0440, and have no . or ~ in the filename because @includedir skips such names.
  • Do not write NOPASSWD: ALL unless requested. It is broader than necessary.
  • New wheel membership needs a new login session. Verify with id frank after su - frank.
  • Do not assign UID 0 to make a user an administrator; grant sudo rights.

⭐ Official Objective
BASH root@servera
usermod -l newname oldnameπŸ’‘login name only
usermod -d /home/newname -m newnameπŸ’‘move home
usermod -c 'Database administrator' user
gpasswd -d user groupπŸ’‘remove one supplementary membership
userdel userπŸ’‘keep home and files
userdel -r userπŸ’‘remove home and mail spool too
groupdel oldgroup
find / -xdev -uid 1451 -ls 2>/dev/nullπŸ’‘find files before deleting/reusing UID
⚠️ Exam Traps & Pitfalls

Traps. Renaming a login does not automatically rename its same-named primary group. Before deleting or reusing a UID, locate files it owns; otherwise they display as a number or may silently belong to a future account.


  1. Create ops with GID 4600 and alex with UID 4500, primary group ops, shell /bin/bash, and expiry 2027-01-31.
  2. Add alex to wheel without losing existing supplementary groups; verify in a new login.
  3. Set future users to a 90-day maximum password age and minimum password length 12; apply aging to alex.
  4. Lock and unlock alex; explain why an existing SSH key session might still work.
  5. Put README in /etc/skel, create a test user, and verify ownership of the copied file.
  6. Add a command-limited sudo drop-in, run both visudo checks, and inspect it with sudo -l -U alex.

πŸ”’ Chapter 6 13 Exam Tasks

Chapter 5 - Ownership, Permissions, ACLs, and Shared Directories

Objectives covered: List, set, and change standard permissions; create and configure set-GID directories for collaboration; diagnose permission problems; manage access control lists; manage default file permissions.

Always translate the requirement into four questions: who owns the object, which group owns it, what do owner/group/other mode bits allow, and whether an ACL or special bit changes the result.


πŸ”₯ 7x in Exam Sets ⭐ Official Objective
S2 Q44 S2 Q45 S3 Q37 S4 Q17 S4 Q18 S4 Q20 +1 more
TEXT root@servera
-rwxr-x--- 1 alice developers 120 Sep 17 10:00 script
||||||||||
| owner |group|other
type: - file, d directory, l symlink
Bit File meaning Directory meaning Value
r read contents list names 4
w change contents create/delete/rename entries (requires x) 2
x execute traverse/search and access entries 1
BASH root@servera
ls -ld /data
stat -c '%A %a %U:%G %n' /data
namei -l /groups/accounting/report
🧠 Why This Works (Concept)

Why. For directories, x is usually essential. r without x may list names but cannot inspect them; w without x cannot practically create or remove entries. Deleting a file is controlled by the parent directory's w+x, not by the file's own write bit.

Symbolic and numeric forms:

BASH root@servera
chmod 770 /dataπŸ’‘owner rwx, group rwx, other ---
chmod u=rwx,g=rwx,o= /dataπŸ’‘exact symbolic equivalent
chmod o-rwx monk reggisπŸ’‘remove all access for others
chmod g+rwx monk reggisπŸ’‘add full owning-group access
chmod 660 fileπŸ’‘owner/group rw, others none
chmod 644 /etc/passwdπŸ’‘owner rw, group/other read
βœ… Verification Steps

Verify. stat -c '%a %A %n' /data /etc/passwd.

⚠️ Exam Traps & Pitfalls

Traps. chmod g=rwx replaces group bits; chmod g+rwx adds them. chmod -R can accidentally make ordinary files executable. Prefer find ... -type d -exec chmod ... and a separate file mode when recursion is needed.


πŸ”₯ 14x in Exam Sets
S1 Q18 S1 Q19 S1 Q20 S1 Q34 S1 Q35 S2 Q19 +8 more
BASH root@servera
chown george /friends
chown elaine:gfandneighbor /gfandneighbor
chown root:contractors /tmp/toolbox
chgrp friends /friends
chown username:groupname new_directory
 
πŸ“ŒRecursive only when the requirement covers existing contents too
chown -R root:accounting /groups/accounting
🧠 Why This Works (Concept)

Why. chown USER:GROUP path changes both in one operation. chown USER path changes only the user; chgrp GROUP path or chown :GROUP path changes only the group.

Create the paired group directories from the sets:

BASH root@servera
mkdir -p /groups/accounting /groups/finance
chown root:accounting /groups/accounting
chown root:finance /groups/finance
 
mkdir -p /groups/monk /groups/reggis
chown root:monk /groups/monk
chown root:reggis /groups/reggis
βœ… Verification Steps

Verify.

BASH root@servera
stat -c '%U:%G %a %n' /friends /gfandneighbor /tmp/toolbox
ls -ldn /groups/accounting /groups/finance
⚠️ Exam Traps & Pitfalls

Traps. chown user.group is ambiguous when names contain dots; use user:group. A copied file normally becomes owned by the copying user unless cp -p/cp -a is used as root. A move within one filesystem preserves ownership and mode.


πŸ”₯ 11x in Exam Sets
S1 Q18 S1 Q19 S1 Q34 S1 Q35 S3 Q21 S3 Q22 +5 more

Representative task. Owner and owning group have full access; everyone else has none.

BASH root@servera
groupadd -f contractors
mkdir -p /tmp/toolbox
chown root:contractors /tmp/toolbox
chmod 770 /tmp/toolbox
 
mkdir -p /gfandneighbor
chown elaine:gfandneighbor /gfandneighbor
chmod 770 /gfandneighbor
 
chmod 770 /groups/accounting /groups/finance
chmod 770 /groups/monk /groups/reggis

This also provides the standard answer for /data: mkdir -m 770 /data, followed by the requested chown or chgrp.

🧠 Why This Works (Concept)

Why. Mode 770 is rwxrwx---: owner full, owning group full, other none. Root can still administer the directory; standard DAC restrictions do not normally stop root.

[S1 Q19] is truncated in the source. It clearly requires a directory associated with friends, group owner friends, and access only for root and that group. If the examiner gives /tmp/friends, use the exact path given:

BASH root@servera
mkdir -p /tmp/friends
chown root:friends /tmp/friends
chmod 770 /tmp/friends
βœ… Verification Steps

Verify.

BASH root@servera
stat -c '%U:%G %a %n' /tmp/toolbox /gfandneighbor
su - george -c 'cd /tmp/toolbox && touch george.test'
su -s /bin/bash nobody -c 'cd /tmp/toolbox'πŸ’‘should fail
⚠️ Exam Traps & Pitfalls

Traps. chmod 760 gives the group no directory traversal, so group members cannot use entries. If users are not in the group, mode 770 cannot grant them access; fix membership or use an ACL.


πŸ”₯ 4x in Exam Sets
S2 Q19 S2 Q31 S3 Q8 S5 Q8

ACL tools are supplied by the acl package. Install it if getfacl is missing:

BASH root@servera
dnf -y install acl

Representative file task. Give Cosmo read/write, deny Peterman all access, and preserve normal root ownership:

BASH root@servera
cp /etc/fstab /tmp/fstab
chown root:root /tmp/fstab
chmod 600 /tmp/fstab
setfacl -m u:cosmo:rw-,u:peterman:--- /tmp/fstab
getfacl /tmp/fstab

Other source variants:

BASH root@servera
πŸ“Œ/var/nhosts: Marty rwx, Peter read-only
setfacl -m u:marty:rwx,u:peter:r-- /var/nhosts
 
πŸ“Œ/tmp/simpletxt: normal mode r-xr-xr-x; Kenny gets rwx
touch /tmp/simpletxt
chown bania:eastside /tmp/simpletxt
chmod 555 /tmp/simpletxt
setfacl -m u:kenny:rwx /tmp/simpletxt
 
πŸ“Œ/tmp/hosts: Jerry rw, George rx, Bobca rw, everyone else read-only
cp /etc/hosts /tmp/hosts
chown root:root /tmp/hosts
chmod 644 /tmp/hosts
setfacl -m u:jerry:rw-,u:george:r-x,u:bobca:rw- /tmp/hosts
🧠 Why This Works (Concept)

Why. setfacl -m modifies entries. A named user entry is u:NAME:PERMS; u:NAME:--- is an explicit denial. The access decision selects one matching class; permissions are not accumulated from other after a named-user ACL matches. Thus Peterman receives --- even if other::r-- exists.

βœ… Verification Steps

Verify.

BASH root@servera
getfacl -p /tmp/fstab /tmp/hosts /tmp/simpletxt
su - cosmo -c 'echo test >> /tmp/fstab'
su - peterman -c 'cat /tmp/fstab'πŸ’‘should fail
su - kenny -c 'echo test >> /tmp/simpletxt'
⚠️ Exam Traps & Pitfalls

Traps.

  • ls -l shows a trailing + but not the full ACL; use getfacl.
  • A file with x permission still needs valid executable content and a suitable interpreter to run.
  • chmod after setfacl can alter the ACL mask and effective named-user/group rights. Apply and verify the final ACL after mode changes.
  • cp may not preserve ACLs. Use cp -a or getfacl source | setfacl --set-file=- destination when preservation is required.

πŸ”₯ 4x in Exam Sets ⭐ Official Objective
S2 Q19 S2 Q31 S3 Q8 S5 Q8
BASH root@servera
setfacl -m u:kenny:rwx /tmp/simpletxt
getfacl /tmp/simpletxt

Typical output:

TEXT root@servera
user::r-x
user:kenny:rwx
group::r-x
mask::rwx
other::r-x

The mask is the maximum effective permission for named users (except the file owner), named groups, and the owning group. It does not limit user:: or other::.

BASH root@servera
setfacl -m m::r-x /tmp/simpletxt
getfacl /tmp/simpletxt
πŸ“Œuser:kenny:rwx #effective:r-x
 
setfacl -m m::rwx /tmp/simpletxt
🧠 Why This Works (Concept)

Why. By default, setfacl recalculates the mask to include modified entries. An existing restrictive mask, setfacl -n, or a later chmod g=... can cap the grant. In ls -l, the displayed group triplet represents the ACL mask when an extended ACL exists, not necessarily group::.

βœ… Verification Steps

Verify. Never stop at the requested entry: inspect #effective: in getfacl and test as the target identity.

⚠️ Exam Traps & Pitfalls

Traps. Do not solve an ACL task by making other permissive. Do not set a narrower mask than a named grant unless the task requires that restriction.


πŸ”₯ 2x in Exam Sets
S3 Q23 S4 Q50
🎯 Exam Objective

Task. Give the finance/reggis group read and traverse access to an existing tree owned by another group.

BASH root@servera
πŸ“ŒExisting accounting tree: directories need r-x; files need r--
find /groups/accounting -type d -exec setfacl -m g:finance:r-x {} +
find /groups/accounting -type f -exec setfacl -m g:finance:r-- {} +
 
πŸ“ŒSource-set variant
find /groups/monk -type d -exec setfacl -m g:reggis:r-x {} +
find /groups/monk -type f -exec setfacl -m g:reggis:r-- {} +

A shorter command, when execute should only be added where already appropriate, is:

BASH root@servera
setfacl -R -m g:finance:r-X /groups/accounting
🧠 Why This Works (Concept)

Why. Capital X adds execute only to directories and to files that already have an execute bit. It avoids making every regular file executable. Every parent directory in the path must grant search (x) permission.

βœ… Verification Steps

Verify.

BASH root@servera
getfacl /groups/accounting
getfacl /groups/accounting/some/existing/file
su - david -c 'find /groups/accounting -maxdepth 2 -type f -readable | head'
namei -l /groups/accounting/some/existing/file
⚠️ Exam Traps & Pitfalls

Traps. setfacl -R -m g:finance:rx makes data files executable. An ACL on only the top directory does not automatically affect existing descendants.


πŸ”₯ 2x in Exam Sets ⭐ Official Objective
S3 Q23 S4 Q50

An access ACL controls the directory itself. A default ACL is a template inherited by newly created children.

BASH root@servera
πŸ“ŒFinance can traverse/read future accounting content
setfacl -m d:g:finance:r-x /groups/accounting
 
πŸ“ŒCollaborative defaults for the owning group; no access for others
setfacl -m d:u::rwx,d:g::rwx,d:o::--- /groups/accounting
 
πŸ“ŒEnsure the default ACL mask permits the desired group entries
setfacl -m d:m::rwx /groups/accounting
getfacl /groups/accounting

Apply the source-set equivalent to /groups/monk with d:g:reggis:r-x.

🧠 Why This Works (Concept)

Why. New directories inherit ACL permissions and can use x; new regular files are not made executable merely because the default contains x. The creating program's requested mode still limits inherited permissions. Default ACLs exist only on directories.

Remove entries when required:

BASH root@servera
setfacl -x g:finance /groups/accountingπŸ’‘remove one access entry
setfacl -k /groups/accountingπŸ’‘remove all default ACL entries
setfacl -b fileπŸ’‘remove all extended access ACL entries
βœ… Verification Steps

Verify.

BASH root@servera
su - babu -c 'mkdir /groups/accounting/newdir; touch /groups/accounting/newfile'
getfacl /groups/accounting/newdir /groups/accounting/newfile
⚠️ Exam Traps & Pitfalls

Traps. A default ACL does nothing to old content; combine it with the recursive access ACL in 5.6. A named default entry may be capped by default:mask::.


πŸ”₯ 2x in Exam Sets
S3 Q33 S4 Q50
🎯 Exam Objective

Task. New entries in a group directory must inherit the directory's group owner.

BASH root@servera
groupadd -f admin
mkdir -p /home/admins
chown root:admin /home/admins
chmod 2770 /home/admins
 
chmod 2770 /groups/monk /groups/reggis
🧠 Why This Works (Concept)

Why. Leading 2 sets SGID. On a directory, SGID makes new children inherit the directory's group instead of the creator's primary group. Mode 2770 is rwxrws---.

For reliable group collaboration, combine SGID with a default ACL so group write is not removed by a user's umask:

BASH root@servera
setfacl -m d:u::rwx,d:g::rwx,d:m::rwx,d:o::--- /home/admins
βœ… Verification Steps

Verify.

BASH root@servera
ls -ld /home/adminsπŸ’‘drwxrws---
su - adminmember -c 'touch /home/admins/test'
stat -c '%U:%G %a %n' /home/admins/test
⚠️ Exam Traps & Pitfalls

Traps. chmod 770 alone does not make future files inherit the group. chgrp alone does not set SGID. A capital S in ls -l means SGID is set but group execute is absent; directories normally need lowercase s.


⭐ Official Objective
BASH root@servera
mkdir -p /srv/dropbox
chmod 1777 /srv/dropbox
ls -ld /srv/dropboxπŸ’‘drwxrwxrwt
🧠 Why This Works (Concept)

Why. A writable directory normally lets users delete or rename other users' files. The sticky bit restricts removal/rename to the file owner, directory owner, or root. /tmp is the standard example.

βœ… Verification Steps

Verify. Create files as two users and confirm one cannot delete the other's file.

⚠️ Exam Traps & Pitfalls

Traps. Sticky does not protect file contents from modification when the file itself is writable. Capital T means sticky is set but other execute is absent.


⭐ Official Objective
BASH root@servera
umask
umask -S
umask 0022πŸ’‘typical private-write default
umask 0002πŸ’‘useful for collaborative groups
touch file
mkdir dir
stat -c '%a %n' file dir

Base requested modes are normally 666 for regular files and 777 for directories. The umask removes bits:

Umask New regular file New directory
0022 644 755
0002 664 775
0077 600 700
🧠 Why This Works (Concept)

Why. A umask does not add permissions and cannot create execute bits on ordinary new files. Programs can request stricter modes. Default ACLs replace the simple group-class calculation with inherited ACL entries, still bounded by the creating program's requested mode.

Persistent locations:

BASH root@servera
πŸ“ŒOne Bash user: add to ~/.bashrc or ~/.bash_profile
umask 0027
 
πŸ“ŒAll login users: create a shell snippet under /etc/profile.d/
πŸ“ŒSystem services: set UMask=0027 in the service unit/drop-in when needed
βœ… Verification Steps

Verify. Test in a new login shell because startup files do not retroactively change an existing shell.

⚠️ Exam Traps & Pitfalls

Traps. Do not compute modes with ordinary decimal subtraction. Think of each bit being removed. Existing files are unchanged when umask changes.


πŸ”₯ 2x in Exam Sets ⭐ Official Objective
S3 Q23 S4 Q50

When Permission denied appears, do not immediately use chmod 777.

BASH root@servera
πŸ“Œ1. Confirm the actual identity and memberships in a fresh session
id targetuser
su - targetuser -c 'id'
 
πŸ“Œ2. Inspect every path component
namei -om /srv/project/subdir/file
 
πŸ“Œ3. Inspect ownership, mode, special bits, and ACL
stat -c '%A %a %U:%G %n' /srv/project/subdir/file
getfacl -p /srv/project/subdir/file
 
πŸ“Œ4. Test exactly as the user
sudo -u targetuser test -r /path/file && echo readable
sudo -u targetuser test -w /path/file && echo writable
sudo -u targetuser test -x /path/dir && echo searchable
 
πŸ“Œ5. Check filesystem and security layers
findmnt -no TARGET,OPTIONS --target /path/file
lsattr /path/file
ls -Zd /path /path/file
ausearch -m AVC -ts recent

Diagnosis order:

  1. Is the process really running as the expected UID and supplementary groups?
  2. Does every parent directory grant x through owner, group, other, or ACL?
  3. Does the selected mode/ACL class grant the requested operation, and does the ACL mask cap it?
  4. Is the filesystem read-only or mounted noexec?
  5. Is immutable (i) or append-only (a) set?
  6. Is SELinux denying an operation that DAC permits? Fix labels/policy; do not disable SELinux.

Common traps.

  • Group membership added after login is absent from the old process credential set.
  • The user owns a file, so only user:: applies; a more generous named group entry does not supplement owner permissions.
  • A named user ACL matches and other:: is not considered, even if other is broader.
  • Parent traversal fails although the final file mode looks correct.
  • The ACL grant exists but mask:: makes its effective permission narrower.
  • chmod 777 hides the original problem, violates requirements, and cannot fix SELinux, read-only mounts, or immutable attributes.

⭐ Official Objective
BASH root@servera
getfacl -R -p /srv/project > /root/project.acl
setfacl --restore=/root/project.acl
 
getfacl file
setfacl -b fileπŸ’‘remove extended access ACL; base entries remain
setfacl -k directoryπŸ’‘remove default ACL only
βœ… Verification Steps

Verify. Save getfacl -R -p output before and after and compare. The -p option preserves absolute path names in ACL output, which is useful for restoration.


  1. Create /srv/ops owned by root:ops, mode 2770; prove new files inherit group ops.
  2. Give user auditor read/traverse access to the existing tree without making regular files executable; add matching defaults for future content.
  3. Deny user intern access with a named ACL even if other::r-x; verify as that user.
  4. Set a named user to rwx, restrict the mask to r-x, and explain #effective:r-x.
  5. Predict new file and directory modes under umask 0027, then test.
  6. Diagnose a failure caused by a missing parent-directory x bit using namei -l.
  7. Create a 1777 drop directory and prove users cannot remove one another's files.

πŸ’Ύ Chapter 7 18 Exam Tasks

Chapter 6 - Storage and file systems

Objectives covered: Inspect block devices and file systems; create GPT partitions; create and remove LVM physical volumes, volume groups, standard logical volumes, and thin logical volumes; understand physical extents; create ext4, XFS, and VFAT file systems; configure persistent mounts by UUID or LABEL; extend mounted file systems without destroying data; configure swap; mount and unmount removable media.

Storage commands can destroy the system immediately. Replace every example device with the device found on the exam system. Never assume that /dev/sdb or /dev/sdc is the extra disk.


Sources Consolidated task
[S1 Q6] [S2 Q5] [S3 Q9] Create a GPT partition, PV, and VG
[S1 Q7] [S2 Q6] [S3 Q10] Create a standard LV
[S1 Q8-Q9] [S2 Q8-Q9] [S3 Q11-Q12] Create, persistently mount, and extend ext4 on an LV
[S1 Q10-Q11] Distinguish a standard LV from a real thin pool/thin LV
[S1 Q25] [S2 Q26] [S4 Q28] [S5 Q20] Create and persist a swap partition
[S1 Q41] Mount a file system at a specified directory
[S2 Q50] [S3 Q50] [S5 Q45] GPT partition, XFS, persistent mount
[S3 Q5] [S4 Q27] ext4 partition and persistent mount
[S3 Q24-Q28] [S3 Q40-Q41] [S4 Q11] [S5 Q9-Q13] PV/VG/LV, XFS, and persistent mount, with several duplicate size/name variants
[S3 Q49] Mount and safely unmount a USB drive
[S4 Q12-Q13] Create a thin LV, ext4, and persistent UUID mount
[S4 Q37] Check a partition's size; do not attempt a risky resize
[S4 Q40] Explain filesystem versus volume
[S4 Q47] Extend an LV and grow XFS or ext4 correctly
[S3 Q35] [S4 Q49] [S5 Q47] Report directory or mounted-file-system utilization
[S5 Q42] Unmount a file system and diagnose a busy mount
[ADDED] lsblk, blkid, PE size/extent counts, LVM deletion, VFAT, LABEL mounts, thin-pool creation, and swap LV

The large-size variants are recipes, not proof that the named disk is large enough. For example, a 50 GiB LV cannot be created from a 4 GiB disk, and two 10 GiB LVs require more than 20 GiB of usable VG space after LVM metadata and extent rounding.


πŸ”₯ 2x in Exam Sets ⭐ Official Objective
S4 Q37 S5 Q47
BASH root@servera
lsblk
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS
lsblk -f
blkid
findmnt
df -hT
parted -l
pvs; vgs; lvs -a -o +devices
🧠 Why This Works (Concept)

Why.

  • lsblk shows the disk/partition/LVM hierarchy and mount points. lsblk -f adds file-system type, UUID, and LABEL.
  • blkid reads file-system and swap signatures. It normally shows only devices that have a recognizable signature.
  • findmnt reports the live mount tree; df -hT reports space used by mounted file systems, not raw disk capacity.
  • parted -l shows partition tables and unallocated disk context.
  • pvs, vgs, and lvs -a -o +devices reveal storage already owned by LVM.

Before modifying a candidate disk, inspect it directly:

BASH root@servera
DISK=/dev/sdc
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS "$DISK"
parted "$DISK" unit MiB print free
wipefs -n "$DISK"

wipefs -n is read-only. It reveals old filesystem, RAID, partition-table, or LVM signatures without erasing them.

Check whether a requested LV can fit:

BASH root@servera
vgs -o vg_name,vg_size,vg_free,vg_extent_size,vg_free_count
pvs -o pv_name,pv_size,pv_free,vg_name

Proceed only when VFree is at least the requested allocation. If the task asks for 70 GiB but the disk or VG has less than 70 GiB usable space, do not issue lvcreate blindly. Recheck the selected disk, look for another supplied disk/PV, or report that the requested size is impossible with the available capacity. Do not substitute a smaller size unless instructed.

To test the exact [S4 Q37] condition safely:

BASH root@servera
size=$(lsblk -bndo SIZE /dev/sda1)
if [ "$size" -eq $((2048 * 1024 * 1024)) ]; then
echo '/dev/sda1 is exactly 2048 MiB'
else
echo 'WARNING: /dev/sda1 is not exactly 2048 MiB; no resize performed' >&2
fi
⚠️ Exam Traps & Pitfalls

Traps.

  • Disk names can be /dev/vdb, /dev/nvme1n1, or /dev/sda; NVMe partition names include p, for example /dev/nvme1n1p1.
  • lsblk sizes are rounded for display. Use lsblk -b for exact bytes.
  • An apparently unused partition may be a PV. Check pvs before formatting it.
  • df cannot show unused raw disks, partitions, or unmounted file systems.
  • Never run wipefs -a, mkfs, pvcreate, or parted mklabel merely to inspect a device.

πŸ”₯ 8x in Exam Sets
S1 Q6 S2 Q5 S2 Q50 S3 Q5 S3 Q50 S4 Q27-Q28 +2 more

This example creates a new 1 GiB partition on an empty disk for LVM. Confirm the disk first; mklabel gpt destroys the existing partition table.

BASH root@servera
DISK=/dev/sdb
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS "$DISK"
parted "$DISK" unit MiB print free
parted -s "$DISK" mklabel gpt
parted -s -a optimal "$DISK" mkpart lvm 1MiB 1025MiB
parted -s "$DISK" set 1 lvm on
partprobe "$DISK"
udevadm settle
parted "$DISK" unit MiB print
lsblk "$DISK"
🧠 Why This Works (Concept)

Why. Sector 0 contains metadata, so start at 1 MiB. The interval 1 MiB through 1025 MiB requests 1024 MiB. -a optimal aligns the partition. The name lvm is only a GPT partition name; set 1 lvm on sets the LVM flag. partprobe asks the kernel to reread the table.

On a disk that already has a GPT table, do not run mklabel. Print free space, choose the required partition number and a non-overlapping range, then create only the new partition:

BASH root@servera
parted /dev/sdb unit MiB print free
parted -s -a optimal /dev/sdb mkpart data 4097MiB 4609MiB
partprobe /dev/sdb

Common recipes after checking free space:

BASH root@servera
πŸ“Œ512 MiB filesystem partition
parted -s -a optimal /dev/sdc mkpart data ext4 1MiB 513MiB
 
πŸ“Œ2 GiB filesystem partition
parted -s -a optimal /dev/sdc mkpart data ext4 1MiB 2049MiB
 
πŸ“Œ100 MiB swap partition
parted -s -a optimal /dev/sdb mkpart swap linux-swap 1MiB 101MiB
parted -s /dev/sdb set 1 swap on
 
πŸ“Œ1 GiB swap as partition 1, as in [S4 Q28]
parted -s -a optimal /dev/sdc mkpart swap linux-swap 1MiB 1025MiB
parted -s /dev/sdc set 1 swap on

For a task that demands partition number 2 or 5 ([S2 Q26] [S5 Q20]), the earlier partition numbers must already exist. parted normally chooses the next available number; verify it rather than assuming:

BASH root@servera
parted /dev/sdb unit MiB print free
πŸ“ŒChoose a free 2 GiB interval; example only:
parted -s -a optimal /dev/sdb mkpart swap linux-swap 8193MiB 10241MiB
parted -s /dev/sdb set 5 swap on
partprobe /dev/sdb
lsblk /dev/sdb

If the result is not /dev/sdb5, stop and inspect the table. Do not format some other partition to force the answer.

βœ… Verification Steps

Verify.

BASH root@servera
parted /dev/sdb unit MiB print
lsblk -o NAME,START,SIZE,TYPE,FSTYPE /dev/sdb
⚠️ Exam Traps & Pitfalls

Traps.

  • parted end positions are coordinates, not sizes. 1MiB 1025MiB gives about 1 GiB; 1MiB 1GiB does not.
  • Decimal GB and binary GiB are different. Match the task's units. LVM G and M are binary-style units.
  • mkpart ... ext4 does not create ext4. It records partition information; run mkfs.ext4 afterward.
  • A kernel warning that a device is busy means stop. Do not reboot or force rereading until you understand what is mounted or in use.

πŸ”₯ 1x in Exam Sets ⭐ Official Objective
S4 Q40

The layers are:

TEXT root@servera
disk -> GPT partition (optional) -> PV -> VG -> LV -> filesystem -> mount point

A volume is a block device that stores raw blocks. A filesystem is the data structure placed on a block device to organize files and directories. Mounting attaches that filesystem to the directory tree. Formatting an LV replaces whatever filesystem/data was on that LV; it does not format the entire VG.

LVM allocates space in physical extents (PEs). Every VG has one PE size, normally 4 MiB. An LV's logical extents map to physical extents in the VG.

BASH root@servera
vgdisplay newvg | grep -E 'PE Size|Total PE|Alloc PE|Free PE'
vgs -o vg_name,vg_size,vg_free,vg_extent_size,vg_extent_count,vg_free_count
lvs -o lv_name,vg_name,lv_size,segtype,seg_size_pe

With 4 MiB PEs:

TEXT root@servera
400 MiB / 4 MiB = 100 extents
1 GiB = 1024 MiB; 1024 / 4 = 256 extents

Create by size or exact extent count:

BASH root@servera
lvcreate -L 400M -n newlv newvg
lvcreate -l 100 -n newlv newvg

The two are equivalent only when newvg has 4 MiB extents. Never assume the PE size if an exact extent count is graded.

Set a non-default PE size only when creating the VG and only when the task requires it:

BASH root@servera
vgcreate -s 8M newvg /dev/sdb1

Allocate all remaining extents:

BASH root@servera
lvcreate -l 100%FREE -n datalv datavg
⚠️ Exam Traps & Pitfalls

Traps.

  • -L means a size; lowercase -l means an extent count or percentage.
  • 100%FREE consumes all currently free VG extents and leaves no room for another LV or later extension.
  • VG/PV metadata and PE rounding mean a nominal 4 GiB partition may provide slightly less than exactly 4 GiB of allocatable LV space.
  • vgcreate -s cannot change the PE size of an existing VG.

πŸ”₯ 7x in Exam Sets
S1 Q6-Q7 S2 Q5-Q6 S3 Q9-Q10 S3 Q24-Q26 S3 Q40-Q41 S4 Q11 +1 more

After creating and confirming /dev/sdb1:

BASH root@servera
pvcreate /dev/sdb1
vgcreate newvg /dev/sdb1
lvcreate -L 400M -n newlv newvg

Equivalent path forms are /dev/newvg/newlv and /dev/mapper/newvg-newlv. Prefer /dev/newvg/newlv in commands because it is easier to read.

Whole disks can be PVs when the task explicitly allows it, as in several /dev/sdc or /dev/sde variants:

BASH root@servera
pvcreate /dev/sdc
vgcreate data_vg /dev/sdc
lvcreate -L 20G -n data_lv data_vg

If the wording specifically asks for a partition, create the GPT partition first and use /dev/sdc1. Do not silently use the whole disk.

Examples consolidated from the named variants:

BASH root@servera
lvcreate -L 1G -n lvnewlv vgnewvg
lvcreate -L 1G -n logvol vgroup
lvcreate -L 5G -n data_lv data_vg
lvcreate -L 10G -n data_lv1 data_vg
lvcreate -L 10G -n data_lv2 data_vg
lvcreate -L 40G -n class_lv class_vg
lvcreate -L 50G -n lv_vol vg_vol

Run the relevant command only after vgs proves that the VG has enough free space.

βœ… Verification Steps

Verify.

BASH root@servera
pvs
vgs
lvs -o lv_name,vg_name,lv_size,lv_attr,segtype,devices
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
⚠️ Exam Traps & Pitfalls

Traps.

  • pvcreate on a device with data is destructive. Confirm with lsblk, blkid, wipefs -n, and pvs first.
  • Names are exact and case-sensitive: data_vg/data_lv is not datavg/datalv.
  • A 50 GiB or 70 GiB request cannot fit in a smaller VG. Check VFree rather than trusting the question's device name.
  • An LV is not usable as a normal file tree until it has a filesystem and is mounted.

πŸ”₯ 2x in Exam Sets ⭐ Official Objective
S1 Q10-Q11 S4 Q12-Q13

A standard LV reserves all requested storage immediately:

BASH root@servera
lvcreate -L 1G -n thinvol newvg

Despite its name, this creates a standard LV. Names such as lthinpool and thinvol have no special effect. Likewise, this is only a 50 GiB standard LV:

BASH root@servera
lvcreate -L 50G -n lthinpool newvg

To create a real thin pool, use --type thin-pool (or -T), then create thin LVs from it:

BASH root@servera
vgs vgname
lvcreate --type thin-pool -L 50G -n 4thinpool vgname
lvcreate --type thin -V 2T -n 4thinvol vgname/4thinpool

For [S4 Q12], the pool is stated to exist, so only the second command is required:

BASH root@servera
lvcreate -V 2T -T vgname/4thinpool -n 4thinvol

-V 2T is the thin LV's virtual size. It may exceed the pool's physical size because blocks are allocated as written.

βœ… Verification Steps

Verify.

BASH root@servera
lvs -a -o lv_name,vg_name,lv_size,lv_attr,segtype,data_percent,metadata_percent

Expected segtype values include linear for a standard LV, thin-pool for the pool, and thin for a thin LV.

⚠️ Exam Traps & Pitfalls

Traps.

  • Never format the thin-pool LV. Format the thin LV, for example /dev/vgname/4thinvol.
  • Thin provisioning does not create real capacity. Monitor both Data% and Meta%; a full pool can make all thin LVs fail.
  • -L allocates physical pool size. -V sets virtual thin-LV size.
  • A standard LV cannot be turned into a thin LV merely by renaming it.

πŸ”₯ 12x in Exam Sets ⭐ Official Objective
S1 Q8 S2 Q8 S2 Q50 S3 Q5 S3 Q11 S3 Q24-Q27 +6 more

Confirm that each target has no needed data, then create the requested filesystem:

BASH root@servera
πŸ“Œext4 on an LV
mkfs.ext4 /dev/newvg/newlv
 
πŸ“ŒXFS on an LV
mkfs.xfs /dev/data_vg/data_lv
 
πŸ“Œext4 or XFS on a partition
mkfs.ext4 /dev/sdc1
mkfs.xfs /dev/sdc1
 
πŸ“ŒVFAT, commonly used by removable media
dnf -y install dosfstools
mkfs.vfat -F 32 /dev/sdc1

Set a label while creating the filesystem:

BASH root@servera
mkfs.ext4 -L project /dev/newvg/newlv
mkfs.xfs -L project /dev/data_vg/data_lv
mkfs.vfat -F 32 -n USBFILES /dev/sdc1

Set or change a label afterward:

BASH root@servera
e2label /dev/newvg/newlv project
πŸ“ŒXFS must be unmounted before xfs_admin changes its label:
umount /mnt/data
xfs_admin -L project /dev/data_vg/data_lv
mount /mnt/data
fatlabel /dev/sdc1 USBFILES
βœ… Verification Steps

Verify.

BASH root@servera
lsblk -f
blkid /dev/newvg/newlv
file -s /dev/newvg/newlv

Why. ext4 and XFS are native Linux file systems. VFAT is useful for interoperability and removable drives, but it does not store normal Unix ownership and permission metadata.

⚠️ Exam Traps & Pitfalls

Traps.

  • Every mkfs command destroys existing filesystem metadata on its target.
  • Do not use mkfs.xfs -f unless you have verified the old signature is disposable. -f bypasses a valuable safety refusal.
  • XFS labels are at most 12 characters. VFAT labels are conventionally uppercase and at most 11 characters.
  • ext4 can grow and shrink offline; XFS can grow while mounted but cannot shrink. Shrinking is not required here and is much riskier than growth.

πŸ”₯ 12x in Exam Sets
S1 Q8 S2 Q8 S2 Q50 S3 Q5 S3 Q11 S3 Q25-Q28 +6 more

Create the mount point, get the real UUID, back up /etc/fstab, and add one line. Example for ext4:

BASH root@servera
mkdir -p /newlv
blkid /dev/newvg/newlv
cp -p /etc/fstab /etc/fstab.pre-storage

Add this line to /etc/fstab, replacing the sample UUID with the exact output from blkid:

FSTAB root@servera
UUID=11111111-2222-3333-4444-555555555555 /newlv ext4 defaults 0 2

XFS example:

FSTAB root@servera
UUID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee /mnt/data xfs defaults 0 0

LABEL examples:

FSTAB root@servera
LABEL=project /mnt/project ext4 defaults 0 2
LABEL=USBFILES /mnt/usb vfat defaults 0 0

UUID is generally safest because it is generated to be unique. LABEL is readable but must be unique if used for mounting. Device names such as /dev/sdc1 can change when disks are detected in a different order.

An exact command can append a UUID-based line without transcribing it:

BASH root@servera
UUID=$(blkid -s UUID -o value /dev/data_vg/data_lv)
printf 'UUID=%s /mnt/data xfs defaults 0 0\n' "$UUID" >> /etc/fstab

Safe mount -a verification

Do not test while the filesystem is already manually mounted; an incorrect new line can be hidden by the existing mount.

BASH root@servera
findmnt --verify --verbose
systemctl daemon-reload
mountpoint -q /mnt/data && umount /mnt/data
mount -av
findmnt /mnt/data
findmnt -no SOURCE,FSTYPE,OPTIONS /mnt/data
df -hT /mnt/data

If mount -a fails, fix /etc/fstab before rebooting. A robust test also writes a file and confirms that it is on the mounted filesystem:

BASH root@servera
touch /mnt/data/.mount-test
findmnt -T /mnt/data/.mount-test
rm -f /mnt/data/.mount-test

fstab fields.

TEXT root@servera
source mount-point type options dump fsck-pass

Use pass 2 for non-root ext4 filesystems. XFS and VFAT commonly use 0 because the traditional boot-time fsck pass is not used for XFS and is usually omitted for removable VFAT in these tasks.

⚠️ Exam Traps & Pitfalls

Traps.

  • Never type a made-up UUID. Copy it from blkid or lsblk -f.
  • Do not quote fields in /etc/fstab; spaces inside a path must be escaped as \040.
  • Duplicate UUIDs or LABELs make mounts ambiguous. Check blkid -t LABEL=project.
  • mount -a normally skips entries with noauto; it also does not prove that a line is correct if that target is already mounted.
  • A missing mount-point directory causes the mount to fail.
  • Keep a root shell open while testing /etc/fstab; a malformed entry can delay or break the next boot.

πŸ”₯ 3x in Exam Sets
S1 Q41 S3 Q49 S5 Q42

One-time mount:

BASH root@servera
mkdir -p /mnt/data
mount /dev/sda1 /mnt/data
findmnt /mnt/data

The script explicitly requested by [S1 Q41] can be written as:

BASH root@servera
πŸ“Œ!/bin/bash
set -euo pipefail
 
device=/dev/sda1
target=/mnt/data
 
mkdir -p "$target"
if ! mountpoint -q "$target"; then
mount "$device" "$target"
fi
findmnt --target "$target"

Make it executable and run it as root:

BASH root@servera
chmod 750 mount-data.sh
./mount-data.sh

set -euo pipefail prevents the script from continuing after a failed command or unset variable. mountpoint makes rerunning it safe. This is a one-time mount script; persistence still requires a valid /etc/fstab entry.

When a correct /etc/fstab entry exists, mount by target only:

BASH root@servera
mount /mnt/data

Unmount by mount point and verify:

BASH root@servera
cd /
sync
umount /mnt/data
findmnt /mnt/data || echo 'not mounted'

If the target is busy:

BASH root@servera
fuser -vm /mnt/data
lsof +f -- /mnt/data

Stop the process or move its current working directory outside the mount, then retry umount. A shell whose current directory is below /mnt/data makes it busy.

⚠️ Exam Traps & Pitfalls

Traps.

  • The command is umount, not unmount.
  • Do not remove a USB device until unmount succeeds. sync alone is not an unmount.
  • Avoid umount -l as a routine fix. Lazy unmount detaches the path while references remain and can conceal the real problem.
  • Do not use umount -f on a healthy local filesystem. Forced unmount is mainly for unreachable network filesystems and risks data loss.

πŸ”₯ 4x in Exam Sets
S1 Q9 S2 Q9 S3 Q12 S4 Q47

First identify the filesystem and confirm VG free space:

BASH root@servera
findmnt -no SOURCE,FSTYPE /newlv
vgs newvg
lvs /dev/newvg/newlv

Extend the LV by an additional 400 MiB, then grow ext4:

BASH root@servera
lvextend -L +400M /dev/newvg/newlv
resize2fs /dev/newvg/newlv

For the 500 MiB and 100 MB-style variants, preserve the requested unit:

BASH root@servera
lvextend -L +500M /dev/vgnewvg/lvnewlv
resize2fs /dev/vgnewvg/lvnewlv
 
lvextend -L +100M /dev/vgroup/logvol
resize2fs /dev/vgroup/logvol

+400M means grow by 400 MiB. Without +, -L 400M means set the final LV size to 400 MiB and may do nothing or request a reduction.

One-command alternative:

BASH root@servera
lvextend -r -L +400M /dev/newvg/newlv

-r invokes fsadm to grow the recognized filesystem. The explicit two-command method makes the layers and any failure easier to see.

βœ… Verification Steps

Verify.

BASH root@servera
lvs /dev/newvg/newlv
findmnt /newlv
df -hT /newlv
⚠️ Exam Traps & Pitfalls

Traps.

  • Check VFree before extending. If insufficient, add a supplied PV to the VG; do not reduce another live LV casually.
  • lvextend alone grows the block device, not ext4's usable filesystem.
  • resize2fs grows ext4 whether mounted or unmounted. It does not grow XFS.
  • Growth is non-destructive when the correct LV is selected, but a typo can target another LV. Verify the full path.

πŸ”₯ 1x in Exam Sets
S4 Q47

XFS must be mounted to grow. Extend the LV, then run xfs_growfs on the mount point, not the block-device path:

BASH root@servera
findmnt -no SOURCE,FSTYPE,TARGET /mnt/data
vgs data_vg
lvextend -L +5G /dev/data_vg/data_lv
xfs_growfs /mnt/data

One-command alternative:

BASH root@servera
lvextend -r -L +5G /dev/data_vg/data_lv

Use all remaining VG space only if explicitly appropriate:

BASH root@servera
lvextend -l +100%FREE /dev/data_vg/data_lv
xfs_growfs /mnt/data
βœ… Verification Steps

Verify.

BASH root@servera
lvs /dev/data_vg/data_lv
xfs_info /mnt/data
df -hT /mnt/data
⚠️ Exam Traps & Pitfalls

Traps.

  • xfs_growfs /dev/data_vg/data_lv is wrong; give it the mounted directory.
  • XFS cannot be shrunk. Never promise to reverse an over-allocation by shrinking XFS.
  • +100%FREE means add all currently free extents. Omitting + means make the LV equal to 100% of free extents, which is not the intended growth expression.

πŸ”₯ 4x in Exam Sets
S1 Q25 S2 Q26 S4 Q28 S5 Q20

After creating and verifying the requested GPT partition and swap flag:

BASH root@servera
mkswap /dev/sdb1
blkid /dev/sdb1

Add its real UUID to /etc/fstab:

FSTAB root@servera
UUID=bbbbbbbb-cccc-dddd-eeee-ffffffffffff none swap defaults 0 0

Activate and verify without rebooting:

BASH root@servera
swapon -a
swapon --show
free -h
findmnt --verify --verbose

To disable a specific swap safely:

BASH root@servera
swapoff /dev/sdb1
swapon --show

Then remove or comment its /etc/fstab entry if it must stay disabled after boot.

🧠 Why This Works (Concept)

Why. mkswap writes a swap signature and UUID. swapon -a tests all fstab swap entries now. Swap has no mount point, so its fstab second field is none.

⚠️ Exam Traps & Pitfalls

Traps.

  • mkswap is destructive. Confirm the partition number and existing signatures first.
  • Creating swap is not enough; activate it now and persist it in /etc/fstab.
  • Do not run swapoff -a on a memory-constrained system; RAM may be unable to absorb all swapped pages.
  • If mkswap regenerates the UUID, update /etc/fstab with the new value.

6.13 Swap LV

⭐ Official Objective

Check free extents, create the LV, format it as swap, and persist it by UUID:

BASH root@servera
vgs data_vg
lvcreate -L 1G -n swap_lv data_vg
mkswap /dev/data_vg/swap_lv
UUID=$(blkid -s UUID -o value /dev/data_vg/swap_lv)
printf 'UUID=%s none swap defaults 0 0\n' "$UUID" >> /etc/fstab
swapon -a
swapon --show

An LV used for swap does not receive ext4 or XFS and is not mounted on a directory.

To extend swap, deactivate it, extend the LV, rewrite the swap signature, update fstab if its UUID changes, and reactivate:

BASH root@servera
swapoff /dev/data_vg/swap_lv
lvextend -L +512M /dev/data_vg/swap_lv
mkswap /dev/data_vg/swap_lv
blkid /dev/data_vg/swap_lv
πŸ“ŒReplace the old UUID in /etc/fstab if mkswap generated a new one.
swapon -a
swapon --show

πŸ”₯ 1x in Exam Sets ⭐ Official Objective
S3 Q49

Insert the device, identify the newly appeared disk by size/model, and inspect its filesystem:

BASH root@servera
lsblk -o NAME,PATH,SIZE,MODEL,TRAN,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS
udevadm settle
lsblk -f
blkid

For example, if inspection proves that the USB filesystem is /dev/sdd1:

BASH root@servera
mkdir -p /mnt/usb
mount /dev/sdd1 /mnt/usb
findmnt /mnt/usb
ls -la /mnt/usb

Unmount before removal:

BASH root@servera
cd /
sync
umount /mnt/usb
findmnt /mnt/usb || echo 'safe to remove'

If the USB filesystem is VFAT and automatic detection fails, install support and specify the type:

BASH root@servera
dnf -y install dosfstools
mount -t vfat /dev/sdd1 /mnt/usb

For a USB device that must mount persistently, use its UUID or LABEL in /etc/fstab. nofail is appropriate when boot must continue while removable media is absent:

FSTAB root@servera
LABEL=USBFILES /mnt/usb vfat defaults,nofail 0 0
⚠️ Exam Traps & Pitfalls

Traps.

  • Identify the partition (/dev/sdd1), not merely the disk (/dev/sdd), unless the filesystem truly occupies the whole disk.
  • Do not create a new filesystem on a USB drive that already contains required files.
  • VFAT cannot enforce normal per-file Unix ownership and permissions. Mount options such as uid=, gid=, umask=, fmask=, and dmask= present synthetic permissions.
  • If umount says busy, use fuser -vm /mnt/usb, close users of the mount, and retry. Do not pull the device.

⭐ Official Objective

Removal proceeds in reverse layer order: unmount/disable use, remove persistence, remove LV, remove VG, remove PV, then optionally remove the partition.

For a data LV:

BASH root@servera
findmnt /mnt/data
umount /mnt/data
πŸ“ŒRemove the matching /mnt/data line from /etc/fstab, then verify the file.
findmnt --verify --verbose
lvremove /dev/data_vg/data_lv
vgremove data_vg
pvremove /dev/sdc1

For a swap LV:

BASH root@servera
swapoff /dev/data_vg/swap_lv
πŸ“ŒRemove the matching swap UUID line from /etc/fstab.
lvremove /dev/data_vg/swap_lv

Remove a partition only after all higher layers are gone:

BASH root@servera
parted /dev/sdc print
parted -s /dev/sdc rm 1
partprobe /dev/sdc

If a VG has other required LVs or PVs, do not run vgremove. If a PV must be removed from a multi-PV VG while its extents are allocated, migrate them first when enough free space exists elsewhere:

BASH root@servera
pvs -o pv_name,pv_used,pv_free,vg_name
pvmove /dev/sdc1
vgreduce data_vg /dev/sdc1
pvremove /dev/sdc1
βœ… Verification Steps

Verify.

BASH root@servera
findmnt /mnt/data
swapon --show
lvs; vgs; pvs
lsblk -f
findmnt --verify --verbose
⚠️ Exam Traps & Pitfalls

Traps.

  • lvremove, vgremove, pvremove, and parted rm are destructive. Read each confirmation prompt and verify the full object name.
  • Removing an fstab line before unmounting does not unmount the live filesystem.
  • pvremove does not make allocated extents disappear safely. Use pvmove/vgreduce or remove all disposable LVs first.
  • Thin LVs must be removed before their thin pool. Check lvs -a for hidden pool data/metadata LVs.

πŸ”₯ 3x in Exam Sets
S3 Q35 S4 Q49 S5 Q47
BASH root@servera
du -sh /var/log
df -hT
df -hT /mnt/data
🧠 Why This Works (Concept)

Why. du -sh totals blocks used by files beneath a directory. df -hT reports capacity, used space, available space, and filesystem type for mounted filesystems. They can differ because deleted files may remain open, sparse files have different apparent and allocated sizes, and reserved filesystem blocks affect df.

To append the df -h report daily at 4:15 PM as root ([S4 Q49]):

BASH root@servera
crontab -e

Add:

CRON root@servera
15 16 * * * /usr/bin/df -h >> /tmp/diskstatus 2>&1

Verify with crontab -l. The five schedule fields are minute, hour, day of month, month, and day of week; 16 is 4 PM in 24-hour time. >> appends instead of replacing earlier reports.

⚠️ Exam Traps & Pitfalls

Traps. du measures a directory tree, while df measures its containing mounted filesystem. Running du across mount boundaries includes nested mounts unless -x is used. A cron environment has a limited PATH, so use /usr/bin/df.


GPT partition, XFS, UUID mount

[S2 Q50] [S3 Q50] [S5 Q45]

BASH root@servera
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS
parted /dev/sdc unit GiB print free
parted -s /dev/sdc mklabel gptπŸ’‘empty disk only
parted -s -a optimal /dev/sdc mkpart data xfs MiB 049MiB
partprobe /dev/sdc
udevadm settle
mkfs.xfs /dev/sdc1
mkdir -p /data
UUID=$(blkid -s UUID -o value /dev/sdc1)
cp -p /etc/fstab /etc/fstab.pre-storage
printf 'UUID=%s /data xfs defaults 0 0\n' "$UUID" >> /etc/fstab
findmnt --verify --verbose
systemctl daemon-reload
mount -av
findmnt /data
df -hT /data

Partition, PV/VG/LV, ext4, UUID mount, and growth

[S1 Q6-Q9] [S2 Q5-Q9] [S3 Q9-Q12]

BASH root@servera
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS
parted /dev/sdb unit MiB print free
parted -s /dev/sdb mklabel gptπŸ’‘empty disk only
parted -s -a optimal /dev/sdb mkpart lvm 1MiB 4097MiB
parted -s /dev/sdb set 1 lvm on
partprobe /dev/sdb
udevadm settle
pvcreate /dev/sdb1
vgcreate newvg /dev/sdb1
vgs newvg
lvcreate -L 400M -n newlv newvg
mkfs.ext4 /dev/newvg/newlv
mkdir -p /newlv
UUID=$(blkid -s UUID -o value /dev/newvg/newlv)
cp -p /etc/fstab /etc/fstab.pre-storage
printf 'UUID=%s /newlv ext4 defaults 0 2\n' "$UUID" >> /etc/fstab
findmnt --verify --verbose
systemctl daemon-reload
mount -av
findmnt /newlv
vgs newvg
lvextend -L +400M /dev/newvg/newlv
resize2fs /dev/newvg/newlv
lvs /dev/newvg/newlv
df -hT /newlv

Whole-disk PV, standard LV, XFS, persistent mount

[S3 Q25-Q26] [S3 Q41] [S4 Q11] [S5 Q9-Q13]

BASH root@servera
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,MOUNTPOINTS /dev/sdc
wipefs -n /dev/sdc
pvcreate /dev/sdcπŸ’‘only if whole-disk use is intended
vgcreate data_vg /dev/sdc
vgs data_vg
lvcreate -L 0G -n data_lv data_vgπŸ’‘only if VFree is sufficient
mkfs.xfs /dev/data_vg/data_lv
mkdir -p /mnt/data
UUID=$(blkid -s UUID -o value /dev/data_vg/data_lv)
cp -p /etc/fstab /etc/fstab.pre-storage
printf 'UUID=%s /mnt/data xfs defaults 0 0\n' "$UUID" >> /etc/fstab
findmnt --verify --verbose
systemctl daemon-reload
mount -av
findmnt /mnt/data
df -hT /mnt/data

BASH root@servera
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS
blkid
pvs
vgs -o vg_name,vg_size,vg_free,vg_extent_size,vg_free_count
lvs -a -o lv_name,vg_name,lv_size,lv_attr,segtype,data_percent,metadata_percent,devices
findmnt --verify --verbose
systemctl daemon-reload
mount -av
findmnt
df -hT
swapon --show

Before finishing, confirm all of the following:

  1. The requested names, sizes, units, filesystem types, and mount points are exact.
  2. Every requested persistent filesystem has one valid /etc/fstab entry using its real UUID or unique LABEL.
  3. findmnt --verify --verbose and mount -av produce no errors.
  4. findmnt shows the expected source, target, filesystem type, and options.
  5. df -hT shows the grown filesystem size, not merely the grown LV size.
  6. swapon --show lists every requested swap area.
  7. The VG still has enough free extents for any remaining task.
  8. No impossible allocation was attempted against a disk or VG that is too small.

🌐 Chapter 8 12 Exam Tasks

Chapter 7 - Networking and firewalld

Objectives covered: Configure IPv4 and IPv6 addresses and hostname resolution; start, stop, and check network services; configure packet forwarding; restrict network access with firewalld.

Network changes can end an exam SSH session. Identify the active device and its NetworkManager connection instead of assuming names such as eth0 or ens160. When working remotely, preserve the SSH path, use the system console if available, and run nmcli con up only after checking every value.


πŸ”₯ 2x in Exam Sets ⭐ Official Objective
S1 Q46 S1 Q50
BASH root@servera
nmcli device status
nmcli connection show --active
ip -br address
ip route
 
πŸ“ŒSelect the device carrying the default route, then find its active profile.
DEV=<interface-from-task>
CON=$(nmcli -g GENERAL.CONNECTION device show "$DEV")
printf 'device=%s connection=%s\n' "$DEV" "$CON"
 
πŸ“ŒIf there is no default route, inspect connected devices and choose the required one.
nmcli -t -f DEVICE,TYPE,STATE device status
nmcli -f NAME,UUID,TYPE,DEVICE connection show --active
🧠 Why This Works (Concept)

Why. A device is the network interface; a connection is the persistent NetworkManager profile applied to it. nmcli con mod takes the connection name, not necessarily the device name. The default route normally identifies the interface used for remote administration.

βœ… Verification Steps

Verify.

BASH root@servera
test -n "$DEV" && test -n "$CON" && nmcli connection show "$CON"
nmcli -g GENERAL.STATE,GENERAL.CONNECTION,IP4.ADDRESS,IP6.ADDRESS device show "$DEV"
⚠️ Exam Traps & Pitfalls

Traps.

  • Do not assume the active profile is named after the interface.
  • More than one connection can be active. Select the profile attached to the interface named in the task, or the default-route interface when the task says "currently active."
  • nmcli connection show lists saved profiles; nmcli connection show --active lists profiles currently in use.
  • Never modify loopback (lo).

πŸ”₯ 6x in Exam Sets
S1 Q2 S1 Q47 S2 Q2 S3 Q16 S4 Q2 S5 Q2

Substitute the values from the question. This example uses the Set 1 values.

BASH root@servera
DEV=$(ip route show default | awk 'NR == 1 {print $5}')
CON=$(nmcli -g GENERAL.CONNECTION device show "$DEV")
 
πŸ“ŒRecord the current state before changing a remote-management path.
nmcli connection show "$CON"
ip route
 
nmcli connection modify "$CON" \
ipv4.method manual \
ipv4.addresses 192.168.1.211/24 \
ipv4.gateway 192.168.1.1 \
ipv4.dns 8.8.8.8 \
connection.autoconnect yes
 
πŸ“ŒActivate from the local console after checking the values.
nmcli connection up "$CON"
🧠 Why This Works (Concept)

Why. manual makes the address static. Assigning ipv4.addresses without a leading + intentionally replaces the profile's address list. Gateway and DNS are stored in the same persistent profile. connection.autoconnect yes activates it at boot.

βœ… Verification Steps

Verify.

BASH root@servera
nmcli -f ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,connection.autoconnect connection show "$CON"
ip -4 address show dev "$DEV"
ip -4 route
getent hosts example.com
⚠️ Exam Traps & Pitfalls

Traps.

  • Prefix length is required: 192.168.1.211/24, not just the address.
  • A wrong gateway, prefix, address, or profile can immediately disconnect SSH.
  • nmcli connection modify saves the profile but does not necessarily change the live interface. nmcli connection up can interrupt traffic while it reapplies the profile.
  • Do not add a second default gateway unless the task requires routing policy. If no gateway is requested, do not invent one.
  • Quoting "$CON" matters because connection names can contain spaces.

πŸ”₯ 5x in Exam Sets
S1 Q3 S2 Q49 S3 Q17 S4 Q3 S5 Q3
BASH root@servera
DEV=$(ip route show default | awk 'NR == 1 {print $5}')
CON=$(nmcli -g GENERAL.CONNECTION device show "$DEV")
 
nmcli connection modify "$CON" +ipv4.addresses 10.0.0.211/24
nmcli connection up "$CON"πŸ’‘use cautiously; prefer the console

Use the address from the set: .211 for Set 1, .212 for Set 2, .213 for Set 3, .214 for Set 4, or .215 for Set 5.

🧠 Why This Works (Concept)

Why. The leading + appends to the existing address list. It is the critical difference between adding a secondary address and replacing the primary configuration.

βœ… Verification Steps

Verify.

BASH root@servera
nmcli -g ipv4.addresses connection show "$CON"
ip -4 address show dev "$DEV"
⚠️ Exam Traps & Pitfalls

Traps.

  • ipv4.addresses 10.0.0.211/24 without + replaces all configured addresses.
  • Do not change ipv4.method, gateway, or DNS when the question asks only for an additional address.
  • ip address add ... changes only the running system and is lost when the connection is reactivated or the host reboots.

πŸ”₯ 2x in Exam Sets
S4 Q5 S2 Q3

Static IPv6 example. Use the address, prefix, gateway, and connection specified by the task.

BASH root@servera
DEV=$(ip route show default | awk 'NR == 1 {print $5}')
CON=$(nmcli -g GENERAL.CONNECTION device show "$DEV")
 
nmcli connection modify "$CON" \
ipv6.method manual \
ipv6.addresses 2001:db8:1::10/64 \
ipv6.gateway 2001:db8:1::1 \
connection.autoconnect yes
nmcli connection up "$CON"

Secondary IPv6.

BASH root@servera
nmcli connection modify "$CON" +ipv6.addresses fd01::212/64
nmcli connection up "$CON"
🧠 Why This Works (Concept)

Why. ipv6.method manual provides a persistent static primary configuration. +ipv6.addresses appends a secondary address and preserves all existing IPv6 addresses.

βœ… Verification Steps

Verify.

BASH root@servera
nmcli -f ipv6.method,ipv6.addresses,ipv6.gateway connection show "$CON"
ip -6 address show dev "$DEV"
ip -6 route
ping -6 -c 3 2001:db8:1::1
⚠️ Exam Traps & Pitfalls

Traps.

  • IPv6 also requires a prefix, normally /64 in exam tasks.
  • Do not set an IPv6 gateway when none is requested.
  • A unique-local address such as fd01::212/64 is not globally routable; that is normal.
  • Do not use ipv6.method disabled on a profile that must retain or receive IPv6 addresses.

πŸ”₯ 1x in Exam Sets ⭐ Official Objective
S1 Q48
BASH root@servera
nmcli connection modify "$CON" connection.autoconnect yes
nmcli -f connection.autoconnect connection show "$CON"
 
πŸ“ŒActivate now only when safe:
nmcli connection up "$CON"
🧠 Why This Works (Concept)

Why. NetworkManager profile edits are persistent immediately. connection.autoconnect yes controls activation at boot; nmcli con up activates the saved configuration now.

Remote SSH safety. Before bringing a connection up, verify the new address, prefix, gateway, and DNS; keep a second console or SSH session when possible. Do not deactivate the only working profile. A profile reactivation may drop even a correctly configured SSH session briefly.


πŸ”₯ 6x in Exam Sets ⭐ Official Objective
S1 Q49 S2 Q13 S4 Q31 S5 Q1 S1 Q33 S5 Q38

Set the system hostname.

BASH root@servera
hostnamectl set-hostname rhel.server.com
hostnamectl
hostname --fqdn

Use the exact requested name, such as rhel.server.com, seinfeld.server.com, or linux5.lab.

Configure DNS persistently through NetworkManager.

BASH root@servera
DEV=$(ip route show default | awk 'NR == 1 {print $5}')
CON=$(nmcli -g GENERAL.CONNECTION device show "$DEV")
nmcli connection modify "$CON" ipv4.dns 172.24.254.254 ipv4.ignore-auto-dns yes
nmcli connection up "$CON"
 
nmcli -g IP4.DNS device show "$DEV"
getent hosts server1.example.com
cat /etc/resolv.conf

For multiple DNS servers, quote a space-separated list: nmcli con mod "$CON" ipv4.dns "172.24.254.254 8.8.8.8". Use +ipv4.dns ADDRESS when the question explicitly says to add a server without replacing existing DNS.

Configure an explicit local mapping only when the task supplies the host's IP.

BASH root@servera
printf '%s\n' '192.168.0.254 server1.example.com server1' >> /etc/hosts
getent hosts server1.example.com
🧠 Why This Works (Concept)

Why. hostnamectl changes the persistent static hostname. NetworkManager owns persistent DNS configuration and generates resolver state. /etc/hosts is a local, exact name-to-address mapping and does not configure a DNS server.

⚠️ Exam Traps & Pitfalls

Traps.

  • If the task says "use DNS server X," configure DNS with nmcli; adding the target to /etc/hosts does not satisfy it.
  • If the task gives an IP/name pair and asks only for local resolution, /etc/hosts is direct and independent of DNS.
  • Do not edit /etc/resolv.conf by hand; NetworkManager can overwrite it.
  • ping is not a pure name-service test. Prefer getent hosts; it follows the system Name Service Switch configuration.
  • A hostname change does not create a DNS record. Ensure DNS or /etc/hosts can resolve the name if resolution is also required.

πŸ”₯ 6x in Exam Sets
S2 Q4 S3 Q3 S4 Q4 S5 Q4 S5 Q33 S5 Q5

These source questions are useful system-administration practice, but packet forwarding/sysctl is not listed explicitly in the official RHEL 10 EX200 objectives verified for this guide.

BASH root@servera
cat > /etc/sysctl.d/90-forwarding.conf <<'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
 
sysctl --system
sysctl net.ipv4.ip_forward
sysctl net.ipv6.conf.all.forwarding

If only one protocol is requested, place only that protocol's setting in the file.

🧠 Why This Works (Concept)

Why. Files in /etc/sysctl.d/ persist across reboot. sysctl --system loads all sysctl configuration immediately in normal precedence order. An in-memory sysctl -w alone does not persist.

βœ… Verification Steps

Verify.

BASH root@servera
sysctl -n net.ipv4.ip_forwardπŸ’‘expected: 1
sysctl -n net.ipv6.conf.all.forwardingπŸ’‘expected: 1
grep -R 'net\.ipv[46].*forward' /etc/sysctl.conf /etc/sysctl.d 2>/dev/null
⚠️ Exam Traps & Pitfalls

Traps.

  • Forwarding only enables the kernel to route packets. Interfaces still need correct addresses/routes, and firewalld must permit forwarded traffic.
  • IPv6 forwarding changes router-advertisement behavior. Do not enable it unless requested.
  • Conflicting settings in a later-loaded sysctl file can override yours; verify the live value after sysctl --system.
  • For routing between two attached networks, no masquerading is needed unless the task explicitly requires NAT.

πŸ”₯ 3x in Exam Sets ⭐ Official Objective
S4 Q38 S1 Q46 S1 Q50
BASH root@servera
πŸ“Œ1. Link, device, profile, and addresses
nmcli device status
nmcli connection show --active
ip -br link
ip -br address
 
πŸ“Œ2. Routes and the exact path the kernel would use
ip route
ip -6 route
ip route get 192.168.1.1
 
πŸ“Œ3. Test in layers: local stack, gateway, remote IP, then name
ping -c 3 127.0.0.1
ping -c 3 192.168.1.1
ping -c 3 8.8.8.8
getent hosts server1.example.com
cat /etc/resolv.conf
 
πŸ“Œ4. Services, sockets, firewall, and logs
ss -lntup
systemctl status NetworkManager firewalld --no-pager
firewall-cmd --get-active-zones
firewall-cmd --list-all
journalctl -u NetworkManager -b --no-pager
🧠 Why This Works (Concept)

Why. Test from the lowest layer upward. A missing carrier/profile/address is not a DNS problem; a reachable IP with an unresolvable name usually is. ip route get exposes the chosen route, source address, and device.

⚠️ Exam Traps & Pitfalls

Traps.

  • ICMP can be blocked even when the application works. Test the actual service with curl, ssh -v, or nc -vz HOST PORT where appropriate.
  • A listening socket shown only on 127.0.0.1 is not remotely reachable.
  • Check both the server's firewalld rules and the application's listening address.
  • Avoid restarting NetworkManager over remote SSH as a first troubleshooting step.

πŸ”₯ 6x in Exam Sets ⭐ Official Objective
S1 Q24 S2 Q10 S4 Q15 S1 Q42 S5 Q28 S2 Q38

Start and inspect firewalld.

BASH root@servera
systemctl enable --now firewalld
systemctl is-active firewalld
systemctl is-enabled firewalld
 
DEV=$(ip route show default | awk 'NR == 1 {print $5}')
firewall-cmd --get-active-zones
firewall-cmd --get-default-zone
ZONE=$(firewall-cmd --get-zone-of-interface="$DEV")
if [ -z "$ZONE" ] || [ "$ZONE" = "no zone" ]; then
ZONE=$(firewall-cmd --get-default-zone)
fi
printf 'zone=%s\n' "$ZONE"

Allow named services persistently.

BASH root@servera
firewall-cmd --permanent --zone="$ZONE" --add-service=http
firewall-cmd --permanent --zone="$ZONE" --add-service=https
firewall-cmd --reload

Use --add-service=ssh for SSH and --add-service=ftp for FTP. A service definition can cover the correct protocol and more than one port, so prefer it when a matching service exists.

Allow an arbitrary port persistently.

BASH root@servera
firewall-cmd --permanent --zone="$ZONE" --add-port=8080/tcp
firewall-cmd --reload
🧠 Why This Works (Concept)

Why. A zone is a policy applied to an interface or source. Rules must be added to the zone carrying the traffic. --permanent writes persistent configuration; --reload makes permanent rules active while preserving connection tracking.

Verify both saved and live state.

BASH root@servera
firewall-cmd --zone="$ZONE" --list-all
firewall-cmd --permanent --zone="$ZONE" --list-all
firewall-cmd --zone="$ZONE" --query-service=http
firewall-cmd --permanent --zone="$ZONE" --query-service=http
firewall-cmd --zone="$ZONE" --query-port=8080/tcp
ss -lntp
⚠️ Exam Traps & Pitfalls

Traps.

  • --permanent alone does not alter the runtime firewall; reload or add the runtime rule too.
  • A runtime-only rule disappears after reload or reboot.
  • Opening a firewall port does not start the service. Verify both systemctl status SERVICE and ss -lntup.
  • Do not assume the default zone is the active interface's zone.
  • Never remove ssh or port 22/tcp from the zone carrying a remote SSH session unless explicitly required and alternate access is available.

⭐ Official Objective

Change the persistent zone for a NetworkManager connection.

BASH root@servera
nmcli connection modify "$CON" connection.zone internal
nmcli connection up "$CON"πŸ’‘disruptive; use cautiously over SSH
firewall-cmd --get-active-zones

For a temporary runtime assignment, firewall-cmd --zone=internal --change-interface="$DEV" is available, but NetworkManager profile configuration is the persistent choice for managed interfaces.

Rich-rule examples.

BASH root@servera
πŸ“ŒAllow SSH only from one IPv4 subnet.
firewall-cmd --permanent --zone="$ZONE" \
--add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept'
 
πŸ“ŒReject TCP/8080 from one host.
firewall-cmd --permanent --zone="$ZONE" \
--add-rich-rule='rule family="ipv4" source address="192.168.1.50/32" port port="8080" protocol="tcp" reject'
 
firewall-cmd --reload
firewall-cmd --zone="$ZONE" --list-rich-rules
firewall-cmd --permanent --zone="$ZONE" --list-rich-rules
🧠 Why This Works (Concept)

Why. Rich rules add source, destination, service/port, logging, and action criteria that simple service and port rules cannot express.

⚠️ Exam Traps & Pitfalls

Traps.

  • Quote the entire rich rule so the shell passes it as one argument.
  • Include the correct address family. IPv6 sources require family="ipv6".
  • A broad --add-service=ssh still allows SSH from other sources; remove it if the requirement is "only this subnet," but protect the current remote session first.
  • To remove a rich rule, pass the exact same quoted rule to --remove-rich-rule.

πŸ”₯ 1x in Exam Sets ⭐ Official Objective
S5 Q23

Close FTP if it was allowed.

BASH root@servera
firewall-cmd --permanent --zone="$ZONE" --remove-service=ftp
firewall-cmd --permanent --zone="$ZONE" --remove-port=21/tcp
firewall-cmd --reload
 
firewall-cmd --zone="$ZONE" --query-service=ftp
firewall-cmd --permanent --zone="$ZONE" --query-service=ftp

An exit status of nonzero and output no from both queries confirms FTP is not allowed by that service rule. Also inspect all rules because a rich rule or another open port could still permit it:

BASH root@servera
firewall-cmd --zone="$ZONE" --list-all
firewall-cmd --zone="$ZONE" --list-rich-rules

Explicit rejection, only if the wording requires a reject rule.

BASH root@servera
firewall-cmd --permanent --zone="$ZONE" \
--add-rich-rule='rule family="ipv4" service name="ftp" reject'
firewall-cmd --reload
firewall-cmd --zone="$ZONE" --list-rich-rules
🧠 Why This Works (Concept)

Why. In a normal firewalld zone, traffic not explicitly allowed is already blocked. Removing ftp is therefore the minimal way to "block FTP." A rich reject actively returns an error and documents an explicit deny requirement.

⚠️ Exam Traps & Pitfalls

Traps.

  • Remove both runtime and permanent exposure by changing permanent configuration and reloading.
  • FTP may use related data connections; use the service definition rather than reasoning from port 21 alone.
  • Firewall blocking and service disabling are different. If the task also says the daemon must not run, use systemctl disable --now vsftpd and verify with systemctl is-enabled and systemctl is-active.

  1. Record nmcli device status, nmcli con show --active, ip -br address, and ip route.
  2. Derive DEV and CON; do not guess either name.
  3. Decide whether the task says replace (ipv4.addresses) or add (+ipv4.addresses).
  4. Modify the persistent connection and set connection.autoconnect yes when boot activation is required.
  5. Re-read the saved profile before nmcli con up, especially over SSH.
  6. Verify address, route, and DNS independently.
  7. For forwarding, write /etc/sysctl.d/*.conf, run sysctl --system, and verify the live value.
  8. For firewalld, identify the active zone, add or remove a permanent rule, reload, and verify both runtime and permanent state.
  9. Confirm the application is actually listening; an open firewall alone never proves service availability.

⏱️ Chapter 9 15 Exam Tasks

Chapter 8 - Running Systems, Services, Processes, Time, Logs, and Scheduling

Objectives covered: Start, stop, enable, disable, and inspect services with systemd - Identify and control processes - Adjust process scheduling priority - Select TuneD profiles - Configure time synchronization and time zones - Preserve and inspect the system journal - Inspect system and audit logs - Schedule work with cron, at, and systemd timers.

Exam rule: make the requested state persistent, then verify the result with a command that does not merely repeat the change command.


πŸ”₯ 11x in Exam Sets
S2 Q41 S2 Q42 S3 Q34 S3 Q38 S3 Q39 S4 Q15 +5 more
🎯 Exam Objective

Task. Inspect sshd, start it now, and make it start automatically at boot.

BASH root@servera
systemctl status sshd
systemctl start sshd
systemctl enable sshd
 
πŸ“ŒThe concise equivalent of the preceding start and enable commands:
systemctl enable --now sshd
🧠 Why This Works (Concept)

Why. start changes the current runtime state. enable creates the boot-time dependency symlinks specified by the unit's [Install] section. Neither command implies the other unless --now is used.

Core operations. Substitute the unit required by the task, such as httpd, chronyd, tuned, or atd.

BASH client.lab
systemctl start httpdπŸ’‘run now
systemctl stop httpdπŸ’‘stop now
systemctl restart httpdπŸ’‘stop and start; may interrupt clients
systemctl reload httpdπŸ’‘ask service to reread config, if supported
systemctl reload-or-restart httpdπŸ’‘reload when supported, otherwise restart
systemctl status httpdπŸ’‘state, recent log lines, main PID
 
systemctl enable httpdπŸ’‘start at future boots; does not start now
systemctl disable httpdπŸ’‘do not start through enablement links; does not stop now
systemctl disable --now httpdπŸ’‘stop now and disable for future boots
systemctl mask httpdπŸ’‘link unit to /dev/null so it cannot be started normally
systemctl mask --now httpdπŸ’‘stop it and prevent manual/dependency starts
systemctl unmask httpdπŸ’‘remove the mask; does not enable or start it
βœ… Verification Steps

Verify. These commands are script-friendly because their exit status states the answer without the long status display.

BASH root@servera
systemctl is-active sshdπŸ’‘active
systemctl is-enabled sshdπŸ’‘enabled, disabled, static, masked, or indirect
systemctl show sshd -p ActiveState -p SubState -p UnitFileState -p MainPID
systemctl list-units --type=service --state=running
systemctl list-unit-files --type=service --state=enabled
journalctl -u sshd -b --no-pager | tail
⚠️ Exam Traps & Pitfalls

Traps.

  • enable alone does not start a service; start alone does not survive reboot. Use enable --now when both are required.
  • disable is not a prohibition. A disabled unit can still be started manually or as another unit's dependency. mask is the stronger operation.
  • A masked service cannot be started until it is unmasked. Check systemctl is-enabled if start reports that a unit is masked.
  • Some units are static: they have no [Install] instructions and cannot be enabled directly. They are activated by another unit, socket, timer, path, or dependency.
  • Use the unit name, not the package name. The OpenSSH server unit is sshd.service; the Apache unit is httpd.service.
  • A service configuration change usually needs that service reloaded or restarted. daemon-reload is for changed systemd unit files, not ordinary daemon configuration such as /etc/ssh/sshd_config.

⭐ Official Objective

System-provided units live under /usr/lib/systemd/system/. Administrator-created units and overrides belong under /etc/systemd/system/; files there take precedence. Do not edit vendor units in /usr/lib, because package updates can replace them.

BASH root@servera
systemctl cat httpd
systemctl show -p FragmentPath httpd
systemctl edit httpdπŸ’‘create a persistent drop-in override
 
πŸ“ŒAfter creating, deleting, or changing a *.service, *.timer, *.socket,
πŸ“Œtarget, mount unit, or drop-in:
systemctl daemon-reload
🧠 Why This Works (Concept)

Why. PID 1 caches unit definitions. daemon-reload makes systemd rerun generators and reread unit files; it does not automatically restart a running service.

βœ… Verification Steps

Verify. systemd-analyze verify /etc/systemd/system/name.service catches many syntax and dependency errors; systemctl cat name.service shows the effective unit and drop-ins.

⚠️ Exam Traps & Pitfalls

Traps. After changing a unit that is already running, use both systemctl daemon-reload and the appropriate systemctl restart name. A daemon-reexec is not normally needed.


πŸ”₯ 4x in Exam Sets
S1 Q36 S1 Q37 S1 Q38 S2 Q47

List and inspect.

BASH student@servera
ps auxπŸ’‘BSD form: every process, user, CPU, memory, command
ps -efπŸ’‘POSIX form: every process with PPID and full command
ps -eo pid,ppid,user,ni,stat,%cpu,%mem,etime,cmd --sort=-%cpu
ps -p 1234 -o pid,ppid,user,ni,stat,etime,cmd
 
topπŸ’‘live display; q quit, P CPU sort, M memory sort
top -u studentπŸ’‘only one user's processes
top -p 1234πŸ’‘monitor one PID
 
pgrep sshdπŸ’‘PIDs whose process name matches
pgrep -a sshdπŸ’‘PIDs plus command lines
pgrep -x sshdπŸ’‘exact process-name match
pgrep -u student -a '.'πŸ’‘all processes owned by student
pgrep -af 'python.*worker'πŸ’‘match and print full command lines
🧠 Why This Works (Concept)

Why. A PID uniquely identifies a running process. pgrep is safer than parsing ps | grep, and -x avoids accidentally matching names such as mysshd.

Terminate. First identify the exact target, send the normal termination signal, allow it to clean up, and use SIGKILL only if it will not exit.

BASH student@servera
kill 1234πŸ’‘SIGTERM (15): request graceful termination
kill -TERM 1234
kill -HUP 1234πŸ’‘commonly requests a configuration reload
kill -KILL 1234πŸ’‘SIGKILL (9): kernel stops it immediately; last resort
 
pkill -x sleepπŸ’‘SIGTERM every process named exactly sleep
pkill -TERM -u student sleepπŸ’‘matching sleep processes owned by student
pkill -f 'python.*worker'πŸ’‘match full command line; inspect with pgrep -af first
βœ… Verification Steps

Verify.

BASH root@servera
ps -p 1234πŸ’‘no process row means it exited
pgrep -ax sleepπŸ’‘no output and exit status 1 means no match
systemctl status service-nameπŸ’‘use this when the process belongs to a service
⚠️ Exam Traps & Pitfalls

Traps.

  • Do not begin with kill -9. SIGKILL cannot be caught, so the process cannot flush data, remove temporary files, or shut down cleanly.
  • pkill can terminate several processes. Confirm its selection first with the corresponding pgrep options.
  • pgrep normally matches the short process name; use -f only when the command line must be matched.
  • A systemd service configured with Restart= may immediately return after a manual kill. Stop the unit with systemctl stop instead.
  • Process state Z is a zombie. Killing the zombie has no effect because it has already exited; its parent must reap it.

⭐ Official Objective

Nice values range from -20 (most favorable CPU scheduling priority) through 0 (default) to 19 (least favorable). A higher nice number gives the process less preference for CPU time; it does not impose a CPU limit.

BASH student@servera
πŸ“ŒStart a new command with a lower CPU preference:
nice -n 10 /usr/local/bin/report-job &
 
πŸ“ŒInspect its nice value:
PID=$!
ps -o pid,user,ni,pri,cmd -p "$PID"
 
πŸ“ŒChange an existing process to nice value 15:
renice -n 15 -p "$PID"
 
πŸ“ŒApply a nice value to all eligible processes owned by student:
renice -n 10 -u student
🧠 Why This Works (Concept)

Why. nice sets priority when a process starts; renice changes a running process. The NI column verifies the nice value. The displayed PRI is the scheduler's derived priority and is not the value supplied to nice.

βœ… Verification Steps

Verify. ps -p "$PID" -o pid,ni,pri,cmd or press r in top, enter a PID, and supply a new nice value.

⚠️ Exam Traps & Pitfalls

Traps. An unprivileged user can normally increase the nice value of their own processes (make them less favored), but cannot decrease it again. Root, or a process with the required capability, can assign negative values. Do not confuse a lower numeric nice value with lower scheduling preference: -10 is more favored than 10.


πŸ”₯ 2x in Exam Sets
S3 Q13 S4 Q14

Install and activate TuneD.

BASH root@servera
dnf -y install tuned
systemctl enable --now tuned
tuned-adm listπŸ’‘available profiles and current profile
tuned-adm activeπŸ’‘current profile
tuned-adm recommendπŸ’‘profile TuneD recommends for this machine

Apply the exact profile requested.

BASH root@servera
tuned-adm profile powersaveπŸ’‘[S3 Q13]: favor lower power consumption
tuned-adm active
 
tuned-adm profile balancedπŸ’‘[S4 Q14]: general, non-specialized profile
tuned-adm active

If the task says to apply the recommended profile rather than naming one:

BASH root@servera
PROFILE=$(tuned-adm recommend)
tuned-adm profile "$PROFILE"
tuned-adm active
tuned-adm verify
🧠 Why This Works (Concept)

Why. Profiles apply coordinated kernel, disk, network, and power settings. tuned-adm recommend only prints a recommendation; it does not activate it. On a virtual machine the recommendation is commonly virtual-guest, but use the command's actual output rather than assuming.

βœ… Verification Steps

Verify. systemctl is-active tuned; systemctl is-enabled tuned; tuned-adm active; tuned-adm verify should report that the current configuration matches the active profile.

⚠️ Exam Traps & Pitfalls

Traps. Profile names are exact and depend on installed TuneD profile packages. If the prompt explicitly says powersave or balanced, do not substitute the recommended profile. Ensure tuned is enabled so the selection is reapplied after boot.


πŸ”₯ 2x in Exam Sets
S1 Q4 S4 Q7
🎯 Exam Objective

Task. Install Chrony and configure test.zone.org as an NTP source.

BASH root@servera
dnf -y install chrony
cp -a /etc/chrony.conf /etc/chrony.conf.bak
vim /etc/chrony.conf

Add this line. If the wording says to use only this source, comment out the existing server and pool lines first.

TEXT root@servera
server test.zone.org iburst

Then activate the configuration:

BASH root@servera
chronyd -pπŸ’‘parse and print configuration; errors must be fixed
systemctl enable --now chronyd
systemctl restart chronydπŸ’‘required if it was already running before the edit
🧠 Why This Works (Concept)

Why. chronyd is the service; chronyc is its control client. iburst sends an initial burst of measurements so the source can be selected faster after startup.

βœ… Verification Steps

Verify. Allow a short time for measurements to accumulate.

BASH root@servera
systemctl is-active chronyd
systemctl is-enabled chronyd
chronyc sources -vπŸ’‘^* marks selected source; ^+ an acceptable combined source
chronyc trackingπŸ’‘leap status, stratum, offset, update interval
timedatectlπŸ’‘NTP service active; synchronized becomes yes after selection
getent hosts test.zone.orgπŸ’‘verify DNS if the source is unresolved

If the clock is far enough wrong that a prompt requires immediate correction, after a source is reachable use chronyc makestep and verify again.

⚠️ Exam Traps & Pitfalls

Traps.

  • Editing /etc/ntp.conf configures the obsolete ntpd service, not Chrony. Use /etc/chrony.conf.
  • Restart Chrony after editing. daemon-reload is not needed because the unit file did not change.
  • A ? in chronyc sources means the source is not selectable yet. Check DNS, reachability, the server name, UDP port 123 policy, and journalctl -u chronyd.
  • Do not expect System clock synchronized: yes instantly; Chrony needs valid samples.

πŸ”₯ 2x in Exam Sets
S1 Q13 S4 Q6
BASH root@servera
timedatectl list-timezones | grep '^America/'
timedatectl set-timezone America/New_York
timedatectl
date
readlink -f /etc/localtime

For [S4 Q6], substitute America/Los_Angeles.

🧠 Why This Works (Concept)

Why. timedatectl set-timezone persistently points /etc/localtime at the selected zoneinfo file. It changes how local time is displayed; it does not set the hardware clock or replace NTP synchronization.

βœ… Verification Steps

Verify. timedatectl shows the requested Time zone, and readlink -f /etc/localtime ends in the requested zone name.

⚠️ Exam Traps & Pitfalls

Traps. Zone names are case-sensitive. Do not merely set the TZ shell variable, which affects only that process and its children.


πŸ”₯ 3x in Exam Sets
S1 Q31 S2 Q32 S4 Q26

By default, a volatile journal under /run/log/journal is lost at reboot. Configure an administrator drop-in rather than modifying the vendor defaults.

BASH root@servera
mkdir -p /etc/systemd/journald.conf.d
vim /etc/systemd/journald.conf.d/10-persistent.conf
INI root@servera
[Journal]
Storage=persistent
SystemMaxUse=100M

Apply and ensure the persistent storage directory has systemd's expected ownership and mode:

BASH root@servera
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald
journalctl --flush
🧠 Why This Works (Concept)

Why. Storage=persistent stores journal files below /var/log/journal. SystemMaxUse=100M limits the space used by persistent active and archived journal files; journald removes old archived files as necessary.

βœ… Verification Steps

Verify.

BASH root@servera
systemd-analyze cat-config systemd/journald.conf | grep -E 'Storage|SystemMaxUse'
find /var/log/journal -type f -name '*.journal' -ls
journalctl --disk-usage
journalctl --verify

To bring existing archived usage under the requested limit immediately:

BASH root@servera
journalctl --rotate
journalctl --vacuum-size=100M
journalctl --disk-usage
⚠️ Exam Traps & Pitfalls

Traps.

  • Creating /var/log/journal alone enables persistence when the default Storage=auto is in effect, but an explicit Storage=persistent drop-in proves the requested configuration.
  • RuntimeMaxUse limits volatile /run storage; the task asks for persistent journal usage, so use SystemMaxUse.
  • Vacuuming acts on archived files. Rotate first if an immediate size reduction must be demonstrated.
  • journalctl --disk-usage can be slightly above a nominal value because journal files use allocation increments and active files are retained. The configured cap is what the grader is likely to inspect.
  • Restarting systemd-journald is supported and does not require daemon-reload; its configuration, not its unit, changed.

πŸ”₯ 1x in Exam Sets ⭐ Official Objective
S2 Q32
BASH root@servera
journalctl --list-bootsπŸ’‘boot offsets and IDs
journalctl -bπŸ’‘current boot (offset 0)
journalctl -b -1πŸ’‘previous boot
journalctl -b -2πŸ’‘two boots ago
journalctl -k -b -1πŸ’‘previous boot, kernel messages only
 
journalctl -u sshdπŸ’‘one unit, all retained boots
journalctl -u sshd -bπŸ’‘one unit in current boot
journalctl -xeu sshdπŸ’‘end, explanations, one unit
journalctl -p warning..alert -bπŸ’‘warning through highest priorities
journalctl --since 'today 08:00' --until 'today 10:00'
journalctl --since '-30 min'
journalctl -fπŸ’‘follow new entries; Ctrl-C exits
 
journalctl _PID=1234
journalctl _COMM=sshd
journalctl _UID=1000 --since today
journalctl -g 'denied|failed' --since todayπŸ’‘regular-expression message filter
journalctl -o short-iso-precise --no-pager

Filters on the same field are alternatives, while different fields are combined. For example, journalctl -u sshd -p err -b selects current-boot errors from sshd.

Verify persistence. After configuring 8.8, reboot once, then run:

BASH root@servera
journalctl --list-bootsπŸ’‘must contain at least -1 and 0
journalctl -b -1 -n 20 --no-pager
⚠️ Exam Traps & Pitfalls

Traps. journalctl -b -1 cannot show an earlier boot that was never stored persistently. -r reverses output; -n 50 limits to the last 50 entries. Use sudo or root for complete system logs because an ordinary user may see only their own journal data.


πŸ”₯ 1x in Exam Sets
S1 Q32
Source Primary purpose Useful command
systemd journal Kernel, boot, services, and structured system events journalctl
/var/log/audit/audit.log Linux Audit records, authentication events, SELinux AVC denials ausearch, aureport
/var/log/messages General syslog text when rsyslog is installed/configured tail, grep
/var/log/secure Authentication/security syslog text when rsyslog is installed/configured tail, grep

Examine and save audit findings.

BASH root@servera
systemctl is-active auditd
ausearch -m AVC,USER_AUTH,USER_LOGIN -ts today -i
 
{
echo 'SELinux AVC denials'
ausearch -m AVC,USER_AVC -ts today -i
echo
echo 'Failed authentication summary'
aureport --auth --failed -i
} > /audit_log.txt

If the requested period is not "today", use the task's dates or -ts recent. For a focused SELinux check:

BASH root@servera
ausearch -m AVC,USER_AVC -ts recent -i
journalctl -t setroubleshoot --since today
wc -l /audit_log.txt
less /audit_log.txt
🧠 Why This Works (Concept)

Why. Audit records can span several lines and include encoded values; ausearch correlates records and -i interprets IDs and encoded fields. aureport provides summaries. Redirecting the final report creates the exact file requested by [S1 Q32].

⚠️ Exam Traps & Pitfalls

Traps. An empty AVC search means no matching denial was recorded in that time range; do not invent findings. Do not disable SELinux to "fix" a denial. Diagnose the expected access, labels, Boolean, or policy instead. Use systemctl status auditd to inspect auditd; on RHEL, normal audit service control is intentionally restricted and service auditd restart is used when a task explicitly requires an auditd restart.

Detailed rsyslog routing and logrotate policy are outside the RHEL 10 EX200 core scope in this guide. See Appendix A for [S5 Q24] and [S5 Q25]; the files above are included only so you know where relevant records may appear.


πŸ”₯ 7x in Exam Sets
S1 Q22 S1 Q44 S1 Q45 S2 Q20 S3 Q4 S4 Q49 +1 more

Manage a user's crontab.

BASH root@servera
crontab -eπŸ’‘edit current user's jobs
crontab -lπŸ’‘list current user's jobs [S1 Q45]
crontab -u root -eπŸ’‘edit root's jobs (when run as root)
crontab -u root -l
crontab -rπŸ’‘remove every current-user job; dangerous
systemctl status crond
systemctl enable --now crond

Each user-crontab line has five schedule fields followed by the command:

TEXT root@servera
minute hour day-of-month month day-of-week command
0-59 0-23 1-31 1-12 0-7 (0 and 7 are Sunday)

Jobs from the sets. Add the requested line with crontab -e as root.

CRON root@servera
πŸ“Œ[S1 Q22] Noon on weekdays; append, do not overwrite.
0 12 * * 1-5 /usr/bin/echo 'These pretzels are making me thirsty!' >> /var/log/messages
 
πŸ“Œ[S2 Q20] Daily at 00:45; remove empty files and directories below /tmp.
45 0 * * * /usr/bin/find /tmp -mindepth 1 -empty -delete
 
πŸ“Œ[S3 Q4] At minute 0 every two hours on weekdays; wall writes to logged-in terminals.
0 */2 * * 1-5 /usr/bin/wall 'Free Time!'
 
πŸ“Œ[S4 Q49] Daily at 16:15; preserve earlier reports by appending.
15 16 * * * /usr/bin/df -h >> /tmp/diskstatus
 
πŸ“Œ[S5 Q18] At 22:00 on Saturday and Sunday.
0 22 * * 0,6 /usr/bin/find /tmp -mindepth 1 -empty -delete
🧠 Why This Works (Concept)

Why. Cron uses 24-hour time. -mindepth 1 protects /tmp itself. GNU find -delete uses depth-first deletion, so empty child entries are removed before newly empty parent directories.

Quoting, percent, and environment caveats.

  • Cron runs with a small environment and a default shell, commonly /bin/sh. Use absolute command paths and put complex logic in an executable script.
  • Shell quoting still applies, but cron processes an unescaped % specially: the first % becomes a newline and the remainder is sent to the command on standard input. Backslash every literal percent in a crontab command, even inside shell quotes.
  • Example: 0 1 * * * /usr/bin/date +\%F >> /tmp/run-dates. In an executable script called by cron, write normal %; cron does not parse the script contents.
  • ! is safe in cron's non-interactive shell, but single quotes clearly preserve the literal phrases in [S1 Q22] and [S3 Q4].
  • A user crontab has no username field. /etc/crontab and files in /etc/cron.d/ have an extra username between the five time fields and the command.
  • Day-of-month and day-of-week are effectively OR conditions when both are restricted. Avoid restricting both unless that behavior is intended.
  • Output not redirected may be mailed locally, but a mail service may not be available. Redirect output when the task requests a file.
βœ… Verification Steps

Verify.

BASH root@servera
crontab -u root -l
systemctl is-active crond
journalctl -u crond --since today
grep CROND /var/log/cron 2>/dev/nullπŸ’‘only where rsyslog creates this file

For safe verification, temporarily use a near-future schedule or run only the command portion manually, inspect its output, then restore the exact requested schedule. Do not manually run a destructive find ... -delete without first previewing the matches using the same command without -delete.


⭐ Official Objective
BASH root@servera
dnf -y install at
systemctl enable --now atd
 
πŸ“ŒSchedule a command for 23:00 today:
echo '/usr/bin/touch /tmp/at-complete' | at 23:00
 
πŸ“ŒOther accepted time forms:
echo '/usr/local/sbin/report-job' | at now + 20 minutes
echo '/usr/bin/systemctl restart httpd' | at 02:30 tomorrow
 
atqπŸ’‘list queued jobs
at -c 3πŸ’‘inspect job number 3, including captured environment
atrm 3πŸ’‘remove job number 3

Interactive form:

TEXT root@servera
at 16:30
at> /usr/bin/df -h > /tmp/at-diskstatus
at> Ctrl-D
🧠 Why This Works (Concept)

Why. atd executes a queued job once. The job runs as the user who submitted it and captures much of that user's current environment and working directory, but absolute paths make the result predictable.

βœ… Verification Steps

Verify. systemctl is-active atd; atq; at -c JOB; after execution, confirm the requested result and use journalctl -u atd if it failed.

⚠️ Exam Traps & Pitfalls

Traps. at is one-time scheduling; use cron or a timer for recurrence. Redirection on echo ... | at must be inside the quoted job text if it should occur when the job runs. For example, echo '/usr/bin/date >> /tmp/date.log' | at now + 5 minutes is correct.


⭐ Official Objective
🎯 Exam Objective

Task. Run /usr/local/sbin/report-job every day at 02:15, and run a missed occurrence after the machine returns from downtime.

Create /etc/systemd/system/report-job.service:

INI root@servera
[Unit]
Description=Generate the daily report
 
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/report-job

Create /etc/systemd/system/report-job.timer:

INI root@servera
[Unit]
Description=Run the daily report at 02:15
 
[Timer]
OnCalendar=*-*-* 02:15:00
Persistent=true
Unit=report-job.service
 
[Install]
WantedBy=timers.target

Apply, enable, and test:

BASH root@servera
chmod +x /usr/local/sbin/report-job
systemd-analyze verify /etc/systemd/system/report-job.service /etc/systemd/system/report-job.timer
systemd-analyze calendar '*-*-* 02:15:00'
systemctl daemon-reload
systemctl enable --now report-job.timer
 
πŸ“ŒTest the service directly; this does not disturb the timer schedule.
systemctl start report-job.service
🧠 Why This Works (Concept)

Why. The .timer activates the matching .service. Persistent=true records the last trigger and causes one missed calendar event to run when the timer becomes active again. WantedBy=timers.target gives enable a boot-time target. A oneshot service exits after its command completes, so inactive (dead) after a successful run is normal.

βœ… Verification Steps

Verify.

BASH root@servera
systemctl is-enabled report-job.timer
systemctl is-active report-job.timer
systemctl list-timers --all | grep report-job
systemctl status report-job.timer report-job.service
journalctl -u report-job.service --since today

Useful calendar forms:

INI root@servera
OnCalendar=hourly
OnCalendar=Mon..Fri *-*-* 12:00:00
OnCalendar=Sat,Sun *-*-* 22:00:00
OnBootSec=5min
OnUnitActiveSec=2h
⚠️ Exam Traps & Pitfalls

Traps.

  • Enable the .timer, not the oneshot .service.
  • Run systemctl daemon-reload after every unit-file edit, then restart the timer if it was already active.
  • ExecStart= is not interpreted by a shell. Redirection such as >>, pipes, globbing, and shell built-ins do not work unless the command explicitly invokes a shell, for example ExecStart=/bin/sh -c '/usr/bin/df -h >> /tmp/diskstatus'. Prefer an executable script for complex commands.
  • Persistent= affects OnCalendar= timers, not monotonic timers such as OnBootSec=.
  • A timer can be active while its service's last run failed. Verify both units and inspect the service journal.

BASH root@servera
πŸ“ŒServices and custom units
systemctl is-active sshd chronyd tuned crond
systemctl is-enabled sshd chronyd tuned crond
systemctl --failed
 
πŸ“ŒProcess and priority
ps -eo pid,user,ni,stat,cmd --sort=ni | head
pgrep -ax sshd
 
πŸ“ŒPerformance and time
tuned-adm active
tuned-adm verify
chronyc sources -v
chronyc tracking
timedatectl
 
πŸ“ŒJournal and audit
journalctl --disk-usage
journalctl --verify
journalctl --list-boots
ausearch -m AVC,USER_AUTH -ts today -i | head
 
πŸ“ŒSchedules
crontab -u root -l
systemctl is-active crond atd
atq
systemctl list-timers --all

Before finishing the exam, reboot if time permits and repeat the persistence checks: is-enabled, is-active, tuned-adm active, chronyc sources -v, journalctl --list-boots, crontab -l, and systemctl list-timers --all. A successful command before reboot is not proof that enablement, configuration, logs, or schedules will survive grading.

  1. Start and enable sshd, then explain why start and enable are separate operations.
  2. Disable httpd, prove it can still be started, then mask it and prove it cannot; restore the original state.
  3. Start a CPU-heavy command at nice value 10, inspect it, and renice it to 15.
  4. Apply powersave, verify it, then apply the output of tuned-adm recommend and verify again.
  5. Configure Chrony with a supplied server and prove which source is selected.
  6. Persist the journal with SystemMaxUse=100M, reboot, and display the previous boot.
  7. Enter all five set-derived cron schedules, list them, and explain the escaped-percent rule.
  8. Queue an at job, inspect it, and remove it.
  9. Build and enable a oneshot service plus calendar timer; verify its next trigger and its journal.

πŸ›‘οΈ Chapter 10 11 Exam Tasks

Chapter 9 - Security: SSH and SELinux

Objectives covered: Configure key-based SSH authentication; restrict SSH access; configure SELinux enforcing and permissive modes; list and identify SELinux file and process contexts; restore default file contexts; manage SELinux port labels and Booleans; investigate SELinux denials.

Security changes can lock out the administrator or stop a service. Keep the current root console or SSH session open, validate the new configuration, and prove a second login works before disconnecting.


πŸ”₯ 1x in Exam Sets
S5 Q48
BASH root@servera
systemctl status sshd
systemctl enable --now sshd
systemctl is-active sshd
systemctl is-enabled sshd
sshd -t
sshd -T | grep -E '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin|allowusers|maxauthtries) '
🧠 Why This Works (Concept)

Why. enable --now starts the service now and enables it for later boots. sshd -t checks syntax and key-file sanity; success is silent. sshd -T prints the effective configuration after includes and defaults have been applied.

For rules inside Match blocks, test as a particular connection:

BASH root@servera
sshd -T -C user=stella,host=linux5.lab,addr=192.168.1.50 | grep -E '^(passwordauthentication|permitrootlogin|maxauthtries) '
⚠️ Exam Traps & Pitfalls

Traps. The daemon is sshd, not ssh. A successful syntax test does not prove that the intended value won. Always inspect sshd -T too.


πŸ”₯ 6x in Exam Sets
S1 Q26 S2 Q23 S2 Q27 S3 Q29 S4 Q30 S5 Q21

RHEL includes /etc/ssh/sshd_config.d/*.conf from /etc/ssh/sshd_config. Prefer a local drop-in rather than editing a vendor file:

BASH root@servera
vi /etc/ssh/sshd_config.d/00-exam.conf
chmod 600 /etc/ssh/sshd_config.d/00-exam.conf
restorecon -v /etc/ssh/sshd_config.d/00-exam.conf
sshd -t
sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|permitrootlogin|allowusers|maxauthtries) '
systemctl reload sshd

OpenSSH normally uses the first obtained value for most scalar keywords. Because included files are processed in lexical order, a low-numbered name such as 00-exam.conf is useful when another drop-in sets the same scalar keyword. List-valued directives such as AllowUsers can accumulate, so search all files and consolidate conflicts. Confirm every result with sshd -T; do not assume the file name won.

Limit authentication attempts to two

[S1 Q26]

TEXT root@servera
MaxAuthTries 2
🧠 Why This Works (Concept)

Why. This limits failed authentication attempts in each connection, not the number of TCP connections or permanent account failures.

βœ… Verification Steps

Verify. sshd -T | grep '^maxauthtries ' must report maxauthtries 2.

Disable password-based SSH login

[S2 Q23]

TEXT root@servera
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
🧠 Why This Works (Concept)

Why. PasswordAuthentication no disables the SSH password method. Disabling keyboard-interactive authentication closes the common PAM-backed password path as well. UsePAM yes can remain enabled; it does not by itself re-enable either SSH authentication method.

βœ… Verification Steps

Verify. Before reloading, prove key login in a second terminal. After reloading:

BASH root@servera
sshd -T | grep -E '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication) '
ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no user@server

Enable password-based SSH login

TEXT root@servera
PasswordAuthentication yes
KbdInteractiveAuthentication yes

Enable only the method the task requires. If it specifically requests ordinary SSH password authentication, PasswordAuthentication yes is the essential setting; keyboard-interactive is separate.

Resolve the "disable password-less login" ambiguity

[S4 Q30]

The wording is not the same as "disable password authentication."

  • If the task says users must use keys or that passwords must be disabled, use PasswordAuthentication no and KbdInteractiveAuthentication no.
  • If it literally says disable password-less login or disable key login, use PubkeyAuthentication no and normally retain PasswordAuthentication yes.
  • Read the required end state, not the phrase alone. Do not disable both key and password methods unless another allowed method is explicitly configured.

Root login policy

[S1 Q30] root key login; [S2 Q27] prevent direct root login; [S3 Q29] enable direct root login

TEXT root@servera
πŸ“ŒNo direct root SSH login:
PermitRootLogin no
 
πŸ“ŒRoot may log in with a key, but not a password:
PermitRootLogin prohibit-password
 
πŸ“ŒDirect root login is explicitly required, including password login:
PermitRootLogin yes

PermitRootLogin prohibit-password is the safer answer for root key authentication. Use yes only when the task explicitly requires root password login or unrestricted direct root login. PasswordAuthentication yes does not override PermitRootLogin no.

βœ… Verification Steps

Verify. sshd -T | grep '^permitrootlogin ' and then test the required root login from another terminal.

Allow only named users

[S5 Q21]

TEXT root@servera
AllowUsers stella

Multiple users go on the same directive, separated by spaces:

TEXT root@servera
AllowUsers stella simpson peter
🧠 Why This Works (Concept)

Why. Once AllowUsers exists, every unlisted user is denied, including root. User and source restrictions may be combined, for example stella@192.168.1.*.

Before declaring that only Stella is allowed, find and remove or consolidate any other active entries: grep -Rni '^[[:space:]]*AllowUsers' /etc/ssh/sshd_config /etc/ssh/sshd_config.d.

βœ… Verification Steps

Verify. Confirm stella can open a new session and an unlisted test user cannot. Keep the existing administrative session open while testing.

Lockout-safe change sequence

  1. Keep a root console or the current SSH session open.
  2. Configure and test the required public key before disabling passwords or restricting users.
  3. Add the drop-in, then run sshd -t. Fix every error before continuing.
  4. Use sshd -T or sshd -T -C ... to inspect the effective values.
  5. Use systemctl reload sshd; a reload preserves established sessions.
  6. Open a second connection and verify the required user, root policy, and authentication method.
  7. Disconnect the old session only after the second login succeeds.
⚠️ Exam Traps & Pitfalls

Traps. Do not restart sshd before syntax validation. Do not add AllowUsers stella while connected only as another account. Do not disable passwords until the key works. Check the firewall separately if port 22 is unreachable: firewall-cmd --list-services and, if required, firewall-cmd --permanent --add-service=ssh; firewall-cmd --reload.


πŸ”₯ 2x in Exam Sets
S2 Q7 S4 Q29

Run key generation as the account that will initiate the connection:

BASH root@servera
su - simpson
ssh-keygen -t ed25519
ssh-copy-id -i ~/.ssh/id_ed25519.pub simpson@linux.lab
ssh -o PreferredAuthentications=publickey simpson@linux.lab

Press Enter to accept the default key path. An empty key passphrase produces a fully passwordless login; a passphrase plus ssh-agent is safer but may not match a lab task that requires no prompt.

🧠 Why This Works (Concept)

Why. The private key remains on the client. ssh-copy-id appends only the public key to the remote account's ~/.ssh/authorized_keys and normally fixes its basic permissions. It needs a working password or another existing login method for the initial copy.

Verify on the client. ssh -v simpson@linux.lab should show that a public key was offered and accepted.

Verify on the server. As root:

BASH root@servera
ls -ldZ /home/simpson /home/simpson/.ssh
ls -lZ /home/simpson/.ssh/authorized_keys
sshd -T | grep '^pubkeyauthentication '

Manual installation when ssh-copy-id is unavailable

Copy the single line from the client's ~/.ssh/id_ed25519.pub, then on the server:

BASH root@servera
install -d -m 700 -o simpson -g simpson /home/simpson/.ssh
touch /home/simpson/.ssh/authorized_keys
chown simpson:simpson /home/simpson/.ssh/authorized_keys
chmod 600 /home/simpson/.ssh/authorized_keys
vi /home/simpson/.ssh/authorized_keys
restorecon -RFv /home/simpson/.ssh

The home directory must not be writable by group or others. The safe modes are 700 for .ssh and 600 for authorized_keys; ownership must be the target user. restorecon gives files under a normal home their expected SELinux labels.

⚠️ Exam Traps & Pitfalls

Traps. Never copy the private key to the server. Do not use scp id_ed25519; copy only id_ed25519.pub. Appending is safer than replacing an existing authorized_keys. A valid key still fails if ownership, modes, SELinux context, PubkeyAuthentication, AllowUsers, or root policy denies it.


πŸ”₯ 1x in Exam Sets
S1 Q30

On ServerA, as root:

BASH root@serverb
ssh-keygen -t ed25519
ssh-copy-id -i /root/.ssh/id_ed25519.pub root@ServerB
ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no root@ServerB

On ServerB, permit root key authentication without permitting a root password:

TEXT root@servera
PubkeyAuthentication yes
PermitRootLogin prohibit-password

Then validate and reload as described in 9.2. For a manual copy, use /root/.ssh with mode 700, /root/.ssh/authorized_keys with mode 600, owner root:root, and run restorecon -RFv /root/.ssh.

Trap. Initial ssh-copy-id root@ServerB cannot work if root login is already completely denied. Install the public key through console or another sudo-capable account, set PermitRootLogin prohibit-password, validate, reload, and test.


πŸ”₯ 3x in Exam Sets
S1 Q14 S2 Q25 S3 Q30

Inspect all three useful views:

BASH root@servera
getenforce
sestatus
grep '^SELINUX=' /etc/selinux/config

Set enforcing now and after reboot

BASH root@servera
setenforce 1
vi /etc/selinux/config

Set this line:

TEXT root@servera
SELINUX=enforcing

Set permissive now and after reboot

BASH root@servera
setenforce 0
vi /etc/selinux/config

Set this line:

TEXT root@servera
SELINUX=permissive
🧠 Why This Works (Concept)

Why. setenforce changes the running kernel immediately but does not survive reboot. /etc/selinux/config controls the next and later boots but does not change the current mode.

βœ… Verification Steps

Verify. Run getenforce now, check /etc/selinux/config, and after a requested reboot run getenforce again.

⚠️ Exam Traps & Pitfalls

Traps. Permissive mode logs denials but allows the operations; enforcing mode blocks and logs them. Do not use SELINUX=disabled when asked for permissive. A system booted with SELinux disabled cannot be made enforcing at runtime with setenforce; enable it persistently and reboot, allowing any required relabel. Do not make the whole system permissive merely to hide one mislabeled file or missing Boolean.


⭐ Official Objective
BASH root@servera
ls -Z /var/www/html/index.html
ls -Zd /var/www/html /web
ps -eZ
ps -eZ | grep httpd
id -Z

A context commonly appears as system_u:object_r:httpd_sys_content_t:s0: SELinux user, role, type, and level. Type is usually the field that controls service access.

Restore the policy-defined default label:

BASH root@servera
restorecon -v /var/www/html/index.html
restorecon -RFv /var/www/html
🧠 Why This Works (Concept)

Why. restorecon consults the persistent file-context policy and repairs labels. -R is recursive, -F forces the complete context, and -v reports changes.

⚠️ Exam Traps & Pitfalls

Traps. chcon changes the current label only; a later restorecon or full relabel can erase it. Use semanage fcontext for a persistent custom path rule. Files copied with cp normally receive the destination label, while moved files can retain the old label; run restorecon after moving content into a service directory.


Install the management command if needed:

BASH root@servera
dnf -y install policycoreutils-python-utils

Label /web and everything below it as read-only Apache content:

BASH root@servera
mkdir -p /web
semanage fcontext -a -t httpd_sys_content_t '/web(/.*)?'
restorecon -RFv /web
ls -Zd /web
semanage fcontext -l | grep -F '/web'
🧠 Why This Works (Concept)

Why. semanage fcontext -a records a persistent path rule; it does not relabel existing files. restorecon applies that rule. The quoted regular expression covers both /web and all descendants.

If httpd must write to a specific subtree, label only that subtree with a writable type rather than making the whole site writable:

BASH root@servera
mkdir -p /web/uploads
semanage fcontext -a -t httpd_sys_rw_content_t '/web/uploads(/.*)?'
restorecon -RFv /web/uploads
ls -Zd /web/uploads

Change or remove an existing local rule with semanage fcontext -m ... or semanage fcontext -d '/web(/.*)?'.

⚠️ Exam Traps & Pitfalls

Traps. Running restorecon without first creating the custom rule can apply an unsuitable default such as default_t. Shell glob /web/* does not describe future nested content; use the quoted SELinux path expression. Correct SELinux labels do not replace normal ownership and permissions.


πŸ”₯ 1x in Exam Sets
S1 Q29
BASH root@servera
getsebool httpd_can_network_connect
setsebool -P httpd_can_network_connect on
getsebool httpd_can_network_connect
semanage boolean -l | grep httpd_can_network_connect
🧠 Why This Works (Concept)

Why. Booleans enable policy-supported behavior without writing a custom policy. -P updates the persistent policy store as well as the current value. Without -P, the change lasts only until reboot or policy reload.

Useful discovery commands:

BASH root@servera
getsebool -a | grep httpd
semanage boolean -l | less
⚠️ Exam Traps & Pitfalls

Traps. Enable only the Boolean justified by the service requirement. httpd_can_network_connect lets httpd initiate network connections; it is not needed merely to serve local static files.


If httpd must listen on TCP 8080, first check whether the port is already assigned:

BASH root@servera
dnf -y install policycoreutils-python-utils
semanage port -l | grep -w 8080
semanage port -m -t http_port_t -p tcp 8080
semanage port -l | grep '^http_port_t'

If -a reports that port 8080 is already defined under another type, modify the existing assignment:

BASH root@servera
semanage port -m -t http_port_t -p tcp 8080
🧠 Why This Works (Concept)

Why. The SELinux port type permits an httpd-domain process to bind that port. This is independent of the httpd Listen setting and the firewall.

A complete nonstandard-port task may require all three controls:

BASH root@servera
vi /etc/httpd/conf/httpd.confπŸ’‘add or change: Listen 8080
semanage port -m -t http_port_t -p tcp 8080
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
systemctl restart httpd
ss -lntp | grep ':8080'
⚠️ Exam Traps & Pitfalls

Traps. semanage port does not open the firewall and does not configure the daemon. firewall-cmd does not grant SELinux permission. Use -p tcp or -p udp exactly as the service requires. Use -m, not a second -a, for a port that already has a policy assignment.


πŸ”₯ 1x in Exam Sets
S1 Q32

The useful packages are:

BASH root@servera
dnf -y install policycoreutils-python-utils setroubleshoot-server
systemctl is-active auditd

Search recent AVC denials in interpreted form:

BASH root@servera
ausearch -m AVC,USER_AVC -ts recent -i
ausearch -m AVC,USER_AVC -ts today -i > /audit_log.txt

Generate a human-readable setroubleshoot analysis and append it:

BASH root@servera
sealert -a /var/log/audit/audit.log >> /audit_log.txt
less /audit_log.txt

Narrow a search by service executable or time when required:

BASH root@servera
ausearch -m AVC,USER_AVC -ts today -x /usr/sbin/httpd -i
ausearch -m AVC -ts 10:00:00 -te 10:15:00 -i
journalctl -t setroubleshoot --since today
🧠 Why This Works (Concept)

Why. ausearch extracts the original audit records; -i resolves numeric values into readable names. sealert correlates denials and suggests likely labels, Booleans, or policy issues. Redirection creates the exact requested report, and >> preserves the first analysis while adding the second.

Use this repair order:

  1. Reproduce the failure and note its time.
  2. Check ordinary ownership, mode, service configuration, and listening ports.
  3. Inspect file and process contexts with ls -Z and ps -eZ.
  4. Repair an incorrect standard label with restorecon.
  5. For a custom path or port, add the correct semanage fcontext or semanage port rule.
  6. Enable a documented Boolean only when the requested behavior requires it.
  7. Reproduce the operation and run ausearch again to verify that no new denial appears.
⚠️ Exam Traps & Pitfalls

Traps. Do not blindly copy every sealert suggestion. In particular, do not start with audit2allow; a custom allow rule can mask a wrong context, unsafe permission, or bad service configuration. setroubleshoot-server provides sealert; policycoreutils-python-utils provides semanage and related analysis tools. An empty ausearch result may mean the time range is wrong, auditd is not running, or the failure is not caused by SELinux.


  1. Configure a key for peter, verify it, then disable password and keyboard-interactive SSH authentication without losing access.
  2. Permit root key login but deny root password login; prove the effective PermitRootLogin value.
  3. Restrict SSH to stella, set MaxAuthTries 2, validate with both sshd -t and sshd -T, then test from a second terminal.
  4. Set SELinux permissive now but enforcing after the next boot; explain the two different commands/files involved.
  5. Label /web persistently for Apache content and /web/uploads for Apache writes; verify with ls -Z and semanage fcontext -l.
  6. Permit httpd to bind TCP 8080, open the firewall, and identify the three independent settings required.
  7. Permanently enable httpd_can_network_connect and prove the saved value.
  8. Save today's AVC records and sealert analysis to /audit_log.txt, then fix the root cause without disabling SELinux.

🌐 Chapter 11 12 Exam Tasks

Chapter 10 - Apache, NFS clients, and AutoFS

Objectives covered: Start, stop, enable, and inspect services; configure services to start at boot; configure firewalld; mount NFS file systems persistently; configure on-demand file systems with AutoFS; diagnose service, firewall, mount, and SELinux failures.

This chapter contains only services that belong to these objectives: Apache HTTP Server, NFS client support, and AutoFS. Replace example hosts, exports, paths, and timeouts with the values in the task.


πŸ”₯ 5x in Exam Sets
S3 Q38 S3 Q39 S4 Q39 S5 Q40 S5 Q48

Use the same sequence for every service task:

BASH root@servera
dnf -y install PACKAGE
systemctl enable --now UNIT
systemctl status UNIT --no-pager
systemctl is-active UNIT
systemctl is-enabled UNIT
🧠 Why This Works (Concept)

Why. start affects the current boot; enable affects future boots. enable --now does both. A package name and its unit can differ: package httpd supplies httpd.service, package autofs supplies autofs.service.

Useful controls:

BASH root@servera
systemctl start UNIT
systemctl stop UNIT
systemctl restart UNITπŸ’‘stop and start; interrupts the service
systemctl reload UNITπŸ’‘reread configuration when supported
systemctl enable UNIT
systemctl disable UNITπŸ’‘does not stop a running unit
systemctl disable --now UNIT
systemctl list-unit-files --type=service
journalctl -u UNIT -b --no-pager
⚠️ Exam Traps & Pitfalls

Traps. active does not mean enabled, and enabled does not mean active. Check both. A failed unit normally explains itself in systemctl status or journalctl -u UNIT.


πŸ”₯ 8x in Exam Sets
S1 Q24 S2 Q10 S3 Q31 S3 Q34 S4 Q15 S4 Q43 +2 more

Example: publish Welcome to RHCSA Practice Exam! and make it survive reboot.

BASH root@servera
dnf -y install httpd
printf '%s\n' 'Welcome to RHCSA Practice Exam!' > /var/www/html/index.html
restorecon -Rv /var/www/html
apachectl configtest
systemctl enable --now httpd
 
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=http
firewall-cmd --reload

For a supplied page such as [S3 Q34]:

BASH root@servera
curl -f http://rhcsa.example.com/pub/serverA.html -o /var/www/html/index.html
restorecon -v /var/www/html/index.html
apachectl configtest
systemctl enable --now httpd
🧠 Why This Works (Concept)

Why. /var/www/html is the default DocumentRoot. restorecon applies the policy-defined web content label. The permanent firewall rule survives reboot; reload activates it now.

βœ… Verification Steps

Verify. Test the content, socket, service state, boot state, firewall, and label rather than trusting one command.

BASH root@servera
curl -s http://localhost/
ss -lntp | grep ':80 '
systemctl is-active httpd
systemctl is-enabled httpd
firewall-cmd --query-service=http
firewall-cmd --permanent --query-service=http
ls -lZ /var/www/html/index.html
⚠️ Exam Traps & Pitfalls

Traps.

  • The daemon is httpd, not apache2; the package, unit, and configuration tree are httpd, httpd.service, and /etc/httpd/.
  • firewall-cmd --add-service=http without --permanent is lost after reboot. A permanent change needs --reload before it becomes active.
  • Opening the firewall does not start Apache. Starting Apache does not open the firewall.
  • apachectl configtest should report Syntax OK before a restart.
  • A local curl can pass while remote access is blocked. Test from another host when possible.

πŸ”₯ 2x in Exam Sets
S1 Q24 S2 Q10

If a task only says to allow HTTP and HTTPS through the firewall, add both service rules:

BASH root@servera
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-services

This permits TCP ports 80 and 443. It does not create a certificate, add an Apache TLS virtual host, or make anything listen on port 443. curl https://localhost can still fail even though firewall-cmd --query-service=https says yes.

If the task explicitly requires the page to be served over HTTPS, configure TLS as well:

BASH root@servera
dnf -y install httpd mod_ssl
πŸ“ŒEdit /etc/httpd/conf.d/ssl.conf to use certificate/key paths supplied by the task:
πŸ“ŒSSLCertificateFile /path/to/server.crt
πŸ“ŒSSLCertificateKeyFile /path/to/server.key
chmod 600 /path/to/server.key
apachectl configtest
systemctl enable httpd
systemctl restart httpd
ss -lntp | grep ':443 '
curl -k https://localhost/

mod_ssl supplies an SSL virtual-host configuration. Use the certificate and private-key paths given by the exam. For a real service, the certificate must match the DNS name and be trusted; curl -k is only a functional lab check that encryption and the listener work.

βœ… Verification Steps

Verify.

BASH root@servera
httpd -M | grep ssl_module
ss -lntp | grep ':443 '
firewall-cmd --query-service=https
curl -kI https://localhost/
⚠️ Exam Traps & Pitfalls

Traps. Do not claim that HTTPS works merely because port 443 is open. Conversely, a working local TLS listener can remain unreachable remotely if the firewall rule is absent.


⭐ Official Objective

Example: serve /web/index.html.

BASH root@servera
dnf -y install httpd policycoreutils-python-utils
mkdir -p /web
printf '%s\n' 'Custom web root' > /web/index.html
 
cat > /etc/httpd/conf.d/webroot.conf <<'EOF'
DocumentRoot "/web"
<Directory "/web">
Require all granted
</Directory>
EOF
 
semanage fcontext -a -t httpd_sys_content_t '/web(/.*)?'
restorecon -Rv /web
apachectl configtest
systemctl enable --now httpd
🧠 Why This Works (Concept)

Why. Apache authorization and SELinux are separate gates. <Directory> permits Apache access; the persistent file-context rule tells SELinux that this non-default tree is web content. restorecon applies that rule now and after future relabeling.

If a matching rule already exists, modify it instead of adding a duplicate:

BASH root@servera
semanage fcontext -m -t httpd_sys_content_t '/web(/.*)?'
restorecon -Rv /web
βœ… Verification Steps

Verify.

BASH root@servera
apachectl -S
ls -ldZ /web /web/index.html
matchpathcon /web/index.html
curl -s http://localhost/
⚠️ Exam Traps & Pitfalls

Traps. chcon changes only the current label and can be undone by restorecon or relabeling. Use semanage fcontext plus restorecon. Every parent directory must also have execute permission for the Apache process.


πŸ”₯ 1x in Exam Sets
S4 Q24

Assume serverb:/exports/web must be mounted at /web and served by Apache.

BASH root@serverb
dnf -y install httpd nfs-utils policycoreutils-python-utils
mkdir -p /web
grep -qE '^[^#]+[[:space:]]+/web[[:space:]]' /etc/fstab || \
printf '%s\n' 'serverb:/exports/web /web nfs defaults,_netdev 0 0' >> /etc/fstab
mount /web
 
cat > /etc/httpd/conf.d/webroot.conf <<'EOF'
DocumentRoot "/web"
<Directory "/web">
Require all granted
</Directory>
EOF
 
setsebool -P httpd_use_nfs on
apachectl configtest
systemctl enable --now httpd
firewall-cmd --permanent --add-service=http
firewall-cmd --reload
🧠 Why This Works (Concept)

Why. NFS files normally carry an NFS SELinux type that the client cannot permanently relabel. httpd_use_nfs is the policy-supported permission for Apache to read NFS content. _netdev identifies a network-dependent mount so systemd orders it after networking.

βœ… Verification Steps

Verify.

BASH root@servera
findmnt -T /web
findmnt -no SOURCE,FSTYPE,OPTIONS /web
getsebool httpd_use_nfs
apachectl configtest
curl -s http://localhost/
⚠️ Exam Traps & Pitfalls

Traps.

  • Do not run semanage fcontext/restorecon against an NFS export as the primary fix; use httpd_use_nfs.
  • Set only the Boolean required by the storage and task. httpd_can_network_connect allows outbound network connections by web applications; it is not the NFS-content Boolean. [S1 Q29]
  • If Apache starts before an unavailable NFS mount, it may expose an empty local mount-point directory. Verify the mount before testing the page.
  • Do not add duplicate /etc/fstab lines while retrying. Search first with grep -F '/web' /etc/fstab.

πŸ”₯ 1x in Exam Sets
S2 Q22

Example: mount linuxB.lab:/share at /nfs on linuxA.lab.

BASH root@servera
dnf -y install nfs-utils
mkdir -p /nfs
mount -t nfs linuxB.lab:/share /nfs
findmnt /nfs
umount /nfs
 
grep -qE '^[^#]+[[:space:]]+/nfs[[:space:]]' /etc/fstab || \
printf '%s\n' 'linuxB.lab:/share /nfs nfs defaults,_netdev 0 0' >> /etc/fstab
findmnt --verify
mount -a
findmnt /nfs
🧠 Why This Works (Concept)

Why. The trial mount validates DNS, export name, server reachability, and client support before editing a boot-critical file. /etc/fstab makes the mount persistent.

βœ… Verification Steps

Verify.

BASH root@servera
findmnt --verify
findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /nfs
mountpoint /nfs
ls -la /nfs

Optional diagnostics:

BASH root@servera
getent hosts linuxB.lab
rpcinfo -p linuxB.lab
showmount -e linuxB.lab
journalctl -b | grep -i nfs

showmount -e may fail against an NFSv4-only server even when the mount works; the trial mount is authoritative.

⚠️ Exam Traps & Pitfalls

Traps. Server paths are exact and case-sensitive. Do not invent /share when the supplied export is /projects/teamA. If the task says "only when accessed," /etc/fstab is the wrong mechanism; use AutoFS.


πŸ”₯ 3x in Exam Sets
S1 Q51 S2 Q51 S5 Q36

AutoFS mounts a remote file system when its path is accessed and unmounts it after inactivity.

BASH root@servera
dnf -y install autofs nfs-utils
systemctl enable --now autofs
systemctl is-active autofs
systemctl is-enabled autofs

Three map forms matter:

Form Master-map mount point Map keys Result
Indirect /mnt teamA /mnt/teamA
Direct /- absolute paths exactly the key path
Wildcard homes /rhome * and & /rhome/<user> from an export per user

Use a task-specified timeout. The examples use 60 seconds so the behavior is easy to test.

⚠️ Exam Traps & Pitfalls

Traps. Do not create the final indirect key directory (/mnt/teamA) yourself; AutoFS supplies it. Create only the indirect parent if needed. After changing maps, run automount -m to inspect them and restart or reload autofs.


πŸ”₯ 1x in Exam Sets
S2 Q51

Task interpretation: mount server.example.com:/projects/teamA as /mnt/teamA on access.

BASH root@servera
mkdir -p /mnt
cat > /etc/auto.master.d/teamA.autofs <<'EOF'
/mnt /etc/auto.teamA --timeout=60
EOF
 
cat > /etc/auto.teamA <<'EOF'
teamA -fstype=nfs4,rw server.example.com:/projects/teamA
EOF
 
chmod 644 /etc/auto.master.d/teamA.autofs /etc/auto.teamA
systemctl enable --now autofs
systemctl reload autofs
🧠 Why This Works (Concept)

Why. /mnt is managed by the indirect map. The key teamA is appended to it, producing /mnt/teamA. Access triggers the NFS mount.

βœ… Verification Steps

Verify.

BASH root@servera
automount -m
ls /mnt/teamA
findmnt -T /mnt/teamA

Leave /mnt/teamA, wait longer than 60 seconds without accessing it, then check from outside the tree:

BASH root@servera
cd /
sleep 70
findmnt -T /mnt/teamA

No NFS mount after the timeout is success. Merely seeing /mnt or an AutoFS pseudo-mount is normal.


⭐ Official Objective

Example: mount the same export exactly at /mnt/teamA.

BASH root@servera
cat > /etc/auto.master.d/direct.autofs <<'EOF'
/- /etc/auto.direct --timeout=60
EOF
 
cat > /etc/auto.direct <<'EOF'
/mnt/teamA -fstype=nfs4,rw server.example.com:/projects/teamA
EOF
 
chmod 644 /etc/auto.master.d/direct.autofs /etc/auto.direct
systemctl enable --now autofs
systemctl reload autofs
🧠 Why This Works (Concept)

Why. /- declares a direct map, so every map key is a complete local path. This is useful when unrelated mount points must be controlled by one map.

βœ… Verification Steps

Verify. automount -m; ls /mnt/teamA; findmnt -T /mnt/teamA.

⚠️ Exam Traps & Pitfalls

Traps. Do not configure the same path in both an indirect and direct map. Direct-map keys must begin with /.


πŸ”₯ 2x in Exam Sets
S1 Q51 S5 Q36

The sensible interpretation of [S5 Q36] is: server.example.com exports individual home directories below /home, and accessing /rhome/alice should mount server.example.com:/home/alice there. [S1 Q51] is the same pattern with server export root /srv/home and local root /remote.

For /rhome:

BASH root@servera
mkdir -p /rhome
cat > /etc/auto.master.d/rhome.autofs <<'EOF'
/rhome /etc/auto.rhome --timeout=60
EOF
 
cat > /etc/auto.rhome <<'EOF'
* -fstype=nfs4,rw server.example.com:/home/&
EOF
 
chmod 644 /etc/auto.master.d/rhome.autofs /etc/auto.rhome
systemctl enable --now autofs
systemctl reload autofs

For the [S1 Q51] names, use:

TEXT root@servera
πŸ“Œ/etc/auto.master.d/remote.autofs
/remote /etc/auto.remote --timeout=60
 
πŸ“Œ/etc/auto.remote
* -fstype=nfs4,rw NFSSERVER:/srv/home/&
🧠 Why This Works (Concept)

Why. * matches any key. & substitutes that key in the remote path: access to /rhome/alice maps to server.example.com:/home/alice.

βœ… Verification Steps

Verify. Use a user known to exist on the NFS server.

BASH root@servera
automount -m
ls -la /rhome/alice
findmnt -T /rhome/alice
su - alice -c 'pwd; touch ~/autofs-test; rm -f ~/autofs-test'

The last test applies only if Alice's account home is /rhome/alice and the export is writable to her.

⚠️ Exam Traps & Pitfalls

Traps.

  • server:/home alone would mount the whole export at every key. The wildcard task requires /home/& when each user directory is a separate export path.
  • Do not literally type NFSSERVER; use the host supplied in the task.
  • A local user account's home field must agree with the AutoFS path if login is expected: getent passwd alice.
  • NFS ownership is numeric UID/GID based. Matching names with different IDs do not grant matching ownership.

When a page or mount fails, test one layer at a time:

BASH root@servera
πŸ“ŒName resolution and reachability
getent hosts server.example.com
 
πŸ“ŒNFS source independent of AutoFS
mkdir -p /mnt/testnfs
mount -t nfs4 server.example.com:/projects/teamA /mnt/testnfs
ls /mnt/testnfs
umount /mnt/testnfs
 
πŸ“ŒAutoFS map and logs
automount -m
systemctl status autofs --no-pager
journalctl -u autofs -b --no-pager
 
πŸ“ŒApache configuration and logs
apachectl configtest
systemctl status httpd --no-pager
journalctl -u httpd -b --no-pager
 
πŸ“ŒSELinux denials
ausearch -m AVC -ts recent
🧠 Why This Works (Concept)

Why. A malformed export should be fixed before debugging AutoFS; an absent mount should be fixed before debugging Apache; an Apache denial should be diagnosed before weakening SELinux.

⚠️ Exam Traps & Pitfalls

Traps. Never solve a web failure by disabling SELinux or firewalld. Read the denial and apply the narrow context, Boolean, or firewall service required by the task.


  • [ADDED] Direct AutoFS map, because the sets provide indirect and wildcard maps but not a clear direct-map task.
  • [ADDED] Persistent SELinux labeling for a local custom DocumentRoot.
  • [ADDED] Layered service diagnosis with systemctl, journalctl, apachectl, findmnt, and ausearch.
  • HTTPS certificate deployment is shown only to distinguish actual TLS from a firewall rule; public-key infrastructure administration is not expanded beyond the service task.

Drill:

  1. Publish a one-line page, enable Apache, open HTTP permanently, and verify from another host.
  2. Explain why --add-service=https does not make curl https://localhost work.
  3. Serve /web under enforcing SELinux and prove its expected context with matchpathcon.
  4. Persistently mount one NFS export and validate /etc/fstab before reboot.
  5. Build one indirect map, one direct map, and one wildcard home map; trigger each and observe expiry.
  6. Reboot, then recheck is-enabled, curl, findmnt, and an AutoFS-triggered path.

πŸ“œ Chapter 12 18 Exam Tasks

Chapter 11 - Simple Bash scripts

Objectives covered: Write simple shell scripts; use conditional execution; use loops; process script arguments; process command output; create scripts that change system state safely and repeatably.

The grader checks the result, exact path, and executable bit. Unless another interpreter is required, use Bash explicitly.


πŸ”₯ 5x in Exam Sets
S2 Q11 S3 Q14 S4 Q16 S5 Q16 S5 Q34
BASH root@servera
cat > /tmp/example.sh <<'EOF'
πŸ“Œ!/bin/bash
printf '%s\n' 'Hello'
EOF
chmod +x /tmp/example.sh
 
bash -n /tmp/example.shπŸ’‘syntax only; does not execute commands
/tmp/example.sh
echo "$?"πŸ’‘0 normally means success
ls -l /tmp/example.sh
🧠 Why This Works (Concept)

Why. The shebang selects the interpreter when the file is executed directly. chmod +x makes direct execution possible. Quoting the heredoc delimiter (<<'EOF') prevents the current shell from expanding $variables while the script is being created.

A useful starting pattern for administrative scripts is:

BASH root@servera
πŸ“Œ!/bin/bash
set -euo pipefail
 
if (( EUID != 0 )); then
printf '%s\n' 'Run this script as root.' >&2
exit 1
fi

-e exits after an unhandled failing command, -u rejects unset variables, and pipefail makes a pipeline fail when any component fails. These are useful, not mandatory. Handle expected failures inside if tests.

⚠️ Exam Traps & Pitfalls

Traps.

  • The first two shebang characters must be #!; no blank line may precede them.
  • sh script ignores the Bash shebang and can break [[ ... ]], arrays, and arithmetic syntax. Execute it or use bash script.
  • Do not name a script test, mount, or firewall-cmd; it can shadow the real command through PATH.
  • DOS line endings can produce bad interpreter: /bin/bash^M. Use sed -i 's/\r$//' script if needed.

πŸ”₯ 1x in Exam Sets
S2 Q11
BASH root@servera
cat > /tmp/yes-no.sh <<'EOF'
πŸ“Œ!/bin/bash
 
case "${1:-}" in
yes)
echo "that's nice"
;;
no)
echo "I am sorry to hear that"
;;
*)
echo "unknown argument provided"
;;
esac
EOF
chmod +x /tmp/yes-no.sh
🧠 Why This Works (Concept)

Why. $0 is the script name, $1 the first argument, $# the argument count, and "$@" all arguments while preserving boundaries. ${1:-} safely becomes empty when no argument is supplied, including under set -u. case is clearer than repeated string comparisons.

βœ… Verification Steps

Verify.

BASH root@servera
/tmp/yes-no.sh yes
/tmp/yes-no.sh no
/tmp/yes-no.sh maybe
/tmp/yes-no.sh
⚠️ Exam Traps & Pitfalls

Traps. Yes is not yes unless the task allows case-insensitive input. To accept variants, use yes|YES|Yes) or normalize with ${1,,}. Quote argument expansions so spaces and wildcard characters are not split or expanded.

Argument validation pattern [ADDED]:

BASH root@servera
if (( $# != 2 )); then
printf 'Usage: %s USER GROUP\n' "$0" >&2
exit 2
fi
user=$1
group=$2

πŸ”₯ 3x in Exam Sets
S5 Q34 S4 Q35 S4 Q37

File-existence task:

BASH root@servera
cat > "$HOME/checkfile.sh" <<'EOF'
πŸ“Œ!/bin/bash
 
if [[ -e /tmp/error.txt ]]; then
echo 'File exists.'
else
echo 'File does not exist.'
fi
EOF
chmod +x "$HOME/checkfile.sh"

Common tests:

Test Meaning
-e path path exists
-f path regular file
-d path directory
-L path symbolic link, including a dangling link
-r, -w, -x readable, writable, executable by current process
-z "$value", -n "$value" empty, non-empty string
"$a" == "$b", "$a" != "$b" string comparison
(( a < b )) numeric comparison in Bash

Safe size inspection for [S4 Q37] (warn only; never resize):

BASH root@servera
cat > /tmp/check-sda1-size.sh <<'EOF'
πŸ“Œ!/bin/bash
set -u
 
device=/dev/sda1
expected=$((2048 * 1024 * 1024))
 
if [[ ! -b $device ]]; then
printf '%s is not a block device.\n' "$device" >&2
exit 1
fi
 
actual=$(lsblk -bndo SIZE "$device") || exit 1
if (( actual == expected )); then
printf '%s is exactly 2048 MiB.\n' "$device"
else
printf 'WARNING: %s is %s bytes, not 2048 MiB; no resize performed.\n' "$device" "$actual" >&2
exit 1
fi
EOF
chmod +x /tmp/check-sda1-size.sh
🧠 Why This Works (Concept)

Why. lsblk -b avoids human-readable rounding, and the script follows the source instruction not to perform risky resizing.

⚠️ Exam Traps & Pitfalls

Traps. [ ... ] requires spaces around every token: [ "$a" = "$b" ]. In Bash, prefer [[ ... ]] for strings and files and (( ... )) for arithmetic. Never parse the decorative columns of ordinary lsblk output.


πŸ”₯ 1x in Exam Sets
S5 Q16
BASH root@servera
cat > /tmp/multifiles <<'EOF'
πŸ“Œ!/bin/bash
 
for number in {1..5}; do
touch "seinfeld${number}"
done
EOF
chmod +x /tmp/multifiles
🧠 Why This Works (Concept)

Why. Brace expansion produces 1 2 3 4 5; each value is assigned to number. touch is naturally repeatable: existing files are not truncated, though their timestamps change.

βœ… Verification Steps

Verify. Run from the directory where the files are required, then use ls -l seinfeld{1..5}. If the required destination is fixed, put the full path in the script rather than depending on its working directory.

Other objective patterns [ADDED]:

BASH root@servera
πŸ“ŒIterate safely over script arguments
for item in "$@"; do
printf '%s\n' "$item"
done
 
πŸ“ŒRead a file without losing its final unterminated line
while IFS= read -r line || [[ -n $line ]]; do
printf '%s\n' "$line"
done < input.txt
 
πŸ“ŒRetry while a condition is true
count=1
while (( count <= 3 )); do
echo "$count"
((count++))
done
⚠️ Exam Traps & Pitfalls

Traps. Always use "$@", not $*, when arguments can contain spaces. Quote path variables inside a loop.


πŸ”₯ 1x in Exam Sets
S4 Q16

Create /tmp/execmd to show the current user, date, and number of logged-in sessions:

BASH root@servera
cat > /tmp/execmd <<'EOF'
πŸ“Œ!/bin/bash
 
current_user=$(whoami)
today=$(date)
logged_in=$(who | wc -l)
 
printf 'Current user: %s\n' "$current_user"
printf 'Date: %s\n' "$today"
printf 'Logged-in sessions: %s\n' "$logged_in"
EOF
chmod +x /tmp/execmd
🧠 Why This Works (Concept)

Why. $(command) captures stdout and removes trailing newlines. The pipeline processes command output before assignment. who | wc -l counts login sessions, not unique usernames; that matches "number currently logged in" in the source's usual interpretation.

βœ… Verification Steps

Verify. /tmp/execmd; who; date.

⚠️ Exam Traps & Pitfalls

Traps. Backticks are harder to nest; use $(...). Do not confuse whoami (effective current user), who (login sessions), and users (names, potentially repeated).


πŸ”₯ 1x in Exam Sets
S3 Q14
BASH root@servera
cat > /tmp/add.sh <<'EOF'
πŸ“Œ!/bin/bash
 
read -r -p 'Enter first integer: ' first
read -r -p 'Enter second integer: ' second
 
if [[ ! $first =~ ^-?(0|[1-9][0-9]*)$ || ! $second =~ ^-?(0|[1-9][0-9]*)$ ]]; then
echo 'Both values must be integers.' >&2
exit 1
fi
 
sum=$((first + second))
printf 'Sum: %d\n' "$sum"
EOF
chmod +x /tmp/add.sh
🧠 Why This Works (Concept)

Why. read -r does not consume backslashes. The regular expression rejects non-integers before arithmetic evaluation. $((...)) performs integer arithmetic without external expr or bc.

βœ… Verification Steps

Verify.

BASH root@servera
printf '2\n3\n' | /tmp/add.sh
printf '%s\n' -2 5 | /tmp/add.sh
bash -n /tmp/add.sh
⚠️ Exam Traps & Pitfalls

Traps. Bash arithmetic is integer-only. Under strict untrusted-input requirements, validation matters because arithmetic expressions can interpret variable names and syntax.


πŸ”₯ 3x in Exam Sets
S2 Q39 S2 Q46 S5 Q46

Idempotent means a second run reaches the same desired state instead of failing or creating duplicates.

BASH root@servera
cat > /tmp/create-testuser.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
if (( EUID != 0 )); then
echo 'Run as root.' >&2
exit 1
fi
 
user=testuser
home=/home/testuser
 
if id "$user" &>/dev/null; then
echo "User $user already exists."
else
useradd -m -d "$home" "$user"
echo "Created $user."
fi
 
πŸ“ŒMove an existing home tree when the account points somewhere else.
if [[ $(getent passwd "$user" | cut -d: -f6) != "$home" ]]; then
usermod -d "$home" -m "$user"
fi
EOF
chmod +x /tmp/create-testuser.sh

Password warning

[S2 Q39] specifies the literal weak password password. Embedding it in a script is dangerous: anyone who can read the file learns the credential, backups retain it, and command history may retain how it was created.

Do not use this:

BASH root@servera
useradd -p password testuserπŸ’‘WRONG: -p expects a password hash, and argv can be exposed

For an interactive exam task, the safest simple method is passwd testuser. If the script itself must assign a password, prompt silently and feed chpasswd through standard input:

BASH root@servera
read -r -s -p 'New password: ' password
echo
printf '%s:%s\n' "$user" "$password" | chpasswd
unset password

chpasswd hashes the password through the system's configured password stack and is safer than passing plaintext in a command argument. The plaintext still briefly exists in shell memory and the pipe. A literal printf 'testuser:password' | chpasswd satisfies the source task but leaves the secret in the script and should be used only in a disposable lab when explicitly required.

βœ… Verification Steps

Verify.

BASH root@servera
id testuser
getent passwd testuser
passwd -S testuser
⚠️ Exam Traps & Pitfalls

Traps. useradd -d alone does not guarantee creation of the directory; include -m. Do not repeatedly reset a user's password on every run unless that repeated state change is explicitly required.


πŸ”₯ 1x in Exam Sets
S2 Q40
BASH root@servera
cat > /tmp/add-testgroup.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
group=testgroup
user=testuser
 
if ! getent group "$group" >/dev/null; then
groupadd "$group"
fi
 
if ! id "$user" &>/dev/null; then
echo "User $user does not exist." >&2
exit 1
fi
 
if id -nG "$user" | tr ' ' '\n' | grep -Fxq "$group"; then
echo "$user is already a member of $group."
else
usermod -aG "$group" "$user"
fi
EOF
chmod +x /tmp/add-testgroup.sh
🧠 Why This Works (Concept)

Why. getent checks the configured identity sources, not only local files. usermod -aG appends a supplementary group; omitting -a replaces all existing supplementary memberships.

βœ… Verification Steps

Verify. /tmp/add-testgroup.sh; /tmp/add-testgroup.sh; id testuser; getent group testgroup.

⚠️ Exam Traps & Pitfalls

Traps. Existing login sessions do not automatically gain new group credentials. Log out/in or test with su - testuser.


πŸ”₯ 2x in Exam Sets
S2 Q46 S5 Q46
BASH root@servera
cat > /tmp/create-new-user.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
user=new_user
home=/home/new_user
 
if ! id "$user" &>/dev/null; then
useradd -m -d "$home" -s /bin/bash "$user"
else
usermod -s /bin/bash "$user"
fi
 
tmp=$(mktemp /root/sudoers.XXXXXX)
printf '%s ALL=(ALL) ALL\n' "$user" > "$tmp"
chmod 0440 "$tmp"
visudo -cf "$tmp"
install -m 0440 -o root -g root "$tmp" "/etc/sudoers.d/$user"
restorecon -v "/etc/sudoers.d/$user"
rm -f "$tmp"
EOF
chmod +x /tmp/create-new-user.sh
🧠 Why This Works (Concept)

Why. A dedicated sudoers drop-in is auditable and avoids risky edits to /etc/sudoers. visudo -c validates syntax. ALL=(ALL) ALL requires the user's password; use NOPASSWD: only if explicitly requested.

βœ… Verification Steps

Verify.

BASH root@servera
getent passwd new_user
ls -l /etc/sudoers.d/new_user
visudo -c
sudo -l -U new_user
⚠️ Exam Traps & Pitfalls

Traps. Adding a user to wheel is also valid when the distribution's existing %wheel sudo rule is enabled. Do not grant UID 0 or weaken the main sudoers file.


πŸ”₯ 1x in Exam Sets
S1 Q43
BASH root@servera
cat > /tmp/install-httpd.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
if rpm -q httpd >/dev/null 2>&1; then
echo 'httpd is already installed.'
else
echo 'Installing httpd...'
if dnf -y install httpd; then
echo 'httpd installed successfully.'
else
echo 'httpd installation failed.' >&2
exit 1
fi
fi
 
rpm -q httpd
EOF
chmod +x /tmp/install-httpd.sh
🧠 Why This Works (Concept)

Why. rpm -q checks installed state without depending on localized dnf text. The if handles the expected possible installation failure even with set -e.

βœ… Verification Steps

Verify. Run twice, then rpm -q httpd; echo $?.

⚠️ Exam Traps & Pitfalls

Traps. Installation does not start or enable Apache. Add service state only when the task asks for it.


πŸ”₯ 2x in Exam Sets
S2 Q41 S2 Q42

Start now and at boot:

BASH root@servera
cat > /tmp/enable-httpd.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
if ! rpm -q httpd >/dev/null 2>&1; then
echo 'httpd is not installed.' >&2
exit 1
fi
 
systemctl enable --now httpd
systemctl is-active --quiet httpd
systemctl is-enabled --quiet httpd
echo 'httpd is active and enabled.'
EOF
chmod +x /tmp/enable-httpd.sh

If the wording says only "enable at boot," use systemctl enable httpd; do not assume it also asks to start now.

🧠 Why This Works (Concept)

Why. enable --now is idempotent and establishes both required states. Quiet checks return useful exit statuses without status-page noise.

βœ… Verification Steps

Verify. /tmp/enable-httpd.sh; systemctl is-active httpd; systemctl is-enabled httpd.

⚠️ Exam Traps & Pitfalls

Traps. Do not hide a failed systemctl behind an unconditional "success" message. Firewall and web content are separate tasks (Ch 10).


πŸ”₯ 1x in Exam Sets
S1 Q42

Permit SSH using firewalld's named service rather than an unexplained raw port:

BASH root@servera
cat > /tmp/allow-ssh.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload
 
firewall-cmd --query-service=ssh >/dev/null
firewall-cmd --permanent --query-service=ssh >/dev/null
echo 'SSH is allowed in the default zone now and permanently.'
EOF
chmod +x /tmp/allow-ssh.sh
🧠 Why This Works (Concept)

Why. The ssh service definition maps to the expected port and is self-documenting. --permanent survives reboot; --reload makes it active now. Repeated additions are harmless.

βœ… Verification Steps

Verify. /tmp/allow-ssh.sh; firewall-cmd --list-all; firewall-cmd --permanent --list-all.

⚠️ Exam Traps & Pitfalls

Traps. Rules apply to a zone. If the interface uses a non-default active zone, determine it with firewall-cmd --get-active-zones and pass --zone=NAME consistently. Opening SSH does not start sshd.


πŸ”₯ 1x in Exam Sets
S1 Q41

Mount /dev/sda1 at /mnt/data without mounting it twice or concealing a different existing mount:

BASH root@servera
cat > /tmp/mount-data.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
source=/dev/sda1
target=/mnt/data
 
if [[ ! -b $source ]]; then
echo "$source is not a block device." >&2
exit 1
fi
 
mkdir -p "$target"
 
if mountpoint -q "$target"; then
current=$(findmnt -nro SOURCE --target "$target")
if [[ $current == "$source" ]]; then
echo "$source is already mounted at $target."
exit 0
fi
echo "$target is already occupied by $current." >&2
exit 1
fi
 
mount "$source" "$target"
findmnt --target "$target"
EOF
chmod +x /tmp/mount-data.sh
🧠 Why This Works (Concept)

Why. The script checks both device type and target state before changing anything. findmnt verifies the actual source.

βœ… Verification Steps

Verify. /tmp/mount-data.sh; /tmp/mount-data.sh; findmnt /mnt/data.

⚠️ Exam Traps & Pitfalls

Traps. This source task asks for a mount script, not persistence. If reboot persistence is required, use a UUID entry in /etc/fstab, validate with findmnt --verify and mount -a, and do not append duplicate lines. Never format the device in a mount script unless explicitly instructed.


πŸ”₯ 3x in Exam Sets
S2 Q44 S2 Q45 S3 Q48

Secure /etc/passwd state:

BASH root@servera
cat > /tmp/secure-passwd.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
chown root:root /etc/passwd
chmod 0644 /etc/passwd
 
[[ $(stat -c '%U:%G %a' /etc/passwd) == 'root:root 644' ]]
echo '/etc/passwd ownership and mode are correct.'
EOF
chmod +x /tmp/secure-passwd.sh

Create and own a directory without recreating it:

BASH root@servera
cat > /tmp/create-owned-directory.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
directory=new_directory
owner=username
group=groupname
 
id "$owner" >/dev/null
getent group "$group" >/dev/null
mkdir -p "$directory"
chown "$owner:$group" "$directory"
stat -c '%U:%G %a %n' "$directory"
EOF
chmod +x /tmp/create-owned-directory.sh
🧠 Why This Works (Concept)

Why. chown and chmod set desired state repeatedly. mkdir -p succeeds when the directory already exists but does not erase its contents. Identity checks prevent a misleading partial result.

βœ… Verification Steps

Verify. Run each script twice and compare stat -c '%U:%G %a %n' PATH.

⚠️ Exam Traps & Pitfalls

Traps. Mode 644 is appropriate for /etc/passwd, not /etc/shadow. Never make /etc/passwd executable. mkdir -p does not guarantee ownership or mode, so set and verify them separately.


πŸ”₯ 1x in Exam Sets
S4 Q36
BASH root@servera
cat > /tmp/create-md0.sh <<'EOF'
πŸ“Œ!/bin/bash
set -euo pipefail
 
array=/dev/md0
devices=(/dev/sda1 /dev/sdb1)
 
if [[ -e $array ]] || grep -qw md0 /proc/mdstat; then
echo "$array already exists."
exit 0
fi
 
for device in "${devices[@]}"; do
[[ -b $device ]] || { echo "$device is not a block device." >&2; exit 1; }
if lsblk -nro MOUNTPOINT "$device" | grep -q .; then
echo "$device or a child is mounted; refusing." >&2
exit 1
fi
done
 
echo 'WARNING: RAID creation overwrites metadata on both component devices.' >&2
read -r -p 'Type CREATE-MD0 to continue: ' answer
[[ $answer == CREATE-MD0 ]] || exit 1
mdadm --create "$array" --level=1 --raid-devices=2 "${devices[@]}"
EOF
chmod +x /tmp/create-md0.sh
🧠 Why This Works (Concept)

Why. Existence and mount checks plus explicit confirmation reduce accidental execution. They do not prove that devices contain no valuable data.

βœ… Verification Steps

Verify. Only in a disposable lab: cat /proc/mdstat; mdadm --detail /dev/md0.

⚠️ Exam Traps & Pitfalls

Traps. Never adapt guessed device names from a practice question to a real host. RAID administration is not expanded elsewhere in the core guide.


⭐ Official Objective

Every command returns an integer status: 0 success, nonzero failure.

BASH root@servera
if systemctl is-active --quiet sshd; then
echo 'sshd is active'
else
echo 'sshd is not active'
fi
 
mkdir -p /tmp/demo && touch /tmp/demo/okπŸ’‘second runs only if first succeeds
test -e /tmp/demo/ok || exit 1πŸ’‘exit if test fails
! grep -q '^bad:' fileπŸ’‘logical negation

Prefer testing commands directly over comparing $? later:

BASH root@servera
if getent passwd testuser >/dev/null; thenπŸ’‘good
echo exists
fi
⚠️ Exam Traps & Pitfalls

Traps. A final echo normally returns 0 and can hide an earlier failure unless the script exits, branches, or uses strict mode. Pipelines normally return only the last command's status; set -o pipefail catches an earlier failure.


The sets already cover arguments, case, conditions, loops, command substitution, pipelines, read, and arithmetic. Added coverage closes these official-skill gaps:

  • [ADDED] Validate argument counts and quote "$@".
  • [ADDED] Use command exit status for conditional execution.
  • [ADDED] Read and process every line of a file safely.
  • [ADDED] Validate syntax with bash -n and verify resulting state rather than only printed messages.

Before considering any script complete:

  1. Confirm its exact name and location.
  2. Confirm line 1 is #!/bin/bash and run bash -n SCRIPT.
  3. Set the executable bit and test direct execution.
  4. Test expected input, missing input, and invalid input.
  5. Run state-changing scripts twice; the second run should be safe.
  6. Verify state with independent tools: id, getent, stat, findmnt, systemctl, or firewall-cmd.
  7. Inspect echo $? after negative and positive paths.
  8. Remove plaintext passwords and unsafe guessed device names from reusable scripts.

βž• Chapter 13 8 Exam Tasks

Appendix A - Beyond the Official RHEL 10 EX200 Objectives

OUTSIDE THE OFFICIAL RHEL 10 EX200 OBJECTIVES AS OF 2026. The current EX200 page does not list Podman or other container tools, UBI, Quadlet, Stratis, vsftpd, software RAID, logrotate, or rsyslog configuration. These answers are retained because the source question sets ask about them. Do not let them displace study time from the official objectives in Chapters 1-11.

Official blueprint checked in 2026: Red Hat EX200 study points.

πŸ”₯ 2x in Exam Sets
S1 Q27 S4 Q25

Install the supported command-line tool set:

BASH root@servera
dnf install -y container-tools
podman --version
buildah --version
skopeo --version

The package roles are:

Tool Main job
podman Run and manage daemonless containers, pods, images, and volumes
buildah Build OCI images, with or without a Dockerfile/Containerfile
skopeo Inspect, copy, sign, and delete images without running them

Pull the fully qualified UBI 10 standard image:

BASH root@servera
podman pull registry.access.redhat.com/ubi10/ubi:latest
podman images
podman run --rm registry.access.redhat.com/ubi10/ubi:latest cat /etc/redhat-release
skopeo inspect docker://registry.access.redhat.com/ubi10/ubi:latest | head

Use a fully qualified image name to avoid short-name prompts or ambiguity. Other variants include ubi10/ubi-minimal, ubi10/ubi-micro, and ubi10/ubi-init. UBI images and their UBI repositories are redistributable; that does not make every package from subscribed RHEL repositories redistributable.

Common operations:

BASH root@servera
podman search registry.access.redhat.com/ubi10
podman run -d --name web -p 8080:80 IMAGE
podman ps; podman ps -a
podman logs web
podman exec -it web /bin/bash
podman stop web; podman rm web
podman inspect web
podman volume create appdata
buildah bud -t localhost/myapp:v1 -f Containerfile .
skopeo copy docker://SOURCE containers-storage:localhost/myapp:v1

Rootless Podman is preferred where the workload permits it. Run rootless commands from a real login session for that user, not from a poorly initialized su environment. Check /etc/subuid and /etc/subgid if user namespace setup fails.

Useful pages: man podman, man podman-run, man buildah-bud, man skopeo-copy, man containers-registries.conf.

πŸ”₯ 1x in Exam Sets
S4 Q51

podman generate systemd is deprecated. It remains available for old workflows, but new configurations should use Quadlet. Quadlet source files are read by a systemd generator and become generated .service units.

Confirm the local image, then create a rootful Quadlet:

BASH root@servera
podman image exists localhost/internal-web:v2
mkdir -p /etc/containers/systemd

Create /etc/containers/systemd/internal-web.container:

INI root@servera
[Unit]
Description=Internal web application container
Wants=network-online.target
After=network-online.target
 
[Container]
Image=localhost/internal-web:v2
ContainerName=internal-web
LogDriver=journald
 
[Service]
Restart=on-failure
RestartSec=5s
 
[Install]
WantedBy=multi-user.target

Generate, start, and verify it:

BASH root@servera
systemctl daemon-reload
systemctl start internal-web.service
systemctl status internal-web.service
podman ps --filter name=internal-web
journalctl -u internal-web.service
systemctl is-active internal-web.service
systemctl cat internal-web.service

The [Install] WantedBy=multi-user.target line gives the generated service its boot dependency. Do not run systemctl enable internal-web.service: Quadlet services are transient generated units and cannot be enabled in the normal way. The source question's word "enable" is satisfied in the modern workflow by [Install], followed by daemon-reload and start. Reboot and verify:

BASH root@servera
reboot
systemctl is-active internal-web.service
journalctl -b -u internal-web.service

Add ports, environment, and persistent content when the image requires them:

INI root@servera
[Container]
Image=localhost/internal-web:v2
ContainerName=internal-web
PublishPort=8080:8080
Environment=APP_MODE=production
Volume=/srv/internal-web:/var/lib/app:Z
LogDriver=journald

The :Z relabels a host bind-mount path for private container use under SELinux. Open a published port separately with firewalld if remote clients need it.

For a rootless service owned by user appuser, put the file in /home/appuser/.config/containers/systemd/internal-web.container, use WantedBy=default.target, and run:

BASH root@servera
loginctl enable-linger appuser
systemctl --user daemon-reload
systemctl --user start internal-web.service
journalctl --user -u internal-web.service

Run those user commands in appuser's login session. Linger keeps that user's systemd manager running after logout and starts it at boot. Diagnose generator errors with:

BASH root@servera
/usr/lib/systemd/system-generators/podman-system-generator --dryrun
systemd-analyze verify internal-web.service
journalctl -b -u internal-web.service

Useful pages: man podman-systemd.unit, man podman-container.unit, man podman-quadlet-basic-usage, man systemd.unit. For historical questions, man podman-generate-systemd explicitly marks that command deprecated.

πŸ”₯ 1x in Exam Sets
S5 Q29

Warning: the device must be unused. Confirm before creating the pool.

BASH root@servera
lsblk -f /dev/sde
wipefs -n /dev/sde
dnf install -y stratisd stratis-cli
systemctl enable --now stratisd
stratis pool create pool1 /dev/sde
stratis filesystem create --size 1GiB pool1 fs1
stratis pool list
stratis filesystem list pool1
mkdir -p /stratis

Stratis file systems are thin-provisioned XFS. Record the pool UUID from stratis pool list, not the XFS file-system UUID. Add this line to /etc/fstab, substituting the pool UUID:

FSTAB root@servera
/dev/stratis/pool1/fs1 /stratis xfs defaults,x-systemd.requires=stratis-fstab-setup@POOL_UUID.service 0 0

Then test persistence before rebooting:

BASH root@servera
systemctl daemon-reload
mount -a
findmnt /stratis
df -hT /stratis
systemctl is-enabled stratisd
reboot
findmnt /stratis

Do not run mkfs.xfs on /dev/stratis/pool1/fs1; Stratis created and manages that XFS file system. Do not use a stale generic x-systemd.requires=stratisd example when the installed Stratis documentation specifies the pool-specific stratis-fstab-setup@POOL_UUID.service dependency. Confirm locally with the installed Stratis docs and man stratis.

Useful pages: man stratis, man stratisd, man 5 fstab.

πŸ”₯ 2x in Exam Sets
S5 Q28 S5 Q23
BASH root@servera
dnf install -y vsftpd

Set these effective values in /etc/vsftpd/vsftpd.conf:

INI root@servera
anonymous_enable=NO
local_enable=YES
write_enable=YES

write_enable=YES is needed only when local users must upload or modify data; omit it for read-only access. Validate, start, and expose the service:

BASH root@servera
systemctl enable --now vsftpd
firewall-cmd --permanent --add-service=ftp
firewall-cmd --reload
systemctl status vsftpd
firewall-cmd --query-service=ftp
ss -ltnp | grep ':21 '

Test with a real local user from another host. If uploads are required, both Unix permissions and SELinux policy must allow the target operation; do not disable SELinux as a workaround. Inspect failures with journalctl -u vsftpd and ausearch -m AVC -ts recent.

To block FTP permanently while leaving the daemon configuration alone:

BASH root@servera
firewall-cmd --permanent --remove-service=ftp
firewall-cmd --permanent --remove-port=21/tcp
firewall-cmd --reload
firewall-cmd --list-all

Remove the rule from the zone to which the interface is actually assigned. Stopping the daemon is stronger if FTP must not run at all: systemctl disable --now vsftpd.

Useful pages: man vsftpd, man 5 vsftpd.conf, man firewall-cmd, man ftpd_selinux.

πŸ”₯ 1x in Exam Sets
S4 Q36

The partitions must be unused, equal enough in size, and normally marked Linux RAID. Never use exam OS/root partitions merely because the source names them.

BASH root@servera
dnf install -y mdadm
lsblk -f /dev/sda1 /dev/sdb1
mdadm --examine /dev/sda1 /dev/sdb1

Example /root/create-raid1.sh:

BASH root@servera
πŸ“Œ!/bin/bash
set -euo pipefail
 
if mdadm --detail /dev/md0 >/dev/null 2>&1; then
echo "/dev/md0 already exists; no changes made"
exit 0
fi
 
for device in /dev/sda1 /dev/sdb1; do
if [[ ! -b $device ]]; then
echo "Missing block device: $device" >&2
exit 1
fi
if findmnt -rn -S "$device" >/dev/null || mdadm --examine "$device" >/dev/null 2>&1; then
echo "Refusing to overwrite mounted or RAID-initialized device: $device" >&2
exit 1
fi
done
 
mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sda1 /dev/sdb1
touch /etc/mdadm.conf
grep -q '[[:space:]]/dev/md0[[:space:]]' /etc/mdadm.conf || \
mdadm --detail --scan | grep '[[:space:]]/dev/md0[[:space:]]' >> /etc/mdadm.conf
mdadm --detail /dev/md0
BASH root@servera
chmod 700 /root/create-raid1.sh
/root/create-raid1.sh
cat /proc/mdstat
mdadm --detail /dev/md0

If storage is also requested, wait for or monitor synchronization, create one file system on /dev/md0, and mount the file-system UUID through /etc/fstab:

BASH root@servera
mkfs.xfs /dev/md0
blkid /dev/md0
mkdir -p /raid
πŸ“ŒUUID=<filesystem-uuid> /raid xfs defaults 0 0
mount -a; findmnt /raid

mdadm --detail --scan records array assembly metadata; the file-system UUID belongs in fstab. Useful pages: man mdadm, man 5 mdadm.conf, man 5 fstab.

πŸ”₯ 1x in Exam Sets
S5 Q24

Create /etc/logrotate.d/rhcsa:

TEXT root@servera
/var/log/rhcsa.log {
weekly
rotate 2
compress
missingok
notifempty
create 0640 root root
}

Validate without changing files, then force one test rotation if safe:

BASH root@servera
logrotate -d /etc/logrotate.d/rhcsa
logrotate -f /etc/logrotate.d/rhcsa
ls -l /var/log/rhcsa.log*
systemctl status logrotate.timer

rotate 2 keeps two old copies; it does not mean "rotate every two weeks." Compression can be delayed one cycle with delaycompress when a daemon might keep writing to the old file. A service that holds the file open may need a postrotate reload block, but do not invent one for an unspecified producer.

Useful pages: man 8 logrotate, man 5 logrotate.conf, systemctl cat logrotate.timer.

πŸ”₯ 1x in Exam Sets
S5 Q25

Create /etc/rsyslog.d/daemon.conf:

TEXT root@servera
daemon.* /var/log/daemonlog.log

Validate before restarting, then generate a deterministic test message:

BASH root@servera
rsyslogd -N1
systemctl enable --now rsyslog
systemctl restart rsyslog
logger -p daemon.notice 'RHCSA daemon facility test'
tail /var/log/daemonlog.log
journalctl -t logger -n 5

daemon.* means every priority for the daemon facility. This adds a destination; matching messages may also remain in /var/log/messages. Use daemon.*;... only when deliberately combining selectors, and do not confuse the syslog facility with "messages emitted by every systemd daemon."

If a file must exist with explicit permissions before testing:

BASH root@servera
install -o root -g root -m 0640 /dev/null /var/log/daemonlog.log
restorecon -v /var/log/daemonlog.log
systemctl restart rsyslog

Useful pages: man 5 rsyslog.conf, man rsyslogd, man logger.

Topic Source questions
Container tools [S1 Q27]
UBI 10 [S4 Q25]
Podman/systemd service [S4 Q51]
Stratis [S5 Q29] (duplicated)
FTP firewall and vsftpd [S5 Q23] [S5 Q28]
RAID 1 [S4 Q36]
logrotate [S5 Q24]
rsyslog daemon facility [S5 Q25]

πŸ“‹ Chapter 14 14 Exam Tasks

Appendix B - Pre-Reboot, Reboot, and Recovery Checklist

Automated grading checks the resulting state after reboot. Verify exact names, paths, values, permissions, and persistence, not merely that a command returned success. Keep console access available before testing network or SSH changes.

BASH root@servera
hostnamectl
date; timedatectl
getenforce
systemctl --failed
journalctl -p err..alert -b --no-pager
df -hT; df -ih
findmnt --verify --verbose
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
dnf repolist --enabled
dnf makecache
dnf list installed PACKAGE
rpm -q PACKAGE
flatpak remotes --show-details
flatpak list
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
getent passwd USER
getent group GROUP
id USER
chage -l USER
passwd -S USER
visudo -c
sudo -l -U USER
su - USER -c 'id; env'
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
stat -c '%A %a %U %G %n' PATH
getfacl -p PATH
namei -l PATH
ls -Zd PATH
restorecon -nRv PATH
getsebool BOOLEAN
semanage fcontext -l | grep PATTERN
semanage port -l | grep PORT
πŸ“‹ Pre-Reboot Sanity Checklist

This is the highest-risk reboot domain.

BASH root@servera
lsblk -f
blkid
pvs; vgs; lvs -a -o +devices
swapon --show
findmnt --verify --verbose
mount -av
findmnt --fstab
systemctl daemon-reload
systemctl status autofs
automount -m
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
nmcli connection show --active
nmcli connection show CONNECTION
nmcli device status
ip -br address; ip route
hostnamectl; getent hosts HOSTNAME
resolvectl status
firewall-cmd --get-active-zones
firewall-cmd --list-all
firewall-cmd --permanent --list-all
firewall-cmd --check-config
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
systemctl is-enabled SERVICE
systemctl is-active SERVICE
systemctl status SERVICE --no-pager
systemctl get-default
systemctl list-timers --all
crontab -l -u USER
atq
chronyc sources -v; chronyc tracking
tuned-adm active; tuned-adm verify
grubby --info=ALL
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
sshd -t
sshd -T | grep -E '^(passwordauthentication|permitrootlogin|maxauthtries|allowusers) '
systemctl is-enabled sshd
systemctl is-active sshd
ss -ltnp
ssh -o BatchMode=yes USER@HOST true
journalctl -u sshd -b --no-pager | tail
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
test -d /var/log/journal && ls -ld /var/log/journal
systemd-analyze cat-config systemd/journald.conf
journalctl --disk-usage
journalctl --list-boots
πŸ“ŒSupplemental checks for Appendix A topics, not core EX200 objectives:
rsyslogd -N1
logrotate -d /etc/logrotate.conf
πŸ“‹ Pre-Reboot Sanity Checklist
BASH root@servera
head -1 /path/to/script
bash -n /path/to/script
test -x /path/to/script
/path/to/script ARGUMENT
πŸ“‹ Pre-Reboot Sanity Checklist

Record baseline evidence, keep console access open, and reboot with enough time to recover:

BASH root@servera
uptime -s
systemctl --failed
findmnt --verify
sync
systemctl reboot

After login:

BASH root@servera
uptime -s
uname -r
systemctl --failed
journalctl -b -p err..alert --no-pager
journalctl --list-boots
findmnt; swapon --show
nmcli connection show --active
ip -br address; ip route
getenforce
firewall-cmd --list-all
systemctl is-active SERVICE
systemctl is-enabled SERVICE

Then repeat the domain-specific checks above. Test the result from the consumer side: access the web page, make a fresh SSH login, trigger autofs, execute the script, and read the requested file. A green service status is not proof that the requested behavior works.

Typical symptom: boot stops at emergency mode with a mount dependency failure.

  1. Read the console error and run journalctl -xb if available.
  2. Enter the root password. If / is read-only, run:
BASH root@servera
mount -o remount,rw /
cp -a /etc/fstab /etc/fstab.bad
vi /etc/fstab
findmnt --verify --verbose
systemctl daemon-reload
mount -av
  1. Correct the UUID, type, options, fields, or missing mount point. Comment only the failing nonessential line if time is critical.
  2. Reboot only after findmnt --verify and mount -av succeed:
BASH root@servera
systemctl reboot

If emergency mode will not provide a usable shell, edit the GRUB kernel line and append rd.break, boot, then:

BASH emergency shell (initramfs)
mount -o remount,rw /sysroot
chroot /sysroot
vi /etc/fstab
findmnt --verify --verbose
exit
exit

Do not run fsck blindly on XFS (xfs_repair is a separate offline tool) and do not add nofail merely to hide a required mount failure.

Use the VM console. First identify the device and saved profiles:

BASH root@servera
nmcli device status
nmcli connection show
nmcli -f GENERAL,IP4,IP6 device show DEVICE
ip -br address; ip route
journalctl -b -u NetworkManager --no-pager

Repair the intended profile, not a similarly named stale profile:

BASH root@servera
nmcli connection modify CONNECTION ipv4.method manual \
ipv4.addresses 192.0.2.10/24 ipv4.gateway 192.0.2.1 \
ipv4.dns 192.0.2.53 connection.autoconnect yes
nmcli connection up CONNECTION
ping -c 2 192.0.2.1
getent hosts server.example.com

Use nmtui from the console if syntax is the blocker. For DHCP recovery:

BASH root@servera
nmcli connection modify CONNECTION ipv4.method auto \
ipv4.addresses '' ipv4.gateway '' ipv4.dns ''
nmcli connection up CONNECTION

Replace example values with task values. Check MAC/interface binding and remove or disable a competing autoconnect profile if NetworkManager activates the wrong one. Do not delete the only known-good profile until the repaired one is active and tested.

Keep the existing SSH session open and use the console if new logins fail.

BASH root@servera
sshd -t
journalctl -b -u sshd --no-pager
systemctl status sshd
grep -RniE '^(PasswordAuthentication|PermitRootLogin|AllowUsers|DenyUsers|Port|MaxAuthTries)' \
/etc/ssh/sshd_config /etc/ssh/sshd_config.d

Fix the syntax or conflicting early drop-in, then verify before restart:

BASH root@servera
sshd -t
sshd -T | grep -E '^(passwordauthentication|permitrootlogin|allowusers|port) '
restorecon -Rv /etc/ssh
systemctl restart sshd
systemctl is-active sshd
ss -ltnp | grep sshd

If the port changed, also repair both controls:

BASH root@servera
semanage port -a -t ssh_port_t -p tcp PORTπŸ’‘use -m if the label exists
firewall-cmd --permanent --add-port=PORT/tcp
firewall-cmd --reload

Confirm a new login from another terminal before ending the recovery console or old SSH session. For an accidental lockout caused by AllowUsers, add the required account or remove the directive; do not weaken unrelated controls.


⚑ Chapter 15 11 Exam Tasks

Appendix C - RHEL 10 EX200 Command Quick Reference

This appendix follows the official RHEL 10 EX200 objective groups as published in 2026. Replace uppercase placeholders with task values. Commands shown as alternatives are not meant to be typed on one line.

Task Command
Shell syntax/help COMMAND --help; type COMMAND; history
Redirect cmd >file; cmd >>file; cmd 2>err; `cmd other;cmd tee file`
Search text grep PATTERN file; grep -E REGEX; grep -ril PATTERN DIR
SSH ssh USER@HOST; ssh -p PORT USER@HOST
Switch user su - USER; sudo -i; sudo -u USER COMMAND
Archive tar -czf out.tgz DIR; tar -cjf out.tar.bz2 DIR; tar -xf ARCHIVE -C DIR
Edit/create vim FILE; touch FILE; mkdir -p DIR
Copy/move/delete cp -a SRC DST; mv SRC DST; rm -r DIR
Links ln TARGET HARDLINK; ln -s TARGET SYMLINK
Permissions chmod 640 FILE; chmod u=rw,g=r,o= FILE; chown USER:GROUP FILE
Find find DIR -type f -name NAME; find DIR -type f -user USER -size +5M
Documentation man COMMAND; man 5 FILE; man -k KEYWORD; info; rpm -qd PACKAGE

High-value pages: man bash, man grep, man find, man tar, man chmod, man chown, man ssh, man 5 passwd, man hier.

BASH root@servera
dnf repolist --enabled
dnf config-manager --set-enabled REPO
dnf clean all; dnf makecache
dnf search TERM; dnf info PACKAGE; dnf provides '*/FILE'
dnf install -y PACKAGE
dnf remove -y PACKAGE
dnf update -y PACKAGE
rpm -q PACKAGE; rpm -ql PACKAGE; rpm -qf /path/file; rpm -V PACKAGE
dnf install -y /path/package.rpm
 
flatpak remote-add --if-not-exists NAME URL
flatpak remotes
flatpak remote-ls NAME
flatpak install -y NAME APP_ID
flatpak uninstall -y APP_ID
flatpak list

Repository stanza in /etc/yum.repos.d/name.repo:

INI root@servera
[repoid]
name=Description
baseurl=file:///mnt/BaseOS
enabled=1
gpgcheck=0

High-value pages: man dnf, man dnf.conf, man yum2dnf, man rpm, man flatpak, man flatpak-remote-add.

BASH root@servera
πŸ“Œ!/bin/bash
set -euo pipefail
 
if [[ $# -lt 1 ]]; then
echo "usage: $0 VALUE" >&2
exit 2
fi
 
for item in "$@"; do
result=$(command "$item")
printf '%s\n' "$result"
done
BASH root@servera
[[ -e FILE ]]; [[ -f FILE ]]; [[ -d DIR ]]; [[ -x FILE ]]
[[ $value == yes ]]; [[ $number -gt 10 ]]
case $1 in yes) echo yes ;; no) echo no ;; *) echo unknown ;; esac
bash -n SCRIPT; chmod +x SCRIPT; ./SCRIPT ARG

High-value pages: man bash (search /CONDITIONAL EXPRESSIONS), help test, help if, help for, help case, help read, help printf.

BASH root@servera
systemctl reboot; systemctl poweroff; shutdown -r now
systemctl isolate rescue.target
systemctl get-default
ps aux; ps -eo pid,ni,comm,%cpu,%mem --sort=-%cpu
top; free -h; uptime
kill PID; kill -TERM PID; kill -KILL PID; pkill NAME
nice -n 10 COMMAND; renice 5 -p PID
tuned-adm list; tuned-adm profile PROFILE; tuned-adm active; tuned-adm verify
journalctl -b; journalctl -b -1; journalctl -u UNIT; journalctl -p err
journalctl --list-boots; journalctl --disk-usage
systemctl start SERVICE; systemctl stop SERVICE; systemctl status SERVICE
scp FILE USER@HOST:/PATH/; sftp USER@HOST

Persistent journal: create /var/log/journal and restart journald, or set Storage=persistent in /etc/systemd/journald.conf.d/*.conf:

BASH root@servera
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald

Root recovery: GRUB e, append rd.break, Ctrl-x, then:

BASH emergency shell (initramfs)
mount -o remount,rw /sysroot
chroot /sysroot
passwd root
touch /.autorelabel
exit
exit

One-time boot target: append systemd.unit=rescue.target or systemd.unit=emergency.target to the GRUB kernel line.

High-value pages: man systemctl, man systemd.special, man ps, man top, man kill, man nice, man tuned-adm, man journalctl, man 5 journald.conf, man scp.

BASH root@servera
lsblk -f; blkid; findmnt; df -hT
parted /dev/DISK print
parted /dev/DISK mklabel gpt
parted /dev/DISK mkpart primary 1MiB 1025MiB
partprobe /dev/DISK
pvcreate /dev/PART; pvs
vgcreate VG /dev/PART; vgs
lvcreate -L 1G -n LV VG; lvs
lvremove /dev/VG/LV; vgremove VG; pvremove /dev/PART
mkswap /dev/PART; swapon /dev/PART; swapon --show
swapoff /dev/PART

Persistent examples in /etc/fstab:

FSTAB root@servera
UUID=FS_UUID /data xfs defaults 0 0
UUID=SWAP_UUID none swap defaults 0 0

Always run findmnt --verify --verbose, mount -a, and swapon -a before rebooting. High-value pages: man parted, man pvcreate, man vgcreate, man lvcreate, man lvm, man mkswap, man swapon, man 5 fstab.

BASH root@servera
mkfs.vfat /dev/PART
mkfs.ext4 /dev/PART
mkfs.xfs /dev/PART
mkdir -p /MOUNT
mount /dev/PART /MOUNT; umount /MOUNT
blkid /dev/PART
findmnt /MOUNT
 
dnf install -y nfs-utils autofs
mount -t nfs SERVER:/EXPORT /MOUNT
umount /MOUNT
showmount -e SERVER
 
lvextend -r -L +500M /dev/VG/LVπŸ’‘grow LV and supported FS together
lvextend -L +500M /dev/VG/LV
xfs_growfs /MOUNTπŸ’‘XFS grows mounted, by mount point
resize2fs /dev/VG/LVπŸ’‘ext4
 
namei -l PATH; stat PATH; getfacl PATH
chmod MODE PATH; chown USER:GROUP PATH; setfacl -m u:USER:rw FILE

Simple autofs indirect map:

TEXT root@servera
πŸ“Œ/etc/auto.master.d/share.autofs
/remote /etc/auto.share --timeout=300
 
πŸ“Œ/etc/auto.share
projects -rw SERVER:/exports/projects
BASH root@servera
systemctl enable --now autofs
automount -m
ls /remote/projects
findmnt /remote/projects

High-value pages: man mkfs, man mkfs.xfs, man mkfs.ext4, man mount, man 5 fstab, man xfs_growfs, man resize2fs, man lvextend, man 5 auto.master, man 5 autofs, man setfacl, man getfacl.

BASH root@servera
echo 'COMMAND' | at 14:30
atq; atrm JOB
crontab -e -u USER; crontab -l -u USER
systemctl enable --now SERVICE
systemctl disable --now SERVICE
systemctl restart SERVICE; systemctl reload SERVICE
systemctl set-default multi-user.target
systemctl set-default graphical.target
timedatectl set-timezone REGION/CITY
chronyc sources -v; chronyc tracking
dnf install PACKAGE; dnf update PACKAGE
grubby --info=ALL
grubby --update-kernel=ALL --args='ARG'
grubby --update-kernel=ALL --remove-args='ARG'
grubby --set-default-index=INDEX
grub2-mkconfig -o /boot/grub2/grub.cfg

Cron fields: minute hour day-of-month month day-of-week command.

Minimal timer pair:

INI root@servera
πŸ“Œ/etc/systemd/system/job.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/job
INI root@servera
πŸ“Œ/etc/systemd/system/job.timer
[Unit]
Description=Run job daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
BASH root@servera
systemd-analyze verify /etc/systemd/system/job.{service,timer}
systemctl daemon-reload
systemctl enable --now job.timer
systemctl list-timers job.timer

Chrony source in /etc/chrony.conf or a supported drop-in: server NTP_HOST iburst; then systemctl enable --now chronyd and systemctl restart chronyd.

High-value pages: man at, man 5 crontab, man systemd.timer, man systemd.time, man systemctl, man timedatectl, man chrony.conf, man chronyc, man grubby, man grub2-mkconfig.

BASH root@servera
nmcli device status
nmcli connection show; nmcli connection show --active
nmcli connection add type ethernet ifname DEVICE con-name NAME \
ipv4.method manual ipv4.addresses ADDRESS/PREFIX \
ipv4.gateway GATEWAY ipv4.dns DNS
nmcli connection modify NAME connection.autoconnect yes
nmcli connection modify NAME +ipv4.addresses SECONDARY/PREFIX
nmcli connection modify NAME ipv6.method manual ipv6.addresses ADDRESS/PREFIX
nmcli connection up NAME
ip -br address; ip route; ip -6 route
hostnamectl set-hostname FQDN
getent hosts NAME; resolvectl status
BASH root@servera
systemctl enable --now NetworkManager
firewall-cmd --get-active-zones
firewall-cmd --list-all
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
firewall-cmd --check-config

Supplemental (not explicitly listed in the official EX200 objectives): persistent forwarding in /etc/sysctl.d/99-forward.conf:

TEXT root@servera
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
BASH root@servera
sysctl --system
sysctl net.ipv4.ip_forward

High-value pages: man nmcli, man nmcli-examples, man NetworkManager, man hostnamectl, man hosts, man resolv.conf, man firewall-cmd, man firewalld, man 5 sysctl.d.

BASH root@servera
useradd -u UID -g PRIMARY -G GROUP1,GROUP2 -s /bin/bash USER
useradd -u UID -s /sbin/nologin USER
usermod -aG GROUP USER
usermod -s SHELL USER; usermod -L USER; usermod -U USER
userdel -r USER
passwd USER
chage -M DAYS USER; chage -E YYYY-MM-DD USER; chage -l USER
groupadd -g GID GROUP; groupmod -n NEW OLD; groupdel GROUP
getent passwd USER; getent group GROUP; id USER
visudo
visudo -cf /etc/sudoers.d/FILE
sudo -l -U USER

Sudo drop-in example: %wheel ALL=(ALL) ALL; mode must be 0440. Defaults for future users: /etc/login.defs, /etc/default/useradd, and /etc/skel.

High-value pages: man useradd, man usermod, man passwd, man chage, man groupadd, man 5 login.defs, man 5 sudoers, man visudo.

BASH root@servera
firewall-cmd --permanent --add-service=SERVICE
firewall-cmd --permanent --remove-service=SERVICE
firewall-cmd --reload; firewall-cmd --list-all
 
umask 027
chmod 2770 GROUPDIR
setfacl -m u:USER:rwx PATH
setfacl -m d:g:GROUP:rwx,d:o::--- DIR
 
ssh-keygen -t ed25519
ssh-copy-id USER@HOST
ssh -o BatchMode=yes USER@HOST true
 
getenforce; setenforce 0; setenforce 1
grep '^SELINUX=' /etc/selinux/config
ps -eZ; ls -lZ PATH
restorecon -Rv PATH
semanage fcontext -a -t httpd_sys_content_t '/web(/.*)?'
restorecon -Rv /web
semanage port -a -t http_port_t -p tcp PORT
semanage port -m -t http_port_t -p tcp PORT
semanage port -l
getsebool -a; setsebool -P httpd_can_network_connect on
ausearch -m AVC -ts recent

SSH validation and key modes:

BASH root@servera
sshd -t; sshd -T | less
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
restorecon -Rv ~/.ssh
systemctl reload sshd

High-value pages: man firewall-cmd, man umask, man setfacl, man ssh-keygen, man ssh-copy-id, man sshd_config, man getenforce, man semanage-fcontext, man semanage-port, man setsebool, man restorecon, man ausearch, man selinux.

BASH root@servera
findmnt --verify --verbose
mount -a
sshd -t
systemctl --failed
sync

After reboot, prove persistence with findmnt, swapon --show, nmcli connection show --active, systemctl is-enabled SERVICE, systemctl is-active SERVICE, getenforce, and an end-to-end functional test.


πŸ—ΊοΈ Chapter 16 7 Exam Tasks

Appendix D β€” Practice-Set Crosswalk and Source Audit

This appendix maps every ## Question ... heading occurrence in the five source files. Repeated labels are separate rows, so this is an occurrence-level inventory rather than an assumed Q1–Q51 grid.

Flags: XD exact duplicate text; DL duplicate label; UL reconstructed unlabeled item; MAL malformed source; APP outside-objective topic assigned wholly or partly to Appendix A; SUP useful supplemental topic not explicitly listed in the official blueprint but retained in a core chapter. No flag means no source anomaly was found.

Audit summary

Source Heading occurrences Distinct labels Missing labels in 1–51 Repeated labels Exact-duplicate later occurrences UL MAL APP occurrences
Set 1 51 51 β€” β€” 2 (Q35, Q38) 0 1 1 mixed
Set 2 51 49 Q35, Q37 Q33Γ—2, Q36Γ—2 5 (Q28, Q29, Q33 occ. 2, Q36 occ. 2, Q45) 2 0 0
Set 3 51 51 β€” β€” 0 0 0 0
Set 4 50 50 Q33 β€” 0 1 0 3
Set 5 51 50 Q30 Q29Γ—2 1 (Q29 occ. 2) 0 0 5
Total 254 β€” β€” β€” 8 3 1 9

Thus the files do not contain 51 headings each: Set 4 contains 50, and all five files contain 254 heading occurrences. β€œExact-duplicate later occurrences” counts the second/later row in each identical pair, while both sides of every pair are flagged below. APP counts occurrences, including repeated source headings; S1 Q27 is mixed because package installation is in scope but container operation is not.

Malformed S1 Q19. Its body begins mid-sentence with β€œ/tmp directory. Then ...” and never names the directory in the surviving opening fragment. In context with S1 Q17–Q20, the likely intended opening is β€œCreate the /friends directory” (possibly with a lost or corrupted location phrase), followed by group ownership friends and access limited to that group and root. This reconstruction is probable, not source text; the crosswalk targets directory ownership and permissions.

Set 1

Q label Target chapter / section / topic Flags / audit note
Q1 Ch 2 Β§2.1 β€” local BaseOS/AppStream repository from mounted ISO β€”
Q2 Ch 7 β€” NetworkManager static IPv4, gateway, DNS β€”
Q3 Ch 7 β€” add a secondary IPv4 address with nmcli β€”
Q4 Ch 8 β€” chrony client and NTP source β€”
Q5 Ch 3 Β§3.1 β€” grep matching lines with ordered redirection β€”
Q6 Ch 6 β€” GPT partition, PV, and volume-group creation β€”
Q7 Ch 6 β€” create a logical volume β€”
Q8 Ch 6 β€” ext4 creation and persistent /etc/fstab mount β€”
Q9 Ch 6 β€” extend an LV and ext4 filesystem β€”
Q10 Ch 6 β€” thin-pool LV creation β€”
Q11 Ch 6 β€” standard LV creation (despite thinvol name) β€”
Q12 Ch 3 Β§3.4 β€” find regular files by size and copy β€”
Q13 Ch 8 β€” timezone configuration with timedatectl β€”
Q14 Ch 9 β€” SELinux permissive mode, runtime and persistent β€”
Q15 Ch 1 Β§1.4 β€” remove quiet/rhgb for detailed boot output β€”
Q16 Ch 4 β€” password defaults and per-user aging β€”
Q17 Ch 4 β€” create users/groups and supplementary memberships β€”
Q18 Ch 5 β€” directory ownership and restrictive mode β€”
Q19 Ch 5 β€” /friends group ownership and restrictive directory mode MAL; likely lost opening β€œCreate the /friends directory”; see audit note
Q20 Ch 5 β€” change /friends owner with chown β€”
Q21 Ch 3 Β§3.3 β€” extract passwd entry and edit copied text with sed β€”
Q22 Ch 8 β€” weekday noon cron job and append redirection β€”
Q23 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”
Q24 Ch 10 β€” Apache content/service plus HTTP(S) firewall rules β€”
Q25 Ch 6 β€” swap partition and persistent activation β€”
Q26 Ch 9 β€” SSH MaxAuthTries hardening β€”
Q27 Ch 2 Β§2.6 / Appendix A β€” install container tools; container use APP (mixed: DNF installation is objective-aligned)
Q28 Ch 2 Β§2.5 β€” Flatpak remote and application installation XD = S2 Q28 and S2 Q29
Q29 Ch 9 β€” persistent SELinux Boolean β€”
Q30 Ch 9 β€” SSH key authentication between hosts β€”
Q31 Ch 8 β€” persistent systemd journal β€”
Q32 Ch 9 β€” inspect audit log for SELinux denials and save findings β€”
Q33 Ch 7 β€” DNS resolver configuration β€”
Q34 Ch 5 β€” group directory ownership and mode XD = S1 Q35
Q35 Ch 5 β€” group directory ownership and mode XD = S1 Q34
Q36 Ch 8 β€” list running processes β€”
Q37 Ch 8 β€” terminate a process XD = S1 Q38
Q38 Ch 8 β€” terminate a process XD = S1 Q37
Q39 Ch 4 β€” create a local group β€”
Q40 Ch 4 β€” add a supplementary group membership β€”
Q41 Ch 11 β€” script a filesystem mount β€”
Q42 Ch 11 / Ch 7 β€” script a permanent SSH firewall rule β€”
Q43 Ch 11 / Ch 10 β€” script idempotent Apache installation β€”
Q44 Ch 8 β€” create a cron job β€”
Q45 Ch 8 β€” list cron jobs β€”
Q46 Ch 7 β€” display an interface IP address β€”
Q47 Ch 7 β€” static IPv4 with nmcli β€”
Q48 Ch 7 β€” NetworkManager connection autoconnect β€”
Q49 Ch 7 β€” persistent hostname change β€”
Q50 Ch 7 β€” display an interface IP address β€”
Q51 Ch 10 β€” AutoFS wildcard NFS home directories and timeout β€”

Set 2

Q label Target chapter / section / topic Flags / audit note
Q1 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”
Q2 Ch 7 β€” NetworkManager static IPv4, gateway, DNS β€”
Q3 Ch 7 β€” add a secondary IPv6 address with nmcli β€”
Q4 Ch 7 β€” persistent IPv4 forwarding with sysctl SUP
Q5 Ch 6 β€” GPT partition, PV, and volume-group creation β€”
Q6 Ch 6 β€” create a logical volume β€”
Q7 Ch 9 β€” per-user SSH key authentication β€”
Q8 Ch 6 β€” ext4 creation and persistent mount β€”
Q9 Ch 6 β€” extend an LV and ext4 filesystem β€”
Q10 Ch 10 β€” Apache content/service plus HTTP(S) firewall rules β€”
Q11 Ch 11 β€” positional argument and case/conditional script β€”
Q12 Ch 1 Β§1.3 β€” set multi-user.target as default β€”
Q13 Ch 7 β€” persistent hostname change β€”
Q14 Ch 1 Β§1.6 β€” kernel update, default selection, retain old kernel β€”
Q15 Ch 3 Β§3.4 β€” find files by owner and copy β€”
Q16 Ch 4 β€” populate new homes through /etc/skel β€”
Q17 Ch 4 β€” password defaults and per-user aging β€”
Q18 Ch 4 β€” user with fixed UID and noninteractive shell UL; source item had no question label
Q19 Ch 5 β€” users, file ownership/mode, and per-user ACLs β€”
Q20 Ch 8 β€” daily root cron cleanup of empty files/directories β€”
Q21 Ch 3 Β§3.5 β€” bzip2 tar archive and extraction β€”
Q22 Ch 10 β€” persistent NFS client mount β€”
Q23 Ch 9 β€” disable SSH password authentication β€”
Q24 Ch 1 Β§1.7 β€” select default kernel by index β€”
Q25 Ch 9 β€” SELinux enforcing mode β€”
Q26 Ch 6 β€” swap partition and persistent activation β€”
Q27 Ch 9 β€” prohibit direct SSH root login β€”
Q28 Ch 2 Β§2.5 β€” Flatpak remote and application installation XD = S1 Q28 and S2 Q29
Q29 Ch 2 Β§2.5 β€” Flatpak remote and application installation XD = S1 Q28 and S2 Q28
Q30 Ch 5 β€” grant administrative access with sudo β€”
Q31 Ch 5 β€” file ownership, mode, users, and multiple ACL entries β€”
Q32 Ch 8 β€” persistent journal and previous-boot verification β€”
Q33 (occ. 1) Ch 2 Β§2.1 β€” local repository from mounted ISO DL; XD = S2 Q33 occ. 2
Q33 (occ. 2) Ch 2 Β§2.1 β€” local repository from mounted ISO DL; XD = S2 Q33 occ. 1
Q34 Ch 3 Β§3.7 β€” view file contents UL; source item had no question label
Q36 (occ. 1) Ch 3 Β§3.7 β€” create a directory DL; XD = S2 Q36 occ. 2
Q36 (occ. 2) Ch 3 Β§3.7 β€” create a directory DL; XD = S2 Q36 occ. 1
Q38 Ch 7 β€” reload firewalld without dropping state β€”
Q39 Ch 11 / Ch 4 β€” idempotent user-creation script β€”
Q40 Ch 11 / Ch 4 β€” idempotent group/membership script β€”
Q41 Ch 11 / Ch 8 β€” enable and start Apache in a script β€”
Q42 Ch 11 / Ch 8 β€” check package and enable Apache in a script β€”
Q43 Ch 4 β€” user with specified UID and primary GID β€”
Q44 Ch 11 / Ch 5 β€” enforce /etc/passwd ownership and mode XD = S2 Q45
Q45 Ch 11 / Ch 5 β€” enforce /etc/passwd ownership and mode XD = S2 Q44
Q46 Ch 11 / Ch 4 / Ch 5 β€” create user and grant sudo access β€”
Q47 Ch 8 β€” view active processes β€”
Q48 Ch 2 Β§2.4 β€” install a package with DNF β€”
Q49 Ch 7 β€” add a secondary IPv4 address with nmcli β€”
Q50 Ch 6 β€” GPT partition, XFS, and persistent mount β€”
Q51 Ch 10 β€” on-demand AutoFS NFS mount and timeout β€”

Set 3

Q label Target chapter / section / topic Flags / audit note
Q1 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”
Q2 Ch 2 Β§2.2 β€” exclusive HTTP DNF repository β€”
Q3 Ch 7 β€” persistent IPv4 forwarding with sysctl SUP
Q4 Ch 8 β€” root cron schedule and wall output β€”
Q5 Ch 6 β€” GPT partition, ext4, and persistent mount β€”
Q6 Ch 4 β€” change a user's login shell β€”
Q7 Ch 4 β€” user UID and account expiration β€”
Q8 Ch 3 Β§3.7 / Ch 5 β€” move/rename file and apply user ACLs β€”
Q9 Ch 6 β€” create PV/VG of requested size β€”
Q10 Ch 6 β€” create a logical volume β€”
Q11 Ch 6 β€” ext4 creation and persistent mount β€”
Q12 Ch 6 β€” extend an LV and ext4 filesystem β€”
Q13 Ch 8 β€” select and verify TuneD profile β€”
Q14 Ch 11 β€” interactive integer arithmetic script β€”
Q15 Ch 3 Β§3.4 β€” find by exact mtime and copy β€”
Q16 Ch 7 β€” NetworkManager static IPv4, gateway, DNS β€”
Q17 Ch 7 β€” add a secondary IPv4 address with nmcli β€”
Q18 Ch 4 β€” populate new homes through /etc/skel β€”
Q19 Ch 4 β€” lock a local account without deleting it β€”
Q20 Ch 4 β€” create users/groups and memberships β€”
Q21 Ch 5 β€” create group-owned directories β€”
Q22 Ch 5 β€” restrictive group-directory modes β€”
Q23 Ch 5 β€” recursive/default ACL access across group directories β€”
Q24 Ch 6 β€” PV/VG/LV and XFS creation β€”
Q25 Ch 6 β€” PV/VG/LV, XFS, and persistent mount β€”
Q26 Ch 6 β€” multiple LVs, XFS filesystems, persistent mounts β€”
Q27 Ch 6 β€” XFS persistent mount by UUID β€”
Q28 Ch 6 β€” persistent LV mount β€”
Q29 Ch 9 β€” permit direct SSH root login β€”
Q30 Ch 9 β€” SELinux enforcing mode β€”
Q31 Ch 2 Β§2.4 β€” search for and install httpd β€”
Q32 Ch 3 Β§3.4 β€” find files by owner and copy β€”
Q33 Ch 5 β€” setgid collaborative group directory β€”
Q34 Ch 10 β€” deploy downloaded Apache content and enable service β€”
Q35 Ch 3 Β§3.7 β€” directory disk usage with du β€”
Q36 Ch 4 / Ch 5 β€” specified UID/GID and sudo access β€”
Q37 Ch 5 β€” file owner/group and restrictive mode β€”
Q38 Ch 8 β€” start a service β€”
Q39 Ch 8 β€” enable a service at boot β€”
Q40 Ch 6 β€” LV, XFS, and persistent mount β€”
Q41 Ch 6 β€” PV/VG/LV, XFS, and persistent mount β€”
Q42 Ch 4 β€” create group with specified GID β€”
Q43 Ch 4 β€” default shell for regular users β€”
Q44 Ch 2 Β§2.4 β€” install a package with DNF β€”
Q45 Ch 3 Β§3.6 β€” symbolic links β€”
Q46 Ch 3 Β§3.6 β€” hard links β€”
Q47 Ch 3 Β§3.7 β€” create a directory β€”
Q48 Ch 11 / Ch 5 β€” idempotent directory ownership script β€”
Q49 Ch 6 β€” mount a removable/VFAT filesystem β€”
Q50 Ch 6 β€” GPT partition and XFS creation β€”
Q51 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”

Set 4

Q label Target chapter / section / topic Flags / audit note
Q1 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”
Q2 Ch 7 β€” NetworkManager static IPv4, gateway, DNS β€”
Q3 Ch 7 β€” add a secondary IPv4 address with nmcli β€”
Q4 Ch 7 β€” persistent IPv4 forwarding with sysctl SUP
Q5 Ch 7 β€” persistent static IPv6 with nmcli β€”
Q6 Ch 8 β€” timezone configuration and verification β€”
Q7 Ch 8 β€” install/configure chrony and NTP source β€”
Q8 Ch 1 Β§1.6 β€” kernel update, default selection, retain old kernel β€”
Q9 Ch 1 Β§1.3 β€” set multi-user.target as default β€”
Q10 Ch 4 β€” user with fixed UID and noninteractive shell β€”
Q11 Ch 6 β€” PV/VG/LV, XFS, and persistent mount β€”
Q12 Ch 6 β€” thin LV from an existing thin pool β€”
Q13 Ch 6 β€” ext4 on thin LV and persistent UUID mount β€”
Q14 Ch 8 β€” select a general TuneD profile for a VM β€”
Q15 Ch 10 β€” Apache content/service and HTTP firewall rule β€”
Q16 Ch 11 β€” script current user, date, and login count β€”
Q17 Ch 5 β€” remove β€œother” directory permissions β€”
Q18 Ch 5 β€” grant full access to owning groups β€”
Q19 Ch 5 β€” create and assign group directories β€”
Q20 Ch 5 β€” grant full access to owning groups β€”
Q21 Ch 5 β€” owner/group-only full directory access β€”
Q22 Ch 3 Β§3.5 β€” bzip2 tar archive β€”
Q23 Ch 3 Β§3.4 β€” find by mtime and save path list β€”
Q24 Ch 10 β€” Apache document root backed by NFS with SELinux context β€”
Q25 Appendix A β€” pull a UBI container image with Podman APP
Q26 Ch 8 β€” persistent journal with size limit β€”
Q27 Ch 6 β€” GPT partition, ext4, and persistent mount β€”
Q28 Ch 6 β€” swap partition and persistent activation β€”
Q29 Ch 9 β€” per-user SSH key authentication β€”
Q30 Ch 9 β€” disable SSH public-key/passwordless authentication β€”
Q31 Ch 7 β€” persistent hostname change β€”
Q32 Ch 2 Β§2.2 β€” local RPM repo access and package install β€”
Q34 Ch 1 Β§1.5 β€” modify GRUB configuration correctly Q33 is absent from this file
Q35 Ch 11 / Ch 3 Β§3.6 β€” idempotent symbolic-link script β€”
Q36 Appendix A β€” RAID 1 detection/creation script UL; APP; source item had no question label
Q37 Ch 11 / Ch 6 β€” safely inspect partition size, no risky resize β€”
Q38 Ch 7 β€” network troubleshooting workflow β€”
Q39 Ch 8 β€” start a service β€”
Q40 Ch 6 β€” filesystem versus storage volume concepts β€”
Q41 Ch 1 Β§1.5 β€” GRUB2 configuration file locations/roles β€”
Q42 Ch 2 Β§2.4 β€” install and verify gcc β€”
Q43 Ch 2 Β§2.4 β€” install/verify httpd with DNF and RPM β€”
Q44 Ch 2 Β§2.4 β€” install and verify httpd β€”
Q45 Ch 2 Β§2.4 β€” list repos, install, and verify gcc β€”
Q46 Ch 8 β€” identify the running kernel β€”
Q47 Ch 6 β€” extend LV and grow XFS/ext4 β€”
Q48 Ch 3 Β§3.5 β€” gzip tar archive with exclusion β€”
Q49 Ch 8 β€” daily cron job with appended output β€”
Q50 Ch 5 β€” setgid inheritance plus recursive/default ACLs β€”
Q51 Appendix A β€” persistent systemd-managed container APP

Set 5

Q label Target chapter / section / topic Flags / audit note
Q1 Ch 7 β€” persistent hostname change β€”
Q2 Ch 7 β€” NetworkManager static IPv4, gateway, DNS β€”
Q3 Ch 7 β€” add a secondary IPv4 address with nmcli β€”
Q4 Ch 7 β€” persistent IPv4 forwarding with sysctl SUP
Q5 Ch 7 β€” persistent IPv6 forwarding with sysctl SUP
Q6 Ch 1 Β§1.3 β€” set graphical.target as default β€”
Q7 Ch 4 β€” user UID and account expiration β€”
Q8 Ch 5 β€” owner/group, mode, and named-user ACL β€”
Q9 Ch 6 β€” PV/VG/LV, XFS, and persistent mount β€”
Q10 Ch 6 β€” PV/VG/LV, XFS, and persistent mount β€”
Q11 Ch 6 β€” create LV and persistently mount it β€”
Q12 Ch 6 β€” XFS on LV and persistent mount β€”
Q13 Ch 6 β€” persistent LV mount β€”
Q14 Ch 10 β€” install/configure Apache and enable at boot β€”
Q15 Ch 3 Β§3.4 / Β§3.5 β€” find/copy owner files and gzip archive β€”
Q16 Ch 11 β€” for loop creating multiple files β€”
Q17 Ch 3 Β§3.1 β€” grep matching lines with ordered redirection β€”
Q18 Ch 8 β€” weekend cron cleanup of empty files/directories β€”
Q19 Ch 3 Β§3.5 β€” gzip tar archive β€”
Q20 Ch 6 β€” swap partition and persistent activation β€”
Q21 Ch 9 β€” SSH AllowUsers access restriction β€”
Q22 Ch 3 Β§3.12 β€” global login environment variable β€”
Q23 Ch 7 β€” permanently block a firewalld service β€”
Q24 Appendix A β€” logrotate policy APP
Q25 Appendix A β€” rsyslog daemon facility routing APP
Q26 Ch 3 Β§3.12 β€” Bash history environment settings β€”
Q27 Ch 2 Β§2.1 β€” local repository from mounted ISO β€”
Q28 Appendix A β€” install/configure vsftpd and firewall APP
Q29 (occ. 1) Appendix A β€” Stratis pool/filesystem and persistent mount DL; XD = S5 Q29 occ. 2; APP
Q29 (occ. 2) Appendix A β€” Stratis pool/filesystem and persistent mount DL; XD = S5 Q29 occ. 1; APP
Q31 Ch 1 Β§1.4 β€” remove quiet from all boot entries Q30 is absent from this file
Q32 Ch 3 Β§3.2 β€” recursive case-insensitive grep, output file names β€”
Q33 Ch 7 β€” persistent IPv4 forwarding/router setup SUP
Q34 Ch 11 β€” file-existence conditional script β€”
Q35 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”
Q36 Ch 10 β€” AutoFS wildcard NFS home directories β€”
Q37 Ch 2 Β§2.3 β€” install an RPM from FTP URL β€”
Q38 Ch 7 β€” DNS resolver configuration β€”
Q39 Ch 1 Β§1.1 / Β§1.3 β€” reset root password and boot default target β€”
Q40 Ch 8 β€” disable a service at boot β€”
Q41 Ch 3 Β§3.4 β€” find regular file by name β€”
Q42 Ch 3 Β§3.7 / Ch 10 β€” unmount a remote filesystem β€”
Q43 Ch 1 Β§1.8 β€” regenerate rescue kernel/initramfs β€”
Q44 Ch 5 β€” create directory and change/verify ownership β€”
Q45 Ch 6 β€” GPT partition, XFS, persistent UUID mount β€”
Q46 Ch 11 / Ch 4 / Ch 3 Β§3.6 β€” user/sudo and symlink script β€”
Q47 Ch 3 Β§3.7 β€” mounted-filesystem usage with df β€”
Q48 Ch 8 β€” inspect, start, enable, and verify a service β€”
Q49 Ch 2 Β§2.7 β€” RHEL 10 AppStream versus traditional packages β€”
Q50 Ch 1 Β§1.1 β€” root-password recovery with rd.break β€”
Q51 Ch 3 Β§3.9 / Β§3.12 β€” consult man page and persist user variable β€”
Set Heading-label sequence exceptions
1 None; Q1–Q51 each occur once.
2 Q33 occurs twice; Q35 is absent; Q36 occurs twice; Q37 is absent. Q18 and Q34 carry reconstructed labels because their source items were unlabeled.
3 None; Q1–Q51 each occur once.
4 Q33 is absent. Q36 carries a reconstructed label because its source item was unlabeled.
5 Q29 occurs twice and Q30 is absent.

πŸ“ Chapter 17 1 Exam Tasks

Appendix E - Two RHCSA RHEL 10 Performance Mock Exams

These are original, blueprint-aligned simulations. They are not leaked, recalled, or claimed to be recent actual exam questions. Each mock is independent, allows 3 hours, and contains only skills represented by the official RHCSA RHEL 10 objectives.

  • servera.lab.example.com (192.168.50.11/24) is the primary system to configure.
  • serverb.lab.example.com (192.168.50.12/24) is a second managed system and provides NFS where stated.
  • DNS and gateway: 192.168.50.10 and 192.168.50.1.
  • DNF repositories: http://content.lab.example.com/rhel10/BaseOS and http://content.lab.example.com/rhel10/AppStream.
  • Flatpak repository file: http://content.lab.example.com/flatpak/lab.flatpakrepo.
  • The Flatpak repository contains org.gnome.Calculator and its required runtime.
  • NFS exports on serverb: /exports/projects and /exports/home.
  • Each VM has an unused 8 GiB disk assumed below to be /dev/vdb.
  • Root's password is unknown on servera. Root access to serverb is supplied with password ServerB-2026.
  • For Mock A, alice already exists on serverb with password UserA-2026. For Mock B, erin already exists there with password UserB-2026.

Hardware caution: /dev/vdb, partition numbers, and NetworkManager connection names are examples, not promises. Before changing storage or networking, detect the real names with lsblk -f, findmnt, nmcli device status, and nmcli connection show. Substitute those detected names in every command. Never format a device that contains existing data or the root filesystem.

General grading rule: all requested configuration must survive reboot unless the task explicitly says it is temporary. A result that exists only in the current shell or runtime configuration is incomplete.


πŸ“ Chapter 18 20 Exam Tasks

Mock Exam A - Tasks Only

Time limit: 3 hours. Complete the tasks in any order. Reboot before finishing and verify the complete system.

Configure servera with hostname servera.lab.example.com, static IPv4 address 192.168.50.11/24, gateway 192.168.50.1, and DNS server 192.168.50.10. The existing NetworkManager connection must start automatically. Do not disable IPv6.

Create persistent DNF repository definitions named lab-baseos and lab-appstream using the supplied URLs. Disable GPG checking because this isolated lab supplies no key. Install httpd, autofs, policycoreutils-python-utils, tuned, and flatpak.

Add the supplied Flatpak repository system-wide under the name lab-flatpak, install org.gnome.Calculator from it, and leave the application installed.

Create groups ops with GID 4100 and contractors. Create users alice and bob with ops as a supplementary group; create carol with primary group contractors and a non-login shell. Set all three passwords to UserA-2026. Require alice to change her password at first login and set Bob's password maximum age to 30 days. Permit members of ops to run all commands through sudo after authenticating.

Create /srv/ops owned by root:ops. Members of ops must have full access, new entries must inherit group ops, and other users must have no access. Give carol read and execute access through an ACL without changing ownership. Create /root/ops-link as a symbolic link to the directory and /srv/ops/NOTICE.hard as a hard link to /srv/ops/NOTICE, whose content must be Authorized operations staff only.

Find regular files below /var/log larger than 1 MiB and save their full paths in /root/large-logs.txt. Copy regular files owned by alice below /home into /root/alice-files, preserving their relative paths and metadata.

Select and activate the recommended TuneD profile persistently. Start sleep 6000 as a background process, change its nice value to 10, then terminate it normally. Record the profile and the process commands used in /root/process-notes.txt.

Schedule /usr/bin/touch /root/at-complete once, two minutes in the future, using at. Add a root cron job that runs /usr/bin/logger -t RHCSA-A cron-check daily at 02:15. Create and enable a systemd timer named health-report.timer that runs /usr/local/sbin/health-report five minutes after boot and every hour thereafter.

Create executable /usr/local/sbin/health-report. It must accept exactly one directory argument, print Usage: health-report DIRECTORY to standard error and exit 2 otherwise, and print the filesystem usage percentage and count of regular files under that directory. Use a conditional and command substitution. The timer from A10 must invoke it for /var and append both output and errors to /var/log/health-report.log.

Use another 3 GiB partition on the unused disk as an LVM physical volume. Create volume group vgdata with 16 MiB physical extents. Create logical volume lvreports using exactly 100 extents, format it as ext4, and mount it persistently at /reports. Extend it by 25 extents and grow the mounted filesystem online.

The supplied NFS server already exports serverb.lab.example.com:/exports/projects. On servera, configure an indirect autofs map so accessing /projects/team mounts that export. The mount must time out after 120 seconds of inactivity.

Leave SELinux enforcing at runtime and after reboot. Persistently enable the boolean that permits HTTP servers to make network connections. Save recent AVC denial records, if any, in human-readable form to /root/recent-avc.txt; an empty file is acceptable when there are no denials.

Reboot both systems. Confirm networking, required mounts and swap, autofs, the web service, firewalld, chronyd, TuneD, the timer, SELinux enforcing mode, and the default target. Correct any failure without weakening security.


πŸ“ Chapter 19 20 Exam Tasks

Mock Exam B - Tasks Only

Time limit: 3 hours. This exam starts from a fresh copy of the lab and is independent of Mock A.

Configure the two supplied DNF repositories persistently as training-baseos and training-appstream. Install nfs-utils, autofs, httpd, policycoreutils-python-utils, chrony, tuned, and flatpak; remove telnet if installed. Add the supplied Flatpak repository system-wide as training-flatpak, install org.gnome.Calculator, verify it, then remove the application while retaining the remote.

Create group developers with GID 4200. Create dana and erin with that supplementary group and Bash login shells. Create lockedsvc as a system account with /usr/sbin/nologin and no home directory. Set Dana's and Erin's password to UserB-2026; set minimum age 2 days, maximum age 45 days, warning 7 days, and account expiration 2026-12-31 for Erin. Lock Dana's password without deleting the account. Allow developers to run only /usr/bin/systemctl restart httpd through sudo without a password.

Write lines in /etc/passwd ending in /bin/bash to /root/bash-users.txt. Append the line count to the same file without overwriting the matches. Create /root/security.tar.bz2 containing /etc/sudoers.d and /etc/ssh, using bzip2 compression, and securely transfer it to /tmp/ on serverb.

Activate the balanced TuneD profile persistently. Run yes > /dev/null in the background, renice it to 15, stop and continue it using signals, then terminate it. Save its PID, nice value, and final signal sequence in /root/process-control.txt before termination.

Schedule /usr/bin/logger -t RHCSA-B at-job once, three minutes from now. Configure user erin to run /usr/bin/date >> /home/erin/date.log every weekday at 06:30. Create cleanup-empty.timer to run /usr/local/sbin/cleanup-empty /var/tmp 7 ten minutes after boot and once per day thereafter.

Create executable /usr/local/sbin/cleanup-empty. It must require a directory and an age in days, reject invalid arguments with usage text and exit 2, loop over empty regular files older than the requested age below the directory, print each path, and remove it. Paths containing spaces must work. Configure its systemd service so timer output is appended to /var/log/cleanup-empty.log.

On the unused disk, create a 1500 MiB XFS partition labeled shared mounted persistently at /shared by UUID. Create a 768 MiB swap partition and configure it by UUID. Create a 512 MiB VFAT partition labeled TRANSFER mounted persistently at /transfer with default permissions usable by all users. Activate and mount everything now.

Ensure SELinux is enforcing now and after boot. Configure the web content and port from B15 using policy, not permissive mode. Persistently enable home-directory sharing for HTTP only if required by policy; otherwise leave that boolean unchanged. Display and save the effective contexts of /srv/docs and its index file to /root/docs-contexts.txt.

Reboot both systems. Validate all persistent network, storage, service, scheduling, security, autofs, tuning, and boot requirements. Confirm no failed units and repair any configuration error.


πŸ“– Chapter 20 2 Exam Tasks

Full Answer Keys

The commands below use the assumed names from the conventions. Detect and substitute actual interface, connection, and unused-device names. Commands are run as root unless sudo -iu USER is shown.

A1. Recover administrative access

At GRUB, highlight the normal entry, press e, append rd.break to the linux line, and press Ctrl-x.

BASH emergency shell (initramfs)
mount -o remount,rw /sysroot
chroot /sysroot
passwd root
πŸ“ŒEnter RootA-2026 twice.
touch /.autorelabel
exit
exit

After the relabel and reboot:

BASH root@servera
systemctl set-default multi-user.target
getenforce
systemctl get-default
ls -Z /etc/shadow

/.autorelabel is essential because the password database was changed while normal SELinux labeling was unavailable.

A2. Configure networking

BASH root@serverb
nmcli device status
nmcli connection show
CON='System eth0'πŸ’‘replace with the detected connection
nmcli connection modify "$CON" ipv4.method manual \
ipv4.addresses 192.168.50.11/24 ipv4.gateway 192.168.50.1 \
ipv4.dns 192.168.50.10 connection.autoconnect yes
nmcli connection up "$CON"
hostnamectl hostname servera.lab.example.com
nmcli connection show "$CON"
ip -4 address; ip route
getent hosts serverb.lab.example.com

NetworkManager stores the profile persistently. No IPv6 property was changed.

A3. Configure software sources

BASH root@servera
cat > /etc/yum.repos.d/lab.repo <<'EOF'
[lab-baseos]
name=Lab BaseOS
baseurl=http://content.lab.example.com/rhel10/BaseOS
enabled=1
gpgcheck=0
 
[lab-appstream]
name=Lab AppStream
baseurl=http://content.lab.example.com/rhel10/AppStream
enabled=1
gpgcheck=0
EOF
dnf clean all
dnf repolist
dnf -y install httpd autofs policycoreutils-python-utils tuned flatpak
flatpak remote-add --system --if-not-exists lab-flatpak \
http://content.lab.example.com/flatpak/lab.flatpakrepo
flatpak install --system -y lab-flatpak org.gnome.Calculator
flatpak remotes --system
flatpak list --system --app

Use --no-gpg-verify only if the supplied repository is explicitly unsigned and is a plain repository URL rather than a signed .flatpakrepo file.

A4. Manage users, groups, and sudo

BASH root@servera
groupadd -g 4100 ops
groupadd contractors
useradd -G ops alice
useradd -G ops bob
useradd -g contractors -s /usr/sbin/nologin carol
printf '%s\n' 'alice:UserA-2026' 'bob:UserA-2026' 'carol:UserA-2026' | chpasswd
chage -d 0 alice
chage -M 30 bob
printf '%%ops ALL=(ALL) ALL\n' > /etc/sudoers.d/ops
chmod 440 /etc/sudoers.d/ops
visudo -cf /etc/sudoers.d/ops
id alice; id bob; id carol
chage -l alice; chage -l bob

A5. Configure shared files and access

BASH root@servera
mkdir -p /srv/ops
chown root:ops /srv/ops
chmod 2770 /srv/ops
setfacl -m u:carol:rx /srv/ops
printf '%s\n' 'Authorized operations staff only' > /srv/ops/NOTICE
chown root:ops /srv/ops/NOTICE
chmod 660 /srv/ops/NOTICE
ln /srv/ops/NOTICE /srv/ops/NOTICE.hard
ln -s /srv/ops /root/ops-link
getfacl /srv/ops
stat -c '%A %U:%G %i %n' /srv/ops /srv/ops/NOTICE /srv/ops/NOTICE.hard
readlink -f /root/ops-link

Setgid (2) makes new entries inherit the directory group. The matching inode numbers verify the hard link.

A6. Use essential tools and transfer data

BASH root@serverb
grep -Ev '^[[:space:]]*(#|$)' /etc/ssh/sshd_config > /root/sshd-active.txt
tar -czf /root/ops-config.tar.gz /etc/ssh /etc/tuned
tar -tzf /root/ops-config.tar.gz
scp /root/ops-config.tar.gz root@serverb.lab.example.com:/root/
ssh root@serverb.lab.example.com 'ls -l /root/ops-config.tar.gz'

The leading / warnings from tar are harmless; stored names remain safely relative.

A7. Find and process files

BASH root@servera
find /var/log -type f -size +1M -print > /root/large-logs.txt
mkdir -p /root/alice-files
cd /home
find . -type f -user alice -exec cp --parents -a -- '{}' /root/alice-files/ \;
find /root/alice-files -type f -ls

Running from /home gives relative names, and cp --parents -a preserves both paths and metadata.

A8. Manage processes and tuning

BASH root@servera
dnf -y install tuned
systemctl enable --now tuned
PROFILE=$(tuned-adm recommend)
tuned-adm profile "$PROFILE"
sleep 6000 & PID=$!
renice 10 -p "$PID"
ps -o pid,ni,cmd -p "$PID"
kill -TERM "$PID"
wait "$PID" 2>/dev/null
printf 'profile=%s\nsleep 6000 &\nrenice 10 -p PID\nkill -TERM PID\n' "$PROFILE" \
> /root/process-notes.txt
tuned-adm active
systemctl is-enabled tuned

TuneD remembers the selected profile below /etc/tuned; enabling the service applies it after boot.

A9. Configure services, targets, and logs

BASH root@servera
systemctl enable --now httpd
mkdir -p /var/log/journal
mkdir -p /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/10-persistent.conf <<'EOF'
[Journal]
Storage=persistent
SystemMaxUse=200M
EOF
systemctl restart systemd-journald
systemctl set-default multi-user.target
journalctl -b -p err..alert > /root/boot-errors.txt
systemctl is-enabled httpd
systemctl get-default
journalctl --disk-usage

A10. Schedule work three ways

BASH root@servera
dnf -y install at cronie
systemctl enable --now atd crond
echo '/usr/bin/touch /root/at-complete' | at now + 2 minutes
printf '15 2 * * * root /usr/bin/logger -t RHCSA-A cron-check\n' > /etc/cron.d/rhcsa-a
chmod 644 /etc/cron.d/rhcsa-a
cat > /etc/systemd/system/health-report.service <<'EOF'
[Unit]
Description=Write filesystem health report
 
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/health-report /var
StandardOutput=append:/var/log/health-report.log
StandardError=append:/var/log/health-report.log
EOF
cat > /etc/systemd/system/health-report.timer <<'EOF'
[Unit]
Description=Run filesystem health report hourly
 
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
Persistent=true
 
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now health-report.timer
atq
systemctl list-timers health-report.timer

Persistent=true catches up a missed calendar-style activation where applicable; enabling the timer provides reboot persistence.

A11. Write a shell script

BASH root@servera
cat > /usr/local/sbin/health-report <<'EOF'
πŸ“Œ!/bin/bash
if [ "$#" -ne 1 ] || [ ! -d "$1" ]; then
echo 'Usage: health-report DIRECTORY' >&2
exit 2
fi
usage=$(df -P "$1" | awk 'NR==2 {print $5}')
count=$(find "$1" -type f -print | wc -l)
printf 'directory=%s usage=%s regular_files=%s\n' "$1" "$usage" "$count"
EOF
chmod 755 /usr/local/sbin/health-report
/usr/local/sbin/health-report /var
/usr/local/sbin/health-report >/dev/null; test "$?" -eq 2
systemctl start health-report.service
tail /var/log/health-report.log

Quoting "$1" protects directory names containing spaces. df -P gives a predictable one-line POSIX layout.

A12. Create partitions and persistent file systems

First confirm /dev/vdb is unused. The following assumes no existing partition table:

BASH root@servera
lsblk -f /dev/vdb
parted -s /dev/vdb mklabel gpt
parted -s /dev/vdb mkpart archive xfs 1MiB 2049MiB
parted -s /dev/vdb mkpart swap linux-swap 2049MiB 3073MiB
partprobe /dev/vdb
mkfs.xfs -L archive /dev/vdb1
mkswap -L swapA /dev/vdb2
mkdir -p /archive
ARCH_UUID=$(blkid -s UUID -o value /dev/vdb1)
SWAP_UUID=$(blkid -s UUID -o value /dev/vdb2)
printf 'UUID=%s /archive xfs defaults 0 0\n' "$ARCH_UUID" >> /etc/fstab
printf 'UUID=%s none swap defaults 0 0\n' "$SWAP_UUID" >> /etc/fstab
mount /archive
swapon /dev/vdb2
findmnt --verify
findmnt /archive; swapon --show

If the disk already has required partitions, do not create a new label; make only the requested partitions in verified free space and adjust numbers.

A13. Create and extend LVM storage

BASH root@servera
parted -s /dev/vdb mkpart lvm 3073MiB 6145MiB
parted -s /dev/vdb set 3 lvm on
partprobe /dev/vdb
pvcreate /dev/vdb3
vgcreate -s 16M vgdata /dev/vdb3
lvcreate -l 100 -n lvreports vgdata
mkfs.ext4 /dev/vgdata/lvreports
mkdir -p /reports
UUID=$(blkid -s UUID -o value /dev/vgdata/lvreports)
printf 'UUID=%s /reports ext4 defaults 0 2\n' "$UUID" >> /etc/fstab
mount /reports
lvextend -l +25 /dev/vgdata/lvreports
resize2fs /dev/vgdata/lvreports
vgs -o vg_name,vg_extent_size
lvs -o lv_name,vg_name,lv_size,seg_size_pe
findmnt /reports; df -hT /reports

Lowercase -l means extents; uppercase -L means a byte size. resize2fs grows ext4 online after the block device is enlarged.

A14. Configure NFS and autofs

BASH root@serverb
dnf -y install autofs nfs-utils
printf '/projects /etc/auto.projects --timeout=120\n' > /etc/auto.master.d/projects.autofs
printf 'team -rw,sync serverb.lab.example.com:/exports/projects\n' > /etc/auto.projects
systemctl enable --now autofs
ls /projects/team
findmnt /projects/team
systemctl is-enabled autofs

The indirect map key team is appended to /projects; autofs mounts on access rather than at boot.

A15. Configure an SELinux-protected web service

BASH root@servera
mkdir -p /web/site
printf '%s\n' 'RHCSA Mock A' > /web/site/index.html
sed -i 's/^Listen 80$/Listen 8088/' /etc/httpd/conf/httpd.conf
cat > /etc/httpd/conf.d/site.conf <<'EOF'
DocumentRoot "/web/site"
<Directory "/web/site">
Require all granted
</Directory>
EOF
semanage fcontext -a -t httpd_sys_content_t '/web(/.*)?'
restorecon -Rv /web
semanage port -a -t http_port_t -p tcp 8088
systemctl enable --now firewalld httpd
DEV=$(ip -4 route show default | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}')
ZONE=$(firewall-cmd --get-zone-of-interface="$DEV")
if [ -z "$ZONE" ] || [ "$ZONE" = "no zone" ]; then ZONE=$(firewall-cmd --get-default-zone); fi
firewall-cmd --permanent --zone="$ZONE" --add-port=8088/tcp
firewall-cmd --reload
apachectl configtest
systemctl restart httpd
curl http://localhost:8088/
ls -Zd /web/site; semanage port -l | grep http_port_t
firewall-cmd --zone="$ZONE" --list-ports

If port 8088 already has another SELinux mapping, use semanage port -m instead of -a after inspecting semanage port -l.

A16. Configure SSH key authentication

BASH root@serverb
sudo -iu alice ssh-keygen -t ed25519 -N '' -f /home/alice/.ssh/id_ed25519
sudo -iu alice ssh-copy-id -i /home/alice/.ssh/id_ed25519.pub alice@serverb.lab.example.com
sudo -iu alice ssh -o PasswordAuthentication=no alice@serverb.lab.example.com id
ssh root@serverb.lab.example.com 'chmod 700 ~alice/.ssh; chmod 600 ~alice/.ssh/authorized_keys; restorecon -RFv ~alice/.ssh'

ssh-copy-id needs Alice's serverb password once. It appends rather than overwrites existing authorized keys.

A17. Configure the time service

BASH root@servera
sed -i -E '/^[[:space:]]*(server|pool)[[:space:]]/d' /etc/chrony.conf
printf 'server 192.168.50.10 iburst\n' >> /etc/chrony.conf
systemctl enable --now chronyd
systemctl restart chronyd
chronyc sources -v

Removing existing server and pool lines satisfies the word only; restarting applies the new source when chronyd was already active.

A18. Modify bootloader behavior

BASH root@servera
grubby --update-kernel=ALL --args='audit=1'
grubby --info=ALL | grep '^args='
reboot
cat /proc/cmdline

grubby updates BootLoaderSpec kernel entries without replacing unrelated arguments. The running command line changes only after reboot.

A19. Manage SELinux modes and booleans

BASH root@servera
setenforce 1
sed -i -E 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
setsebool -P httpd_can_network_connect on
ausearch -m AVC -ts recent -i > /root/recent-avc.txt
getenforce
grep '^SELINUX=' /etc/selinux/config
getsebool httpd_can_network_connect

-P writes the boolean to policy storage. Do not use setenforce 0 to hide a labeling or port error.

A20. Final validation

BASH root@serverb
ssh root@serverb.lab.example.com 'systemctl reboot' || true
reboot
hostnamectl; ip -4 address; ip route
findmnt --verify; findmnt --target /archive; findmnt --target /reports; swapon --show
ls /projects/team; findmnt /projects/team
curl http://localhost:8088/
systemctl is-enabled httpd firewalld chronyd tuned autofs health-report.timer
systemctl --failed
systemctl list-timers health-report.timer
tuned-adm active
getenforce; systemctl get-default
flatpak list --system --app

On serverb, verify the copied archive. Diagnose client failures with showmount -e serverb.lab.example.com, journalctl -b -u autofs, and a manual test mount rather than weakening security controls.

B1. Recover root and select the boot target

Use the A1 GRUB rd.break sequence, but set RootB-2026, create /.autorelabel, and exit twice. Then:

BASH root@servera
systemctl set-default graphical.target
systemctl isolate multi-user.target
systemctl get-default
systemctl is-active multi-user.target
getenforce

isolate changes the current target only; set-default controls later boots.

B2. Configure a second static network profile

BASH root@serverb
nmcli device status; nmcli connection show
CON='System eth0'πŸ’‘substitute detected active connection
nmcli connection modify "$CON" ipv4.method manual \
ipv4.addresses 192.168.50.11/24 ipv4.gateway 192.168.50.1 \
ipv4.dns 192.168.50.10 ipv4.dns-search lab.example.com \
ipv6.method auto connection.autoconnect yes
nmcli connection up "$CON"
hostnamectl hostname servera.lab.example.com
nmcli -f ipv4,ipv6,connection connection show "$CON"
getent hosts serverb.lab.example.com

B3. Manage RPM and Flatpak software

BASH root@servera
cat > /etc/yum.repos.d/training.repo <<'EOF'
[training-baseos]
name=Training BaseOS
baseurl=http://content.lab.example.com/rhel10/BaseOS
enabled=1
gpgcheck=0
 
[training-appstream]
name=Training AppStream
baseurl=http://content.lab.example.com/rhel10/AppStream
enabled=1
gpgcheck=0
EOF
dnf clean all; dnf repolist
dnf -y install nfs-utils autofs httpd policycoreutils-python-utils chrony tuned flatpak
dnf -y remove telnet
flatpak remote-add --system --if-not-exists training-flatpak \
http://content.lab.example.com/flatpak/lab.flatpakrepo
flatpak install --system -y training-flatpak org.gnome.Calculator
flatpak info --system org.gnome.Calculator
flatpak uninstall --system -y org.gnome.Calculator
flatpak remotes --system
! flatpak list --system --app | grep -q org.gnome.Calculator

B4. Manage identities and account policy

BASH root@servera
groupadd -g 4200 developers
useradd -G developers -s /bin/bash dana
useradd -G developers -s /bin/bash erin
useradd -r -M -s /usr/sbin/nologin lockedsvc
printf '%s\n' 'dana:UserB-2026' 'erin:UserB-2026' | chpasswd
chage -m 2 -M 45 -W 7 -E 2026-12-31 erin
passwd -l dana
printf '%%developers ALL=(root) NOPASSWD: /usr/bin/systemctl restart httpd\n' \
> /etc/sudoers.d/developers
chmod 440 /etc/sudoers.d/developers
visudo -cf /etc/sudoers.d/developers
id dana; id erin; getent passwd lockedsvc
passwd -S dana; chage -l erin

Password locking prefixes Dana's password hash; it does not expire or remove the account.

BASH root@servera
mkdir -p /srv/devel
chown root:developers /srv/devel
chmod 2770 /srv/devel
setfacl -m u:erin:rwx /srv/devel
printf 'umask 027\n' > /home/dana/.bashrc.d-umask
printf '\n# RHCSA per-user umask\nsource ~/.bashrc.d-umask\n' >> /home/dana/.bashrc
chown dana:dana /home/dana/.bashrc /home/dana/.bashrc.d-umask
printf '%s\n' 'Development area' > /srv/devel/readme
ln -s readme /srv/devel/readme.sym
ln /srv/devel/readme /srv/devel/readme.hard
getfacl /srv/devel
sudo -iu dana bash -lc umask
stat -c '%i %n' /srv/devel/readme /srv/devel/readme.hard
readlink /srv/devel/readme.sym

The symbolic link target is relative to its own directory. A simpler acceptable umask configuration is umask 027 directly at the end of Dana's .bashrc.

B6. Search, redirect, archive, and copy

BASH root@serverb
grep '/bin/bash$' /etc/passwd > /root/bash-users.txt
grep -c '/bin/bash$' /etc/passwd >> /root/bash-users.txt
tar -cjf /root/security.tar.bz2 /etc/sudoers.d /etc/ssh
tar -tjf /root/security.tar.bz2
scp /root/security.tar.bz2 root@serverb.lab.example.com:/tmp/
ssh root@serverb.lab.example.com 'ls -l /tmp/security.tar.bz2'

The second >> appends; using > there would erase the requested matches.

B7. Locate files with exact criteria

BASH root@servera
mkdir -p /root/dana-small
cd /home
find . -type f -user dana -size -100k -exec cp --parents -a -- '{}' /root/dana-small/ \;
find /usr/local -xtype l -print | sort > /root/broken-links.txt
find /root/dana-small -type f -ls
cat /root/broken-links.txt

For symlinks, -xtype l follows the link for testing and selects links whose targets cannot be resolved.

B8. Control processes and performance

BASH root@servera
systemctl enable --now tuned
tuned-adm profile balanced
yes > /dev/null & PID=$!
renice 15 -p "$PID"
kill -STOP "$PID"
kill -CONT "$PID"
{
ps -o pid=,ni=,cmd= -p "$PID"
printf 'signals: STOP CONT TERM\n'
} > /root/process-control.txt
kill -TERM "$PID"
wait "$PID" 2>/dev/null
tuned-adm active
systemctl is-enabled tuned

Use the saved PID, not killall yes, so unrelated processes are untouched.

B9. Services, targets, and journal persistence

BASH root@servera
systemctl enable --now chronyd httpd
systemctl mask ctrl-alt-del.target
mkdir -p /var/log/journal /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/10-persistent.conf <<'EOF'
[Journal]
Storage=persistent
SystemMaxUse=150M
EOF
systemctl restart systemd-journald
journalctl -b -u httpd > /root/httpd-journal.txt
systemctl set-default graphical.target
systemctl is-enabled chronyd httpd
systemctl is-enabled ctrl-alt-del.target
systemctl get-default

A masked unit resolves to /dev/null and cannot be started accidentally.

B10. Schedule cron, at, and a timer

BASH root@servera
dnf -y install at cronie
systemctl enable --now atd crond
echo '/usr/bin/logger -t RHCSA-B at-job' | at now + 3 minutes
crontab -u erin -l 2>/dev/null > /tmp/erin.cron || :
printf '30 6 * * 1-5 /usr/bin/date >> /home/erin/date.log\n' >> /tmp/erin.cron
crontab -u erin /tmp/erin.cron
rm -f /tmp/erin.cron
cat > /etc/systemd/system/cleanup-empty.service <<'EOF'
[Unit]
Description=Remove old empty temporary files
 
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/cleanup-empty /var/tmp 7
StandardOutput=append:/var/log/cleanup-empty.log
StandardError=append:/var/log/cleanup-empty.log
EOF
cat > /etc/systemd/system/cleanup-empty.timer <<'EOF'
[Unit]
Description=Daily empty-file cleanup
 
[Timer]
OnBootSec=10min
OnUnitActiveSec=1d
Persistent=true
 
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now cleanup-empty.timer
atq; crontab -u erin -l
systemctl list-timers cleanup-empty.timer

B11. Write an argument-processing script

BASH root@servera
cat > /usr/local/sbin/cleanup-empty <<'EOF'
πŸ“Œ!/bin/bash
if [ "$#" -ne 2 ] || [ ! -d "$1" ] || ! [[ "$2" =~ ^[0-9]+$ ]]; then
echo 'Usage: cleanup-empty DIRECTORY DAYS' >&2
exit 2
fi
while IFS= read -r -d '' file; do
printf '%s\n' "$file"
rm -- "$file"
done < <(find "$1" -type f -empty -mtime "+$2" -print0)
EOF
chmod 755 /usr/local/sbin/cleanup-empty
/usr/local/sbin/cleanup-empty >/dev/null; test "$?" -eq 2
touch -d '10 days ago' '/var/tmp/old empty test'
systemctl start cleanup-empty.service
test ! -e '/var/tmp/old empty test'
tail /var/log/cleanup-empty.log

NUL-delimited find -print0 plus read -d '' handles spaces and other ordinary special characters safely. Quoted rm -- prevents a leading dash in a name from becoming an option.

B12. Build persistent disk storage

After proving the disk is unused:

BASH root@servera
lsblk -f /dev/vdb
parted -s /dev/vdb mklabel gpt
parted -s /dev/vdb mkpart shared xfs 1MiB 1501MiB
parted -s /dev/vdb mkpart swap linux-swap 1501MiB 2269MiB
parted -s /dev/vdb mkpart transfer fat32 2269MiB 2781MiB
partprobe /dev/vdb
mkfs.xfs -L shared /dev/vdb1
mkswap -L swapB /dev/vdb2
mkfs.vfat -n TRANSFER /dev/vdb3
mkdir -p /shared /transfer
U1=$(blkid -s UUID -o value /dev/vdb1)
U2=$(blkid -s UUID -o value /dev/vdb2)
U3=$(blkid -s UUID -o value /dev/vdb3)
printf 'UUID=%s /shared xfs defaults 0 0\n' "$U1" >> /etc/fstab
printf 'UUID=%s none swap defaults 0 0\n' "$U2" >> /etc/fstab
printf 'UUID=%s /transfer vfat defaults,umask=000 0 0\n' "$U3" >> /etc/fstab
mount /shared; mount /transfer; swapon /dev/vdb2
findmnt --verify
findmnt --target /shared; findmnt --target /transfer; swapon --show

VFAT has no Unix mode bits; umask=000 supplies the requested all-user access. Use only verified free space if the disk is not blank.

B13. Build LVM by extents and resize it

BASH root@servera
parted -s /dev/vdb mkpart lvm 2781MiB 5853MiB
parted -s /dev/vdb set 4 lvm on
partprobe /dev/vdb
pvcreate /dev/vdb4
vgcreate -s 8M vgapps /dev/vdb4
lvcreate -l 180 -n lvapp vgapps
mkfs.xfs /dev/vgapps/lvapp
mkdir -p /apps
UUID=$(blkid -s UUID -o value /dev/vgapps/lvapp)
printf 'UUID=%s /apps xfs defaults 0 0\n' "$UUID" >> /etc/fstab
mount /apps
lvextend -l +40 /dev/vgapps/lvapp
xfs_growfs /apps
vgs -o vg_name,vg_extent_size
lvs -o lv_name,lv_size,seg_size_pe
findmnt /apps; df -hT /apps

Unlike ext4, XFS is grown using its mounted directory and cannot be shrunk.

B14. Configure wildcard autofs home directories

BASH root@serverb
printf '/remotehome /etc/auto.remotehome --timeout=90\n' > /etc/auto.master.d/remotehome.autofs
printf '* -rw,sync serverb.lab.example.com:/exports/home/&\n' > /etc/auto.remotehome
systemctl enable --now autofs
ls /remotehome/dana
findmnt /remotehome/dana

In an autofs map, * matches the requested key and & substitutes that key into the NFS path.

B15. Publish content on a nonstandard port

BASH root@servera
mkdir -p /srv/docs
printf '%s\n' 'RHCSA Mock B' > /srv/docs/index.html
sed -i 's/^Listen 80$/Listen 8090/' /etc/httpd/conf/httpd.conf
cat > /etc/httpd/conf.d/docs.conf <<'EOF'
DocumentRoot "/srv/docs"
<Directory "/srv/docs">
Require all granted
</Directory>
EOF
semanage fcontext -a -t httpd_sys_content_t '/srv/docs(/.*)?'
restorecon -Rv /srv/docs
semanage port -a -t http_port_t -p tcp 8090
systemctl enable --now firewalld
DEV=$(ip -4 route show default | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}')
ZONE=$(firewall-cmd --get-zone-of-interface="$DEV")
if [ -z "$ZONE" ] || [ "$ZONE" = "no zone" ]; then ZONE=$(firewall-cmd --get-default-zone); fi
firewall-cmd --permanent --zone="$ZONE" --add-port=8090/tcp
firewall-cmd --reload
apachectl configtest
systemctl restart httpd
curl http://localhost:8090/
ls -lZd /srv/docs /srv/docs/index.html
semanage port -l | grep http_port_t

Use semanage port -m if inspection shows the port already exists under a different type.

B16. Configure SSH and secure transfer access

BASH root@serverb
sudo -iu erin ssh-keygen -t ed25519 -N '' -f /home/erin/.ssh/id_ed25519
sudo -iu erin ssh-copy-id -i /home/erin/.ssh/id_ed25519.pub erin@serverb.lab.example.com
cat > /etc/ssh/sshd_config.d/60-no-root.conf <<'EOF'
PermitRootLogin no
EOF
sshd -t
systemctl reload sshd
sudo -iu erin ssh -o PasswordAuthentication=no erin@serverb.lab.example.com id
sshd -T | grep permitrootlogin

Validating with sshd -t before reload prevents a typo from breaking remote access. No password-authentication directive is changed.

B17. Configure the time service

BASH root@servera
sed -i -E '/^[[:space:]]*(server|pool)[[:space:]]/d' /etc/chrony.conf
printf 'server 192.168.50.10 iburst\n' >> /etc/chrony.conf
systemctl enable --now chronyd
systemctl restart chronyd
chronyc sources -v

B18. Manage the bootloader

BASH root@servera
grubby --update-kernel=ALL --args='systemd.show_status=true' --remove-args='rhgb'
grubby --info=ALL | grep '^args='
reboot
cat /proc/cmdline

grubby changes every installed BootLoaderSpec entry while preserving arguments not named for removal.

B19. Manage SELinux comprehensively

BASH root@servera
setenforce 1
sed -i -E 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
restorecon -Rv /srv/docs
ls -lZd /srv/docs /srv/docs/index.html | tee /root/docs-contexts.txt
getenforce
grep '^SELINUX=' /etc/selinux/config
getsebool httpd_enable_homedirs

The content is under /srv/docs, not a home directory, so httpd_enable_homedirs is not required and must not be changed. The httpd_sys_content_t fcontext and http_port_t port mapping from B15 are the least-privilege solution.

B20. Final validation

BASH root@serverb
ssh root@serverb.lab.example.com 'systemctl reboot' || true
reboot
hostnamectl; nmcli connection show --active; ip route
findmnt --verify; findmnt --target /shared; findmnt --target /transfer; findmnt --target /apps; swapon --show
ls /remotehome/dana; findmnt /remotehome/dana
curl http://localhost:8090/
systemctl is-enabled httpd chronyd tuned autofs cleanup-empty.timer
systemctl is-enabled ctrl-alt-del.target
systemctl get-default
systemctl list-timers cleanup-empty.timer
systemctl --failed
tuned-adm active
getenforce
firewall-cmd --list-all
cat /proc/cmdline

On serverb, verify the transferred archive. An empty systemctl --failed result, successful local HTTP response, and successful NFS mount access are stronger checks than merely seeing enabled unit files.

ESC
βœ“ Copied to clipboard!