SSD Nodes Learn 🎉 VPS from $5.50/mo
How to do am Matt ConnorBy Matt Connor

How to Change SSH Port for SELinux and firewalld

You wan change SSH port on Rocky or AlmaLinux? See the firewalld rule, SELinux semanage command and sshd_config order wey keeps port 22 alive.

Why changing the SSH port need three steps for here

To change SSH port for Rocky Linux, AlmaLinux, CentOS Stream or Fedora, one edit no dey enough. Three separate systems each get say for whether connection on the new port go work. firewalld decide whether the packet go reach the machine. SELinux decide whether sshd fit bind that port number at all. sshd_config decide which port the daemon go ask for. If you miss the SELinux step, the daemon no go start. If you miss the firewalld step, e go start and listen, but nobody go fit reach am.

For Ubuntu, na one edit and restart dey handle the same work, because Ubuntu dey use AppArmor instead of SELinux and e no ship with profile wey restrict which ports sshd fit bind. If ufw dey run there, add one rule. Na the whole difference be that. The RHEL family dey ship with firewalld running and SELinux enforcing for fresh install, and both of dem care about port numbers.

Do the work for this order so your current session go remain alive through every step:

  1. Open the new port for firewalld, but leave port 22 open for now.
  2. Add the SELinux label for the new port with semanage.
  3. Set the port for the sshd configuration.
  4. Restart sshd, then log in through the new port from a second terminal before you close the first one.
Find your provider's web console (VNC or serial) before you start, and confirm say you fit log in through am. That console na your way back in if the change go wrong. Port change na one of the commonest reasons tenant lock themselves out of server wey dem just pay for.

First, make sure say semanage dey installed

semanage na the tool wey dey edit SELinux policy settings, but minimal Rocky Linux or AlmaLinux install no include am. E dey inside policycoreutils-python-utils.

sudo dnf install -y policycoreutils-python-utils

If you run the command before you install that package, e go show sudo: semanage: command not found. Na for this point many readers dey conclude say SELinux no dey installed and dem skip the step. SELinux dey installed. Na only the management tool dey missing. If dnf syntax dey new to you, the dnf and apt command equivalents fit connect am to wetin you already know.

Choose port and check say nothing dey use am

Any free TCP port from 1024 to 65535 go work. Do these 2 checks before you settle for one:

sudo ss -tlnp | grep -w 2222
sudo semanage port -l | grep -w 2222

The first one show whether any process dey listen on that number. The second one show whether SELinux policy don already assign am to another service type. Free port no go return anything from both commands. If policy don already claim am, the semanage port -a for step 2 go fail with ValueError: Port tcp/2222 already defined, and you need choose another number.

We use 2222 as example throughout this guide. Scanner dey also try am as the first port after 22, so choose a less obvious number for real server.

Step 1: open port for firewalld

sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports

--permanent dey write the rule inside the zone file for disk, and e no touch the firewall wey dey run. --reload dey load the configuration wey dey for disk into the firewall wey dey run. If you skip the reload, the rule go dey exist but e no go do anything until firewalld restart again. This na one of the commonest reasons why the whole procedure fit look like e fail for no reason.

For now, leave the ssh service entry as e be. Na this entry dey keep port 22 open, and e go serve as fallback while you dey test.

Check your provider control panel too. Plenty hosts dey run network firewall for front of the VPS, outside the operating system. So upstream fit still drop port wey you open for firewalld. The firewalld basics guide for VPS explain zones and the difference between runtime and permanent configuration if this model new to you.

Step 2: label port for SELinux

sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -l | grep ssh_port_t

-a dey add new port assignment. -t ssh_port_t na the type wey SSH ports dey carry. The second command dey list everything wey ssh_port_t now cover, so you fit confirm say your number enter before you touch the daemon.

Why SELinux dey block the port at all

SELinux (security-enhanced Linux) dey give every object for the system one label, and TCP port numbers na objects like any other one. SSH daemon dey run confined inside one domain wey dem call sshd_t. Policy allow sshd_t bind TCP ports wey get label ssh_port_t, and out of the box na only port 22 get that label. If you tell the daemon make e bind 2222, kernel go check the label, see the generic type wey policy assign to that number, then reject the name_bind permission for the socket.

Na why this failure no be like firewall problem. Kernel go reject am before any listening socket fit exist, so sshd go report the error and exit. Firewall problem na the opposite: daemon dey run well, but packets dey get discarded as dem dey enter.

getenforce go tell you the mode wey the box dey use. For Permissive, system go record denial but e no go enforce am, so the port change go look like say e work, then e go break the day person run setenforce 1 or the box reboot into enforcing mode. Label the port either way. The SELinux basics guide for one server go explain modes, contexts and booleans properly.

Step 3: set port for sshd configuration

For Rocky Linux 9 and 10, AlmaLinux 9 and 10, and current Fedora, /etc/ssh/sshd_config dey start with include line, so the neat place to put your change na drop-in file. Package updates no go ever clash with your edit.

grep -n '^Include' /etc/ssh/sshd_config
echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf
sudo sshd -t

If grep no find any Include line, as e dey happen for Rocky Linux 8 and other older images, put Port 2222 directly inside /etc/ssh/sshd_config instead. sshd -t dey parse the complete configuration, including drop-ins, and e dey report syntax errors. Fix anything wey e report before you restart, because if configuration no parse, daemon no go come back up.

Port fit show more than once, and sshd go listen on every port wey you list. Keeping Port 22 together with Port 2222 for the first day na cheap safety net, as long as you remember to remove am.

Your sshd na socket unit start am?

Some images dey start SSH through systemd socket activation instead of as service wey dey run continuously. Where dem set am up like this, systemd dey own the listening socket and hand connections over to sshd, so e dey ignore the Port line for sshd_config completely. Check this before you restart anything:

systemctl is-enabled sshd.socket

If answer na enabled, e mean say dem set the port for socket unit, not for sshd_config:

sudo systemctl edit sshd.socket
[Socket]
ListenStream=
ListenStream=2222

You must use the bare ListenStream=. Values dey accumulate across drop-ins. So if you no first use empty assignment clear the list, the socket go still listen on 22 as well as 2222. Apply am with sudo systemctl daemon-reload, then run sudo systemctl restart sshd.socket. If the unit disabled or e no dey your server, this section no apply to you.

Step 4: restart, then test from a second terminal

sudo systemctl restart sshd
systemctl status sshd
sudo ss -tlnp | grep sshd

Keep dis terminal open. No log out from am. Open second terminal for your own machine and connect through the new port:

ssh -p 2222 youruser@203.0.113.10

Close the first session only after the second login don work. If e no work, you still get shell wey you fit use undo everything. Na this one habit dey make difference between change wey take five minutes and whole afternoon for provider console.

Firewall drop or SELinux denial? How you go know the difference

From your laptop, both failures fit look almost the same. For the server, dem no resemble at all.

  • If systemctl status sshd show say the unit fail, the daemon never get the socket. Na configuration error or SELinux denial cause am.
  • If the unit dey active and ss -tlnp show say sshd bind to the new port, the daemon dey okay. The problem dey for the network path: firewalld, the provider separate firewall, or the address and port wey you dial.

For SELinux case, read the audit record instead of guessing:

sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pager

A name_bind denial for class tcp_socket name the process for comm="sshd", the port number for src=, and the label wey the port actually carry for tcontext=. Na that last field be the answer. Anything wey no be ssh_port_t mean say step 2 no apply to the port wey you dey use. Usually, na typo for the number or wrong protocol cause am. Install setroubleshoot-server if you prefer make sealert turn the record into one sentence.

The message wey sshd itself write when the kernel refuse the bind dey look like this:

error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.

Permission denied for port wey pass 1024, where root privilege no dey needed to bind, na the SELinux signature. Address already in use for that same line na different fault: another process dey hold the port. From the client side, the difference between connection refused and connection timed out help separate the two network cases. Connection refused mean say your packet reach the host and nothing dey listen, while timeout mean say nothing answer at all.

Close port 22 and update your clients

After several logins don work for the new port, remove 22:

sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Leave the SELinux label for port 22 as e be. Na base policy provide am, and e no grant anything again once firewall stop allowing packets enter.

Then update the clients, because every tool wey assume the default port now need the correct instruction. Put am for ~/.ssh/config for your own machine one time, instead of typing -p every time:

Host myvps
  HostName 203.0.113.10
  Port 2222
  User youruser

scp, sftp, rsync and Ansible all dey read that file. Backup jobs, monitoring checks and cron scripts wey hardcode port 22 no dey read am, so find and update dem while the change still fresh for your mind.

Wetin moving the port fit do and wetin e no fit do

E dey reduce log noise. Automated scanners dey hammer port 22 steady, and when you move commot from there, e remove most of those lines from the journal. This one make real events easier to see. E no be security control. Any scanner wey scan the full port range go find your daemon and read its version banner anyway. Treat the port change as housekeeping. Put the real protection for key-only authentication with password logins turned off. the SSH hardening guide for a VPS go show you the steps one by one.

Everything above dey work the same way for both major RHEL rebuilds, because dem build am from the same sources. See Rocky Linux and AlmaLinux compared if you never choose between dem. Before you follow any old guide, check the release wey dem actually give you with cat /etc/os-release. Guides wey dem write for Rocky Linux 8 still rank well, and their semanage and firewall-cmd steps still correct. But Rocky 8 no get sshd_config.d include line and no socket unit to consider, so the sshd part of those guides no match current box.

fail2ban need you tell am about the new port

fail2ban no dey inside the base repositories. E dey come from EPEL (extra packages for enterprise Linux):

sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalld

The fail2ban-firewalld subpackage make fail2ban write its bans through firewalld. Na this one you want for machine wey firewalld dey control the ruleset.

The stock sshd jail set port = ssh. This name resolve through /etc/services to 22. After your change, the jail dey watch port wey nobody dey attack. So e no go ban anybody while failed logins dey pile up for 2222. Set the port with number inside /etc/fail2ban/jail.local:

[sshd]
enabled = true
port = 2222
backend = systemd
maxretry = 5
bantime = 3600

backend = systemd dey read failures from the journal instead of /var/log/secure. This one safer for minimal install wey rsyslog fit no dey available. Start am with sudo systemctl enable --now fail2ban, then inspect the jail with sudo fail2ban-client status sshd. The jail syntax na the same one wey the fail2ban setup for SSH on Ubuntu 24.04 use. Na only the package source and ban action dey different.

Patching dey more important pass the port

Server wey move SSH port but get four months security updates wey dem never apply dey worse pass server wey still dey use port 22 but dey patch itself every night. Turn on unattended updates for the same session while you still dey root: automatic dnf updates for Rocky Linux and AlmaLinux explain the timer and the choice between downloading updates and applying dem.

FAQ

Why sshd no dey start after I change the port for Rocky Linux?

Almost every time, na SELinux port label dey miss. sshd dey run inside the confined sshd_t domain, and policy only allow am bind to ports wey get ssh_port_t label. By default, na port 22 only get this label. Kernel go reject the bind, so daemon go exit instead of listening, and journalctl -u sshd go carry line wey look like error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. Run sudo semanage port -a -t ssh_port_t -p tcp 2222 with your own port number, then restart the service. If semanage no dey found, install policycoreutils-python-utils first.

I still need semanage if SELinux dey permissive mode?

Yes. For permissive mode, system go record the denial but still allow the bind, so e go look like say the change work. But the label still dey miss. As soon as anybody runs setenforce 1, or the box boot with SELINUX=enforcing inside /etc/selinux/config, sshd go stop starting on that port. Adding the label na one command, and e go remove one failure wey fit otherwise show up weeks later without clear cause.

The port get label and sshd dey run, so why my connection dey time out?

When daemon dey run, e mean SELinux don accept am. So na the packet dey get dropped while e dey enter. Check sudo firewall-cmd --list-ports for your port, and confirm say you run firewall-cmd --reload after the --permanent rule, because permanent rule by itself no dey reach the firewall wey dey run now. Then check your host control panel for separate network firewall wey dey in front of the VPS. Na the second place wey people dey get blocked, and nothing inside the operating system go show am.

Which port I suppose use instead of 22?

Use any free TCP port from 1024 to 65535. For real server, avoid 2222 and 22222, because scanners dey try dem immediately after 22. Confirm the number free with sudo ss -tlnp, confirm say SELinux policy never already claim am with sudo semanage port -l, and avoid any port assigned to service wey you fit install later. High number wey no easy remember dey okay, because you go write am inside ~/.ssh/config once and never type am again.