EX342 Practice ExamLesson 4 of 4
Part C: Network, security and logging (100 points)
Seven tasks: a network path with four faults, a web service, central logging, AIDE, account policy, five users who cannot log in, and a PAM repair.
ip route getgetentcurlnmclifirewall-cmdsemanagersyslogdloggeraidefaillockpwscorechageauthselectjournalctl -u sshd
After this lesson you can
- Repair the network, logging, integrity and authentication faults of the exam within about 72 minutes, each with its measurable checks.
- Finish with the reboot of both nodes and the final checks that decide the score.
Setup for Part C
Run as root on node1 before the Part B and Part A setups (the order is C, then B, then A, because Part A ends by rebooting node1 into emergency mode). It uses ssh root@node2 while the root password of node2 is still known. As with the other parts, it is here so that you can rebuild the exam, not as a source of hints.
# --- common: rsyslog on both nodes, httpd on node1
dnf install -y rsyslog httpd
systemctl enable --now rsyslog
ssh root@node2.lab.example 'dnf install -y rsyslog && systemctl enable --now rsyslog'
# --- C1: backend address and web page on node2, and four faults
ssh root@node2.lab.example bash -s <<'EOT'
IF=$(ip -o -4 route show to 192.168.50.0/24 | awk '{print $3}' | head -1)
CON=$(nmcli -g GENERAL.CONNECTION device show "$IF")
nmcli connection modify "$CON" +ipv4.addresses 192.168.50.22/24
nmcli device reapply "$IF"
mkdir -p /srv/backend && echo "backend ok" > /srv/backend/index.html
cat > /etc/systemd/system/backend-web.service <<'EOS'
[Unit]
Description=Backend web
After=network-online.target
[Service]
WorkingDirectory=/srv/backend
ExecStart=/usr/bin/python3 -m http.server 8080 --bind 192.168.50.22
[Install]
WantedBy=multi-user.target
EOS
systemctl daemon-reload && systemctl enable --now backend-web
echo "192.168.50.111 node1.lab.example node1" >> /etc/hosts
EOT
echo "192.168.50.23 backend.lab.example backend" >> /etc/hosts
IF=$(ip -o -4 route show to 192.168.50.0/24 | awk '{print $3}' | head -1)
CON=$(nmcli -g GENERAL.CONNECTION device show "$IF")
nmcli connection modify "$CON" +ipv4.routes "192.168.50.22/32 192.168.50.99"
nmcli device reapply "$IF"
# --- C2: Apache on a port that SELinux, the firewall and the listen address all refuse
echo "lab web ok" > /var/www/html/index.html
sed -i 's/^Listen 80$/Listen 127.0.0.1:8500/' /etc/httpd/conf/httpd.conf
systemctl enable httpd
# --- C4: the directory to watch
mkdir -p /opt/vault/conf /opt/vault/cache
echo "setting=1" > /opt/vault/conf/app.conf
echo "k3y-material" > /opt/vault/conf/secret.key; chmod 600 /opt/vault/conf/secret.key
echo "cache-1" > /opt/vault/cache/c1.tmp
# --- C6: five users with five different reasons
for u in dana eli fay gus hana; do useradd -m "$u"; echo "$u:Ex342!Lab" | chpasswd; done
usermod -L dana
chage -E 2020-01-01 eli
usermod -s /sbin/nologin fay
echo "DenyUsers gus" > /etc/ssh/sshd_config.d/05-restrict.conf
chage -d 2024-01-01 -M 90 -I 0 hana
systemctl reload sshd
# --- C5/C7: faillock enabled in the stack, then a hand edit that authselect does not own
authselect current >/dev/null 2>&1 || authselect select local --force
authselect enable-feature with-faillock
authselect apply-changes
sed -i '/pam_pwquality.so/ s/^/#/' /etc/pam.d/system-auth
Final reboot and checks
When every task is done and C7 is the last change you made:
- Reboot
node2, thennode1, and wait until both answer ssh. - On
node1, run the check lines of A2, A3 (kdumpctl status), B5, B6, C2 and C4 again: services running, ports listening, timers enabled. - On
node2, confirmss -tln | grep ':514 'and the log file of C3 receives a newloggermessage fromnode1. - Check that
getenforceisEnforcingandfirewall-cmd --stateisrunningon both machines.
A task whose check fails after the reboot earns no points: that is the habit the exam rewards, so make the reboot part of your plan, not an afterthought.
Part C marking
| Task | Points | Earned |
|---|---|---|
| C1 Network path | 18 | |
| C2 Web service | 14 | |
| C3 Central logging | 18 | |
| C4 AIDE | 12 | |
| C5 Account policy | 14 | |
| C6 Five users | 14 | |
| C7 PAM repair | 10 | |
| Part C | 100 |
Practice
0 of 7 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
C1 · A path that does not work (18 points, 12 min)
The application team says: "From node1,
http://backend.lab.example:8080/does not answer, and node2 cannot use the namenode1.lab.example." The backend runs on node2 with the extra address 192.168.50.22. Find every fault between the two machines (there are four, on both nodes) and fix them so that:- On node1,
backend.lab.exampleresolves to 192.168.50.22 andcurl http://backend.lab.example:8080/prints the page. - On node2,
node1.lab.exampleresolves to 192.168.50.11. - All fixes survive a reboot, with the firewall still running on both nodes and SELinux still enforcing.
Work from the evidence in this order: name, route, then the firewall at the far end.
Check your result
- node1:
getent hosts backend.lab.exampleprints192.168.50.22;ip route get 192.168.50.22shows noviaand the interface directly. - node1:
curl -s --max-time 5 http://backend.lab.example:8080/printsbackend ok. - node1:
nmcli -g ipv4.routes connection show "$(nmcli -g GENERAL.CONNECTION device show $(ip -o route get 192.168.50.22 | awk '{print $3}'))"does not contain192.168.50.22. - node2:
getent hosts node1.lab.exampleprints192.168.50.11. - node2:
sudo firewall-cmd --permanent --list-ports(or--list-services) permits8080/tcp;sudo firewall-cmd --stateisrunning.
Hint
A name that resolves to the wrong address is not a DNS problem:
getent hostsshows what the system uses, and/etc/hostsis read first.ip route get ADDRESSshows the route that would be used; aviaaddress nobody answers for makesDestination Host Unreachable. When ping works butcurltimes out, test the port from node2 itself (curlto its own address works), then look at its firewall.Solution
# node1: name and route getent hosts backend.lab.example sudo sed -i 's/^192.168.50.23 /192.168.50.22 /' /etc/hosts ip route get 192.168.50.22 IF=$(ip -o route get 192.168.50.22 | awk '{print $3}') CON=$(nmcli -g GENERAL.CONNECTION device show "$IF") sudo nmcli connection modify "$CON" -ipv4.routes "192.168.50.22/32 192.168.50.99" sudo nmcli device reapply "$IF" curl -s --max-time 5 http://backend.lab.example:8080/ ; echo "rc=$?" # node2: firewall for 8080 and the wrong name for node1 ssh node2.lab.example sudo sed -i 's/^192.168.50.111 /192.168.50.11 /' /etc/hosts sudo firewall-cmd --permanent --add-port=8080/tcp && sudo firewall-cmd --reload exit curl -s http://backend.lab.example:8080/ - On node1,
- Practice
C2 · A web service nobody can reach (14 points, 10 min)
The Apache web server on node1 must serve the page
/var/www/html/index.htmlon TCP port 8500, for the whole lab network, and start at boot. It currently does not run. Do not disable SELinux or the firewall and do not use permissive mode.Prove it from node2:
curl http://node1.lab.example:8500/prints the page.Check your result
systemctl is-enabled httpdisenabled;systemctl is-active httpdisactive.sudo semanage port -l | grep '^http_port_t'lists8500.sudo ss -tlnp | grep ':8500'shows0.0.0.0:8500or*:8500(not127.0.0.1).sudo firewall-cmd --permanent --list-portscontains8500/tcp(or an equivalent permanent rule);getenforceisEnforcing.- From node2:
curl -s http://node1.lab.example:8500/printslab web ok. sudo ausearch -m avc -ts recentshows no new denial forhttpdafter your last change.
Hint
journalctl -u httpd -bnames the error.Permission denied ... could not bindwith SELinux enforcing is a port label problem (ausearch -m avc). Once it starts,ss -tlnpshows the address it listens on. Last, check the firewall, because a service that answers locally and not from outside is the firewall or the listen address.Solution
sudo journalctl -u httpd -b --no-pager | tail -5 sudo ausearch -m avc -ts recent | tail -3 sudo semanage port -a -t http_port_t -p tcp 8500 sudo grep -n '^Listen' /etc/httpd/conf/httpd.conf sudo sed -i 's/^Listen 127.0.0.1:8500/Listen 8500/' /etc/httpd/conf/httpd.conf sudo systemctl enable --now httpd sudo firewall-cmd --permanent --add-port=8500/tcp && sudo firewall-cmd --reload ss -tln | grep 8500 ssh node2.lab.example curl -s http://node1.lab.example:8500/ - Challenge
C3 · Central logging (18 points, 14 min)
Make node2 the log server and node1 a client, with
rsyslog:- node2 receives syslog over TCP 514. Messages from remote hosts are written to
/var/log/remote/<sending host>/<program>.logand are not mixed into node2's own local files. - The firewall on node2 accepts port 514/tcp only from node1 (192.168.50.11); the port must not be open to the whole zone. SELinux stays enforcing.
- node1 forwards everything of facility
local5and everything of facilityauthprivto node2 over TCP. - Messages are not lost when node2's rsyslog is stopped for a minute: they are queued on node1 and arrive after the restart.
- Everything starts at boot on both nodes.
You may work on node2 as
root(the password you set in A5) or asalexwith sudo.Check your result
- node2:
sudo ss -tln | grep ':514 'shows a listener;sudo rsyslogd -N1ends without errors on both nodes. - node2:
sudo firewall-cmd --permanent --list-rich-rulescontains a rule for source192.168.50.11and port 514 tcp;sudo firewall-cmd --permanent --list-portsdoes not list514/tcp. - node1:
logger -t c3test -p local5.notice "ex342-c3-one"; on node2sudo grep -r ex342-c3-one /var/log/remote/finds it in/var/log/remote/<node1 name>/c3test.log. - node1:
logger -t c3auth -p authpriv.info "ex342-c3-auth"arrives the same way. - Resilience:
sudo systemctl stop rsyslogon node2; on node1logger -t c3test -p local5.notice "ex342-c3-queued"; start rsyslog on node2; within about a minute the message is in the file. systemctl is-enabled rsyslogisenabledon both nodes;getenforceisEnforcingon both.
Hint
Server: load
imtcp, a template with%HOSTNAME%and%PROGRAMNAME%, and a rule that tests$fromhost-ipso local messages are left alone. Client: anomfwdaction withprotocol="tcp",action.resumeRetryCount="-1"and a queue with aqueue.filename. A firewall rich rule limits the source. 514 is alreadysyslogd_port_tin the SELinux policy.Solution
# node2 (server) sudo tee /etc/rsyslog.d/10-remote.conf <<'EOT' module(load="imtcp") input(type="imtcp" port="514") template(name="PerHost" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log") if $fromhost-ip != '127.0.0.1' then { action(type="omfile" dynaFile="PerHost") stop } EOT sudo rsyslogd -N1 && sudo systemctl enable --now rsyslog && sudo systemctl restart rsyslog sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.50.11" port port="514" protocol="tcp" accept' sudo firewall-cmd --reload # node1 (client) sudo tee /etc/rsyslog.d/20-forward.conf <<'EOT' if ($syslogfacility-text == 'local5' or $syslogfacility-text == 'authpriv') then { action(type="omfwd" target="node2.lab.example" port="514" protocol="tcp" action.resumeRetryCount="-1" queue.type="LinkedList" queue.size="10000" queue.filename="fwd2" queue.saveOnShutdown="on") } EOT sudo rsyslogd -N1 && sudo systemctl enable --now rsyslog && sudo systemctl restart rsyslog logger -t c3test -p local5.notice "ex342-c3-one" ssh node2.lab.example 'sudo grep -r ex342-c3-one /var/log/remote/' - node2 receives syslog over TCP 514. Messages from remote hosts are written to
- Practice
C4 · File integrity with AIDE (12 points, 9 min)
The directory
/opt/vaulton node1 holds configuration and keys. Installaideand watch only that directory with a separate configuration (/etc/aide-vault.conf) and a separate database directory (/var/lib/aide-vault), so the default system watch is untouched and the check does not report the rest of the exam's changes.- The rules must detect a changed content (hash), changed permissions, changed owner/group and new or removed files under
/opt/vault, but must ignore everything below/opt/vault/cache. - Create the baseline and activate it.
- A systemd timer
aide-vault.timerruns the check daily throughaide-vault.service; the service must succeed when nothing changed and fail when something did.
Check your result
rpm -q aide;sudo ls -l /var/lib/aide-vault/aide.db.gzexists.sudo aide -c /etc/aide-vault.conf --check; echo $?prints0on the untouched directory.- After
echo x | sudo tee -a /opt/vault/conf/app.conf, the check exits non-zero and lists/opt/vault/conf/app.conf. Aftersudo chmod 644 /opt/vault/conf/secret.keyit lists that file with the permission change. Aftersudo touch /opt/vault/new.fileit lists an added entry. (Revert each change.) - After
sudo touch /opt/vault/cache/anything, the check still exits0. systemctl is-enabled aide-vault.timerisenabled;systemctl list-timers aide-vault.timer --no-pagershows a next run;sudo systemctl start aide-vault.servicesucceeds on a clean directory.
Hint
A rule group is
NAME = p+u+g+s+m+sha256; a path line is/opt/vault NAME; an exclusion is!/opt/vault/cache. Usedatabase_in,database_outandreport_urlin the custom file.aide --initwrites the new database; copy it over the name thatdatabase_inreads. The service isType=oneshotand runsaide -c /etc/aide-vault.conf --check.Solution
sudo dnf install -y aide sudo mkdir -p /var/lib/aide-vault sudo tee /etc/aide-vault.conf <<'EOT' database_in=file:/var/lib/aide-vault/aide.db.gz database_out=file:/var/lib/aide-vault/aide.db.new.gz gzip_dbout=yes report_url=stdout VAULT = p+u+g+s+m+sha256 /opt/vault VAULT !/opt/vault/cache EOT sudo aide -c /etc/aide-vault.conf --init sudo cp -p /var/lib/aide-vault/aide.db.new.gz /var/lib/aide-vault/aide.db.gz sudo aide -c /etc/aide-vault.conf --check; echo "rc=$?" sudo tee /etc/systemd/system/aide-vault.service <<'EOT' [Unit] Description=AIDE check of /opt/vault [Service] Type=oneshot ExecStart=/usr/sbin/aide -c /etc/aide-vault.conf --check EOT sudo tee /etc/systemd/system/aide-vault.timer <<'EOT' [Unit] Description=Daily AIDE check of /opt/vault [Timer] OnCalendar=daily Persistent=true [Install] WantedBy=timers.target EOT sudo systemctl daemon-reload && sudo systemctl enable --now aide-vault.timer - The rules must detect a changed content (hash), changed permissions, changed owner/group and new or removed files under
- Practice
C5 · Account policy (14 points, 9 min)
On node1, set a local account policy that survives reboots and applies without editing files by hand that
authselectowns:- Lockout: lock an account after 4 failed logins within 10 minutes; the lock lasts 5 minutes; failures are written to the audit log;
rootis not locked by this policy. - Password quality: at least 14 characters, at least 3 character classes, no more than 2 identical characters in a row, and the dictionary check on.
- Aging defaults for accounts created later: maximum age 60 days, minimum 2, warning 10. Apply the same three values to the existing user
alex.
Check your result
grep -E '^(deny|fail_interval|unlock_time|audit|even_deny_root)' /etc/security/faillock.confshowsdeny = 4,fail_interval = 600,unlock_time = 300,audit, and noeven_deny_root.grep -E '^(minlen|minclass|maxrepeat|dictcheck)' /etc/security/pwquality.confshowsminlen = 14,minclass = 3,maxrepeat = 2,dictcheck = 1;echo 'Short1!' | pwscorefails andecho 'Tr1cky-Lab-Pass-2026' | pwscoreprints a number.grep -E '^PASS_(MAX|MIN|WARN)' /etc/login.defsshows60,2,10; a newsudo useradd c5test; sudo chage -l c5testshows Maximum 60, Minimum 2, warning 10.sudo chage -l alexshows the same values. (Deletec5testafterwards.)- Lockout works: four wrong passwords for
c5test(su - c5testfrom another user, or ssh) lock the account;sudo faillock --user c5testlists the failures;sudo faillock --user c5test --resetunlocks it.
Hint
faillockis already enabled in the PAM stack (see C7); you set its numbers in/etc/security/faillock.conf. Flag options such asauditstand alone on a line.login.defsonly affects accounts that are created later, soalexneedschage. Quality rules apply when a password is changed through PAM.Solution
sudo sed -i -E 's/^#?\s*deny\s*=.*/deny = 4/; s/^#?\s*fail_interval\s*=.*/fail_interval = 600/; s/^#?\s*unlock_time\s*=.*/unlock_time = 300/; s/^#?\s*audit\s*$/audit/' /etc/security/faillock.conf sudo sed -i -E 's/^\s*even_deny_root/# even_deny_root/' /etc/security/faillock.conf grep -vE '^\s*(#|$)' /etc/security/faillock.conf sudo sed -i -E 's/^#?\s*minlen\s*=.*/minlen = 14/; s/^#?\s*minclass\s*=.*/minclass = 3/; s/^#?\s*maxrepeat\s*=.*/maxrepeat = 2/; s/^#?\s*dictcheck\s*=.*/dictcheck = 1/' /etc/security/pwquality.conf echo 'Short1!' | pwscore sudo sed -i -E 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 60/; s/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 2/; s/^PASS_WARN_AGE.*/PASS_WARN_AGE 10/' /etc/login.defs sudo chage -M 60 -m 2 -W 10 alex sudo useradd c5test && sudo chage -l c5test | sed -n 5,7p; sudo userdel -r c5test - Lockout: lock an account after 4 failed logins within 10 minutes; the lock lasts 5 minutes; failures are written to the audit log;
- Challenge
C6 · Five users who cannot log in (14 points, 10 min)
The users
dana,eli,fay,gusandhanacannot log in to node1 over ssh with their password (Ex342!Lab). Each has a different reason. Find each from the evidence (logs,passwd -S,chage -l,getent passwd,sshd -T) and fix it with the smallest change.Rules: do not change any of the five passwords, do not turn off password authentication, and do not remove
PasswordAuthenticationor other settings that apply to everyone.Check your result
- From node2:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no USER@192.168.50.11 idprints the user'suid=for each of the five, typingEx342!Lab. sudo passwd -S danasecond field isP;sudo chage -l elishowsAccount expires : never;getent passwd fayends in a real shell (/bin/bash).sudo sshd -T | grep -i denyusersdoes not listgus; the otherDenyUsers/AllowUserssettings of the lab are not changed.sudo chage -l hanashows no pastPassword inactivedate and a password expiry in the future.- If you ran into the lock-out of C5 while testing, the failure records are cleared:
sudo faillocklists nothing.
Hint
sudo grep sshd /var/log/secure | tail -30andjournalctl -u sshd -bgive a different sentence for each user.account is locked,Your account has expired,This account is currently not available,not allowed because listed in DenyUsers,Your account has expired; please contact your system administrator(inactive). Drop-ins in/etc/ssh/sshd_config.d/are read in file-name order. Testing wrong passwords can trigger the faillock of C5:faillock --user NAME --reset.Solution
sudo grep sshd /var/log/secure | tail -30 sudo passwd -S dana eli fay gus hana sudo chage -l eli | head -5; sudo chage -l hana | head -6 getent passwd fay sudo sshd -T | grep -i denyusers; ls /etc/ssh/sshd_config.d/ sudo usermod -U dana # locked account sudo chage -E -1 eli # account expired sudo usermod -s /bin/bash fay # nologin shell sudo rm /etc/ssh/sshd_config.d/05-restrict.conf && sudo sshd -t && sudo systemctl reload sshd # DenyUsers gus sudo chage -I -1 -d "$(date +%F)" hana # inactive days exceeded sudo faillock --reset - From node2:
- Practice
C7 · Repair the PAM configuration (10 points, 8 min)
A contractor hand-edited the PAM configuration on node1, and a security scan says that
authselectno longer manages it. Return to the supported configuration ofauthselect, keeping the profile that is in use and every feature that is enabled (the lockout of C5 depends on one of them). Afterwards, ordinary users must again be refused weak passwords when they change their own password.Do this task last, after all other tasks, and then reboot both nodes (see below).
Check your result
sudo authselect checkends withCurrent configuration is valid.and exit status 0.ls -l /etc/pam.d/system-authis a symbolic link into/etc/authselect/.authselect currentlists the same profile as before and still listswith-faillock;grep -c pam_faillock /etc/pam.d/system-authis at least2.grep -v '^#' /etc/pam.d/system-auth | grep -c pam_pwqualityis at least1.- As a normal user (create
c7testwith passwordEx342!Lab),passwdwith the new passwordShort1!is refused with aBAD PASSWORDmessage about the length.
Hint
authselect checktells you which file is not a link or differs. Write downauthselect currentfirst:authselect select PROFILE FEATURES --forcereturns to the supported files but replaces the feature list with what you type.Solution
sudo authselect check sudo authselect current # note the profile and the features sudo authselect select local with-faillock --force # use YOUR profile and ALL features shown above sudo authselect check ls -l /etc/pam.d/system-auth grep -c pam_faillock /etc/pam.d/system-auth sudo useradd c7test && echo 'c7test:Ex342!Lab' | sudo chpasswd sudo -u c7test passwd # current: Ex342!Lab ; new: Short1! -> BAD PASSWORD sudo userdel -r c7test