macOS SSH/VNC Remote Access: Custom Ports and IP-Based ACL
I recently reinstalled macOS, so I’m writing down the port changes and ACL setup.
Of course, you’ll want to change the default port to a different number,
and to further enhance security, I’ll configure an ACL (Access Control List) to allow access only from specific IP addresses.
macOS security keeps evolving, so these methods change with each OS update.
Tested and working on macOS Sequoia 15.7.3 (January 2026).
This article explains step by step why changing the port is important
and how ACL configuration can further enhance security.
The Risks of Default Ports
Remote access is convenient, but it does open up some security risks.
SSH port 22 and VNC port 5900 are well-known default ports.
Attackers target these ports with port scans and brute-force attacks.
Just changing the port number can stop most of these attacks.
In fact, many corporate security policies mandate changing default ports due to its significant effectiveness.
And if you add IP-based ACLs (Access Control Lists) on top of that?
Even if an attacker finds an open port via port scanning, access is impossible unless the IP is explicitly allowed.
Sure, it’s a bit inconvenient, but it makes your remote access way more secure.
Moreover, services with ports constantly open face an enormous volume of scan attacks,
so I really recommend setting up ACLs.
Now, this post will demonstrate the configuration method using the example below.
| Service | Default Port | Changed Port | Allowed IPs |
|---|---|---|---|
| SSH | 22 | 12428 | 192.168.100.0/24, 172.16.0.82, 172.16.0.83 |
| VNC | 5900 | 49917 | 192.168.100.0/24, 172.16.0.82, 172.16.0.83 |
The port numbers and allowed IPs are arbitrary examples.
Adjust them to match your own environment.
Note: This post assumes macOS is using a static IP on a wired network.
Limitations of macOS Remote Access Settings
Enabling remote access in macOS System Settings allows immediate use.
However, it has limitations from a security enhancement perspective.
| Item | System Settings | Limitations |
|---|---|---|
| SSH | Remote Login | Port 22 fixed, cannot be changed |
| Firewall | Application Firewall | Only app-level control possible, no IP-based ACLs |
| System Files | /System/Library/ | Cannot be modified due to SIP (System Integrity Protection) |
Additionally, the system’s default SSH uses /System/Library/LaunchDaemons/ssh.plist.
This file resides in the SIP-protected zone, making port changes impossible.
macOS’s Application Firewall only allows permitting/blocking at the app level.
It lacks an ACL feature to allow access only from specific IPs.
While the setup method is simple and convenient,
it’s a bit disappointing for those like me who want granular configuration.
To overcome this limitation, use the following methods:
- SSH: Use a
custom LaunchDaemoninstead of system remote login - Firewall: Use
PF (Packet Filter)instead of the Application Firewall - VNC: Use system screen sharing +
PF for port forwarding and access restrictions
Changing the SSH Port
Changing the Port in sshd_config
Change the port in the SSH configuration file.
sudo vi /etc/ssh/sshd_config
Find the Port entry, remove the comment, and change it to your desired port.
Port 12428
Disable System Remote Login
Go to System Settings → General → Sharing → Set Remote Login to OFF.
If the system’s default SSH is enabled, it runs on the unchangeable port 22.
Since I’ll use a custom port, I disable the default remote login
and create a LaunchDaemon to run it.
Creating an SSH LaunchDaemon
Create a custom service to use instead of the system’s default SSH.
sudo vi /Library/LaunchDaemons/com.user.sshd.plist
Enter the following content.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.sshd</string>
<key>Program</key>
<string>/usr/sbin/sshd</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/sshd</string>
<string>-D</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
Set permissions for the created plist file and start the service.
sudo chown root:wheel /Library/LaunchDaemons/com.user.sshd.plist
sudo chmod 644 /Library/LaunchDaemons/com.user.sshd.plist
sudo launchctl load /Library/LaunchDaemons/com.user.sshd.plist
Checking SSH Port Listening
Verify that SSH is listening on the changed port.
sudo lsof -i :12428
If configured correctly, you should see port listening similar to the following:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 12345 root 3u IPv4 0x1234567890abcdef 0t0 TCP *:12428 (LISTEN)
sshd 12345 root 4u IPv6 0xfedcba0987654321 0t0 TCP *:12428 (LISTEN)
Running the VNC Screen Sharing Service
VNC uses macOS System Preferences’ Screen Sharing feature directly.
Port changes and IP restrictions will be handled later via the PF firewall.
Go to System Settings → General → Sharing → Screen Sharing and set it to ON.
Then run the following command to allow VNC connections from external IPs:
sudo defaults write /Library/Preferences/com.apple.RemoteManagement VNCOnlyLocalConnections -bool no
This setting may seem risky for security,
but I’ll change the VNC service port number and configure ACLs to allow access only from specific IPs.
Now, let’s check the PF firewall to strengthen security settings!
Changing Service Ports and Configuring ACLs with PF Settings
macOS includes a powerful built-in firewall called PF (Packet Filter).
It operates independently of the Application Firewall and supports IP-based ACLs and port forwarding.
Configuring ACLs to allow access only from specific IPs can be inconvenient in real-world use.
If you’re not connecting from a pre-specified IP, you won’t be able to access it yourself either.
However, the benefits are significant enough to justify this inconvenience.
Even if open ports are found via port scanning, access is blocked unless it’s from an allowed IP.
If you want to significantly strengthen remote access security, I highly recommend setting this up.
In security, sometimes the simplest approach—just blocking IPs—is surprisingly effective.
If you need to connect from various IPs, consider using a VPN.
Adding the VPN range to the allowed IPs will let VPN traffic pass through the ACL simply by connecting through VPN.
This approach maintains the security of the ACL while preserving convenience.
Creating the PF Rule File
sudo vi /etc/pf.anchors/vnc_ssh_security
Enter the following content.
table <allowed_ips> persist { 192.168.100.0/24, 172.16.0.82, 172.16.0.83 }
rdr pass on en0 inet proto tcp from <allowed_ips> to any port 49917 -> 172.16.1.104 port 5900
pass in quick on en0 inet proto tcp from <allowed_ips> to any port 12428 keep state
pass in quick on en0 inet proto tcp from <allowed_ips> to any port 49917 keep state
block drop in quick on en0 inet proto tcp from any to any port 5900
block drop in quick on en0 inet proto tcp from any to any port 12428
block drop in quick on en0 inet proto tcp from any to any port 49917
pass in quick on lo0 all
pass out quick on lo0 all
Rule Explanation:
table <allowed_ips>: List of IP addresses allowed to connect.rdr pass: Forwards traffic entering port 49917 to port 5900 (VNC).pass in quick: Allows connections to this port from the allowed IPs.block drop in quick: Blocks connections to this port from all other IPs. Since the allow rule matches first, the allowed IP passes through.
en0 is the wired network interface. Verify this matches your environment.
172.16.1.104 is the IP address of this Mac. Change it to match your environment.
Modifying pf.conf
sudo vi /etc/pf.conf
Add the following content before the com.apple anchor.
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "vnc_ssh_security"
rdr-anchor "com.apple/*"
dummynet-anchor "com.apple/*"
anchor "vnc_ssh_security"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"
load anchor "vnc_ssh_security" from "/etc/pf.anchors/vnc_ssh_security"
Place rdr-anchor and anchor before com.apple/* to ensure rules are applied with the correct priority.
Registering LaunchDaemon for Automatic Startup
Configure PF to start automatically after rebooting.
sudo vi /Library/LaunchDaemons/com.user.pfctl.plist
Enter the following content.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.pfctl</string>
<key>Program</key>
<string>/sbin/pfctl</string>
<key>ProgramArguments</key>
<array>
<string>/sbin/pfctl</string>
<string>-e</string>
<string>-f</string>
<string>/etc/pf.conf</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Set permissions for the plist file and register the service.
sudo chown root:wheel /Library/LaunchDaemons/com.user.pfctl.plist
sudo chmod 644 /Library/LaunchDaemons/com.user.pfctl.plist
sudo launchctl load /Library/LaunchDaemons/com.user.pfctl.plist
Applying PF Rules
sudo pfctl -f /etc/pf.conf
sudo pfctl -e
If everything’s correct, you won’t see any output.
If an error occurs, an error message will appear.
Verifying Settings
Alright, everything’s set up. Let’s make sure it’s all working.
You can check the service listening status with the commands below.
# SSH
sudo lsof -i :12428
# VNC
sudo lsof -i :5900
You can check the PF status with the command below.
# PF active status
sudo pfctl -s info | head -3
# Check allowed IP table
sudo pfctl -a vnc_ssh_security -t allowed_ips -T show
# Check rules
sudo pfctl -a vnc_ssh_security -s rules
# Check rdr rules
sudo pfctl -a vnc_ssh_security -s nat
From a computer on the allowed IP list, use nmap to check the PF service with the following command.
# Port scan
nmap -Pn -p 22,5900,12428,49917 172.16.1.104
# Expected results:
# 22/tcp closed
# 5900/tcp filtered
# 12428/tcp open
# 49917/tcp open
# SSH connection
ssh -p 12428 [email protected]
# VNC connection
open vnc://172.16.1.104:49917
For a more thorough test, try running nmap from an IP that’s not on the allowed list.
# Port scan
nmap -Pn -p 22,5900,12428,49917 172.16.1.104
# Expected result:
# 22/tcp closed
# 5900/tcp filtered
# 12428/tcp filtered
# 49917/tcp filtered
Final Notes
Here is a summary of key configuration files.
| File | Purpose |
|---|---|
/etc/ssh/sshd_config |
SSH port configuration |
/Library/LaunchDaemons/com.user.sshd.plist |
SSH custom service |
/etc/pf.anchors/vnc_ssh_security |
PF rules (ACL, port forwarding) |
/etc/pf.conf |
PF main configuration |
/Library/LaunchDaemons/com.user.pfctl.plist |
PF auto-start service |
Important Notes:
- Enabling Remote Login in System Settings → Remote Login will additionally run SSH on port 22. Ensure it remains OFF.
- System Settings → Application Firewall is separate from PF. Enabling both may cause conflicts; I recommend keeping Application Firewall OFF.
- To change allowed IPs, modify the
table <allowed_ips>in the/etc/pf.anchors/vnc_ssh_securityfile, then runsudo pfctl -f /etc/pf.conf. - All settings will automatically apply after reboot.
Changing the port alone can block most random attacks.
Adding ACL configuration here further strengthens remote access security.
While slightly inconvenient, to prevent information security incidents,
make sure you always change port numbers for services running constantly and set ACLs to restrict IP access.