#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : SELinux blocks a service on a non-standard port # Fix : Label the port for the service # Source: https://jbtecwiz.com/support/lnx-sec-selinux-port # # Run as : Root shell # Expect : 20 minutes # Risk : medium # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # A service cannot bind to a port it has been moved to. # # HOW TO UNDO IT # sudo semanage port -d -t http_port_t -p tcp 8443 removes the label. # # Walks the fix one step at a time and asks before each. Steps with no # command are yours to do -- it prints those and waits. DRYRUN=1 prints # without executing; UNATTENDED=1 does not ask. # # -------------------------------------------------------------------- # NO WARRANTY - USE AT YOUR OWN RISK # # This script is provided by JbTecWiz as-is and with no warranty of any # kind, express or implied. You run it entirely at your own risk. # # JbTecWiz accepts no liability for any loss or damage arising from its # use, including but not limited to data loss, downtime, or configuration # changes that turn out to be wrong for your system. # # You are responsible for reading this script before running it, for # satisfying yourself that it suits the machine in front of you, and for # having a working backup first. Some steps cannot be undone. # -------------------------------------------------------------------- set -uo pipefail DRYRUN="${DRYRUN:-0}" UNATTENDED="${UNATTENDED:-0}" failed=0 if [ "$(id -u)" -ne 0 ]; then echo " This fix is documented as needing root. Re-run with sudo." >&2 exit 3 fi rule() { printf "\n%s\n" "$(printf '-%.0s' $(seq 1 70))"; if [ $# -gt 0 ]; then echo "$1"; fi; } prose() { echo "$1" | fold -s -w 74 | sed "s/^/ /"; } # Returns 0 when the caller should run the command, 1 when it should not. # A manual step always returns 1 -- there is nothing for the caller to run. step() { # step [command lines...] local n="$1" dotext="$2" why="$3" mode="$4"; shift 4 rule " Step $n of 6" prose "$dotext" if [ -n "$why" ]; then echo; prose "$why"; fi if [ "$mode" = "manual" ]; then echo; echo " -> Do this yourself, then press Enter to carry on." if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r _; fi return 1 fi echo; printf " %s\n" "$@"; echo if [ "$DRYRUN" = "1" ]; then echo " (dry run -- not executed)"; return 1; fi if [ "$UNATTENDED" = "0" ]; then read -r -p " Run this step? [Y]es / [S]kip / [Q]uit " a case "$a" in [Qq]*) echo " Stopped at your request."; exit 0 ;; [Ss]*) echo " Skipped."; return 1 ;; esac fi return 0 } rule echo " SELinux blocks a service on a non-standard port" echo " Label the port for the service" echo echo " Risk: medium Reversible 20 minutes" echo prose 'No warranty. Use at your own risk - JbTecWiz accepts no liability. Read it before you run it, and have a backup.' rule echo if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r -p " Ready? [y/N] " go case "$go" in [Yy]*) ;; *) echo " Nothing was changed."; exit 0;; esac fi if step 1 'Confirm SELinux is the cause rather than something already using the port.' 'Permission denied on a bind as root is almost always SELinux -- the kernel is refusing on the label, not the user. Root cannot override a policy denial, which is why running with sudo changes nothing.' cmd 'sudo ausearch -m avc -ts recent | grep name_bind | tail' 'sudo ss -tlnp | grep 8443'; then sudo ausearch -m avc -ts recent | grep name_bind | tail sudo ss -tlnp | grep 8443 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 1 failed. The rest of the fix may depend on it." >&2 fi fi if step 2 'See what the port is currently labelled as.' '' cmd 'sudo semanage port -l | grep -E '\''8443|http_port_t|ssh_port_t'\'''; then sudo semanage port -l | grep -E '8443|http_port_t|ssh_port_t' if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 2 failed. The rest of the fix may depend on it." >&2 fi fi if step 3 'Add the port to the service'\''s type.' '' cmd 'sudo semanage port -a -t http_port_t -p tcp 8443'; then sudo semanage port -a -t http_port_t -p tcp 8443 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 3 failed. The rest of the fix may depend on it." >&2 fi fi if step 4 'If the port is already assigned to another type, modify rather than add.' '' cmd 'sudo semanage port -m -t http_port_t -p tcp 8443'; then sudo semanage port -m -t http_port_t -p tcp 8443 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 4 failed. The rest of the fix may depend on it." >&2 fi fi if step 5 'For SSH specifically, both the port label and the firewall need changing, and it is worth keeping the old port open until the new one is proven.' '' cmd 'sudo semanage port -a -t ssh_port_t -p tcp 2222' 'sudo firewall-cmd --add-port=2222/tcp --permanent' 'sudo firewall-cmd --reload'; then sudo semanage port -a -t ssh_port_t -p tcp 2222 sudo firewall-cmd --add-port=2222/tcp --permanent sudo firewall-cmd --reload if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 5 failed. The rest of the fix may depend on it." >&2 fi fi if step 6 'Do not set SELinux to permissive as the fix. Use it to confirm a diagnosis and then label the port properly.' '' cmd 'getenforce'; then getenforce if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 6 failed. The rest of the fix may depend on it." >&2 fi fi rule " Confirm it worked" prose 'The service starts and binds, with SELinux still enforcing.' if [ "$DRYRUN" = "0" ]; then getenforce sudo ss -tlnp | grep 8443 sudo semanage port -l | grep 8443 fi rule if [ "$failed" -gt 0 ]; then echo " Finished with $failed failed step(s)." echo " Read the full write-up at https://jbtecwiz.com/support/lnx-sec-selinux-port" else echo " Finished." fi echo prose 'To undo: sudo semanage port -d -t http_port_t -p tcp 8443 removes the label.' rule