SELinux blocks SSH local port forwarding

Topics: Secure Shell, SELinux

This note explains how SELinux policy can prevent SSH local port forwarding and describes a policy-based fix.

Subtopics

Symptoms

An SSH local forward accepts a connection locally, then OpenSSH reports:

channel 2: open failed: connect failed: open failed

For example:

ssh -N -L 3000:127.0.0.1:3000 user@host

The target service can still be reached from an interactive SSH shell on the host. This rules out the service and its loopback listener, but not SELinux: the forwarding connection is opened by sshd-session, not by the shell process.

Diagnosis

Look for an SELinux access-vector-cache denial for sshd-session:

comm="sshd-session" ... denied { name_connect } ... scontext=...:sshd_session_t:... tcontext=...:ntop_port_t:... tclass=tcp_socket

The target port type depends on the port. On Fedora, TCP port 3000 is labelled ntop_port_t.

This failure appeared after a dnf upgrade that updated both openssh-server and selinux-policy-targeted. SELinux had remained enabled and enforcing; the updated policy changed what the SSH session domain could connect to.

Fix for development hosts

When a development host needs forwards to several local services, add one local policy that permits the SSH session domain to open TCP connections to ports. It is preferable to making SELinux permissive or adding a new exception for every development port.

mkdir -p ~/selinux/ssh-local-forward
cd ~/selinux/ssh-local-forward

cat > ssh_local_forward.te <<'EOF'
module ssh_local_forward 1.0;

require {
    type sshd_session_t;
    attribute port_type;
    class tcp_socket name_connect;
}

allow sshd_session_t port_type:tcp_socket name_connect;
EOF

checkmodule -M -m -o ssh_local_forward.mod ssh_local_forward.te
semodule_package -o ssh_local_forward.pp -m ssh_local_forward.mod
sudo semodule -i ssh_local_forward.pp

The policy permits only TCP name_connect from sshd_session_t. It does not grant listening, binding, UDP, file, process, or raw-network permissions. It does allow an SSH account that OpenSSH permits to use local forwarding to connect to any TCP destination reachable from the host. Use PermitOpen in sshd_config if that needs to be restricted.

For a single service, replace port_type with the target type shown in the denial, such as ntop_port_t. That is more restrictive, but becomes tedious when a development machine uses multiple ports.

Permissive mode on a development host

SELinux permissive mode is a simpler alternative on a private, disposable development host. It removes enforcement for every domain, not only SSH forwarding.

Verification

Start a temporary forward on an unused local port, then request it from another terminal:

ssh -N -L 3005:127.0.0.1:3000 user@host
curl -I http://127.0.0.1:3005/

The request should return the target service’s HTTP status rather than another direct-tcpip connection failure.

Bookmarks

Questions