I sometimes test randomly downloaded code on macOS in a Sandbox that has limited and network access. I posted about this way back in Jan 2017, Creating a macOS Sandbox to run Kodi, and this is a short refresher for me...
The latest macOS man sandbox-exec
page states that this command is DEPRECATED! However, I am not aware of any command line alternative... though someone replied on this topic at Stack Overflow, indicating that the sandbox is not going away (yet).
Sandbox Log
I use this command to view log output from the sandbox:
log stream --style compact --predicate 'sender=="Sandbox"'
And in this case, since I want to filter only for messages to do with python
:
log stream --style compact --predicate 'sender=="Sandbox" and eventMessage contains "python"'
The Console.app may also help - hit the Start button and then search for sandbox
:
Sandbox Exec that Denies by Default
The command sandbox-exec
runs a process in a sandbox with a “profile” applied. The -p
option allows the profile to be specified in-line rather than being read from a file.
Understanding the profile format is a challenge, and you can refer to my prior post for a bit more explanation. Also, check out the in-built sandbox profiles at /System/Library/Sandbox/Profiles
for more examples.
The profile below will deny
everything by default. Often the process I am testing will totally fail to run, so I have to monitor the log output above and bit by bit, add additional allow
s. This is a long and tedious process!
In the case of Python:
- Deny all networking... but to allow outgoing networking, I just add
(allow network-outbound)
, or to allow local networking, I add(allow network* (local ip "localhost:*"))(allow network* (remote ip "localhost:*"))
- Allow mandatory IO access for console and files
- Allow reads for core system directory (root,
/System
,/Library
,/private
,/dev
and/usr
) and certain user directories (~/Library
) - Allow read and write to the current working directory
$PWD
. - Allow specific processes to be run - in this case
python3
is a symbolic link to executables in the Xcode Command Line Tools directory.
sandbox-exec -p '(version 1)(deny default)
(allow iokit-open)(allow mach* sysctl-read)
(allow file-ioctl)(allow file-read-metadata)
(allow file-read-data (literal "/")
(regex "^/System" "^/Library" "^/private" "^/dev" "^/usr" "~/Library"))
(allow file-read* file-write* (regex "^'$PWD'"))
(allow process-exec
(regex "^/usr/bin/python3" "^/Library/Developer/CommandLineTools/"))' \
python3
Quick tests: try opening a port python3 -m http.server 8888
or connecting to Google:
import urllib.request; print(urllib.request.urlopen("http://google.com").read())
Sandbox Exec that Allows by Default
Here is a profile that does the opposite - it allows everything except networking and certain user folders:
sandbox-exec -p '(version 1)(allow default)(deny network*)(deny file-read-data
(regex "^/Users/'$USER'/(Documents|Desktop|Developer|Movies|Music|Pictures)"))' \
zsh
Quick tests: try curl google.com
or ls ~/Documents
.
You can replace zsh
with other command line programs or run executables within Apps (e.g. /Applications/myApp.app/Contents/MacOS/myApp
). Don’t use open /Applications/my.app
. Also, apps that manipulate the sandbox or spawn sandboxed children will fail, e.g. with errors like deny forbidden-sandbox-reinit
due to missing entitlements for com.apple.security.app-sandbox
, as described here.
To quickly test something from the command like without networking and folder access, I use this alias so I can just sandbox zsh
:
alias sandbox='sandbox-exec -p '\''(version 1)(allow default)(deny network*)(deny file-read-data (regex "^/Users/'$USER'/(Documents|Desktop|Developer|Movies|Music|Pictures)"))'\'''
That’s all for now!