diff --git a/Makefile b/Makefile index 7c3b16483bfc7..4fe79a8f83582 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,10 @@ TELEPORT_DEBUG ?= false GITTAG=v$(VERSION) CGOFLAG ?= CGO_ENABLED=1 +# RELEASE_DIR is where the release artifacts (tarballs, pacakges, etc) are put. It +# should be an absolute directory as it is used by e/Makefile too, from the e/ directory. +RELEASE_DIR := $(CURDIR)/$(BUILDDIR)/artifacts + # When TELEPORT_DEBUG is true, set flags to produce # debugger-friendly builds. ifeq ("$(TELEPORT_DEBUG)","true") @@ -75,10 +79,21 @@ PAM_MESSAGE := with-PAM-support endif endif +# darwin universal (Intel + Apple Silicon combined) binary support +RELEASE_darwin_arm64 = $(RELEASE_DIR)/teleport-$(GITTAG)-darwin-arm64-bin.tar.gz +RELEASE_darwin_amd64 = $(RELEASE_DIR)/teleport-$(GITTAG)-darwin-amd64-bin.tar.gz +BUILDDIR_arm64 = $(BUILDDIR)/arm64 +BUILDDIR_amd64 = $(BUILDDIR)/amd64 +# TARBINS is the path of the binaries in the release tarballs +TARBINS = $(addprefix teleport/,$(BINS)) + # Check if rust and cargo are installed before compiling CHECK_CARGO := $(shell cargo --version 2>/dev/null) CHECK_RUST := $(shell rustc --version 2>/dev/null) +RUST_VERSION ?= $(shell make --no-print-directory -C build.assets print-rust-version) +RUST_TARGET_ARCH ?= $(CARGO_TARGET_$(OS)_$(ARCH)) + # Have cargo use sparse crates.io protocol: # https://blog.rust-lang.org/2023/03/09/Rust-1.68.0.html # TODO: Delete when it becomes default in Rust 1.70.0 @@ -109,6 +124,13 @@ endif endif endif +# Set C_ARCH for building libfido2 and dependencies. ARCH is the Go +# architecture which uses different names for architectures than C +# uses. Export it for the build.assets/build-fido2-macos.sh script. +C_ARCH_amd64 = x86_64 +C_ARCH = $(or $(C_ARCH_$(ARCH)),$(ARCH)) +export C_ARCH + # Enable libfido2 for testing? # Eagerly enable if we detect the package, we want to test as much as possible. ifeq ("$(shell pkg-config libfido2 2>/dev/null; echo $$?)", "0") @@ -170,10 +192,10 @@ endif # On Windows only build tsh. On all other platforms build teleport, tctl, # and tsh. -BINARIES=$(BUILDDIR)/teleport $(BUILDDIR)/tctl $(BUILDDIR)/tsh $(BUILDDIR)/tbot -ifeq ("$(OS)","windows") -BINARIES=$(BUILDDIR)/tsh -endif +BINS_default = teleport tctl tsh tbot +BINS_windows = tsh +BINS = $(or $(BINS_$(OS)),$(BINS_default)) +BINARIES = $(addprefix $(BUILDDIR)/,$(BINS)) # Joins elements of the list in arg 2 with the given separator. # 1. Element separator. @@ -236,6 +258,11 @@ endif CGOFLAG_TSH ?= $(CGOFLAG) +# Map ARCH into the architecture flag for electron-builder if they +# are different to the Go $(ARCH) we use as an input. +ELECTRON_BUILDER_ARCH_amd64 = x64 +ELECTRON_BUILDER_ARCH = $(or $(ELECTRON_BUILDER_ARCH_$(ARCH)),$(ARCH)) + # # 'make all' builds all 4 executables and places them in the current directory. # @@ -328,6 +355,15 @@ else rdpclient: endif +# Build libfido2 and dependencies for MacOS. Uses exported C_ARCH variable defined earlier. +.PHONY: build-fido2 +build-fido2: + ./build.assets/build-fido2-macos.sh build + +.PHONY: print-fido2-pkg-path +print-fido2-pkg-path: + @./build.assets/build-fido2-macos.sh pkg_config_path + # # make full - Builds Teleport binaries with the built-in web assets and # places them into $(BUILDDIR). On Windows, this target is skipped because @@ -379,6 +415,11 @@ clean-ui: # # make release - Produces a binary release tarball. # + +# RELEASE_DIR is where release artifact files are put, such as tarballs, packages, etc. +$(RELEASE_DIR): + mkdir $@ + .PHONY: export release: @@ -412,10 +453,10 @@ release-arm64: # make build-archive - Packages the results of a build into a release tarball # .PHONY: build-archive -build-archive: +build-archive: | $(RELEASE_DIR) @echo "---> Creating OSS release archive." mkdir teleport - cp -rf $(BUILDDIR)/* \ + cp -rf $(BINARIES) \ examples \ build.assets/install\ README.md \ @@ -423,6 +464,7 @@ build-archive: teleport/ echo $(GITTAG) > teleport/VERSION tar $(TAR_FLAGS) -c teleport | gzip -n > $(RELEASE).tar.gz + cp $(RELEASE).tar.gz $(RELEASE_DIR) rm -rf teleport @echo "---> Created $(RELEASE).tar.gz." @@ -438,14 +480,41 @@ include darwin-signing.mk .PHONY: release-darwin-unsigned release-darwin-unsigned: RELEASE:=$(RELEASE)-unsigned -release-darwin-unsigned: clean full build-archive +release-darwin-unsigned: full build-archive .PHONY: release-darwin +ifneq ($(ARCH),universal) release-darwin: ABSOLUTE_BINARY_PATHS:=$(addprefix $(CURDIR)/,$(BINARIES)) release-darwin: release-darwin-unsigned $(NOTARIZE_BINARIES) $(MAKE) build-archive @if [ -f e/Makefile ]; then $(MAKE) -C e release; fi +else + +# release-darwin for ARCH == universal does not build binaries, but instead +# combines previously-built binaries. For this, it depends on the ARM64 and +# AMD64 signed tarballs being built into $(RELEASE_DIR). The dependencies +# expressed here will not make that happen as this is typically done on CI +# where these two tarballs are built in separate pipelines, and copied in for +# the universal build. +# +# For local manual runs, create these tarballs with: +# make ARCH=arm64 release-darwin +# make ARCH=amd64 release-darwin +# Ensure you have the rust toolchains for these installed by running +# make ARCH=arm64 rustup-install-target-toolchain +# make ARCH=amd64 rustup-install-target-toolchain +release-darwin: $(RELEASE_darwin_arm64) $(RELEASE_darwin_amd64) + mkdir -p $(BUILDDIR_arm64) $(BUILDDIR_amd64) + tar -C $(BUILDDIR_arm64) -xzf $(RELEASE_darwin_arm64) --strip-components=1 $(TARBINS) + tar -C $(BUILDDIR_amd64) -xzf $(RELEASE_darwin_amd64) --strip-components=1 $(TARBINS) + lipo -create -output $(BUILDDIR)/teleport $(BUILDDIR_arm64)/teleport $(BUILDDIR_amd64)/teleport + lipo -create -output $(BUILDDIR)/tctl $(BUILDDIR_arm64)/tctl $(BUILDDIR_amd64)/tctl + lipo -create -output $(BUILDDIR)/tsh $(BUILDDIR_arm64)/tsh $(BUILDDIR_amd64)/tsh + lipo -create -output $(BUILDDIR)/tbot $(BUILDDIR_arm64)/tbot $(BUILDDIR_amd64)/tbot + $(MAKE) ARCH=universal build-archive + @if [ -f e/Makefile ]; then $(MAKE) -C e release; fi +endif # # make release-unix-only - Produces an Enterprise binary release tarball containing @@ -521,13 +590,22 @@ release-windows: release-windows-unsigned # proper release (a proper release needs the APP_PATH as that points to # the complete signed package). See web/packages/teleterm/README.md for # details. -# + .PHONY: release-connect -release-connect: +release-connect: | $(RELEASE_DIR) $(eval export CSC_NAME) yarn install --frozen-lockfile yarn build-term - yarn package-term -c.extraMetadata.version=$(VERSION) + yarn package-term -c.extraMetadata.version=$(VERSION) --$(ELECTRON_BUILDER_ARCH) + # Only copy proper builds with tsh.app to $(RELEASE_DIR) + # Drop -universal "arch" from dmg name when copying to $(RELEASE_DIR) + if [ -n "$$CONNECT_TSH_APP_PATH" ]; then \ + TARGET_NAME="Teleport Connect-$(VERSION)-$(ARCH).dmg"; \ + if [ "$(ARCH)" = 'universal' ]; then \ + TARGET_NAME="$${TARGET_NAME/-universal/}"; \ + fi; \ + cp web/packages/teleterm/build/release/"Teleport Connect-$(VERSION)-$(ELECTRON_BUILDER_ARCH).dmg" "$(RELEASE_DIR)/$${TARGET_NAME}"; \ + fi # # Remove trailing whitespace in all markdown files under docs/. @@ -1140,23 +1218,25 @@ endif # build .pkg .PHONY: pkg -pkg: +pkg: | $(RELEASE_DIR) $(eval export DEVELOPER_ID_APPLICATION DEVELOPER_ID_INSTALLER) mkdir -p $(BUILDDIR)/ cp ./build.assets/build-package.sh ./build.assets/build-common.sh $(BUILDDIR)/ chmod +x $(BUILDDIR)/build-package.sh - # arch and runtime are currently ignored on OS X - # we pass them through for consistency - they will be dropped by the build script + # runtime is currently ignored on OS X + # we pass it through for consistency - it will be dropped by the build script cd $(BUILDDIR) && ./build-package.sh -t oss -v $(VERSION) -p pkg -b $(TELEPORT_BUNDLEID) -a $(ARCH) $(RUNTIME_SECTION) $(TARBALL_PATH_SECTION) + cp $(BUILDDIR)/teleport-*.pkg $(RELEASE_DIR) if [ -f e/Makefile ]; then $(MAKE) -C e pkg; fi # build tsh client-only .pkg .PHONY: pkg-tsh -pkg-tsh: +pkg-tsh: | $(RELEASE_DIR) $(eval export DEVELOPER_ID_APPLICATION DEVELOPER_ID_INSTALLER) - ./build.assets/build-pkg-tsh.sh -t oss -v $(VERSION) -b $(TSH_BUNDLEID) $(TARBALL_PATH_SECTION) + ./build.assets/build-pkg-tsh.sh -t oss -v $(VERSION) -b $(TSH_BUNDLEID) -a $(ARCH) $(TARBALL_PATH_SECTION) mkdir -p $(BUILDDIR)/ mv tsh*.pkg* $(BUILDDIR)/ + cp $(BUILDDIR)/tsh-*.pkg $(RELEASE_DIR) # build .rpm .PHONY: rpm @@ -1241,3 +1321,12 @@ build-ui-e: ensure-js-deps .PHONY: docker-ui docker-ui: $(MAKE) -C build.assets ui + +# rustup-install-target-toolchain ensures the required rust compiler is +# installed to build for $(ARCH)/$(OS) for the version of rust we use, as +# defined in build.assets/Makefile. It assumes that `rustup` is already +# installed for managing the rust toolchain. +.PHONY: rustup-install-target-toolchain +rustup-install-target-toolchain: + rustup override set $(RUST_VERSION) + rustup target add $(RUST_TARGET_ARCH) diff --git a/build.assets/build-fido2-macos.sh b/build.assets/build-fido2-macos.sh index a82c1383b68ef..982eb9c09714a 100755 --- a/build.assets/build-fido2-macos.sh +++ b/build.assets/build-fido2-macos.sh @@ -13,6 +13,13 @@ set -eu readonly MACOS_VERSION_MIN=10.13 +# Cross-architecture building +# Set C_ARCH to $(uname -m) if unset, and validate supported architecture +if ! [[ "${C_ARCH:=$(uname -m)}" =~ ^(x86_64|arm64)$ ]]; then + echo "unknown or unsupported build architecture: $C_ARCH" >&2 + exit 1 +fi + # Note: versions are the same as the corresponding git tags for each repo. readonly CBOR_VERSION=v0.10.2 readonly CBOR_COMMIT=efa6c0886bae46bdaef9b679f61f4b9d8bc296ae @@ -21,7 +28,7 @@ readonly CRYPTO_COMMIT=31157bc0b46e04227b8468d3e6915e4d0332777c readonly FIDO2_VERSION=1.13.0 readonly FIDO2_COMMIT=486a8f8667e42f55cee2bba301b41433cacec830 -readonly LIB_CACHE="/tmp/teleport-fido2-cache" +readonly LIB_CACHE="/tmp/teleport-fido2-cache-$C_ARCH" readonly PKGFILE_DIR="$LIB_CACHE/fido2-${FIDO2_VERSION}_cbor-${CBOR_VERSION}_crypto-${CRYPTO_VERSION}" # Library cache paths, implicitly matched by fetch_and_build. @@ -82,6 +89,7 @@ cbor_build() { cd "$src" cmake \ + -DCMAKE_OSX_ARCHITECTURES="$C_ARCH" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="$dest" \ -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_VERSION_MIN" \ @@ -105,7 +113,8 @@ crypto_build() { echo 'crypto: building' >&2 cd "$src" - ./config \ + ./Configure \ + "darwin64-$C_ARCH-cc" \ -mmacosx-version-min="$MACOS_VERSION_MIN" \ --prefix="$dest" \ no-shared \ @@ -137,6 +146,7 @@ fido2_build() { -DBUILD_EXAMPLES=OFF \ -DBUILD_MANPAGES=OFF \ -DBUILD_TOOLS=OFF \ + -DCMAKE_OSX_ARCHITECTURES="$C_ARCH" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="$dest" \ -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_VERSION_MIN" \ @@ -172,6 +182,7 @@ EOF # Word splitting desired for pkg-config. #shellcheck disable=SC2046 gcc \ + -arch "$C_ARCH" \ $(pkg-config --cflags --libs libfido2-static) \ -o "$toydir/toy.bin" \ "$toydir/toy.c" diff --git a/build.assets/build-package.sh b/build.assets/build-package.sh index 77d347b9518b3..469d6bfd5242f 100755 --- a/build.assets/build-package.sh +++ b/build.assets/build-package.sh @@ -26,7 +26,7 @@ while getopts ":t:v:p:a:r:s:b:n" o; do ;; a) a=${OPTARG} - if [[ ${a} != "amd64" && ${a} != "x86_64" && ${a} != "386" && ${a} != "i386" && ${a} != "arm" && ${a} != "arm64" ]]; then usage; fi + if [[ ${a} != "amd64" && ${a} != "x86_64" && ${a} != "386" && ${a} != "i386" && ${a} != "arm" && ${a} != "arm64" && ${a} != "universal" ]]; then usage; fi ;; r) r=${OPTARG} @@ -101,16 +101,11 @@ if [[ "${PACKAGE_TYPE}" == "pkg" ]]; then echo "You must be running on OS X to build .pkg files" exit 4 fi - if [[ "${ARCH}" != "" ]]; then - echo "arch parameter is ignored when building for OS X" - unset ARCH - fi if [[ "${RUNTIME}" != "" ]]; then echo "runtime parameter is ignored when building for OS X" unset RUNTIME fi PLATFORM="darwin" - ARCH="amd64" if [[ ! $(type pkgbuild) ]]; then echo "You need to install pkgbuild" echo "Run: xcode-select --install" @@ -136,6 +131,7 @@ else fi fi +PACKAGE_ARCH="" # handle differences between 'gravitational' arch and system arch if [[ "${ARCH}" == "386" || "${ARCH}" == "i386" ]]; then TEXT_ARCH="32-bit x86" @@ -147,6 +143,7 @@ if [[ "${ARCH}" == "386" || "${ARCH}" == "i386" ]]; then elif [[ "${ARCH}" == "amd64" || "${ARCH}" == "x86_64" ]]; then TEXT_ARCH="64-bit x86" TARBALL_ARCH="amd64" + PACKAGE_ARCH="amd64" DEB_PACKAGE_ARCH="amd64" DEB_OUTPUT_ARCH="amd64" RPM_PACKAGE_ARCH="x86_64" @@ -162,10 +159,14 @@ elif [[ "${ARCH}" == "arm" ]]; then elif [[ "${ARCH}" == "arm64" ]]; then TEXT_ARCH="64-bit ARM" TARBALL_ARCH="arm64" + PACKAGE_ARCH="arm64" DEB_PACKAGE_ARCH="arm64" DEB_OUTPUT_ARCH="arm64" RPM_PACKAGE_ARCH="aarch64" RPM_OUTPUT_ARCH="arm64" # backwards compatibility +elif [[ "${ARCH}" == "universal" ]]; then + TARBALL_ARCH="universal" + PACKAGE_ARCH="universal" fi # amd64 RPMs should use CentOS 7 compatible artifacts @@ -203,13 +204,22 @@ fi # set file list if [[ "${PACKAGE_TYPE}" == "pkg" ]]; then + if [[ -z "${PACKAGE_ARCH}" ]]; then + echo "Unsupported architecture: ${ARCH}" + exit 1 + fi + # No architecture tag on package filename for universal (multi-arch) binaries. + ARCH_TAG="" + if [[ "${PACKAGE_ARCH}" != "universal" ]]; then + ARCH_TAG="-${PACKAGE_ARCH}" + fi SIGN_PKG="true" FILE_LIST="${TAR_PATH}/tsh ${TAR_PATH}/tctl ${TAR_PATH}/teleport ${TAR_PATH}/tbot" BUNDLE_ID="${b:-com.gravitational.teleport}" if [[ "${TELEPORT_TYPE}" == "ent" ]]; then - PKG_FILENAME="teleport-ent-${TELEPORT_VERSION}.${PACKAGE_TYPE}" + PKG_FILENAME="teleport-ent-${TELEPORT_VERSION}${ARCH_TAG}.${PACKAGE_TYPE}" else - PKG_FILENAME="teleport-${TELEPORT_VERSION}.${PACKAGE_TYPE}" + PKG_FILENAME="teleport-${TELEPORT_VERSION}${ARCH_TAG}.${PACKAGE_TYPE}" fi else FILE_LIST="${TAR_PATH}/tsh ${TAR_PATH}/tctl ${TAR_PATH}/teleport ${TAR_PATH}/tbot ${TAR_PATH}/examples/systemd/teleport.service" diff --git a/build.assets/build-pkg-tsh.sh b/build.assets/build-pkg-tsh.sh index 125ca4080ebd0..b0f31d199a751 100755 --- a/build.assets/build-pkg-tsh.sh +++ b/build.assets/build-pkg-tsh.sh @@ -6,6 +6,7 @@ TELEPORT_TYPE='' # -t, oss or ent TELEPORT_VERSION='' # -v, version, without leading 'v' TARBALL_DIRECTORY='' # -s BUNDLEID="${TSH_BUNDLEID}" +PACKAGE_ARCH=amd64 # -a, default to amd64 for backward-compatibilty. usage() { log "Usage: $0 -t oss|eng -v version [-s tarball_directory] [-b bundle_id] [-n]" @@ -35,7 +36,7 @@ main() { . "$buildassets/build-common.sh" local opt='' - while getopts "t:v:s:b:n" opt; do + while getopts "t:v:s:b:a:n" opt; do case "$opt" in t) if [[ "$OPTARG" != "oss" && "$OPTARG" != "ent" ]]; then @@ -58,6 +59,9 @@ main() { b) BUNDLEID="$OPTARG" ;; + a) + PACKAGE_ARCH="$OPTARG" + ;; n) DRY_RUN_PREFIX='echo + ' # declared by build-common.sh ;; @@ -119,8 +123,8 @@ of the key to sign packages" [[ "$TELEPORT_TYPE" == 'ent' ]] && ent='-ent' local tarname='' tarname="$(printf \ - "teleport%s-v%s-darwin-amd64-bin.tar.gz" \ - "$ent" "$TELEPORT_VERSION")" + "teleport%s-v%s-darwin-%s-bin.tar.gz" \ + "$ent" "$TELEPORT_VERSION" "$PACKAGE_ARCH")" [[ -n "$TARBALL_DIRECTORY" ]] && tarname="$TARBALL_DIRECTORY/$tarname" tarout='' # find_or_fetch_tarball writes to this @@ -165,7 +169,12 @@ of the key to sign packages" # Prepare and sign the installer package. # Note that the installer does __NOT__ have a `v` in the version number. - target="$tmp/tsh-$TELEPORT_VERSION.pkg" # switches from app to pkg + # The package for the universal binary does not have an architecture in the name. + local arch_tag="" + if [[ "$PACKAGE_ARCH" != "universal" ]]; then + arch_tag="-$PACKAGE_ARCH" + fi + target="$tmp/tsh-$TELEPORT_VERSION$arch_tag.pkg" # switches from app to pkg local pkg_root="$tmp/root" local pkg_component_plist="$tmp/tsh-component.plist" local pkg_scripts="$buildassets/macos/scripts" diff --git a/e b/e index 54b498ba85ec6..a4e1a66a40dd8 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit 54b498ba85ec630013d9600fa17ed2e32f94daab +Subproject commit a4e1a66a40dd87283a29f0a061d7b8edae4530f8 diff --git a/web/packages/teleterm/electron-builder-config.js b/web/packages/teleterm/electron-builder-config.js index e06eef6355f5c..6fa1a50b25545 100644 --- a/web/packages/teleterm/electron-builder-config.js +++ b/web/packages/teleterm/electron-builder-config.js @@ -1,4 +1,5 @@ const { env, platform } = require('process'); +const fs = require('fs'); const isMac = platform === 'darwin'; @@ -28,6 +29,9 @@ if (!isMac && env.CONNECT_TSH_BIN_PATH === undefined) { throw new Error('You must provide CONNECT_TSH_BIN_PATH'); } +// Holds tsh.app Info.plist during build. Used in afterPack. +let tshAppPlist; + /** * @type { import('electron-builder').Configuration } */ @@ -36,7 +40,36 @@ module.exports = { asar: true, asarUnpack: '**\\*.{node,dll}', afterSign: 'notarize.js', - files: ['build/app/dist'], + afterPack: packed => { + // @electron-universal adds the `ElectronAsarIntegrity` key to every .plist + // file it finds, causing signature verification to fail for tsh.app that gets + // embedded in Teleport Connect. This causes the error "invalid Info.plist (plist + // or signature have been modified)". + // Workaround this by copying the tsp.app plist file before adding the key and + // replace it after it is done. + + if (!env.CONNECT_TSH_APP_PATH) { + // Not embedding tsh.app + return; + } + + const path = `${packed.appOutDir}/Teleport Connect.app/Contents/MacOS/tsh.app/Contents/Info.plist`; + if (packed.appOutDir.endsWith('mac-universal--x64')) { + tshAppPlist = fs.readFileSync(path); + } + if (packed.appOutDir.endsWith('mac-universal')) { + fs.writeFileSync(path, tshAppPlist); + } + }, + files: [ + 'build/app/dist', + // node-pty creates some files that differ across architecture builds causing + // the error "can't reconcile the non-macho files" as they cant be combined + // with lipo for a universal build. They aren't needed so skip them. + '!node_modules/node-pty/build/*/.forge-meta', + '!node_modules/node-pty/build/Debug/.deps/**', + '!node_modules/node-pty/bin', + ], mac: { target: 'dmg', category: 'public.app-category.developer-tools', @@ -46,6 +79,8 @@ module.exports = { // If CONNECT_TSH_APP_PATH is provided, we assume that tsh.app is already signed. signIgnore: env.CONNECT_TSH_APP_PATH && ['tsh.app'], icon: 'build_resources/icon-mac.png', + // x64ArchFiles is for x64 and universal files (lipo tool should skip them) + x64ArchFiles: 'Contents/MacOS/tsh.app/Contents/MacOS/tsh', // On macOS, helper apps (such as tsh.app) should be under Contents/MacOS, hence using // `extraFiles` instead of `extraResources`. // https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle @@ -67,6 +102,7 @@ module.exports = { ].filter(Boolean), }, dmg: { + artifactName: '${productName}-${version}-${arch}.${ext}', contents: [ { x: 130,