RHCSA Sample Exam
This sample exam is similar to Sander van Vugt’s, but should be different enough that I’m not infringing on his copyright by sharing. I did find quite a few errors in his grading scripts. I’m assuming those were a product of releasing a lot of slightly different versions of the material over the years without fully updating the scripts. But, who knows, maybe fixing the scripts was an implied additional task. In any case, I’ll cut him some slack because I found his videos to be generally excellent.
Update
I easily passed the RHCSA with a perfect score on the first attempt. I can confirm that if you can pass this practice exam within a reasonable amount of time, you are thoroughly prepared.
However, one part of the initial setup did not go as planned. I outlined some
sample “essential configurations” here. One of those
configurations, and I would consider it to be the most important by far, is
swapping caps-lock and escape. After many years using vim and the shell in
vi-mode with caps-lock and escape swapped, it’s basically a necessity for me.
First, I applied the caps:swapescape option with gsettings in the main testing
environment to no avail. Next, I tried creating custom key mappings using
showkey and loadkeys from inside the virtual machines. This produced a weird
situation where the escape and caps-lock keys were BOTH escape and caps-lock.
I didn’t have time to fiddle any further, so I just carried on pressing escape
(the physical caps-lock key) twice every time I needed it.
Initial Setup
Installation
- Install RHEL 10 on two virtual machines using the “Server with GUI” software selection
- Use the default partitioning scheme
- Leave the root account disabled, only create an administrative user
Ensure that your network is set up such that the virtual machines are not isolated. Each of them should be assigned a unique IP address.
Initialization
- Set the hostnames to server1.myorg.foo and server2.myorg.foo
- Set up hostname resolution on both servers
- Create an additional administrative user, admin, with the password ‘pass’
Recover root Access
- Assume that you need to perform emergency troubleshooting/recovery operations on server2 and there is no root password set, set a root password.
Solution
Add init=/bin/bash to grub boot command and boot into minimal shell.
mount -o remount,rw /
passwd root
touch /.autorelabel
echo s > /proc/sysrq-trigger
echo b > /proc/sysrq-trigger
Bootloader Configuration
Both servers:
- Modify the system bootloader to show the text details during the boot process for the current default kernel only
- Modify the system bootloader to show the text details during the boot process for all kernels including those installed in the future
Solution
- Remove
rhgb quietfrom grub file in /boot/loader/entries/ - In /etc/default/grub:
- Set GRUB_ENABLE_BLSCFG=false
- Remove
rhgb quietfrom the GRUB_CMDLINE_LINUX parameter
Default Target
- Ensure that both servers boot into a multi-user target by default
Solution
systemctl set-default multi-user.target
Logging Configuration
Both servers:
- Ensure that journal entries are persistent
- Create an entry in rsyslog that writes all messages with a severity of error or higher to /var/log/error
- Ensure that /var/log/error is rotated on a weekly basis and the last 6 logs are kept before they are rotated out. Also ensure the rotated logs are compressed in storage.
Solution
- Ensure that storage is set to auto or persistent in
/usr/lib/systemd/journald.conf or drop-ins in /etc/systemd/journald.d
- Create /var/log/journal
- Flush journal with
systemctl restart systemd-journal-flush.service - Ensure that /var/log/journal is not empty
- In /etc/rsyslog.conf or /etc/rsyslog.d/error.conf, add
*.err /var/log/error - /etc/logrotate.d/error
/var/log/error { weekly rotate 6 compress }
Networking
- Add an additional IPv4 address to both servers
Solution
- Use
nmtui
Managing Tuning Profiles
- Configure server1 to use the powersave tuned profile
- Configure server2 to use the balanced tuned profile
Solution
tuned-adm profile powersavetuned-adm profile balanced
Managing SSH Access
- Set up key-based authentication which allows the root user to login from server1 to server2 without a password.
- Ensure that root login is permitted on server2 via SSH by key only.
Solution
server2 - permit password login for root:
- Modify
/etc/ssh/sshd_configto includePermitRootLogin Yes - Restart sshd
server1 - copy ssh key to server2
ssh-keygen
ssh-copy-id root@server2
server2 - permit ssh authentication via key only
- Modify
/etc/ssh/sshd_configto includePermitRootLogin prohibit-password - Restart sshd
Configure Repository Access
- Use scp to copy the installation disk from the virtual machine host to server1.
- Move the installation disk to /repo.iso and mount it persistently to /repo. Verify that the directories /repo/BaseOS and /repo/AppStream are present.
- Install Apache web server and provide http access to the repositories by creating symbolic links to /repo/BaseOS and /repo/AppStream in the default Apache document root.
- Configure server2 as a repository client to this server
Solution
- virtual machine host:
scp rhel-10.0-x86_64-dvd.iso user@server1:~/ - server1:
mv /home/user/rhel-10.0-x86_64-dvd.iso /repo.iso mkdir /repo echo "/repo.iso /repo iso9660 defaults 0 0" >> /etc/fstab systemctl daemon-reload mount -a dnf config-manager --add-repo 'file:///repo/BaseOS' dnf config-manager --add-repo 'file:///repo/AppStream' echo "gpgcheck=0" >> /etc/yum.repos.d/repo_BaseOS echo "gpgcheck=0" >> /etc/yum.repos.d/repo_AppStream dnf install httpd systemctl enable --now httpd firewall-cmd --add-service http --permanent firewall-cmd --reload ln -s /repo/BaseOS /var/www/html/BaseOS ln -s /repo/AppStream /var/www/html/AppStream - server2:
dnf config-manager --add-repo 'http://server1.myorg.foo/BaseOS' dnf config-manager --add-repo 'http://server1.myorg.foo/AppStream' echo "gpgcheck=0" >> /etc/yum.repos.d/server1.myorg.foo_BaseOS echo "gpgcheck=0" >> /etc/yum.repos.d/server1.myorg.foo_AppStream
Archiving
server1:
- Create an archive, /root/archives/archive.tar, containing the /var/log and /etc directories.
- Create a compressed archive using gzip, /root/archives/archive.tar.gz, containing the /var/log and /etc directories.
- Create a compressed archive using bzip2, /root/archives/archive.tar.bz2, containing the /var/log and /etc directories.
- Create a compressed archive using xz, /root/archives/archive.tar.xz, containing the /var/log and /etc directories.
Solution
tar -cf ~/archives/archive.tar /var/log /etctar -czf ~/archives/archive.tar.gz /var/log /etctar -cjf ~/archives/archive.tar.bz2 /var/log /etctar -cJf ~/archives/archive.tar.xz /var/log /etc
Managing Partitions
- Increase the size of the primary disk for server1 by 15GiB
- In the free disk space, create a 2GiB partition, format it with the xfs
filesystem, and mount it persistently to the /var/log directory using the
UUID.
- The new mount point should include all the current contents of the /var/log directory. In other words, take care to avoid masking the current contents.
- Ensure that the contents are relocated to the new mount point in such a way that the SELinux labels are preserved. Otherwise, services may not be able to write new logs.
- Create another 2GiB partition and set it up to be used as swap space. Ensure it is also mounted persistently.
Solution
# first, increase size of vm disk
fdisk /dev/sda # add partition of type linux filesystem
mkfs.xfs /dev/partition
blkid | awk '/partition/ {print $2}' >> /etc/fstab
# complete mount spec in /etc/fstab
systemctl daemon-reload
# temporarily mount partition to move contents of /var/log
mount /dev/partition /mnt
mv /var/log/* /mnt
mount -a
umount /mnt
restorecon -R /var/log
fdisk /dev/sda # add partition of type linux swap
mkswap /dev/partition
swapon /dev/partition
blkid | awk '/partition/ {print $2}' >> /etc/fstab
# complete mount spec in /etc/fstab
systemctl daemon-reload
mount -a
Managing LVM Partitions
- On server1, create a logical volume with the name ldap that uses 500 8MiB extents.
- Format the ldap logical volume with the ext4 filesystem and ensure it mounts persistently on /home/ldap
- Increase the size of the rhel/root logical volume by 2GiB and the rhel/swap logical volume by 1GiB
Solution
- First, calculate the required partition size and then create a new partition. The partition size will need to be at least (number of extents + 1)*(extent size)
vgcreate vgroup -s 8M /dev/partition
lvcreate -l 250 -n ldap /dev/vgroup
- —
mkfs.ext4 /dev/vgroup/ldap
blkid | awk '/ldap/ {print $2}' >> /etc/fstab
# complete mount spec in /etc/fstab
systemctl daemon-reload
mount -a
- First, calculate the required partition size and then create a new partition.
vgextend rhel /dev/partition
lvextend -r -L +2G /dev/rhel/root
lvextend -L +1G /dev/rhel/swap
Creating Users and Groups
On server2:
- Ensure that every new user on the system is required to reset their password every 45 days
- Ensure members of the group wheel have to reauthenticate after 240 minutes
- Ensure all other users with sudo privileges have to reauthenticate after 30 minutes
- Ensure members of the group management have the ability to change the password for all users on the system except root
- Create a group management with a GID of 2700
- Create a group engineering with a GID of 2800
- Create a group design with a GID of 2900
- Create a user with the following properties:
- Name: boss
- Password: “pass”
- UID: 1700
- Secondary groups: engineering, design, management
- Create a user with the following properties:
- Name: bob
- Password: “pass”
- UID: 1800
- Secondary groups: engineering
- Create a user with the following properties:
- Name: sally
- Password: “pass”
- UID: 1801
- Secondary groups: engineering
- Create a user with the following properties:
- Name: joe
- UID: 1900
- Secondary groups: design
- Create a user mystery-app. Ensure this user cannot open an interactive shell and is using UID 567. Set this user’s home directory to /var/lib/mystery-app, but don’t create it.
- For each user except mystery-app, create a file in $HOME called ${USER}file
- For each user except mystery-app, create a file in $HOME called .myconfig
Solution
- Set
PASS_MAX_DAYS 45in /etc/login.defs - Add
Defaults:%wheel timestamp_timeout=240to sudoers file or drop in - Add
Defaults timestamp_timeout=30to sudoers file or drop in - Add
%management ALL = /bin/passwd, !/bin/passwd root groupadd --gid 2700 managementgroupadd --gid 2800 engineeringgroupadd --gid 2900 design- —
useradd boss -G engineering,design --uid 1700 passwd boss - —
useradd bob -G engineering --uid 1800 passwd bob - —
useradd sally -G engineering --uid 1801 passwd sally - —
useradd joe -G design --uid 1900 passwd joe - —
useradd mystery-app --uid 567 -s /sbin/nologin - —
for u in ${users[@]}; do su -l $u -c 'touch ~/${USER}file'; done for u in ${users[@]}; do su -l $u -c 'touch ~/.myconfig'; done
Managing Permissions
- On server2, create a shared group directory for the engineering team, /data/engineering, and ensure bob is the owner of that directory.
- Only the owner and the members of group engineering should have read and write permissions in this directory.
- Ensure that any new file that is created in this directory is group-owned automatically and can only be deleted by the owner or the user that created the file.
Solution
mkdir /data/engineering
chown bob:engineering /data/engineering
chmod 0770 /data/engineering
chmod g+s /data/engineering
chmod +t /data/engineering
# or
chmod 3770 /data/engineering
Managing Autofs
On server1:
- Create the directories boss, bob, joe, and sally in the /home/ldap directory from a copy of /etc/skel.
- Use NFS to share these directories with all hosts in server1’s 24-bit subnet mask.
- Ensure that the directory ownerships are appropriate.
On server2:
- Create a solution that automatically mounts server1:/home/ldap/$USER on /home/ldap/$USER on demand when these directories are accessed.
- Copy all the contents from boss, bob, joe, and sally’s home directories into the appropriate /home/ldap subdirectory. Ensure that ownership is preserved.
- Set the home directories for boss, bob, joe, and sally to the appropriate directories in /home/ldap. Remove their original default home directories.
- Ensure that the directories are automatically mounted when the users login.
Solution
- server1
mkdir -p /home/ldap/boss chown 1700:1700 /home/ldap/boss mkdir -p /home/ldap/bob chown 1800:1800 /home/ldap/bob mkdir -p /home/ldap/sally chown 1801:1801 /home/ldap/sally mkdir -p /home/ldap/joe chown 1900:1900 /home/ldap/joe dnf install nfs-utils echo '/home/ldap 10.0.69.0/24(rw,no_root_squash)' >> /etc/exports firewall-cmd --permanent --add-service nfs firewall-cmd --permanent --add-service mountd firewall-cmd --permanent --add-service rpc-bind firewall-cmd --reload systemctl start nfs-server - server2
dnf install nfs-utils autofs showmount -e server1 # add '/home/ldap /etc/auto.ldap' to /etc/auto.master # add '* server1:/home/ldap/&' to /etc/auto.ldap systemctl enable --now autofs users=(boss bob sally joe) { for u in ${users[@]}; do cp -p /home/$u/.?* /home/ldap/$u usermod -d /home/ldap/$u chown -R $u:$u /home/ldap/$u rm -rf /home/$u done }
Scheduling Jobs
server2:
- Schedule a systemd timer job that writes “Good morning!” to syslog every Monday through Friday at 8AM. Make sure this job is executed as the user root with a 1 second accuracy.
- Schedule a systemd timer job that writes “Happy Monday!” to syslog every Monday at 8:01AM. Make sure this job is executed as the user boss with a 1 second accuracy. Ensure that this job will run even if the boss is out of the office.
- As the user bob:
- Schedule a systemd timer job that creates the file /data/engineering/EOW-REMINDER with the contents “Have a great weekend, engineers! Don’t forget to submit your time sheets!” every Friday at 3:45PM. Make sure this job is executed as the user bob.
- Schedule a systemd timer job that removes the /data/engineering/EOW-REMINDER file at 7:00PM every Friday.
- Schedule a cron job as user joe that creates a file every Friday at 3:30PM called $HOME/happy-friday containing the contents “submit your time sheet!”
Solution
Helpful start for any systemd timer
man systemd.time # view examples of time specification
systemctl list-timers # to get an example
systemctl cat logrotate.timer > ~/.config/systemd/user/mytimer.timer # start from example
-
/etc/systemd/system/goodmorning.service
[Unit] Description="Write Good morning! to syslog" [Service] Type=oneshot ExecStart=/usr/bin/logger "Good morning!"/etc/systemd/system/goodmorning.timer
[Unit] Description="Timer for goodmorning.service" [Timer] OnCalendar=Mon..Fri *-*-* 08:00:00 AccuracySec=1 Unit=goodmorning.service [Install] WantedBy=default.targetsystemctl enable --now goodmorning.timer -
—
loginctl enable-linger boss ssh boss@localhost # user units must be started by an actual login shell mkdir -p ~/.config/systemd/user~/.config/systemd/user/happymonday.service
[Unit] Description="Write Happy Monday! to syslog" [Service] Type=oneshot ExecStart=/usr/bin/logger "Happy Monday!"~/.config/systemd/user/happymonday.timer
[Unit] Description="Timer for happymonday.service" [Timer] OnCalendar=Mon *-*-* 08:01:00 AccuracySec=1 Unit=happymonday.service [Install] WantedBy=default.targetsystemctl --user enable --now happymonday.timer -
—
loginctl enable-linger bob ssh bob@localhost # user units must be started by an actual login shell mkdir -p ~/.config/systemd/user~/.config/systemd/user/eowreminder_send.service
[Unit] Description="eow reminder" [Service] Type=oneshot ExecStart=/bin/bash -c 'echo "Have a great weekend, engineers! Don'\''t forget to submit your time sheets!" > /data/engineering/EOW-REMINDER'~/.config/systemd/user/eowreminder_send.timer
[Unit] Description="Timer for eowreminder-send.service" [Timer] OnCalendar=Fri *-*-* 15:45:00 AccuracySec=1 [Install] WantedBy=default.target~/.config/systemd/user/eowreminder_clean.service
[Unit] Description="eow reminder" [Service] Type=oneshot ExecStart=/bin/rm /data/engineering/EOW-REMINDER~/.config/systemd/user/eowreminder_clean.timer
[Unit] Description="Timer for eowreminder-clean.service" [Timer] OnCalendar=Fri *-*-* 19:00:00 AccuracySec=1 [Install] WantedBy=default.targetsystemctl --user enable --now eowreminder_send.timer systemctl --user enable --now eowreminder_clean.timer
Managing Flatpak
server2:
- Add the rhel flatpak repository
https://flatpaks.redhat.io/rhel.flatpakrepo. Ensure that this repository is accessible to everyone on the system. - Add the Flatpak repository
https://dl.flathub.org/repo/flathub.flatpakrepoin such a way that it is accessible to user bob only. - As user bob, install the Scilab software package from the flathub repository
in such a way that it is accessible to bob only
- Hint: A symbolic link to a local directory will probably be needed. For example, /opt/flatpak/bob -> /home/ldap/bob/.local/share/flatpak
Solution
# Add system-wide repository
flatpak remote-add --system rhel https://flatpaks.redhat.io/rhel.flatpakrepo
# Add user-specific repository for bob
su -l bob
flatpak -u remote-add flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak -u install flathub org.scilab.Scilab
Setting Time
- Configure server1 and server2 as an NTP client for pool.ntp.org
Solution
- Replace current pool with
pool pool.ntp.org iburstin /etc/chrony.conf - Restart chronyd
Managing SELinux
On server2:
- Create the file /web/index.html with the contents “hello from server2”
- Configure an Apache web server to listen on port 82 and serve content from /web.
- Ensure that you can access the content both locally and from server1
Solution
# change port to 82 in /etc/httpd/conf/httpd.conf
# change document root to /web
semanage port list | grep http
semanage port -a -t http_port_t -p tcp 82
semanage fcontext -a -t http_sys_content_t '/web(/.*)?'
firewall-cmd --permanent --service http --get-ports
firewall-cmd --permanent --service http --add-port 82/tcp
firewall-cmd --reload
firewall-cmd --permanent --service http --get-ports
systemctl restart httpd; systemctl status httpd
curl localhost:82
Finding Files
On server1:
- Find all files in the /usr directory that have either the SUID or the SGID
permission set and are smaller than 100KiB
- Write each of these filenames (not the path) on separate lines to /root/sugidfiles.txt
- Find all files in the /usr directory that are executable by root:root only
and not readable by any other user.
- Copy these files to the directory /root/rootexec.
Solution
- —
find /usr -type f \( -perm -2000 -o -perm -4000 \) -size -100k -printf '%f\n' > /root/sugidfiles.txt - —
mkdir /root/rootexec find /usr -type f -user root -group root -perm -0110 \! -perm -0001 \! -perm -0004 -exec cp {} /root/rootexec \;
Finding Patterns in Files
On server1:
- Find all lines that start with a ’#’ in the /etc directory and its subdirectories and following all symbolic links. Write the names of the files and matching lines to /root/comments.txt. Redirect standard error to /root/comments.err.
- Find all lines in /etc and its subdirectories that contain the words ‘Key’ or ‘keys’. Do not search any files with numerical, .bin, or .so extensions. Write the filename, line numbers, and matching lines to the file /root/keyfiles.txt. Redirect standard error to /root/keyfiles.err
Solution
-
—
grep -R '^#' /etc > /root/comments.txt 2> /root/comments.err -
—
find /etc -type f \! \( -regex '.*\.[0-9]+$' -o -name '*.bin' -o -name '*.so' \) -exec grep -wnHE 'Key|keys' {} \; > /root/keyfiles.txt 2> /root/keyfiles.err
Grading the Exam
Grading scripts can be found here. Use
whatever method you prefer to transfer the grading-scripts directory to
server1 and run the evaluate script as root. My method for creating those
grading scripts improved as I went along, but I didn’t have the time to go back
and make all of them completely consistent. However, if you’ve done everything
correctly, all the checks should pass. Feel free to submit a pull request if you
have any improvement suggestions.
FYI: That repo also contains a qemu wrapper script called vm.sh. I encourage
you to write your own, but it might be worth checking out if you’ve got a lot of
studying left.
- Clint Jordan