I am deploying an extremely simple Swift helper command line app together with my Electron + .NET Core macOS application. The source code consist of only one small file and only one import (AppKit).
When deploying it together with my app, the whole app won't start (even though I code-sign and notarize everything), complaining that it comes from an "unidentified developer". I've checked the logs and found out that it is an RPATH entry that macOS (Gatekeeper?) is angry about:
after the build and before signing / notarizing, everything works fine, at least in a fresh Big Sur VM. So it doesn't seem like the RPATH entry is needed.
My question is: How can I keep the swift compiler from adding the RPATH entry in the first place, so I don't have to remove it after the build?
As I am totally new to swift, it could be that I'm asking the wrong question and that I have misunderstood something about dependencies, frameworks, ...
This is my Package.swift file. I use VSCode, not the Xcode UI.
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Server.MacOSHelper",
dependencies: [
],
targets: [
.target(
name: "Server.MacOSHelper",
dependencies: [])
]
)
The compiler has some hidden flags to turn that off:
> swift --help-hidden | ag stdlib-rpath -A1
-no-stdlib-rpath Don't add any rpath entries.
-no-toolchain-stdlib-rpath
Do not add an rpath entry for the toolchain's standard library (default)
-toolchain-stdlib-rpath Add an rpath entry for the toolchain's standard library, rather than the OS's
-trace-stats-events Trace changes to stats in -stats-output-dir
You can try swift build -c release -Xswiftc -no-toolchain-stdlib-rpath, I think that will do what you want (I've never used Swift on a Mac, that definitely works on linux and Android).
Gatekeeper only runs these checks if you have library validation disabled, either because you haven’t enabled the hardened runtime or because you’ve applied the disable-library-validation hardened runtime exception entitlement.
Thank you! Unfortunately neither -no-stdlib-rpath nor -no-toolchain-stdlib-rpath helped. Perhaps I have to deliver something together with my binary because I import AppKit? But, as I said, my single-file binary works totally fine on a fresh Big Sur VM.
You only need to disable library validation if you're linking with code that was signed by another third-party developer (that is, not you and not Apple). How is that the case here?
In my experience most folks using third-party runtimes include a copy of the runtime with their app and thus can (and in some cases must) re-sign the runtime with their own signing identity. This is the easiest and best solution to this problem. Disabling library validation has its place — specifically, if you plan to load plug-ins from various other third-party developers — but most standalone apps should ship with it enabled because it’s a significant security benefit.
Which isn’t to say that SPM should be adding random rpath entries to release code. That is, IMO, eminently bugworthy. You can work around that by removing the rpath entry using install_name_tool. However, in most cases that’s the wrong solution to this Gatekeeper issue because it continues to leave library validation disabled, which is a bad option security-wise.
Alright, I dug deeper and apparently SPM also sets a stdlib RPATH for Darwin executables only, which explains why I never heard of it, and there's no way to disable it. Looks like you're stuck removing it manually, or maybe you could submit a pull to SPM with a flag to disable that.
Quinn "The Eskimo", thank you for the excellent explanation! This makes total sense to me.
I was indeed able to solve my problem by removing the com.apple.security.cs.disable-library-validation
entitlement, and code-signing all executables files that MSBuild generates / copies, except for DLL files.
I assume that the DLL files ("assemblies") don't need to be (and probably cannot be) code-signed because they are no "real" executables to macOS, but files that are just in time-compiled by the .NET runtime.
I assume that the DLL files ("assemblies") don't need to be (and
probably cannot be) code-signed because they are no "real" executables
to macOS
Correct. The Apple code signing architecture supports code and data, where code means a Mach-O. If you try to sign data as code, the code signature gets stored in extended attributes, which often doesn’t end well. Thus, if you have ‘code’ that’s not Mach-O, such as a .NET DLL, it’s best to sign it as data. This extends to code-like things, for example, shell scripts.