Working signed macOS GUI build

This commit is contained in:
Jason Rhinelander 2022-08-12 19:57:00 -03:00
parent 496c1d274c
commit 2b7b1fcc79
15 changed files with 361 additions and 231 deletions

View File

@ -32,7 +32,7 @@ project(lokinet
if(APPLE)
# Apple build number: must be incremented to submit a new build for the same lokinet version,
# should be reset to 0 when the lokinet version increments.
set(LOKINET_APPLE_BUILD 0)
set(LOKINET_APPLE_BUILD 1)
endif()
set(RELEASE_MOTTO "Gluten Free Edition" CACHE STRING "Release motto")
@ -106,6 +106,7 @@ endif()
include(cmake/solaris.cmake)
include(cmake/win32.cmake)
include(cmake/macos.cmake)
# No in-source building
include(MacroEnsureOutOfSourceBuild)
@ -302,6 +303,10 @@ add_subdirectory(docs)
include(cmake/gui.cmake)
if(APPLE)
macos_target_setup()
endif()
# uninstall target
if(NOT TARGET uninstall)
configure_file(

View File

@ -18,20 +18,34 @@ if (BUILD_GUI)
find_program(YARN NAMES yarn yarnpkg REQUIRED)
message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}")
set(wine_env)
if(WIN32)
set(wine_env WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix")
endif()
add_custom_target(lokinet-gui
COMMAND ${YARN} install --frozen-lockfile &&
WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix" ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
if(APPLE)
add_custom_target(copy_gui ALL
DEPENDS lokinet lokinet-extension lokinet-gui
# FIXME: we really shouldn't be building inside the source directory but this is npm...
COMMAND ${CMAKE_COMMAND} -E copy_directory
${PROJECT_SOURCE_DIR}/lokinet-gui/release/mac/lokinet-gui.app
$<TARGET_BUNDLE_DIR:lokinet>
add_custom_target(assemble_gui ALL
DEPENDS assemble lokinet-gui
COMMAND mkdir "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Helpers"
COMMAND cp -a "${PROJECT_SOURCE_DIR}/gui/release/mac/Lokinet-GUI.app" "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Helpers/"
COMMAND mkdir -p "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Resources/en.lproj"
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Resources/en.lproj/"
COMMAND cp "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Resources/icon.icns" "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns"
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/"
COMMAND /usr/libexec/PlistBuddy
-c "Delete :CFBundleDisplayName"
-c "Add :LSHasLocalizedDisplayName bool true"
-c "Add :CFBundleDevelopmentRegion string en"
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
"${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
)
add_dependencies(assemble copy_gui)
elseif(WIN32)
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui")
add_custom_target(copy_gui ALL

176
cmake/macos.cmake Normal file
View File

@ -0,0 +1,176 @@
if(NOT APPLE)
return()
endif()
option(MACOS_SYSTEM_EXTENSION
"Build the network extension as a system extension rather than a plugin. This must be ON for non-app store release builds, and must be OFF for dev builds and Mac App Store distribution builds"
OFF)
option(CODESIGN "codesign the resulting app and extension" ON)
set(CODESIGN_ID "" CACHE STRING "codesign the macos app using this key identity; if empty we'll try to guess")
set(default_profile_type "dev")
if(MACOS_SYSTEM_EXTENSION)
set(default_profile_type "release")
endif()
set(CODESIGN_PROFILE "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.${default_profile_type}.provisionprofile" CACHE FILEPATH
"Path to a .provisionprofile to use for the main app")
if(CODESIGN AND NOT CODESIGN_ID)
if(MACOS_SYSTEM_EXTENSION)
set(codesign_cert_pattern "Developer ID Application")
else()
set(codesign_cert_pattern "Apple Development")
endif()
execute_process(
COMMAND security find-identity -v -p codesigning
COMMAND sed -n "s/^ *[0-9][0-9]*) *\\([A-F0-9]\\{40\\}\\) *\"\\(${codesign_cert_pattern}.*\\)\"\$/\\1 \\2/p"
RESULT_VARIABLE find_id_exit_code
OUTPUT_VARIABLE find_id_output)
if(NOT find_id_exit_code EQUAL 0)
message(FATAL_ERROR "Finding signing identities with security find-identity failed; try specifying an id using -DCODESIGN_ID=...")
endif()
string(REGEX MATCHALL "(^|\n)[0-9A-F]+" find_id_sign_id "${find_id_output}")
if(NOT find_id_sign_id)
message(FATAL_ERROR "Did not find any \"${codesign_cert_pattern}\" identity; try specifying an id using -DCODESIGN_ID=...")
endif()
if (find_id_sign_id MATCHES ";")
message(FATAL_ERROR "Found multiple \"${codesign_cert_pattern}\" identities:\n${find_id_output}\nSpecify an identify using -DCODESIGN_ID=...")
endif()
set(CODESIGN_ID "${find_id_sign_id}" CACHE STRING "" FORCE)
endif()
if(CODESIGN)
message(STATUS "Codesigning using ${CODESIGN_ID}")
if (NOT MACOS_NOTARIZE_USER AND NOT MACOS_NOTARIZE_PASS AND NOT MACOS_NOTARIZE_ASC AND EXISTS "$ENV{HOME}/.notarization.cmake")
message(STATUS "Loading notarization info from ~/.notarization.cmake")
include("$ENV{HOME}/.notarization.cmake")
endif()
if (MACOS_NOTARIZE_USER AND MACOS_NOTARIZE_PASS AND MACOS_NOTARIZE_ASC)
message(STATUS "Enabling notarization with account ${MACOS_NOTARIZE_ASC}/${MACOS_NOTARIZE_USER}")
else()
message(WARNING "You have not set one or more of MACOS_NOTARIZE_USER, MACOS_NOTARIZE_PASS, MACOS_NOTARIZE_ASC: notarization will fail; see contrib/macos/README.txt")
endif()
else()
message(WARNING "Codesigning disabled; the resulting build will not run on most macOS systems")
endif()
if(NOT CODESIGN_PROFILE)
message(WARNING "Missing a CODESIGN_PROFILE provisioning profile: Apple will most likely log an uninformative error message to the system log and then kill harmless kittens if you try to run the result")
endif()
if(NOT EXISTS "${CODESIGN_PROFILE}")
message(FATAL_ERROR "Provisioning profile ${CODESIGN_PROFILE} does not exist; fix your -DCODESIGN_PROFILE path")
endif()
message(STATUS "Using ${CODESIGN_PROFILE} provisioning profile")
if(MACOS_SYSTEM_EXTENSION)
set(lokinet_ext_dir Contents/Library/SystemExtensions)
else()
set(lokinet_ext_dir Contents/PlugIns)
endif()
if(CODESIGN)
if(MACOS_SYSTEM_EXTENSION)
set(LOKINET_ENTITLEMENTS_TYPE sysext)
set(notarize_py_is_sysext True)
else()
set(LOKINET_ENTITLEMENTS_TYPE plugin)
set(notarize_py_is_sysext False)
endif()
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/sign.sh.in"
"${PROJECT_BINARY_DIR}/sign.sh"
@ONLY)
add_custom_target(
sign
DEPENDS "${PROJECT_BINARY_DIR}/sign.sh"
COMMAND "${PROJECT_BINARY_DIR}/sign.sh"
)
if(MACOS_NOTARIZE_USER AND MACOS_NOTARIZE_PASS AND MACOS_NOTARIZE_ASC)
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/notarize.py.in"
"${PROJECT_BINARY_DIR}/notarize.py"
@ONLY)
add_custom_target(
notarize
DEPENDS "${PROJECT_BINARY_DIR}/notarize.py" sign
COMMAND "${PROJECT_BINARY_DIR}/notarize.py"
)
else()
message(WARNING "You have not set one or more of MACOS_NOTARIZE_USER, MACOS_NOTARIZE_PASS, MACOS_NOTARIZE_ASC: notarization disabled")
endif()
else()
add_custom_target(sign COMMAND "true")
add_custom_target(notarize DEPENDS sign COMMAND "true")
endif()
# Called later to set things up, after the main lokinet targets are set up
function(macos_target_setup)
if(MACOS_SYSTEM_EXTENSION)
target_compile_definitions(lokinet PRIVATE MACOS_SYSTEM_EXTENSION)
endif()
set_target_properties(lokinet
PROPERTIES
OUTPUT_NAME Lokinet
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_STRING "Lokinet IP Packet Onion Router"
MACOSX_BUNDLE_BUNDLE_NAME "Lokinet"
MACOSX_BUNDLE_BUNDLE_VERSION "${lokinet_VERSION}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${lokinet_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${lokinet_VERSION_MAJOR}.${lokinet_VERSION_MINOR}"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.lokinet"
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.Info.plist.in"
MACOSX_BUNDLE_COPYRIGHT "© 2022, The Oxen Project"
)
add_custom_target(copy_bootstrap
DEPENDS lokinet-extension
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed
$<TARGET_BUNDLE_DIR:lokinet-extension>/Contents/Resources/bootstrap.signed
)
set(mac_icon ${PROJECT_BINARY_DIR}/lokinet.icns)
add_custom_command(OUTPUT ${mac_icon}
COMMAND ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet-mac.svg ${mac_icon}
DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh)
add_custom_target(icon DEPENDS ${mac_icon})
add_dependencies(lokinet lokinet-extension icon)
if(CODESIGN_PROFILE)
add_custom_target(copy_prov_prof
DEPENDS lokinet
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CODESIGN_PROFILE}
$<TARGET_BUNDLE_DIR:lokinet>/Contents/embedded.provisionprofile
)
else()
add_custom_target(copy_prov_prof COMMAND true)
endif()
add_custom_target(assemble ALL
DEPENDS lokinet lokinet-extension icon copy_prov_prof copy_bootstrap
COMMAND rm -rf "${PROJECT_BINARY_DIR}/Lokinet.app"
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet> "${PROJECT_BINARY_DIR}/Lokinet.app"
COMMAND mkdir -p "${PROJECT_BINARY_DIR}/Lokinet.app/${lokinet_ext_dir}"
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet-extension> "${PROJECT_BINARY_DIR}/Lokinet.app/${lokinet_ext_dir}/"
COMMAND mkdir -p "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Resources"
COMMAND cp -a "${mac_icon}" "${PROJECT_BINARY_DIR}/Lokinet.app/Contents/Resources/icon.icns"
)
if(CODESIGN)
add_dependencies(sign assemble)
endif()
endfunction()

21
contrib/lokinet-mac.svg Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M897.5,1024H126.5C56.6,1024,0,967.4,0,897.5V126.5C0,56.6,56.6,0,126.5,0h771.1C967.4,0,1024,56.6,1024,126.5
v771.1C1024,967.4,967.4,1024,897.5,1024z"/>
</g>
<g>
<polygon points="585.2,658.9 512,732.1 438.8,658.9 365.1,732.1 512,879 658.9,732.1 "/>
<polygon points="658.9,585.2 732.1,512 658.9,438.8 732.1,365.1 879,512 732.1,658.9 "/>
<polygon points="365.1,438.8 291.9,512 365.1,585.2 291.9,658.9 145,512 291.9,365.1 "/>
<polygon points="438.8,365.1 512,291.9 585.2,365.1 658.9,291.9 512,145 365.1,291.9 "/>
<rect x="533.4" y="533.3" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -242.3375 585.3179)" width="103.9" height="103.9"/>
<rect x="386.7" y="386.9" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -181.837 438.6521)" width="103.9" height="103.9"/>
<rect x="533.2" y="386.7" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -138.7545 542.2352)" width="103.9" height="103.9"/>
<rect x="386.9" y="533.5" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -285.4199 481.7348)" width="103.9" height="103.9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -20,6 +20,7 @@ cmake \
-DBUILD_STATIC_DEPS=ON \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \
-DWITH_LTO=ON \
-DCMAKE_BUILD_TYPE=Release \

Binary file not shown.

View File

@ -5,11 +5,8 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Lokinet</string>
<key>CFBundleExecutable</key>
<string>lokinet-gui</string>
<string>Lokinet</string>
<key>CFBundleIdentifier</key>
<string>org.lokinet</string>
@ -20,6 +17,9 @@
<key>CFBundleName</key>
<string>Lokinet</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
@ -32,8 +32,14 @@
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2022 The Oxen Project, licensed under GPLv3-or-later</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2022 The Oxen Project, licensed under GPLv3-or-later</string>
</dict>
<key>LSUIElement</key>
<true/>
<key>LSHasLocalizedDisplayName</key>
<true/>
</dict>
</plist>

View File

@ -14,7 +14,7 @@ done
mv "${outdir}/icon_1024x1024.png" "${outdir}/icon_512x512@2x.png"
for size in 16 32 128 256; do
double=$((size * 2))
cp "${outdir}/icon_${double}x${double}.png" "${outdir}/icon_${size}x${size}@2x.png"
ln -f "${outdir}/icon_${double}x${double}.png" "${outdir}/icon_${size}x${size}@2x.png"
done
iconutil -c icns "${outdir}"

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>network.loki.lokinet.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/var/lib/lokinet/lokinet_macos_daemon_script.sh</string>
</array>
<!-- Keep Lokinet alive unless magic file exists -->
<key>KeepAlive</key>
<dict>
<key>PathState</key>
<dict>
<key>/var/lib/lokinet/suspend-launchd-service</key>
<false/>
</dict>
</dict>
<key>StandardOutPath</key>
<string>/var/log/lokinet.log</string>
</dict>
</plist>

View File

@ -24,11 +24,11 @@ if not all(("@MACOS_NOTARIZE_USER@", "@MACOS_NOTARIZE_PASS@", "@MACOS_NOTARIZE_A
os.chdir("@PROJECT_BINARY_DIR@")
app = "Lokinet.app"
zipfile = "Lokinet.app.notarize.zip"
print(f"Creating lokinet.app.notarize.zip from lokinet.app")
zipfile = f"{app}.notarize.zip"
print(f"Creating {zipfile} from {app}")
if os.path.exists(zipfile):
os.remove(zipfile)
subprocess.run(['zip', '-r', zipfile, app])
subprocess.run(['ditto', '-v', '-c', '-k', '--sequesterRsrc', '--keepParent', app, zipfile])
userpass = ('--username', "@MACOS_NOTARIZE_USER@", '--password', "@MACOS_NOTARIZE_PASS@")
print("Submitting {} for notarization; this may take a minute...".format(zipfile))

View File

@ -2,24 +2,71 @@
set -e
if [ -z "@CODESIGN" ]; then
if [ "@CODESIGN@" != "ON" ]; then
echo "Cannot codesign: this build was not configured with codesigning" >&2
exit 1
fi
signit() {
target="$1"
entitlements="$2"
echo -e "\n\e[33;1mSigning ${target/*\/Lokinet.app/Lokinet.app}...\e[0m" >&2
codesign \
--verbose=4 \
--force \
-s "@CODESIGN_ID@" \
--entitlements "$entitlements" \
--strict \
--timestamp \
--options=runtime \
"$target"
}
gui_entitlements="@PROJECT_SOURCE_DIR@/gui/node_modules/app-builder-lib/templates/entitlements.mac.plist"
ext_entitlements="@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-extension.@LOKINET_ENTITLEMENTS_TYPE@.entitlements.plist"
app_entitlements="@PROJECT_SOURCE_DIR@/contrib/macos/lokinet.@LOKINET_ENTITLEMENTS_TYPE@.entitlements.plist"
SIGN_TARGET="@PROJECT_BINARY_DIR@/Lokinet.app"
for ext in systemextension appex; do
netext="@lokinet_ext_dir@/org.lokinet.network-extension.$ext"
if [ -e "@SIGN_TARGET@/$netext" ]; then
echo -e "\n\e[33;1mSigning $netext...\e[0m\n" >&2
codesign --verbose=4 --force -s "@CODESIGN_ID@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-extension.@LOKINET_ENTITLEMENTS_TYPE@.entitlements.plist" \
--deep --strict --timestamp --options=runtime "@SIGN_TARGET@/$netext"
netext="$SIGN_TARGET/@lokinet_ext_dir@/org.lokinet.network-extension.$ext"
if [ -e "$netext" ]; then
signit "$netext" "$ext_entitlements"
fi
done
for sub in "/Contents/MacOS/Lokinet" "" ; do
echo -e "\n\e[33;1mSigning $(basename @SIGN_TARGET@)$sub...\e[0m\n" >&2
codesign --verbose=4 --force -s "@CODESIGN_ID@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet.@LOKINET_ENTITLEMENTS_TYPE@.entitlements.plist" \
--deep --strict --timestamp --options=runtime "@SIGN_TARGET@$sub"
done
if [ "@BUILD_GUI@" == "ON" ]; then
gui_app="$SIGN_TARGET"/Contents/Helpers/Lokinet-GUI.app
gui_sign_targets=()
for bundle in \
"$gui_app"/Contents/Frameworks/*.framework \
"$gui_app"/Contents/Frameworks/*.app
do
if [ -d "$bundle/Libraries" ]; then
gui_sign_targets+=("$bundle"/Libraries/*.dylib)
fi
if [ -d "$bundle/Helpers" ]; then
gui_sign_targets+=("$bundle"/Helpers/*)
fi
if [ -d "$bundle/Resources" ]; then
for f in "$bundle/Resources"/*; do
if [[ -f "$f" && -x "$f" && "$(file -b "$f")" == Mach-O* ]]; then
gui_sign_targets+=("$f")
fi
done
fi
gui_sign_targets+=("$bundle")
done
gui_sign_targets+=("$gui_app")
for target in "${gui_sign_targets[@]}"; do
signit "$target" "$gui_entitlements"
done
signit "$SIGN_TARGET"/Contents/MacOS/Lokinet "$app_entitlements"
fi
signit "$SIGN_TARGET" "$app_entitlements"

View File

@ -67,171 +67,6 @@ foreach(exe ${exetargets})
endif()
endforeach()
if(APPLE)
option(MACOS_SYSTEM_EXTENSION
"Build the network extension as a system extension rather than a plugin. This must be ON for non-app store release builds, and must be OFF for dev builds and Mac App Store distribution builds"
OFF)
option(CODESIGN "codesign the resulting app and extension" ON)
set(CODESIGN_ID "" CACHE STRING "codesign the macos app using this key identity; if empty we'll try to guess")
set(default_profile_type "dev")
if(MACOS_SYSTEM_EXTENSION)
set(default_profile_type "release")
endif()
set(CODESIGN_PROFILE "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.${default_profile_type}.provisionprofile" CACHE FILEPATH
"Path to a .provisionprofile to use for the main app")
set(CODESIGN_EXT_PROFILE "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet-extension.${default_profile_type}.provisionprofile" CACHE FILEPATH
"Path to a .provisionprofile to use for the extension")
if(CODESIGN AND NOT CODESIGN_ID)
if(MACOS_SYSTEM_EXTENSION)
set(codesign_cert_pattern "Developer ID Application")
else()
set(codesign_cert_pattern "Apple Development")
endif()
execute_process(
COMMAND security find-identity -v -p codesigning
COMMAND sed -n "s/^ *[0-9][0-9]*) *\\([A-F0-9]\\{40\\}\\) *\"\\(${codesign_cert_pattern}.*\\)\"\$/\\1 \\2/p"
RESULT_VARIABLE find_id_exit_code
OUTPUT_VARIABLE find_id_output)
if(NOT find_id_exit_code EQUAL 0)
message(FATAL_ERROR "Finding signing identities with security find-identity failed; try specifying an id using -DCODESIGN_ID=...")
endif()
string(REGEX MATCHALL "(^|\n)[0-9A-F]+" find_id_sign_id "${find_id_output}")
if(NOT find_id_sign_id)
message(FATAL_ERROR "Did not find any \"${codesign_cert_pattern}\" identity; try specifying an id using -DCODESIGN_ID=...")
endif()
if (find_id_sign_id MATCHES ";")
message(FATAL_ERROR "Found multiple \"${codesign_cert_pattern}\" identities:\n${find_id_output}\nSpecify an identify using -DCODESIGN_ID=...")
endif()
set(CODESIGN_ID "${find_id_sign_id}" CACHE STRING "" FORCE)
endif()
if(CODESIGN)
message(STATUS "Codesigning using ${CODESIGN_ID}")
else()
message(WARNING "Codesigning disabled; the resulting build will not run on most macOS systems")
endif()
if(MACOS_SYSTEM_EXTENSION)
set(lokinet_ext_dir Contents/Library/SystemExtensions)
target_compile_definitions(lokinet PRIVATE MACOS_SYSTEM_EXTENSION)
if (NOT MACOS_NOTARIZE_USER AND NOT MACOS_NOTARIZE_PASS AND NOT MACOS_NOTARIZE_ASC AND EXISTS "$ENV{HOME}/.notarization.cmake")
message(STATUS "Loading notarization info from ~/.notarization.cmake")
include("$ENV{HOME}/.notarization.cmake")
endif()
if (MACOS_NOTARIZE_USER AND MACOS_NOTARIZE_PASS AND MACOS_NOTARIZE_ASC)
message(STATUS "Enabling notarization with account ${MACOS_NOTARIZE_ASC}/${MACOS_NOTARIZE_USER}")
else()
message(WARNING "You have not set one or more of MACOS_NOTARIZE_USER, MACOS_NOTARIZE_PASS, MACOS_NOTARIZE_ASC: notarization will fail; see contrib/macos/README.txt")
endif()
else()
set(lokinet_ext_dir Contents/PlugIns)
endif()
foreach(var CODESIGN_PROFILE CODESIGN_EXT_PROFILE)
if(NOT ${var})
message(WARNING "Missing a ${var} provisioning profile, and not building a system extension: Apple will most likely log an uninformative error message to the system log and then kill harmless kittens if you try to run the result")
endif()
if(NOT EXISTS "${${var}}")
message(FATAL_ERROR "Provisioning profile ${${var}} does not exist; fix your -D${var} path")
endif()
endforeach()
message(STATUS "Using ${CODESIGN_PROFILE} provisioning profile")
message(STATUS "Using ${CODESIGN_EXT_PROFILE} extension provisioning profile")
set(mac_icon ${CMAKE_CURRENT_BINARY_DIR}/lokinet.icns)
add_custom_command(OUTPUT ${mac_icon}
COMMAND ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${mac_icon}
DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh)
add_custom_target(icons DEPENDS ${mac_icon})
add_dependencies(lokinet icons lokinet-extension)
set(post_build_pp)
if(CODESIGN AND CODESIGN_PROFILE)
set(post_build_pp COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CODESIGN_PROFILE}
$<TARGET_BUNDLE_DIR:lokinet>/Contents/embedded.provisionprofile)
endif()
set_target_properties(lokinet
PROPERTIES
OUTPUT_NAME Lokinet
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_STRING "Lokinet IP Packet Onion Router"
MACOSX_BUNDLE_BUNDLE_NAME "Lokinet"
MACOSX_BUNDLE_BUNDLE_VERSION "${lokinet_VERSION}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${lokinet_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${lokinet_VERSION_MAJOR}.${lokinet_VERSION_MINOR}"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.lokinet"
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.Info.plist.in"
MACOSX_BUNDLE_ICON_FILE "${mac_icon}"
MACOSX_BUNDLE_COPYRIGHT "© 2022, The Oxen Project"
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}"
)
add_custom_target(assemble
DEPENDS lokinet lokinet-extension
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed
$<TARGET_BUNDLE_DIR:lokinet-extension>/Contents/Resources/bootstrap.signed
COMMAND mkdir -p $<TARGET_BUNDLE_DIR:lokinet>/${lokinet_ext_dir}
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet-extension> $<TARGET_BUNDLE_DIR:lokinet>/${lokinet_ext_dir}
${post_build_pp})
if(TARGET lokinet-gui)
add_custom_target(copy_gui
DEPENDS lokinet lokinet-extension lokinet-gui
# FIXME: we really shouldn't be building inside the source directory but this is npm...
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/lokinet-gui/release/mac/lokinet-gui.app $<TARGET_BUNDLE_DIR:lokinet>
)
add_dependencies(assemble copy_gui)
endif()
if(NOT CODESIGN)
message(STATUS "codesigning disabled")
add_custom_target(
sign
DEPENDS assemble
COMMAND "true")
elseif(CODESIGN)
set(SIGN_TARGET "${PROJECT_BINARY_DIR}/Lokinet.app")
if(MACOS_SYSTEM_EXTENSION)
set(LOKINET_ENTITLEMENTS_TYPE sysext)
else()
set(LOKINET_ENTITLEMENTS_TYPE plugin)
endif()
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/sign.sh.in"
"${PROJECT_BINARY_DIR}/sign.sh"
@ONLY)
add_custom_target(
sign
DEPENDS "${PROJECT_BINARY_DIR}/sign.sh" assemble
COMMAND "${PROJECT_BINARY_DIR}/sign.sh"
)
if(NOT (MACOS_NOTARIZE_USER AND MACOS_NOTARIZE_PASS AND MACOS_NOTARIZE_ASC))
message(WARNING "You have not set one or more of MACOS_NOTARIZE_USER, MACOS_NOTARIZE_PASS, MACOS_NOTARIZE_ASC: notarization disabled")
endif()
if (MACOS_SYSTEM_EXTENSION)
set(notarize_py_is_sysext True)
else()
set(notarize_py_is_sysext False)
endif()
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/notarize.py.in"
"${PROJECT_BINARY_DIR}/notarize.py"
@ONLY)
add_custom_target(
notarize
DEPENDS "${PROJECT_BINARY_DIR}/notarize.py" sign
COMMAND "${PROJECT_BINARY_DIR}/notarize.py"
)
else()
message(FATAL_ERROR "CODESIGN_APP (=${CODESIGN_APP}) and/or CODESIGN_EXT (=${CODESIGN_EXT}) are not set. To disable code signing use -DCODESIGN=OFF")
endif()
endif()
if(SETCAP)
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
endif()

View File

@ -8,7 +8,7 @@ let app = NSApplication.shared
let START = "--start"
let STOP = "--stop"
let HELP_STRING = "usage: lokinet [--start|--stop]"
let HELP_STRING = "usage: lokinet {--start|--stop}"
class LokinetMain: NSObject, NSApplicationDelegate {
var vpnManager = NETunnelProviderManager()
@ -46,9 +46,8 @@ class LokinetMain: NSObject, NSApplicationDelegate {
if let savedManagers = savedManagers {
for manager in savedManagers {
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.netextBundleId {
manager.isEnabled = false
manager.connection.stopVPNTunnel()
self.result(msg: "Lokinet Down")
return
}
}
}
@ -155,6 +154,7 @@ class LokinetMain: NSObject, NSApplicationDelegate {
func request(_: OSSystemExtensionRequest, didFailWithError error: Error) {
NSLog("System extension request failed: %@", error.localizedDescription)
self.bail()
}
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
@ -174,9 +174,60 @@ class LokinetMain: NSObject, NSApplicationDelegate {
let args = CommandLine.arguments
if args.count <= 2 {
// If we are invoked with no arguments then exec the gui. This is dumb, but there doesn't seem to
// be a nicer way to do this on Apple's half-baked platform because:
// - we have three "bundles" we need to manage: the GUI app, the system extension, and the Lokinet
// app (this file) which loads the system extension.
// - if we embed the system extension directly inside the GUI then it fails to launch because the
// electron GUI's requirements (needed for JIT) conflict with the ability to load a system
// extensions.
// - if we embed Lokinet.app inside Lokinet-GUI.app and then the system extension inside Lokinet.app
// then it works, but macos loses track of the system extension and doesn't remove it when you
// remove the application. (It breaks your system, leaving an impossible-to-remove system
// extension, in just the same way it breaks if you don't use Finder to remove the Application.
// Apple used to say (around 2 years ago as of writing) that they would fix this situation "soon",
// but hasn't, and has stopped saying anything about it.)
// - if we try to use multiple executables (one to launch the system extension, one simple shell
// script to execs the embedded GUI app) inside the Lokinet.app and make the GUI the default for
// the application then Lokinet gets killed by gatekeeper because code signing only applies the
// (required-for-system-extensions) provisioningprofile to the main binary in the app.
//
// So we are left needing *one* single binary that isn't the GUI but has to do double-duty for both
// exec'ing the binary and loading lokinet, depending on how it is called.
//
// But of course there is no way to specify command-line arguments to the default binary macOS runs,
// so we can't use a `--gui` flag or anything so abhorrent to macos purity, thus this nasty
// solution:
// - no args -- exec the GUI
// - `--start` -- load the system extension and start lokinet
// - `--stop` -- stop lokinet
//
// macOS: land of half-baked implementations and nasty hacks to make anything work.
if args.count == 1 {
let gui_path = Bundle.main.resourcePath! + "/../Helpers/Lokinet-GUI.app"
if !FileManager.default.fileExists(atPath: gui_path) {
NSLog("Could not find gui app at %@", gui_path)
exit(1)
}
let gui_url = URL(fileURLWithPath: gui_path, isDirectory: false)
let gui_app_conf = NSWorkspace.OpenConfiguration()
let group = DispatchGroup()
group.enter()
NSWorkspace.shared.openApplication(at: gui_url, configuration: gui_app_conf,
completionHandler: { (app, error) in
if error != nil {
NSLog("Error launching gui: %@", error!.localizedDescription)
} else {
NSLog("Lauched GUI");
}
group.leave()
})
group.wait()
} else if args.count == 2 {
let delegate = LokinetMain()
delegate.mode = args.count > 1 ? args[1] : START
delegate.mode = args[1]
app.delegate = delegate
app.run()
} else {

2
gui

@ -1 +1 @@
Subproject commit 4861da59c3f1251a002199e185cc8a638845e692
Subproject commit abcd94814e261ffd88104357e3946201d0d2c8e0

View File

@ -3,7 +3,7 @@
cmake_minimum_required(VERSION 3.13)
if (BUILD_SHARED_LIBS OR NOT BUILD_STATIC_DEPS OR NOT STATIC_LINK)
message(FATAL_ERROR "macOS builds require a full static build; perhaps use the contrib/macos.sh script to build?")
message(FATAL_ERROR "macOS builds require a full static build; perhaps use the contrib/mac.sh script to build?")
endif()
# god (steve jobs) made apple so that man may suffer