Skip to content

fix: sign macOS releases with Developer ID and notarize them - #264

Merged
inureyes merged 1 commit into
mainfrom
fix/macos-developer-id-signing
Aug 14, 2026
Merged

fix: sign macOS releases with Developer ID and notarize them#264
inureyes merged 1 commit into
mainfrom
fix/macos-developer-id-signing

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

Every macOS release up to v2.4.1 was signed with an Apple Distribution: Lablup Inc. certificate, an App Store submission identity that carries no Developer ID leaf extension (1.2.840.113635.100.6.1.13), and was never submitted to notarytool. When that certificate was revoked on 2026-08-12 during the signing overhaul in lablup/continuum-router#1313, Gatekeeper began returning CSSMERR_TP_CERT_REVOKED for every installed bssh binary: macOS kills the process on launch (SIGKILL, exit 137), shows the "will damage your computer" dialog, and deletes the file as malware. Every Homebrew installation on macOS is currently broken this way.

This PR ports the signing and notarization pipeline from continuum-router (itself mirrored from backend.ai-go) so official macOS binaries are signed with a Developer ID Application certificate and notarized.

Changes

  • Add .github/actions/macos-signing-setup: extracts the Developer ID p12 to a PEM (selecting an OpenSSL that supports pkcs12 -legacy by capability, since Apple's LibreSSL has no such option), rejects a p12 that holds no Developer ID Application certificate before anything is signed, and installs rcodesign, which reads the PEM directly and needs no keychain or GUI session.
  • Add .github/actions/macos-sign-notarize-binary: signs a staged copy with the hardened runtime and a pinned reverse-DNS identifier, asserts the Developer ID Application authority, the runtime flag, and the identifier after signing, packages the flat zip with ditto, submits it to notarytool --wait, and gates on status: Accepted. A bare Mach-O cannot be stapled, so the ticket stays on Apple's servers and Gatekeeper resolves it online.
  • release.yml: replace the import-codesign-certs and codesign --sign "Distribution" steps (the substring match is what selected the wrong certificate) with one setup call and three sign-package-notarize calls, one per released binary (bssh, bssh-server, bssh-keygen), sealed as BUNDLE_ID, BUNDLE_ID-server, and BUNDLE_ID-keygen. The unused job-level BIN_NAME/BUNDLE_ID env block is removed.
  • ARCHITECTURE.md: record the pipeline and its rationale under Security Model.

The zip names and flat layout (binary plus man page) are unchanged, so the Homebrew formula and update_homebrew_formula.yml need no changes.

Release prerequisites

The packaging environment needs APPLE_CERTIFICATE (base64 Developer ID Application p12), APPLE_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_TEAM_ID, APPLE_PASSWORD (app-specific password), and the BUNDLE_ID variable (com.lablup.bssh). The old DEV_ID_CERT_P12 / DEV_ID_CERT_PASSWORD secrets are no longer used. A wrong certificate now fails the release at the setup step instead of shipping quietly.

Tests

  • actionlint passes on .github/workflows/release.yml (validates the local action references and inputs).
  • Both action YAML files parse cleanly.
  • The reference implementation is the identical action pair running in continuum-router since #1313.
  • Full end-to-end verification happens on the next release run, which exercises signing, the three assertions, and notarization for all three binaries.

Risk notes

  • Release-pipeline only; no runtime code changes.
  • The next release fails fast if the new secrets are missing or the certificate is not a Developer ID Application identity. This is deliberate: official binaries must never ship unsigned again.
  • Notarization adds one notarytool --wait round per binary (three total) to the macOS job.

Releases up to v2.4.1 were signed with an Apple Distribution certificate, an App Store submission identity that carries no Developer ID leaf extension, and were never submitted to notarytool. When that certificate was revoked on 2026-08-12 during the signing overhaul in continuum-router, Gatekeeper began reporting CSSMERR_TP_CERT_REVOKED for every installed bssh binary: macOS kills the process on launch (SIGKILL) and deletes the file as malware, which broke every Homebrew installation.

This ports the signing and notarization pipeline from continuum-router (lablup/continuum-router#1313), which itself mirrors backend.ai-go. Two composite actions replace the import-codesign-certs plus codesign steps: macos-signing-setup extracts the p12 to a PEM with an OpenSSL selected for pkcs12 -legacy support, rejects any p12 that holds no Developer ID Application certificate, and installs rcodesign; macos-sign-notarize-binary signs a staged copy with the hardened runtime and a pinned reverse-DNS identifier, asserts the resulting authority, runtime flag, and identifier, packages the flat zip with ditto, and gates on notarytool status Accepted. Each of the three released binaries (bssh, bssh-server, bssh-keygen) goes through its own sign-package-notarize call, sealed as BUNDLE_ID, BUNDLE_ID-server, and BUNDLE_ID-keygen respectively.

The zip names and flat layout (binary plus man page) are unchanged, so the Homebrew formula and the update_homebrew_formula workflow need no changes. The release now requires APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_TEAM_ID, and APPLE_PASSWORD secrets plus the BUNDLE_ID variable on the packaging environment; the old DEV_ID_CERT_P12 and DEV_ID_CERT_PASSWORD secrets are no longer used. The rationale is recorded in the ARCHITECTURE.md Security Model section.
@inureyes inureyes added type:bug Something isn't working status:review Under review priority:critical Requires immediate attention labels Aug 14, 2026
@inureyes
inureyes merged commit 6fdc598 into main Aug 14, 2026
3 checks passed
@inureyes
inureyes deleted the fix/macos-developer-id-signing branch August 14, 2026 09:57
@inureyes inureyes added status:done Completed and removed status:review Under review labels Aug 14, 2026
inureyes added a commit that referenced this pull request Aug 14, 2026
Bumps the version to 2.4.2 and records the release in README.md, CHANGELOG.md, debian/changelog, and the three manpages. No source changed between 2.4.1 and 2.4.2: the release exists to republish the macOS binaries with a Developer ID signature and Apple notarization, since the certificate that signed every earlier macOS build was revoked and macOS now kills and deletes those binaries on launch (#264). The member crate bssh-russh-sftp stays on its own version line at 2.3.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority:critical Requires immediate attention status:done Completed type:bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant