diff --git a/.ci/macos.entitlements b/.ci/macos.entitlements new file mode 100644 index 000000000..2bff1d681 --- /dev/null +++ b/.ci/macos.entitlements @@ -0,0 +1,17 @@ + + + + + com.apple.security.app-sandbox + + + com.apple.security.cs.disable-library-validation + + + com.apple.security.cs.disable-executable-page-protection + + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 2f4e669d8..1762cf0fe 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -254,7 +254,7 @@ jobs: brew update brew install protobuf qt --force-bottle - - name: Build on Xcode ${{matrix.xcode}} + - name: Build & Sign on Xcode ${{matrix.xcode}} shell: bash id: build env: @@ -262,10 +262,60 @@ jobs: MAKE_TEST: 1 MAKE_PACKAGE: '${{matrix.make_package}}' PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}' + MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} # macOS runner have 3 cores usually - only the macos-13 image has 4: # https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories # https://github.com/actions/runner-images?tab=readme-ov-file#available-images - run: .ci/compile.sh --server --parallel ${{matrix.core_count}} + run: | + echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + security default-keychain -s build.keychain + security set-keychain-settings -t 3600 -l build.keychain + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain + .ci/compile.sh --server --parallel ${{matrix.core_count}} + + - name: Sign app bundle + if: matrix.make_package + env: + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} + run: | + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + /usr/bin/codesign --sign="$MACOS_CERTIFICATE_NAME" --entitlements=".ci/macos.entitlements" --options=runtime --force --deep --timestamp --verbose ${{steps.build.outputs.path}} + + - name: Notarize app bundle + if: matrix.make_package + env: + MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} + MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} + MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} + run: | + # Store the notarization credentials so that we can prevent a UI password dialog from blocking the CI + echo "Create keychain profile" + xcrun notarytool store-credentials "notarytool-profile" --apple-id "$MACOS_NOTARIZATION_APPLE_ID" --team-id "$MACOS_NOTARIZATION_TEAM_ID" --password "$MACOS_NOTARIZATION_PWD" + + # We can't notarize an app bundle directly, but we need to compress it as an archive. + # Therefore, we create a zip file containing our app bundle, so that we can send it to the + # notarization service + echo "Creating temp notarization archive" + ditto -c -k --keepParent ${{steps.build.outputs.path}} "notarization.zip" + + # Here we send the notarization request to the Apple's Notarization service, waiting for the result. + # This typically takes a few seconds inside a CI environment, but it might take more depending on the App + # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if + # you're curious + echo "Notarize app" + xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait + + # Finally, we need to "attach the staple" to our executable, which will allow our app to be + # validated by macOS even when an internet connection is not available. + echo "Attach staple" + xcrun stapler staple ${{steps.build.outputs.path}} - name: Upload artifact if: matrix.make_package diff --git a/CMakeLists.txt b/CMakeLists.txt index 68281f235..0e458146b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,7 @@ if(UNIX) set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice/resources/appicon.icns") set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeDMGSetup.script") set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/dmgBackground.tif") + set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SignMacApplications.cmake") else() # linux if(CPACK_GENERATOR STREQUAL "RPM") diff --git a/cmake/SignMacApplications.cmake b/cmake/SignMacApplications.cmake new file mode 100644 index 000000000..8b9db19b7 --- /dev/null +++ b/cmake/SignMacApplications.cmake @@ -0,0 +1,25 @@ +# This script re-signs all apps after CPack packages them. This is necessary because CPack modifies +# the library references used by Cockatrice to App relative paths, invalidating the code signature. +if(APPLE) + set(APPLICATIONS "cockatrice" "servatrice" "oracle" "dbconverter") + foreach(app_name IN LISTS APPLICATIONS) + set(FULL_APP_PATH "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/${app_name}.app") + + message(STATUS "Signing Interior Dynamically Loaded Libraries for ${app_name}.app") + execute_process(COMMAND "find" "${FULL_APP_PATH}" "-name" "*.dylib" OUTPUT_VARIABLE INTERIOR_DLLS) + string(REPLACE "\n" ";" INTERIOR_DLLS_LIST ${INTERIOR_DLLS}) + + foreach(INTERIOR_DLL IN LISTS INTERIOR_DLLS_LIST) + execute_process( + COMMAND "codesign" "--sign" "$ENV{MACOS_CERTIFICATE_NAME}" "--entitlements" "../.ci/macos.entitlements" + "--options" "runtime" "--force" "--deep" "--timestamp" "--verbose" "${INTERIOR_DLL}" + ) + endforeach() + + message(STATUS "Signing Exterior Applications ${app_name}.app") + execute_process( + COMMAND "codesign" "--sign" "$ENV{MACOS_CERTIFICATE_NAME}" "--entitlements" "../.ci/macos.entitlements" + "--options" "runtime" "--force" "--deep" "--timestamp" "--verbose" "${FULL_APP_PATH}" + ) + endforeach() +endif()