mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2024-10-01 01:06:10 -04:00
Add basic signing of app bundle and binaries (#2472)
Adds verification functionality to codesign script Adds required context to enable XCode to perform the signing Adds install time check + signing for all binaries Adds instructions allowing macdeployqt to sign the finalized app bundle Signed-off-by: John Parent <john.parent@kitware.com>
This commit is contained in:
parent
dc6d01a0bb
commit
23e8b187a4
@ -56,6 +56,15 @@ jobs:
|
||||
key: macos-qt-cache-v3
|
||||
paths:
|
||||
- ~/Qt
|
||||
- run:
|
||||
name: Setup Keychain
|
||||
command: |
|
||||
echo $MAC_SIGNING_CERT | base64 --decode > cert.p12
|
||||
security create-keychain -p "$MAC_KEYCHAIN_KEY" sign.keychain
|
||||
security default-keychain -s sign.keychain
|
||||
security unlock-keychain -p "$MAC_KEYCHAIN_KEY" sign.keychain
|
||||
security import cert.p12 -k sign.keychain -P "$MAC_SIGNING_CERT_PWD" -T /usr/bin/codesign
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MAC_KEYCHAIN_KEY" sign.keychain
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
@ -67,6 +76,7 @@ jobs:
|
||||
-DBUILD_UNIVERSAL=ON \
|
||||
-DMACDEPLOYQT=~/Qt/6.5.1/macos/bin/macdeployqt \
|
||||
-DGPT4ALL_OFFLINE_INSTALLER=ON \
|
||||
-DGPT4ALL_SIGN_INSTALL=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH:PATH=~/Qt/6.5.1/macos/lib/cmake/Qt6 \
|
||||
-DCMAKE_MAKE_PROGRAM:FILEPATH=~/Qt/Tools/Ninja/ninja \
|
||||
|
@ -33,6 +33,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option(GPT4ALL_LOCALHOST OFF "Build installer for localhost repo")
|
||||
option(GPT4ALL_OFFLINE_INSTALLER "Build an offline installer" OFF)
|
||||
option(GPT4ALL_SIGN_INSTALL "Sign installed binaries and installers (requires signing identities)" OFF)
|
||||
|
||||
# Generate a header file with the version number
|
||||
configure_file(
|
||||
@ -220,6 +221,13 @@ set_target_properties(chat PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
macro(REPORT_MISSING_SIGNING_CONTEXT)
|
||||
message(FATAL_ERROR [=[
|
||||
Signing requested but no identity configured.
|
||||
Please set the correct env variable or provide the MAC_SIGNING_IDENTITY argument on the command line
|
||||
]=])
|
||||
endmacro()
|
||||
|
||||
if (APPLE)
|
||||
set_target_properties(chat PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
@ -230,6 +238,28 @@ if (APPLE)
|
||||
OUTPUT_NAME gpt4all
|
||||
)
|
||||
add_dependencies(chat ggml-metal)
|
||||
|
||||
if(NOT MAC_SIGNING_IDENTITY)
|
||||
if(NOT DEFINED ENV{MAC_SIGNING_CERT_NAME} AND GPT4ALL_SIGN_INSTALL)
|
||||
REPORT_MISSING_SIGNING_CONTEXT()
|
||||
endif()
|
||||
set(MAC_SIGNING_IDENTITY $ENV{MAC_SIGNING_CERT_NAME})
|
||||
endif()
|
||||
if(NOT MAC_SIGNING_TID)
|
||||
if(NOT DEFINED ENV{MAC_NOTARIZATION_TID} AND GPT4ALL_SIGN_INSTALL)
|
||||
REPORT_MISSING_SIGNING_CONTEXT()
|
||||
endif()
|
||||
set(MAC_SIGNING_TID $ENV{MAC_NOTARIZATION_TID})
|
||||
endif()
|
||||
|
||||
# Setup MacOS signing for individual binaries
|
||||
set_target_properties(chat PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Manual"
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${MAC_SIGNING_TID}
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${MAC_SIGNING_IDENTITY}
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED True
|
||||
XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--timestamp=http://timestamp.apple.com/ts01 --options=runtime,library"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(chat
|
||||
@ -254,6 +284,10 @@ target_link_libraries(chat
|
||||
|
||||
# -- install --
|
||||
|
||||
function(install_sign_osx tgt)
|
||||
install(CODE "execute_process(COMMAND codesign --options runtime --timestamp -s \"${MAC_SIGNING_IDENTITY}\" $<TARGET_FILE:${tgt}>)")
|
||||
endfunction()
|
||||
|
||||
set(COMPONENT_NAME_MAIN ${PROJECT_NAME})
|
||||
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
@ -296,6 +330,14 @@ install(
|
||||
RUNTIME DESTINATION lib COMPONENT ${COMPONENT_NAME_MAIN} # .dll
|
||||
)
|
||||
|
||||
if(APPLE AND GPT4ALL_SIGN_INSTALL)
|
||||
install_sign_osx(chat)
|
||||
install_sign_osx(llmodel)
|
||||
foreach(tgt ${MODEL_IMPL_TARGETS})
|
||||
install_sign_osx(${tgt})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if (LLMODEL_CUDA)
|
||||
set_property(TARGET llamamodel-mainline-cuda llamamodel-mainline-cuda-avxonly
|
||||
APPEND PROPERTY INSTALL_RPATH "$ORIGIN")
|
||||
|
@ -1,7 +1,8 @@
|
||||
set(MACDEPLOYQT "@MACDEPLOYQT@")
|
||||
set(COMPONENT_NAME_MAIN "@COMPONENT_NAME_MAIN@")
|
||||
set(CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@")
|
||||
execute_process(COMMAND ${MACDEPLOYQT} ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data/bin/gpt4all.app -qmldir=${CMAKE_CURRENT_SOURCE_DIR} -verbose=2)
|
||||
set(GPT4ALL_SIGNING_ID "@MAC_SIGNING_IDENTITY@")
|
||||
execute_process(COMMAND ${MACDEPLOYQT} ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data/bin/gpt4all.app -qmldir=${CMAKE_CURRENT_SOURCE_DIR} -verbose=2 -sign-for-notarization=${GPT4ALL_SIGNING_ID})
|
||||
file(GLOB MYGPTJLIBS ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data/lib/libgptj*)
|
||||
file(GLOB MYLLAMALIBS ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data/lib/libllama*)
|
||||
file(GLOB MYLLMODELLIBS ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data/lib/libllmodel.*)
|
||||
|
@ -4,6 +4,7 @@ import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
import click
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
# Requires click
|
||||
@ -20,7 +21,8 @@ from typing import Optional
|
||||
@click.option('--output-dmg', required=True, help='Path to the output signed DMG file.')
|
||||
@click.option('--sha1-hash', help='SHA-1 hash of the Developer ID Application certificate')
|
||||
@click.option('--signing-identity', default=None, help='Common name of the Developer ID Application certificate')
|
||||
def sign_dmg(input_dmg: str, output_dmg: str, signing_identity: Optional[str] = None, sha1_hash: Optional[str] = None) -> None:
|
||||
@click.option('--verify', is_flag=True, show_default=True, required=False, default=False, help='Perform verification of signed app bundle' )
|
||||
def sign_dmg(input_dmg: str, output_dmg: str, signing_identity: Optional[str] = None, sha1_hash: Optional[str] = None, verify: Optional[bool] = False) -> None:
|
||||
if not signing_identity and not sha1_hash:
|
||||
print("Error: Either --signing-identity or --sha1-hash must be provided.")
|
||||
exit(1)
|
||||
@ -64,6 +66,43 @@ def sign_dmg(input_dmg: str, output_dmg: str, signing_identity: Optional[str] =
|
||||
shutil.rmtree(mount_point)
|
||||
exit(1)
|
||||
|
||||
# Validate signature and entitlements of signed app bundle
|
||||
if verify:
|
||||
try:
|
||||
code_ver_proc = subprocess.run([
|
||||
'codesign',
|
||||
'--deep',
|
||||
'--verify',
|
||||
'--verbose=2',
|
||||
'--strict',
|
||||
app_bundle
|
||||
], check=True, capture_output=True)
|
||||
if not re.search(fr"{app_bundle}: valid", code_ver_proc.stdout.decode()):
|
||||
raise RuntimeError(f"codesign validation failed: {code_ver_proc.stdout.decode()}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error during codesign validation: {e}")
|
||||
# Clean up temporary directories
|
||||
shutil.rmtree(temp_dir)
|
||||
shutil.rmtree(mount_point)
|
||||
exit(1)
|
||||
try:
|
||||
spctl_proc = subprocess.run([
|
||||
'spctl',
|
||||
'-a',
|
||||
'-t',
|
||||
'exec',
|
||||
'-vv',
|
||||
app_bundle
|
||||
], check=True, capture_output=True)
|
||||
if not re.search(fr"{app_bundle}: accepted", spctl_proc.stdout.decode()):
|
||||
raise RuntimeError(f"spctl validation failed: {spctl_proc.stdout.decode()}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error during spctl validation: {e}")
|
||||
# Clean up temporary directories
|
||||
shutil.rmtree(temp_dir)
|
||||
shutil.rmtree(mount_point)
|
||||
exit(1)
|
||||
|
||||
# Create a new DMG containing the signed .app bundle
|
||||
subprocess.run([
|
||||
'hdiutil', 'create',
|
||||
|
Loading…
Reference in New Issue
Block a user