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.}

These problems are usually caused by things like incorrectly structured bundles, bad Info.plists, incorrect entitlements, and just crazy stuff.


I'm curious about exactly what you are doing with zsh in this app. I suggest posting an outline of your bundle structure to start with. And maybe list your entitlements too.


And what are you doing with the sandbox? Is this going to be a Mac App Store app? Since you are having problems, it is best to focus on what your eventual target is - Developer ID or Mac App Store. We can debate the relative merits of the sandbox outside of the Mac App Store at a later date.

Interesting, Its definitely possible that one of those things could be the issue, as i'm not well versed in, well, any of them.


The zsh was just what responds when the broken file is opened (the executable normally outputs data to command line). "exit; zsh: segmentation fault" But my main application doesn;t appear to be able to start the process at all, nothing happens.


The sandbox isn't neccicary as far as I know, I was just under the impression that it's considered good practice. Developer ID. The application involves cryptocurrency, so I definitely don't want to get anywhere near the App Store. Thanks for the help.

Is there an easy way to post the bundle structure? Should I just type it out? Screenshot? I don't think I need Resource Access at all, and i'm currently not sure about Runtime Exceptions.

Just type it out.


You can also just look at any old existing app and state what you do differently. TextEdit is a good example. Look at the structure of TextEdit. Don't consider any of the "pl.lproj", "zh_TW.lproj", etc. language resources. How is your app structured differently?


For Resource Access or Runtime exceptions, you talking about hardened runtime exceptions. Exceptions don't break anything. They don't prevent signing or notarization. Sometimes you need Info.plist explations for why you are asking for the camera or something. But that is something else. Just turn them all off. If that breaks something in your app, then you can deal with that later.


You can also deal with zsh later too. It sounds like that only breaks when you are trying to hack the code signing. An easy solution to many code signing/notarization issues is to not try to hack the code signing. It does seem odd that your native app would be doing anything with zsh. I'm almost certain that's wrong, but, again, it's something you can look at later.

Let me confirm my understanding here:

  • You can an app written in Swift.

  • Embedded within that app is a command-line tool, also written in Swift.

  • You want to run the latter from the former using

    Process
    .

First question: Are you building your command-line tool from source within your project? Or was it built separately and you’re trying to integrate the pre-built executable into you app?

Share and Enjoy

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

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

I took a peek at TextEdit, looks like the only differences are that I have a frameworks folder and a library folder in "contents," and Textedit has a version.plist file in contents.


My app:

Contents

_CodeSignature

CodeResources

Frameworks

Sparkle.framework

LaunchAtLogin.framework

Library

LoginItems

MacOS

myAppExec

Resources

myProblemExec

Assets.car

Base.Iproj

storyboard1.storyboardc

storyboard2.storyboardc

Info.plist

PkgInfo


Is that what you were looking for?


Okay, i'll make sure not to hack the code signing. I'm not sure about zsh, It only ever appears when I break the exec with the code signing hack.

Yes, my main app is written in swift. The command line exec is definitly not written in Swift and is from an open source project. I believe it's acutally C++. I am succussfully running it using process (took me a while to get that one working), the issue is notarization fails on the exec.


I am building it seperately using cmake (per the developer's instructions), then integrating it into my app. Did that answer your question?

Create a new folder named "Helpers" and move "myProblemExec" to "Helpers".


Do you actually have any items in LoginHelpers? If so, that's fine. If not, then you probably shouldn't have the "Library" folder. I'm not sure if this would be a problem or not. The executable inside the resources folder is definitely a problem.


You can definitely build the helper externally. Just make sure to sign it with the hardened runtime and timestamp flags. I'm still concerned by the zsh reference. Are you executing it via zsh? Or is it doing something with zsh? Regardless, zsh is an interactive shell. It shouldn't be used in this context. If you really, really need a shell in an automated task, use "/bin/sh". But when running one app from another like this, you normally don't want to route it through a shell at all if you can help it. This probably doesn't have anything to do with notarization, but it could be a problem in other ways.

Okay, I moved it to Helpers. I'm not sure how to retrive the path for the exec in this new folder. "Bundle.path(forResource: "execname", ofType: nil, inDirectory: "Helpers")" doesn't seem to work. Before I was using Bundle.main.path(forResource: <String?>, ofType: <String?>).


Yes I do have a helper in LoginHelpers.


How can I sign it with the hardened runtime and timestamp flags that when the "codesign --force --deep --options runtime <Devloper ID Application cert identity> <path to executable>" (specifically the --options runtime option ) breaks it?


I'm not sure how its being executed (I'm just using let a = process() , a.run()) How would I run it without a shell? Normally, when this problem exec is run without my program, it is an automated command line application.

I also believe moving the exec may have fixed the notarazation issue, but I don't have a reliable internet connection at the moment, so i'm going to have to try again later.

Okay, I moved it to Helpers. I'm not sure how to retrive the path for the exec in this new folder. "Bundle.path(forResource: "execname", ofType: nil, inDirectory: "Helpers")" doesn't seem to work. Before I was using Bundle.main.path(forResource: <String?>, ofType: <String?>).


Just use bundlePath and append Contents/Helpers/myProblemExec


How can I sign it with the hardened runtime and timestamp flags that when the "codesign --force --deep --options runtime <Devloper ID Application cert identity> <path to executable>" (specifically the --options runtime option ) breaks it?


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


To be clear - both "--force" and "--deep" are hacks. They are option that you use when you want to spend weeks and weeks debugging notarization issues. If you don't want to do that, remove them. The app may not work now. But now your app doesn't work for a reason that clearly identifies the bug, allowing you to fix it. Use of "--force" and "--deep" only obscure the bug so that you can be mystified by random other failures further down the line.


To code sign an executable, do this:

codesign --timestamp --options=runtime -s "Developer ID Application: Whoeveryouare" -v /path/to/executable


I'm not sure how its being executed (I'm just using let a = process() , a.run()) How would I run it without a shell? Normally, when this problem exec is run without my program, it is an automated command line application.


I'm pretty sure there is more to it than that. What I am asking is this - are you using the string "zsh" or not? I strongly suggest you figure out exactly what code you are executing. What if you release this cryptocurrency app and someone comes to you asking where their money is. They ask what the app is doing and you don't know. You might want to double-up on that ole' business liability insurance for this one.


Normally, if you need to run some helper app, you are going to want to extract data from it somehow. Normally that takes a more work. I've got almost 600 lines of Objective-C to do this. Some of the tools I run need a pseudo terminal, and that is 200 lines of slightly different code.

Just use bundlePath and append Contents/Helpers/myProblemExec


I'm getting this: Error Domain=NSCocoaErrorDomain Code=4 "The file “xmrig” doesn’t exist." I dont think xcode is acutally adding the Helpers folder in the contents folder. Should the folder's location be "relative to group?" The file definitely exists in my project files, just not in the actual application contents folder?


Thanks for clarifying on the code signing stuff.


No, it should not be using zsh. Funny enough, i don't actually need to extract data from it, I have another way of making sure it gets its job done. Thanks so much.

I don't know what xmrig is. I just use a "Copy Files" build script to copy the helper to the "Wrapper" destination at subpath "Contents/Helpers"

xmrig is the problem exec (https://github.com/xmrig/xmrig). After adding the copy phase, looks like we're back to the same old issue. " 'xmrig' 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." Enabling the "Code sign on copy" checkbox in the Copy Files Phase causes the exec to stop working, and notarization fails regardless of whether that option is enabled or not.

Did that answer your question?

Yes. It seems like you’re integrating a pre-built executable.

Next question: Is your main app sandboxed?

Share and Enjoy

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

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

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"
Xcode notarization and hardened runtime issue
 
 
Q