Server Security
AlmaLinux and Rocky Linux Security: SSH, firewalld, and SELinux
1. Start with updates and an access check
cat /etc/os-release
sudo dnf upgrade --refresh
sudo ss -lntup
getenforce
Use a maintenance window for updates that require service or kernel restarts. Verify console access before editing authentication. Create a named administrator, add it to the wheel group, install its public SSH key, and test both login and sudo before disabling direct root login.
2. Use a deliberate SSH policy
For a key-only deployment that does not use keyboard-interactive MFA, review these values in the effective SSH configuration:
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
Check included snippets and any Match blocks. Keep system cryptographic policy in place instead of pasting an arbitrary list of ciphers from an old tutorial. Validate before reloading:
sudo /usr/sbin/sshd -t
sudo /usr/sbin/sshd -T | grep -E "^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin) "
After successful validation, reload sshd.service and open a second connection. If the new connection fails, use the still-open session or console to restore the changed configuration.
3. Confirm the active firewalld zone
Do not assume the default zone is the one attached to your public interface. Inspect the active zone before adding rules. These commands assume firewalld is already running:
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --list-all
Replace public below with the zone actually used by the interface. Add your existing SSH port before restrictive changes; these examples use 22. Add web services only for a web workload.
sudo firewall-cmd --zone=public --add-port=22/tcp
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https
Test the runtime rules from another machine. Then save those specific rules in the permanent configuration:
sudo firewall-cmd --permanent --zone=public --add-port=22/tcp
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
If firewalld is not running, prepare the corresponding permanent rules with firewall-offline-cmd from a verified console before enabling it. Do not start an unreviewed default policy over your only remote connection. See Rocky Linux’s firewall guide and runtime versus permanent configuration.
4. Keep SELinux enforcing
An SELinux denial is a signal to inspect service labels or policy, not a reason to turn SELinux off. Review recent denials and the affected service logs. Use persistent file-context rules and restorecon for custom web roots, with write access limited to the directories that need it.
5. Changing the SSH port requires one more permission
Changing ports is optional and does not replace strong authentication. If you intentionally choose 22397, first check whether that port already has a policy assignment:
sudo dnf install policycoreutils-python-utils
sudo semanage port -l | grep ssh_port_t
sudo semanage port -l | grep 22397
If the port is unassigned, allow SSH to bind it:
sudo semanage port -a -t ssh_port_t -p tcp 22397
If it belongs to another service, investigate instead of blindly reassigning it. Add the new firewall rule, adjust SSH, validate, and test a new connection before removing the old port. Red Hat’s non-default SSH port guidance covers the SELinux requirement.
6. Finish with verification
Check IPv4 and IPv6 exposure, confirm that databases listen only where intended, and review failed services. Keep independent backups and test a restore. Document a rollback for each firewall and authentication change so that recovery does not depend on remembering the last command.
