TT Lab
Get started
Learn Learning paths Courses

LFCS — Linux Foundation System Administrator

SELinux and firewalld by Hand — and Users, ACL, sshd, dnf

Continue in TT Lab

Goal

On an AlmaLinux 9 VM, you actually apply SELinux booleans, file contexts, and port contexts, work by hand with firewalld's runtime/permanent, zones, and rich rules, and configure users, sudoers, profile.d, limits, ACLs, an sshd drop-in, and dnf locking so that the results remain.

Why it matters

The Operations domain of the LFCS explicitly says "manage access with SELinux", and the Networking domain requires packet filtering and OpenSSH. A Pod has neither SELinux nor firewalld, so these items only brushed past in the practice exams. This VM is Enforcing and firewalld is alive, so if you leave out the port context, httpd really does not start, and if you leave out permanent, the rule disappears the moment you reload.

Three things recur. First, the value now and the permanent value live in different places — -P of setsebool, chcon and semanage fcontext, and firewalld's runtime and permanent. Second, the effective value is told to you by a tool, not by a file — sshd -T, getsebool, sudo -l -U, getfacl. Third, check first so you do not lock yourself out — visudo -cf, sshd -t, apachectl configtest.

⚠️ The grading agent comes in on 8899/tcp. When you touch firewalld, if you remove this port or change the default zone to drop, grading cannot reach the VM and from the next step you get failures of unknown cause. A session starts at 60 minutes and can be extended up to 180 minutes, and the VM disappears when the session ends.

Steps

  1. Save the sestatus output to /root/lfcs/selinux/status.txt (it must be Enforcing). Then turn on the boolean httpd_can_network_connect so that it persists after a reboot.
  2. Write selinux ok in /srv/web/index.html, make everything under /srv/web have the httpd_sys_content_t context permanently, and then apply it (semanage fcontext + restorecon). Register tcp 8081 as http_port_t, make httpd serve /srv/web on 8081 with /etc/httpd/conf.d/lfcs.conf, and enable and start it. curl http://127.0.0.1:8081/ must return selinux ok.
  3. In the default zone (public), open the service http and the port 8081/tcp in both runtime and permanent. The grading port 8899/tcp must remain as it is. Finally, save the output of firewall-cmd --list-all to /root/lfcs/firewall/public.txt.
  4. Create a new zone lab and assign the source 198.51.100.0/24 to that zone. In the lab zone, put the service ssh and a rich rule that allows tcp 8081 coming from 198.51.100.0/24 (rule family="ipv4" source address="198.51.100.0/24" port port="8081" protocol="tcp" accept). Write everything as permanent and reload so that it shows in runtime too.
  5. Create a group ops and a user deploy (with a home directory, shell /bin/bash), and put it in ops as a supplementary group. In /etc/sudoers.d/ops, write a rule that lets the ops group run only /usr/bin/systemctl with sudo without a password, and set the permissions to 0440. The syntax must pass visudo -cf.
  6. In /etc/profile.d/lfcs.sh, export the environment variable LFCS_ENV as lab. In /etc/security/limits.d/90-lfcs.conf, write a hard limit of 4096 on the number of open files (nofile) for the user deploy, and a soft limit of 2048 on the number of processes (nproc) for the group ops. When you open a login shell with su - deploy, the variable must be visible in env and ulimit -Hn must be 4096.
  7. Create the /srv/web/upload directory owned by root with permissions 750 (do not widen it with chmod), give the user deploy rwx with an ACL, and give a default ACL so that files newly created inside this directory inherit r-x for the group ops. Save the output of getfacl /srv/web/upload to /root/lfcs/acl/upload.acl. (After you give the ACL, the group position of ls -ld shows the mask (rwx) — that is normal.)
  8. In the /etc/ssh/sshd_config.d/50-lfcs.conf drop-in, write MaxAuthTries 3, ClientAliveInterval 300, and PermitRootLogin no, and restart sshd. The final values seen with sshd -T must be those values for all three.
  9. Install the tree package and lock it with versionlock or put it in exclude= of /etc/dnf/dnf.conf so that it is excluded from upgrades. Save the output of dnf history list to /root/lfcs/pkg/history.txt and the output of rpm -q tree to /root/lfcs/pkg/tree.txt.

Notes

Checking the SELinux mode, and booleans

Save the sestatus output to /root/lfcs/selinux/status.txt (it must be Enforcing). Then turn on the boolean httpd_can_network_connect so that it persists after a reboot.

If you add -P to setsebool, it is written to the policy store. To check, use getsebool and semanage boolean -l -C (show only local changes).

Putting httpd on 8081 with file and port contexts

Write selinux ok in /srv/web/index.html, make everything under /srv/web have the httpd_sys_content_t context permanently, and then apply it (semanage fcontext + restorecon). Register tcp 8081 as http_port_t, make httpd serve /srv/web on 8081 with /etc/httpd/conf.d/lfcs.conf, and enable and start it. curl http://127.0.0.1:8081/ must return selinux ok.

chcon disappears when you restorecon — semanage fcontext -a -t '/srv/web(/.*)?' is the permanent one. If you do not register the port, httpd cannot bind 8081 and the start itself fails (check with journalctl -u httpd and ausearch -m avc).

firewalld services and ports — runtime and permanent

In the default zone (public), open the service http and the port 8081/tcp in both runtime and permanent. The grading port 8899/tcp must remain as it is. Finally, save the output of firewall-cmd --list-all to /root/lfcs/firewall/public.txt.

With --permanent it applies only to the files, and without it only to the rules running now. Do both, or write with --permanent and --reload. To check, --list-services / --permanent --list-services.

A new zone and a rich rule

Create a new zone lab and assign the source 198.51.100.0/24 to that zone. In the lab zone, put the service ssh and a rich rule that allows tcp 8081 coming from 198.51.100.0/24 (rule family="ipv4" source address="198.51.100.0/24" port port="8081" protocol="tcp" accept). Write everything as permanent and reload so that it shows in runtime too.

A new zone can be created only with --permanent --new-zone=lab and appears in runtime after a reload. --zone=lab --add-source=..., --add-service=..., --add-rich-rule='...'. To check, --zone=lab --list-all.

Group, user, and sudoers rule

Create a group ops and a user deploy (with a home directory, shell /bin/bash), and put it in ops as a supplementary group. In /etc/sudoers.d/ops, write a rule that lets the ops group run only /usr/bin/systemctl with sudo without a password, and set the permissions to 0440. The syntax must pass visudo -cf.

useradd -m -s /bin/bash -G ops deploy. The sudoers line: %ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl. If the syntax is wrong, all of sudo is locked, so always check with visudo -cf. See the result with sudo -l -U deploy.

Login environment and resource limits

In /etc/profile.d/lfcs.sh, export the environment variable LFCS_ENV as lab. In /etc/security/limits.d/90-lfcs.conf, write a hard limit of 4096 on the number of open files (nofile) for the user deploy, and a soft limit of 2048 on the number of processes (nproc) for the group ops. When you open a login shell with su - deploy, the variable must be visible in env and ulimit -Hn must be 4096.

profile.d is read by the login shell as it passes through /etc/profile. Without export, it is not visible to child processes (env). A limits line has four fields, 'target type item value', and a group is @ops. pam_limits applies it at login.

Granting permission beyond ownership with an ACL

Create the /srv/web/upload directory owned by root with permissions 750 (do not widen it with chmod), give the user deploy rwx with an ACL, and give a default ACL so that files newly created inside this directory inherit r-x for the group ops. Save the output of getfacl /srv/web/upload to /root/lfcs/acl/upload.acl. (After you give the ACL, the group position of ls -ld shows the mask (rwx) — that is normal.)

setfacl -m u:deploy:rwx , and the default ACL is setfacl -d -m g:ops:rx (or -m d:g:ops:rx). If + is attached in ls -l, an ACL is present. The mask line trims the effective permissions.

The sshd drop-in and sshd -T

In the /etc/ssh/sshd_config.d/50-lfcs.conf drop-in, write MaxAuthTries 3, ClientAliveInterval 300, and PermitRootLogin no, and restart sshd. The final values seen with sshd -T must be those values for all three.

The Include in sshd_config is at the very top, so the drop-in is read before the main body, and sshd uses the first value of the same key. Syntax with sshd -t, effective values with sshd -T. Console access has nothing to do with sshd, so there is no worry about locking yourself out.

dnf install, lock, and history

Install the tree package and lock it with versionlock or put it in exclude= of /etc/dnf/dnf.conf so that it is excluded from upgrades. Save the output of dnf history list to /root/lfcs/pkg/history.txt and the output of rpm -q tree to /root/lfcs/pkg/tree.txt.

The versionlock plugin is already installed: dnf versionlock add tree / dnf versionlock list. exclude is exclude=tree in the [main] section of dnf.conf. history is per transaction, so the install shows as one line.