Cannot change ssh port on High Sierra

Solution 1:

Has Apple locked this file [/System/Library/LaunchDaemons/ssh.plist] down definitively?

Yes, they have, you need to disable SIP (System Integrity Protection), but note that editing ssh.plist is not the only option to change the SSH server port, there are other ways to achieve the same result.

There are (at least) 4 ways to change the port sshd listens on:

  1. Modifying /System/Library/LaunchDaemons/ssh.plist:

    • Pros: cleanest way to configure sshd, sshd started with System Preferences>Sharing>Remote Login listens on new port.

    • Cons: convoluted setup (requires two restarts and disabling/re-enabling SIP).

    • Use case: definitive change of sshd port.

  2. Creating a new plist /Library/LaunchDaemons/ssh2.plist:

    • Pros: dual operation of sshd listening on standard and new port.

    • Cons: CLI-only method to start sshd on the new port.

    • Use case: sshd must listen on both the standard port and the additional port.

  3. Modifying "ssh" entries in /etc/services:

    • Pros: simplest method, no need to deal with SIP, sshd started with System Preferences>Sharing>Remote Login listens on new port.

    • Cons: side effect: ssh defaults to new port when connecting to remote server (there's a workaround for that).

    • Use case: temporary port change or testing sshd running on a different port.

  4. Redirecting port 22 to new port with packet filter:

    • Pros: no need to deal with SIP, sshd started with System Preferences>Sharing>Remote Login seems to listen on new port.

    • Cons: somewhat non-transparent (sshd configuration is untouched but sshd listens on another/additional port) and confusing (firewall status in System Preferences not reliable), tiny side effect (remote client thinks it is connecting to port 22 (through env variables SSH_CLIENT and SSH_CONNECTION)).

    • Use case: definitive or temporary change of the sshd port, sshd must listen on both the standard port and the additional port.

Let's take a closer look at them. (When choosing the new port, make sure it is not used by another service by running sudo lsof -i -n -P | grep <your port>.)


1. Modifying /System/Library/LaunchDaemons/ssh.plist

This is the method you tried, let me describe it here for completion:

  1. Restart your Mac and hold down R immediately after your Mac begins to restart to enter macOS Recovery.
  2. Select Utilities>Terminal from the menu bar.
  3. Type csrutil disable to disable SIP.
  4. Select Apple menu>Restart.
  5. Log in, edit /System/Library/LaunchDaemons/ssh.plist and modify the Listeners section. For example, to change the port to 2222:

    enter image description here

  6. Restart your Mac and hold down R immediately after your Mac begins to restart to enter macOS Recovery.

  7. Select Utilities>Terminal from the menu bar.
  8. Type csrutil enable to enable SIP and restart.

SSH server will now listen on the new port and you can start sshd as usual through System Preferences>Sharing>Remote Login.


2. Creating a new plist /System/Library/LaunchDaemons/ssh2.plist

This method is nicely described in this answer, which basically says:

  1. Copy /System/Library/LaunchDaemons/ssh.plist to /Library/LaunchDaemons/ssh2.plist to create a new sshd startup script.
  2. Modify the label in /Library/LaunchDaemons/ssh2.plist to differenciate the new startup script from the built-in one, for example by appending a number "2":

    <key>Label</key>
    <string>com.openssh.sshd2</string>
    
  3. Modify the port in the Listeners section as in the previous method. For example, to change the port to 2222:

    <key>Sockets</key>
    <dict>
       <key>Listeners</key>
       <dict>
          <key>SockServiceName</key>
          <string>2222</string>
          <key>Bonjour</key>
          <array>
              <string>ssh</string>
              <string>sftp-ssh</string>
          </array>
       </dict>
    </dict>
    
  4. Start sshd on the new port:

    sudo launchctl load -w /Library/LaunchDaemons/ssh2.plist

    (To stop it, run sudo launchctl unload /Library/LaunchDaemons/ssh2.plist)

The SSH server will now listen on the new port. You can still use System Preferences>Sharing>Remote Login to start another instance of the SSH server that listens on the standard port (22/tcp). launchd will listen on both ports:

$ sudo lsof -i -P -n
launchd     1           root   42u  IPv6 0x9df385961b132cdf      0t0    TCP *:2222 (LISTEN)
launchd     1           root   44u  IPv4 0x9df385961c81db1f      0t0    TCP *:2222 (LISTEN)
launchd     1           root   45u  IPv6 0x9df385961b132cdf      0t0    TCP *:2222 (LISTEN)
launchd     1           root   47u  IPv4 0x9df385961c81db1f      0t0    TCP *:2222 (LISTEN)
launchd     1           root   48u  IPv6 0x9df385961b133e1f      0t0    TCP *:22 (LISTEN)
launchd     1           root   49u  IPv4 0x9df385961b22d51f      0t0    TCP *:22 (LISTEN)
launchd     1           root   50u  IPv6 0x9df385961b133e1f      0t0    TCP *:22 (LISTEN)
launchd     1           root   51u  IPv4 0x9df385961b22d51f      0t0    TCP *:22 (LISTEN)

3. Modifying "ssh" entries in /etc/services

To change the sshd port with this method, proceed as follows:

  1. Edit /etc/services, look for these entries:

    ssh  22/udp  # SSH Remote Login Protocol
    ssh  22/tcp  # SSH Remote Login Protocol
    

    and replace port 22 with a port of your choosing.

You can enable SSH with System Preferences>Sharing>Remote Login and sshd will listen on the new port.

Why does this work? If you take a look at the Listeners section of the /System/Library/LaunchDaemons/ssh.plist file, you will see that sshd is configured to listen on the port assigned to the service named ssh in /etc/services:

<key>Sockets</key>
<dict>
    <key>Listeners</key>
    <dict>
        <key>SockServiceName</key>
        <string>ssh</string>
(...)
</dict>

This is undoubtedly the simplest method, but it has a drawback: the SSH client will expect remote SSH servers to listen on the new port (instead of port 22) (thanks to jcaron for the hint). That is, ssh reads /etc/services to find out which the default SSH port is.

Fortunately there is an easy solution for this: uncomment Port 22 in /etc/ssh/ssh_config (see man ssh_config for more information).


4. Redirecting port 22 to new port with packet filter

The method uses the macOS packet filter (PF) to redirect all requests received at port 22 to the new sshd port (thanks to Andrew Morton for the idea):

  1. Create a new anchor file /etc/pf.anchors/sshd with contents (replace 2222 with a port of your choosing):

    rdr pass inet proto tcp from any to any port = 2222 -> 127.0.0.1 port 22
    # If you want `sshd` to listen on port 22 too, comment out the line below
    block drop in quick proto tcp from any to any port = 22
    
  2. Add the "sshd" anchor rules by editing /etc/pf.conf (the order is relevant!):

    scrub-anchor "com.apple/*"
    nat-anchor "com.apple/*"
    rdr-anchor "com.apple/*"
    # Load sshd redirect rule
    rdr-anchor "sshd"
    dummynet-anchor "com.apple/*"
    anchor "com.apple/*"
    # Load other sshd rules
    anchor "sshd"
    load anchor "com.apple" from "/etc/pf.anchors/com.apple"
    # Tell PF where to find the sshd anchor
    load anchor "sshd" from "/etc/pf.anchors/sshd"
    
  3. Enable and reload the packet filter:

    sudo pfctl -F all -ef /etc/pf.conf
    

    (To disable it, run sudo pfctl -d.)

Note that:

  • the macOS firewall (System Preferences>Security & Privacy>Firewall) is under the hood the packet filter, so that when you disable the packet filter on the command line, you are also disabling the firewall, even if the Preferences Panel doesn't reflect the change.
  • SSH clients will think they are connecting to port 22 through the SSH_CLIENT and SSH_CONNECTION env variables.

Editing sshd_config doesn't work

Users running sshd on other operating systems may be tempted to edit the SSH daemon configuration file, /etc/ssh/sshd_config. In macOS, however, editing the Port directive in /etc/ssh/sshd_config won't achieve the desired result.