Xcode notarization and hardened runtime issue

Hi, I'm somewhat new to swift and coding in general, so thanks for your help in advance. I'm building an application that uses Process class to launch a Unix Executable (Swift 5, Cocoa). Unfortunately, when I go to notarize my application, I get the below error. The trouble is, hardened runtime is already enabled and working (along with sandbox). I have tried everything I can think of including removing and re-adding the executable, cleaning build folders, messing with targets, changing hardened runtime exceptions, etc. I have even attempted to force enable hardened runtime on the executable by using "codesign --force --deep --options runtime <Devloper ID Application cert identity> <path to executable>, however, this appears to break the executable, making zsh throw a "Segmentation Fault" when it is run. (the broken executable successfuly passes notarization, but that doesn't help me much) Thanks so much for your help, please tell me if anything was unclear or confusing. 🙂



Error:

Distribution items ineligible: Error Domain=IDEDistributionMethodDeveloperIDErrorDomain Code=1 "Hardened Runtime is not enabled." UserInfo={NSLocalizedDescription=Hardened Runtime is not enabled., NSLocalizedRecoverySuggestion="myunixexecutable" must be rebuilt with support for the Hardened Runtime. Enable the Hardened Runtime capability in the project editor, test your app, rebuild your archive, and upload again.}

Replies

I have attempted both with and without sandboxing. It currently does not have it.

That message is just saying that you don't have the hardened runtime enabled at all. That is a requirement. I entered a sample codesign command above that would add the hardened runtime flag.


I'm sorry, but I just don't understand what the issue is.

1) You have some 3rd party app. Fine. Build it however you do that.

2) Codesign it with codesign --timestamp --options=runtime -s "Developer ID Application: Whoeveryouare" -v /path/to/xmrig

3) Add xmrig to your Xcode project

4) Add a build phase to stuff it into Contents/Helpers


Checking "Code sign on copy" is not going to break the app. What exactly do you mean by "stop working"? Do you mean your app? It is your responsibility to launch the helper and run it. The helper should run on your machine regardless of code signing status. I don't know this software so I don't know if the hardened runtime would affect it or not. You are the one with software. You could test this in 30 seconds or however long it takes to run the cryptocurrency miner. Run it from the command line after signing it. Does it work? If not, you're done. Find another project. If it does work, then find out where it is broken.


PS: You do realise xmrig is a GPL 3.0 project? You are OK with making your project GPL 3.0 too? If so, just post the github link to your project. You are legally obligated to do that, after all.

Hardened runtime must affect the exec, since it simply wont run anymore after going through the codesign process. I assumed there was some error in what I was doing, but the way its looking, your telling me it just can't be done. Unfortuantly Xmrig is one of a kind, and there aren't really any other open source miners that support MacOS at all. So unless anyone has any ideas, it looks like I'll have to find a way to live without notarization. Is this correct?


PS: I did, and according to my understanding, I'm only required to do that once the code is distributed to the public, which it currently isn't.

It currently does not have it.

Cool. Sandboxing complicates things a little, so let’s assume it’s off for the moment. Here’s how I’d set this up:

  1. First build your tool in your external build system.

  2. Make sure to sign it with a meaningful code signing identifier and with the hardened runtime enabled. The code signing identity doesn’t matter here; in my case, I used ad hoc signing (Sign To Run Locally). Here’s the exact command I used:

    % codesign -s - -f --identifier "com.example.apple-samplecode.Test129544.Tool" -o runtime Test129544Tool

    .

  3. In Xcode 11.3 on macOS 10.15.3, create a new app target (File > New > Project > macOS > App). Set the language popup to Swift and the User Interface popup to Storyboard.

  4. In the target editor’s Signing & Capabilities tab, set up Signing to use Development signing for your team.

  5. Also remove App Sandbox and add Hardened Runtime.

  6. Add your tool to your project.

  7. In the target editor’s Build Phases tab, add a Copy Files build phase. Name it

    Embed Helper Tool
    and set the Destination popup to Executables.
  8. Add the tool to the build phase. Make sure Code Sign On Copy is set.

  9. Choose Product > Archive.

  10. In the Organizer, click Distribute App and then follow the Developer ID > Export workflow.

  11. Now look at the resulting app:

    % codesign -d -vvv "Test129544 2020-02-26 10-10-38/Test129544.app"
    …
    Identifier=com.example.apple-samplecode.Test129544
    …
    CodeDirectory v=20500 size=499 flags=0x10000(runtime) …
    …
    Authority=Developer ID Application: Quinn Quinn (SKMME9E2Y8)
    …
    Timestamp=26 Feb 2020 at 10:10:35
    …
    % codesign -d -vvv "Test129544 2020-02-26 10-10-38/Test129544.app/Contents/MacOS/Test129544Tool" 
    …
    Identifier=com.example.apple-samplecode.Test129544.Tool
    …
    CodeDirectory v=20500 size=407 flags=0x10000(runtime) …
    …
    Authority=Developer ID Application: Quinn Quinn (SKMME9E2Y8)
    …
    Timestamp=26 Feb 2020 at 10:10:35
    …

    As you can see:

    • The code signing identifier is set to sensible values in both cases.

    • The hardened runtime flag is set on both the app and the tool.

    • They are both signed with my Developer ID.

    • They both have a secure timestamp.

I would expect this app to pass notarisation just fine.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

your telling me it just can't be done


No. I was telling you the exact opposite. Notarization is as simple as falling off a log. I had planned to give you step-by-step instructions on how to create a demo app using the built-in Xcode templates, add your helper and run. I did that, but discovered that you do have a special case here. The xmrig executable does need special entitlements. Unfortunately, I have no idea what you are doing, so I will continue with my "sanity check" instructions. Due to the nature of the xmrig project, they are more complicated than normal.


1) Build xmrig according to official directions. Note that the official instructions will never, ever work in a real app.

2) Copy the xmrig app to working location

3) Copy the file /usr/local/opt/hwloc/lib/libhwloc.15.dylib to the same location

4) Fix the dylib to make it embeddable with the following command:

install_name_tool -id @rpath/libhwloc.15.dylib libhwloc.15.dylib

5) Fix the xmrig tool so that it will work with the dylib:

install_name_tool -change /usr/local/opt/hwloc/lib/libhwloc.15.dylib @executable_path/../Frameworks/libhwloc.15.dylib xmrig

Note that xmrig will not work at this point. It has to be properly embedded in the app before it will work.

6) Create a new macOS Swift app in Xcode from the template

7) Add the xmrig tool and the dylib to the project. Click the "copy" option.

8) Xcode will try to figure out what to do with these two files. It will guess wrong. Go into Build Phases and remove xmrig from the "Copy Bundle Resources" step. Also remove the dylib from "Link Files" step. In this simple case, I don't have any other dylibs, so I just removed the entire step. The executable can't be in the Resources folder. The dylib is for the helper executable, so it does no good to link it to the app itself.

9) Add a new Copy Files step to copy the dylib to the Frameworks Destination

10) Add another Copy Files step to copy the xmrig tool to the Wrapper Destination with the Subpath "Contents/Helpers". Use can uncheck "Code Sign On Copy" if you want because we need a custom code signing step for this file. Or you can leave it in and add "--force" to overwrite it. Doesn't matter.

11) Add a Run Script step to do the manual code signing. Use the following content:

/usr/bin/codesign --timestamp --options=runtime -s "Developer ID Application: whoeveryouare" --entitlements $SRCROOT/xmrigapp/xmrig.entitlements --force -v $TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/Helpers/xmrig

12) The key in the above step is that entitlements file. You will need to create that. Use the following as contents:

<?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.cs.allow-unsigned-executable-memory</key>

<true/>

<key>com.apple.security.cs.disable-library-validation</key>

<true/>

</dict>

</plist>

13) You will have to create this file manually and save it as "xmrig.entitlements". My project is named "xmrigapp". You may have to adjust paths if you used something different. You can add it to your Xcode project just so it is handy. But you aren't using it directly in Xcode.

14) In your Swift source code, load and run the tool. Just print out the help text for now. Here is the source for that:

  func applicationDidFinishLaunching(_ aNotification: Notification)
    {
    let xmrig = Process()
    
    xmrig.executableURL =
      Bundle.main.bundleURL.appendingPathComponent("Contents/Helpers/xmrig")
    
    xmrig.arguments = ["--help"]
    
    do
      {
      let outputPipe = Pipe()
      let errorPipe = Pipe()
      xmrig.standardOutput = outputPipe
      xmrig.standardError = errorPipe
      
      try xmrig.run()
      
      xmrig.waitUntilExit()
      
      let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
      let errorData = outputPipe.fileHandleForReading.readDataToEndOfFile()
      let output = String(decoding: outputData, as: UTF8.self)
      let error = String(decoding: errorData, as: UTF8.self)

      print(output)
      print(error)
      print("Success!")
      }
    catch
      {
      print("xmrig failed!")
      }
    }

This is not production-quality code. It just a quick-n-dirty example.

15) Build and test it. Hopefully it works.

16) Archive it and distribute as Developer ID. Click the "Upload" option to Notarize.

17) Wait for the notification that the notarization was successful.

18) Export your notarized app from the archive using the Organizer.


I realize this is rather complicated. There are some important takeaways here:

1) Notarization is the easy part. Always.

2) Your app would have never worked on a different machine, even without the notarization requirement. The dylib would have always broken it. All of your problems came from that funky open-source project

3) Never use homebrew. Homebrew is fine for what it is: "home" "brewing". Never distribute code from homebrew or from anything built using homebrew. It seems to have worked in this example only because this particular project was ridiculously easy to build and hack up the linkage. Normally, such things are much more difficult. I used Homebrew in a VM to build it. It was just dumb luck that I only hack to hack up a single dylib. I have no idea if the tool will do anything more than print the help text. The Homebrew instructions had several dependences, but only one dylib seemd to be required in the end. That's suspicious.

4) All of your problems came from the xmrig tool. I have no idea what it is doing. Neither do you. This is a Red Flag. This exact tool has been used in malware distributions. It is possible that your app's notarization could be revoked at some point just because the xmrig code gets flagged as a malware component. Isn't cryptocurrency fun!


I don't think I've missed any steps. You should strongly consider a VM or a factory-fresh machine to test your built code. One you install homebrew on your development machine, you can't ever trust it to build distributable software again. All of the above could be substantially improved. It just a quick-n-dirty demonstration. Ironically enough, the only part that is easy and would exactly the same in a production-quality app is the notarization part.

Suppose the command-line application doesn't have hardened runtime enabled (some open-source projects can be quite complicated to build, or perhaps its a vendor-supplied component). Can the OP notarize the containing application bundle and have it work on other people's machines? Given that the containing app can have hardened runtime enabled, but the helper can not.

It would be better to start your own question instead of piggy-backing onto this one.


The hardened runtime is a notarization requirement, so that's the end of the story. There are several exceptions that should accomodate even the funkiest of open-source packages. Whether any such package would run properly on a Mac to begin with is a completely different question. In many (most? all?) cases, they haven't been fully tested on a Mac.

What john daniel said but also…

some open-source projects can be quite complicated to build

The hardened runtime flag is part of the code signature, as are the hardened runtime exception entitlements. Open-source projects rarely know anything about code signing, so you end up doing that yourself, and that gives you the opportunity to enable the hardened runtime and apply any exception entitlements that may be necessary.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hello,

I have the same problem with "Segmentation fault"


>>Maybe this should be re-worded. The app is broken. The code signing identifies the bug. Not the other way around.

Wow, Apple reports error in our application by message "Segmentation fault" ?


Till January 2020 I had workaround by using parameter "--timestamp=none". Using that the codesign does not fail. With your words, problem is in mine application (I have the same symptoms like JacobMcD). Interesting is that turning off secure timestamps it does not fail. So it fixed my application?


I'd appreciate if the codesign say what's wrong, not that it fails. Especially when the code signing and notarization process is a must and Apple makes every obstacle possible to distribute the app.


Regards,

Libor

Wow, Apple reports error in our application by message "Segmentation fault"?

Till January 2020 I had workaround by using parameter

--timestamp=none
. Using that the
codesign
does not fail.

Are you saying that your executable crashes with a segmentation fault? Or that

codesign
crashes with a segmentation fault? Because JacobMcD started this thread with the former. Consider this quote from their initial post:

I have even attempted to force enable hardened runtime on the executable … however, this appears to break the executable, making

zsh
throw a "Segmentation Fault" when it is run.

This is not

codesign
crashing, but their executable.

OTOH, if you’re seeing

codesign
crash, that’s definitely weird.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"