Skip to content

Commit

Permalink
Improve Apple code signing (#419)
Browse files Browse the repository at this point in the history
- We remove the `skipIfSigned` functionality from the `AppleCodeSigner`
- We add verbose logging to `xcrun` calls if it is enabled

We've concluded that it is not the responsibility of a maintainer of an
open-source library to sign their code for MacOS installers, as such we
want to sign these packages ourselves. This may cause us to assume
responsibility for issues in our application caused by these packages;
this seems reasonable.

We have previously encountered issues when releasing `typedb-studio` due
to a package that was signed, but not specifically for MacOS installers,
failing Apple's checks. As such, we want to sign *all* packages captured
by our `deepSignJarsRegex` - not just unsigned ones. This ensures that
all signatures are valid.

However, we still value the `deepSignJarsRegex` to avoid unnecessarily
unpackaging and re-signing every single jar we depend on.

We remove the `skipIfSigned` argument and the functionality gated behind
it.

We also fix an issue where the `verbose` logging flag was not being
correctly propagated to our code signing commands.
  • Loading branch information
sam-butcher committed Nov 22, 2024
1 parent 425493c commit a44871f
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 36 deletions.
4 changes: 2 additions & 2 deletions common/shell/Shell.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class Shell(private val logger: Logger, private val verbose: Boolean = false, pr
return verbose && (!sensitive || printSensitiveData)
}

class Command(vararg args: Argument) {
val args = args.toList()
class Command(val args: List<Argument>) {
constructor(vararg args: Argument): this(args.toList())

override fun toString(): String {
return args.toString()
Expand Down
20 changes: 2 additions & 18 deletions platform/jvm/AppleCodeSigner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args.KEYCHAIN
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args.OPTIONS
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args.SIGN
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args.STRICT
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args.TIMESTAMP
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Codesign.Args.VERIFY
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Paths.CONTENTS
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Paths.MAC_OS
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Paths.RUNTIME
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Paths.TMP
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Security.CN
import com.vaticle.bazel.distribution.platform.jvm.AppleCodeSigner.Security.CREATE_KEYCHAIN
Expand Down Expand Up @@ -140,7 +135,7 @@ class AppleCodeSigner(private val shell: Shell, private val macEntitlements: Fil

val nativeLibs = tmpDir.listFilesRecursively().filter { it.extension in listOf(JNILIB, DYLIB) }
if (nativeLibs.isNotEmpty()) {
nativeLibs.forEach { signFile(file = it, skipIfSigned = true) }
nativeLibs.forEach { signFile(file = it) }
jar.setWritable(true)
jar.delete()
shell.execute(listOf(ShellArgs.Programs.JAR, "cMf", "../${jar.path}", "."), baseDir = tmpPath)
Expand All @@ -150,18 +145,7 @@ class AppleCodeSigner(private val shell: Shell, private val macEntitlements: Fil
}
}

fun signFile(file: File, skipIfSigned: Boolean = false) {
if (skipIfSigned) {
val verifySignatureResult = VerifySignatureResult(
shell.execute(listOf(CODESIGN, VERIFY, STRICT, file.path), throwOnError = false)
)
if (verifySignatureResult.status == VerifySignatureResult.Status.SIGNED) return
else if (verifySignatureResult.status == VerifySignatureResult.Status.ERROR) {
throw IllegalStateException("Command '${CODESIGN}' failed with exit code " +
"${verifySignatureResult.exitValue} and output: ${verifySignatureResult.outputString()}")
}
}

fun signFile(file: File) {
file.setWritable(true)
val signCommand: MutableList<String> = mutableListOf(
CODESIGN, SIGN, certSubject,
Expand Down
6 changes: 4 additions & 2 deletions platform/jvm/JVMPlatformAssembler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,10 @@ object JVMPlatformAssembler {
null -> logger.debug { "Skipping notarizing step: Apple code signing is not enabled" }
else -> {
MacAppNotarizer(
dmgPath = Path.of(distDir.path, "${options.image.filename}-$version.dmg")
).notarize(codeSigningOptions)
dmgPath = Path.of(distDir.path, "${options.image.filename}-$version.dmg"),
appleCodeSigning = codeSigningOptions,
logging = options.logging,
).notarize()
appleCodeSigner!!.deleteKeychain()
}
}
Expand Down
38 changes: 24 additions & 14 deletions platform/jvm/MacAppNotarizer.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.vaticle.bazel.distribution.platform.jvm

import com.vaticle.bazel.distribution.common.Logging.LogLevel.DEBUG
import com.vaticle.bazel.distribution.common.Logging.LogLevel.ERROR
import com.vaticle.bazel.distribution.common.Logging.Logger
import com.vaticle.bazel.distribution.common.shell.Shell
import com.vaticle.bazel.distribution.common.shell.Shell.Command.Companion.arg
import com.vaticle.bazel.distribution.platform.jvm.JVMPlatformAssembler.shell
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.APPLE_ID
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.NOTARYTOOL
Expand All @@ -11,29 +15,34 @@ import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.STAPLER
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.SUBMIT
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.TEAM_ID
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.TIMEOUT
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.VERBOSE
import com.vaticle.bazel.distribution.platform.jvm.MacAppNotarizer.Args.WAIT
import com.vaticle.bazel.distribution.platform.jvm.ShellArgs.Programs.XCRUN
import java.nio.file.Path

class MacAppNotarizer(private val dmgPath: Path) {
fun notarize(appleCodeSigning: Options.AppleCodeSigning) {
shell.execute(notarizeCommand(appleCodeSigning)).outputString()
class MacAppNotarizer(
private val dmgPath: Path, appleCodeSigning: Options.AppleCodeSigning, private val logging: Options.Logging
) {
private val logger = Logger(logLevel = if (logging.verbose) DEBUG else ERROR)

fun notarize() {
shell.execute(notarizeCommand).outputString()
logger.debug { "\nUse `xcrun notarytool log <submission-id>` to view further information about this notarization\n" }
markPackageAsApproved()
}

private fun notarizeCommand(appleCodeSigning: Options.AppleCodeSigning): Shell.Command {
return Shell.Command(
Shell.Command.arg(XCRUN), Shell.Command.arg(NOTARYTOOL), Shell.Command.arg(SUBMIT),
Shell.Command.arg(APPLE_ID), Shell.Command.arg(appleCodeSigning.appleID),
Shell.Command.arg(PASSWORD), Shell.Command.arg(appleCodeSigning.appleIDPassword, printable = false),
Shell.Command.arg(TEAM_ID), Shell.Command.arg(appleCodeSigning.appleTeamID, printable = false),
Shell.Command.arg(WAIT), Shell.Command.arg(TIMEOUT), Shell.Command.arg(ONE_HOUR),
Shell.Command.arg(dmgPath.toString()),
)
}
private val notarizeCommand = Shell.Command(listOfNotNull(
arg(XCRUN), arg(NOTARYTOOL), arg(SUBMIT),
if (logging.verbose) arg(VERBOSE) else null,
arg(APPLE_ID), arg(appleCodeSigning.appleID),
arg(PASSWORD), arg(appleCodeSigning.appleIDPassword, printable = false),
arg(TEAM_ID), arg(appleCodeSigning.appleTeamID, printable = false),
arg(WAIT), arg(TIMEOUT), arg(ONE_HOUR),
arg(dmgPath.toString()),
))

private fun markPackageAsApproved() {
shell.execute(listOf(XCRUN, STAPLER, STAPLE, dmgPath.toString()))
shell.execute(listOfNotNull(XCRUN, STAPLER, STAPLE, if (logging.verbose) VERBOSE else null, dmgPath.toString()))
}

private object Args {
Expand All @@ -46,6 +55,7 @@ class MacAppNotarizer(private val dmgPath: Path) {
const val SUBMIT = "submit"
const val TIMEOUT = "--timeout"
const val TEAM_ID = "--team-id"
const val VERBOSE = "-v"
const val WAIT = "--wait"
}
}

0 comments on commit a44871f

Please sign in to comment.