Hardened Runtime Code Signing for the macOS .app bundle

401 views
Skip to first unread message

Efrem Braun

unread,
Apr 28, 2021, 12:39:21 PM4/28/21
to PyInstaller
I'm trying to distribute my PyInstaller-bundled application to other Mac users outside the app store in a manner in which they don't need to have admin access to allow the application to run. This, of course, requires code signing. Very good instructions for this are given on https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OSX-Code-Signing.

However, per https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution, "Beginning in macOS 10.14.5, software signed with a new Developer ID certificate and all new or updated kernel extensions must be notarized to run. Beginning in macOS 10.15, all software built after June 1, 2019, and distributed with Developer ID must be notarized."

To get notarized, per https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution, one must "Enable the Hardened Runtime capability for your app and command line targets."

This would mean that instead of doing `codesign --deep -s "Code Signing Test" MyAppName.app`, one would have to do `codesign --deep -o runtime -s "Code Signing Test" MyAppName.app`.

Here comes the problem. For some Python codes, this works fine, but for other, it doesn't allow the PyInstaller-generated bundle to run. At least in one example script, I was able to isolate the difference that caused this discrepancy to importing NumPy. At the bottom of this email is the Python script test.py which shows a simple PySide2-enabled GUI. With the `import numpy as np` line commented out, code signing with Hardened Runtime allows the PyInstaller-bundled application to run. After uncommenting the line, running the app generates the following error:
```
$ ./MyAppName.app/Contents/MacOS/MyAppName
Traceback (most recent call last):
File "PyInstaller/loader/pyiboot01_bootstrap.py", line 122, in <module>
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "/Users/edbraun/Documents/xslide-git-repos/xslide-desktop/release/release_venv/lib/python3.7/site-packages/PyInstaller/loader/pyimod03_importers.py", line 493, in exec_module
File "ctypes/__init__.py", line 551, in <module>
File "ctypes/__init__.py", line 273, in _reset_cache
MemoryError
[3273] Failed to execute script pyiboot01_bootstrap
```

I figured that an entitlement might be needed as per https://developer.apple.com/documentation/security/hardened_runtime, so I tried adding to Info.plist a few of the entitlements listed (e.g., com.apple.security.cs.allow-unsigned-executable-memory), and I tried signing with `codesign --deep -o runtime --entitlements MyAppName.app/Contents/Info.plist -s "Code Signing Test" MyAppName.app`. The stack trace shown above no longer appears, but launching the application immediately results in `zsh: killed` being shown to the terminal, and the app doesn't launch. (This occurs regardless of any edits to the Info.plist file.)

Does anyone have any other ideas for how to code sign the PyInstaller-bundled application with Hardened Runtime in a way that will allow the NumPy application to run?


I'm using PyInstaller 4.1, Python 3.7.10, macOS Catalina 10.15.7, PySide2 5.15.2, and NumPy 1.19.4. I generate the PyInstaller-bundled application with `pyinstaller --name="MyAppName" --windowed test.py`. test.py is below:
```
import sys
#import numpy as np
from PySide2.QtWidgets import *

class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setWindowTitle("My Awesome App")
        label = QLabel("This is a PySide2 window!")
        self.setCentralWidget(label)

app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
```

bwoodsend

unread,
Apr 28, 2021, 2:23:23 PM4/28/21
to PyInstaller

There’s another more recent gist here from someone who’s managed to do notarization - albeit no NumPy. I lack a paid Apple developer account so I can’t even test it.

Efrem Braun

unread,
Apr 30, 2021, 2:51:22 AM4/30/21
to PyInstaller
That fixes it! Apparently those entitlements need to be in a separate file from the Info.plist file.

Thanks so much!

Reply all
Reply to author
Forward
0 new messages