14

As subject: I cannot edit the file ssh.plist which is located in /System/Library/LaunchDaemons. I want to change the sshd port.

I tried to use 'information' in the finder and unlock it. This didn't work. I tried:

As root, I tried..

macos:LaunchDaemons root# pwd && chown $USER ssh.plist 
/System/Library/LaunchDaemons
chown: ssh.plist: Operation not permitted

I also tried to cp the file to my own Desktop, edit it in situ and cp it back.

macos:LaunchDaemons root# pwd && cp /Users/darren/Desktop/ssh.plist .
/System/Library/LaunchDaemons
cp: ./ssh.plist: Operation not permitted

I did this before - a while back. I think it was maybe the one before Sierra, but I don't recall the 'code name' of that release.

Has Apple locked this file down definitively?

jaume
  • 15,010
Darren Matheson
  • 431
  • 4
  • 19
  • Hi Darren, I was revising open questions and you may want to upvote/mark my answer as accepted, since, from your feedback, I understand it solved your issue. Marking a question as solved also helps others who have a similar issue, as it shows your question has the answer you were looking for. – jaume Jun 25 '20 at 09:21

1 Answers1

30

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.


Edit: macOS 11 Big Sur and later add strong cryptographic protections to system content, which seems to make the first proposed solution (modifying /System/Library/LaunchDaemons/ssh.plist) not feasible. Thank you to Jo Liss and Carl for their feedback in the comments below!


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.

  1. 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.

  1. 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.

  1. 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

  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 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.

jaume
  • 15,010
  • 1
    Would it make more sense to use the firewall to translate the port number? Especially as /etc/services is meant to be the IANA-assigned port numbers: Non standard ssh port: should I edit /etc/services? – Andrew Morton Sep 01 '18 at 19:16
  • @AndrewMorton Yes, I like your idea and I agree, modifying /etc/services feels like a hack. But as someone who has dealt with the packet filter (PF) in the past, I see editing /etc/services as the simplest and less error-prone solution. Unfortunately, ssh.plist doesn't support the Port directive defined in sshd_config, which is in my opinion the right way to configure the sshd port (that was the first thing I tried, and was baffled when I noticed it didn't work). – jaume Sep 01 '18 at 19:54
  • @AndrewMorton Yes, I guess so. – jaume Sep 01 '18 at 20:01
  • Didn't try it, but wouldn't that affect the default port for outgoing ssh connections? – jcaron Sep 01 '18 at 21:50
  • @jcaron Outgoing SSH connections use an ephemeral port unrelated to the sshd port. – jaume Sep 01 '18 at 22:03
  • I mean the port being connected to. Don't know if ssh uses a hardwired default of 22, a default setting in ssh_config, or if it looks up the ssh service. – jcaron Sep 01 '18 at 22:15
  • @jcaron I guess you mean sshd and sshd_config instead of ssh and ssh_config, you are asking about the SSH server, right? (sorry if I'm being pedantic, but words matter). To your question: after modifying /etc/services/, the port being connected to is always 2222, that is, all communication with the SSH server goes through port 2222. The reason is that, in macOS, sshd runs in inetd mode (-i option), and the "ssh" entry in /etc/services takes effect. When not run in inetd mode, sshd defaults to port 22, unless Port/ListenAdress are set to a different value in sshd_config. – jaume Sep 02 '18 at 10:44
  • 1
    @jaume, no, I actually mean ssh, the client. If it looks up the ssh service rather than use a hardcoded 22, then modifying /etc/services would also affect outgoing ssh connections (the default remote port the client would connect to). Not saying it’s actually the case (haven’t tried), just wondering. – jcaron Sep 02 '18 at 11:52
  • 1
    @jcaron Now I understand what you mean, very good question, yes, I tested it and the SSH client reads the value of the "ssh" entry in /etc/services and tries to connect to port 2222 (or whatever port is defined in there). That is, /etc/services not only changes the behavior of sshd, but also of ssh. I didn't expect that, I will edit the answer and point that out (with a workaround). – jaume Sep 02 '18 at 12:59
  • 1
    I'd recommend copying ssh.plist into /Library/LaunchDaemons, changing the filename, label, and port, and loading that as a separate service. See this page at codedmemes.com for details. – Gordon Davisson Sep 03 '18 at 13:45
  • @GordonDavisson Thanks, I appreciate your comment, I saw the solution you mention yesterday here on Ask Different, see klanomath's answer at apple.stackexchange.com/questions/307940/…. There are several ways to change the SSH server port, some are simple but unelegant, some are flexible but complex. I've been thinking quite a bit about this question, I was planning on expanding my answer, and I will definitely mention the method you propose. – jaume Sep 03 '18 at 15:05
  • 1
    Confirmed #1 works on Mojave – Elle Mundy Sep 29 '18 at 23:33
  • im considering installing a 3rd party ssh server at this stage. – Tomachi Oct 02 '18 at 06:53
  • I've just tested (1) and after rebooting the sshd service couldn't start. I used the same synthax – Dimitri Kopriwa Oct 27 '19 at 21:02
  • Great post. However, I just tried the first method and couldn't accomplish the task because the file was still read-only even though csrutil was disabled; double checked. I get Apple's security concerns but the Mac is the Mac, leave it alone and have power users do what they want. – Can Sürmeli May 25 '20 at 11:04
  • @CanSürmeli That's weird, the plist should be writable for root: -rw-r--r-- 1 root wheel 1034 Aug 25 2019 /System/Library/LaunchDaemons/ssh.plist. Have you tried editing it as root? (you can either sudo to root first: sudo su - or edit it like this (replace vi with your favorite command-line editor): sudo vi /System/Library/LaunchDaemons/ssh.plist). – jaume May 25 '20 at 11:17
  • @jaume Actually, I haven't tried with becoming root altogether first. Just initiated the vim with sudo. Can also try that but disabling csrutil first really sucks. Boooo Apple! But thanks. – Can Sürmeli May 25 '20 at 11:24
  • 1
    On M1 with Monterey (maybe also Big Sur), the root filesystem is mounted readonly, so you cannot normally edit /System/Library/LaunchDaemons/ssh.plist even with system integrity protection disabled. I tried entering recovery mode, in the recovery terminal mounting the main partition read-write (mount -uw "/Volumes/Macintosh HD"), and then editing /Volumes/Macintosh HD/System/Library/LaunchDaemons/ssh.plist, but the changes seem to have gotten reverted, possibly because I re-enabled SIP. Any ideas? – Jo Liss Nov 08 '21 at 16:58
  • @JoLiss From what you explain, it seems Monterey rolls backs changes when you re-enable SIP, but since I'm still on Big Sur and can't test it. I'd recommend that you create a plist file in /Library/LaunchDaemons/ with your desired configuration, disable the built-in one: sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist and enable yours with sudo launchctl unload /Library/LaunchDaemons/<your plist file> – jaume Nov 09 '21 at 13:55
  • 1
    Possibly connected to comment by @JoLiss above: From other answers and Apple Docs [1], it seems that for Big Sur and later, /System/ volume is sealed <-> checksum-ed. If editing it, at next reboot changes will be reverted or system refuse to boot. Not sure if that is correct though?

    [1]: https://superuser.com/questions/1771166/cant-edit-readonly-file-even-when-root-and-without-system-integrity-protection, https://support.apple.com/en-gb/guide/security/secd698747c9/web

    – Carl Jun 09 '23 at 14:04
  • 1
    After struggeling with various solutions with this for a few years, I ended up configuring my router with a port forward to translate an external high-numbered port to internal port 22. The hackers on the internet may slam my port 22 as much as they want, contributing to the heat death of the universe at most. My macOS is left untouched and understands all ssh traffic to happen on port 22. This was the perfect solution for me, however, it requires a configureable external router/firewall. Also, occasional direct internet connections with the mac exposes port 22 if not actively disabled. – Carl Jun 10 '23 at 16:07