Helper app cannot access any web pages?

Hello. I have an application that consists of two parts: Python (the main app, which works as a server) and Electron ("helper" app, which works as a UI). I plan to submit it to the App Store, so it's sandboxed. Right now, I'm testing the sandboxed development-signed build, and I have a problem with it.

Some info about the entitlements and signing: The Python app is packaged with Py2App (I heard that it's the only possible way to package a Python app for the App Store). The Python app has com.apple.security.network.client, com.apple.security.network.server, and sandbox entitlements, I sign it using the Mac Development certificate. The Electron app is packaged with electron-builder and signed with electron-osx-sign (Mac Development certificate, as well). The Electron app has standard entitlements, I just added the sandbox and security.inherit to its entitlements. I have generated the development provision profile and embedded it into the app's bundle.

Yes, I know that this architecture is a bad choice for the macOS/App Store, I'm aware of it. The project is 99% done, and it's just easier for me to somehow overcome this issue, rather than rewriting everything from zero to Swift/Obj-C.

So, when the user clicks on the .APP, this is what happens: the Python app starts, it creates the server, and finally, launches the Electron. The problem begins here: the Electron successfully starts but fails to load the server's URL. I tried to open my server's URL in Chrome and everything works fine. So this problem is related to the Electron or maybe entitlements.

I also tried to load any other webpages, like google.com, and it still doesn't work, I get the exact same error. When I load the page (like calling the app.loadURL or changing the window.location.href), these messages get printed out in the Console:

    default 13:36:40.749975 +0200 trustd cert[2]: AnchorTrusted =(leaf)[force]> 0
    default 13:36:42.903489 +0200 symptomsd rssi (-49) or transmitRate (145.000000) changed on interface en1 for BSSID:b0:95:75:21:bc:d8
    default 13:36:50.909786 +0200 symptomsd rssi (-50) or transmitRate (145.000000) changed on interface en1 for BSSID:b0:95:75:21:bc:d8
    default 13:36:51.321708 +0200 trustd could not enable test hierarchy: no UAT pinning preferences set

I googled this "no UAT pinning preferences set", and didn't find anything useful. These messages are always the same, it doesn't matter if I try to open a localhost page or google.com. I also tried using "fetch" in the Electron's app console, it outputs this error:

>>> await fetch("https://google.com")
   ---> VM123:1 GET https://google.com/ net::ERR_NAME_NOT_RESOLVED
   ---> VM123:2 Uncaught TypeError: Failed to fetch
   --->    at <anonymous>:1:7

 I think that this issue is somehow related to security.inherit entitlement. Maybe when I launch the Electron, Python's entitlements don't get passed to the Electron? So, Electron doesn't inherit the "com.apple.security.network.client" entitlement and has no right to load any web pages, am I right? If yes, then how should I properly launch the Electron?

Currently, I tried using the "open" command and an AppleScript, the error stays the same in any case. Here are the commands I used:

Open:

open "MyPythonApp.app/Contents/MacOS/MyElectronApp.app"

AppleScript:

osascript -e "tell application \"MyPythonApp.app/Contents/MacOS/MyElectronApp.app\" to activate"

I sign the Python app with these entitlements:

    <?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>CFBundleIdentifier</key>
     <string>com.abtco.myquickmaclite</string>
     <key>com.apple.security.app-sandbox</key>
     <true/>
     <key>com.apple.security.network.server</key>
     <true/>
     <key>com.apple.security.network.client</key>
     <true/>
     <key>com.apple.security.files.user-selected.read-write</key>
     <true/>
     <key>com.apple.security.files.downloads.read-write</key>
     <true/>
     <key>com.apple.security.assets.pictures.read-write</key>
     <true/>
     <key>com.apple.security.assets.music.read-write</key>
     <true/>
     <key>com.apple.security.assets.movies.read-write</key>
     <true/>
    </dict>
    </plist>

And the Electron app with these ones:

    <?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>com.apple.security.app-sandbox</key>
        <true/>
        <key>com.apple.security.inherit</key>
        <true/>
      </dict>
    </plist>

Mac Mini 2012 (macOS 10.13.6) Python 3.9.1 Electron 16.0.5

Thank you.

I have an application that consists of two parts

Wow, you’re really ‘crossing the streams’ there. Just add some Qt in there and you’ll have the Mac code signing trifecta! (-:

Let’s start with a thing that’s definitely wrong:

I sign the Python app with these entitlements:

<key>CFBundleIdentifier</key>
<string>com.abtco.myquickmaclite</string>

CFBundleIdentifier is an Info.plist key, not an entitlement. Adding it to your entitlements file is likely to cause problems down the line. You definitely need to remove that.

And the Electron app with these ones:

<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>

The com.apple.security.inherit is only relevant for child processes (in Python that typically means using the subprocess module). Launching an entire app as a child process is weird, but it’s supported and should work. However, this:

Currently, I tried using the open command and an AppleScript, the error stays the same in any case

will cause problems. The open command doesn’t start a child process but rather uses NSWorkspace to launch the app as if it had been double clicked in the Finder. That is not compatible with com.apple.security.inherit.


As to what’s causing Electron’s networking to fail, it’s hard to say. I tried this here in my office using native tools and things worked as expected. Specifically:

  1. I created a test app with an embedded helper tool per the instructions in Embedding a Command-Line Tool in a Sandboxed App.

  2. I added a button to the app and wired it up to call this code:

    func runTool() {
        print("will start tool")
        let p = Process()
        p.executableURL = Bundle.main.url(forAuxiliaryExecutable: "Tool")!
        p.terminationHandler = { _ in
            print("tool did finish, status: \(p.terminationStatus)")
        }
        try! p.run()
        print("did start tool")
    }
    
  3. I modified the tool’s main code as follows:

    import Foundation
    
    func main() {
        print("task will start")
        let url = URL(string: "https://www.apple.com")!
        let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60.0)
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let error = error as NSError? {
                print("task did fail, error: \(error.domain) / \(error.code)")
                exit(EXIT_FAILURE)
            }
            let response = response as! HTTPURLResponse
            let data = data!
            print("task did finish, status: \(response.statusCode), bytes: \(data.count)")
            exit(EXIT_SUCCESS)
        }.resume()
        print("task did start")
        dispatchMain()
    }
    
    main()
    
  4. I ran this code as is. The tool is unable to access the network because the container app doesn’t have the com.apple.security.network.client entitlement.

  5. I added that entitlement to the main app and retested. This time the tool can access the network:

    will start tool
    did start tool
    task will start
    task did start
    …
    task did finish, status: 200, bytes: 67560
    tool did finish, status: 0
    

I recommend that you split this problem into two by (temporarily) replacing your Electron app with a test tool that issues a simple network request, like the one I’ve shown above. If that works, you’ve confirmed that the child process has access to the network and so this must be an Electron-specific issue.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for your reply and suggestions.

As you recommended, I replaced the Electron with another Python app, and successfully reproduced the error! So the problem is not with the Electron but maybe related to the security.inherit.

So, what I did:

  1. Created two python programs "main" and "child", and packaged them with py2app.

The child app is located like this: -- Main App.app/Contents/MacOS/Child connection test.app

  1. "Main" just starts the "child" using the "open" command, then the "child" requests some website (in my case "worldtimeapi.org").
  2. I signed these two programs and embedded the provisioning profile.

Result: The "main" successfully launched the "child", but the "child" crashed due to a network error.

Here is the child's terminal output:

File "urllib3/connection.pyc", line 174, in _new_conn
File "urllib3/util/connection.pyc", line 72, in create_connection
File "socket.pyc", line 954, in getaddrinfo
socket.gaierror: [Errno 8] nodename nor servname provided, or not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "urllib3/connectionpool.pyc", line 703, in urlopen
File "urllib3/connectionpool.pyc", line 398, in _make_request
File "urllib3/connection.pyc", line 239, in request
File "http/client.pyc", line 1285, in request
File "http/client.pyc", line 1331, in _send_request
File "http/client.pyc", line 1280, in endheaders
File "http/client.pyc", line 1040, in _send_output
File "http/client.pyc", line 980, in send
File "urllib3/connection.pyc", line 205, in connect
File "urllib3/connection.pyc", line 186, in _new_conn
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x105927700>: Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "requests/adapters.pyc", line 440, in send
File "urllib3/connectionpool.pyc", line 785, in urlopen
File "urllib3/util/retry.pyc", line 592, in increment
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='worldtimeapi.org', port=80): Max retries exceeded with url: /api/timezone/Europe/London (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x105927700>: Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/Users/root1/fourorganizer/MyQuickMac/bin/net_test/dist/Main test.app/Contents/MacOS/Connection test.app/Contents/Resources/__boot__.py", line 130, in <module>
  _run()
File "/Users/root1/fourorganizer/MyQuickMac/bin/net_test/dist/Main test.app/Contents/MacOS/Connection test.app/Contents/Resources/__boot__.py", line 84, in _run
  exec(compile(source, path, "exec"), globals(), globals())
File "/Users/root1/fourorganizer/MyQuickMac/bin/net_test/dist/Main test.app/Contents/MacOS/Connection test.app/Contents/Resources/connection_test.py", line 3, in <module>
  r = requests.get('http://worldtimeapi.org/api/timezone/Europe/London')
File "requests/api.pyc", line 75, in get
File "requests/api.pyc", line 61, in request
File "requests/sessions.pyc", line 529, in request
File "requests/sessions.pyc", line 645, in send
File "requests/adapters.pyc", line 519, in send
requests.exceptions.ConnectionError: HTTPConnectionPool(host='worldtimeapi.org', port=80): Max retries exceeded with url: /api/timezone/Europe/London (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x105927700>: Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known'))

Part 2, it didn't fit into one message.

In the Console, I see this:

default 12:58:50.396796 +0200 systemsoundserverd 202: AudioFileReadPackets:result 0
default 12:58:50.450804 +0200 opendirectoryd AuthenticationAllowed: Evaluation result for record "root1", record type "users": Success
default 12:58:50.569033 +0200 trustd cert[2]: AnchorTrusted =(leaf)[force]> 0
default 12:58:50.576198 +0200 secinitd MacOS error: -67050
default 12:58:51.027905 +0200 trustd cert[2]: AnchorTrusted =(leaf)[force]> 0
default 12:58:51.037686 +0200 secinitd MacOS error: -67050
error 12:58:51.131837 +0200 WindowServer [ERROR] - Unknown CGXDisplayDevice: 0x41dc9d00
error 12:58:51.133565 +0200 WindowServer [ERROR] - Unknown CGXDisplayDevice: 0x41dc9d00
default 12:58:51.656016 +0200 Connection test dnssd_clientstub ConnectToServer: connect()-> No of tries: 1
default 12:58:52.632375 +0200 symptomsd rssi (-74) or transmitRate (78.000000) changed on interface en1 for BSSID:98:da:c4:29:43:03
default 12:58:52.656344 +0200 Connection test dnssd_clientstub ConnectToServer: connect()-> No of tries: 2
default 12:58:53.657230 +0200 Connection test dnssd_clientstub ConnectToServer: connect()-> No of tries: 3
default 12:58:54.658477 +0200 Connection test dnssd_clientstub ConnectToServer: connect() failed path:/var/run/mDNSResponder Socket:5 Err:-1 Errno:1 Operation not permitted
default 12:58:54.661882 +0200 Connection test dnssd_clientstub ConnectToServer: connect()-> No of tries: 1
default 12:58:55.663223 +0200 Connection test dnssd_clientstub ConnectToServer: connect()-> No of tries: 2
default 12:58:56.664690 +0200 Connection test dnssd_clientstub ConnectToServer: connect()-> No of tries: 3
default 12:58:57.640320 +0200 symptomsd rssi (-76) or transmitRate (78.000000) changed on interface en1 for BSSID:98:da:c4:29:43:03
default 12:58:57.665647 +0200 Connection test dnssd_clientstub ConnectToServer: connect() failed path:/var/run/mDNSResponder Socket:5 Err:-1 Errno:1 Operation not permitted

I googled this "Err:-1 Errno:1 Operation not permitted" error" and found out that it happens when the .app doesn't have proper entitlement for outgoing connections. So, the child doesn't inherit the entitlements from the "main". You said that "open" is not a proper way to launch the child app, so which method should I use to start it properly? So, how should I properly launch the child so it inherits the main app's entitlements?

A bit about the entitlements: codesign -dv --entitlements - "dist/Main test.app"

<?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>com.apple.application-identifier</key>
 <string>MY_TEAM_ID.com.abtco.myquickmaclite</string>
 <key>com.apple.developer.team-identifier</key>
 <string>MY_TEAM_ID</string>
 <key>com.apple.security.app-sandbox</key>
 <true/>
 <key>com.apple.security.application-groups</key>
 <array>
  <string>MY_TEAM_ID.com.abtco.myquickmaclite</string>
 </array>
 <key>com.apple.security.network.server</key>
 <true/>
 <key>com.apple.security.network.client</key>
 <true/>
</dict>
</plist>

codesign -dv --entitlements - "dist/Main test.app/Contents/MacOS/Connection test.app"

<?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>com.apple.security.app-sandbox</key>
      <true/>
      <key>com.apple.security.inherit</key>
      <true/>
      <key>com.apple.application-identifier</key>
      <string>MY_TEAM_ID.com.abtco.myquickmaclite</string>
      <key>com.apple.developer.team-identifier</key>
      <string>MY_TEAM_ID</string>
      <key>com.apple.security.application-groups</key>
      <array>
      <string>MY_TEAM_ID.com.abtco.myquickmaclite</string>
      </array>
  </dict>
</plist>

If I remove these "team-identifier" or "application groups" entitlements, it doesn't start.

If you need any other information (maybe info.plist, or something else), please let me know, and I will send it to you ASAP.

Thank you for your time and response.

"Main" just starts the "child" using the "open" command

Is this the open command-line tool, as documented in the open man page? If so, that’s going to be a problem. The open command doesn’t launch the process as a child of your process, it launches it as a separate app (it’s a child of launchd). This means it won’t inherit the parent app’s sandbox.

In my previous post I wrote:

The com.apple.security.inherit is only relevant for child processes (in Python that typically means using the subprocess module).

If you use open, the child isn’t actually a child but rather a peer, and you’ll need to configure its sandbox independently.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Ok. As I understand, you recommend me to configure Electron's sandbox independently. So, I will need to remove the security.inherit and it will work? If I do it, can Electron stay in the MacOS directory? Do I need to move it to other directory like Frameworks or somewhere else? I will try it and update. Thank you.

So, I tried resigning Electron app as the main app and embedding it into the Python app. The result stays the same: it doesn't load the server's webpage and it can't make any network requests.

I still launch it with the "open" command (I still don't have any alternatives). But right now, it has its own entitlements required to send network requests, why doesn't it work? Any ideas on how this can be resolved?

Here's the Console log:

One caveat with all of this: I’m answering questions from an Apple perspective. You’re using a bunch of third-party tooling and it may impose additional requirements.

you recommend me to configure Electron's sandbox independently.

My specific recommendation is that you be consistent:

  • If you launch via open, you must use an independent sandbox.

  • If you launch as a child process, you must use sandbox inheritance.

Mixing those two definitely won’t work.

can Electron stay in the MacOS directory?

Yes (subject to the caveat above).

I still launch it with the open command (I still don't have any alternatives).

The alternative is, as I’ve suggested multiple times, using Python’s subprocess module to launch it as a child process.

Note I’m not say that this will improve things; I just want to be clear that there is an alternative.

why doesn't it work?

I’ve no idea.

As I mentioned earlier, I tested the sandbox inheritance using Apple tools and libraries and it works. I’m confident that the independent sandbox case will work equally well. That gives you cases that you know work and cases that you know don’t work. The next step is to mix’n’match to try to isolate the critical factor (or factors). So, you could:

  • Continue with your Python container app but instead launch a tiny native helper that issues a network request and displays the results.

  • Continue with your Electron helper but with a native container app to launch it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Helper app cannot access any web pages?
 
 
Q