From 7a2c4a89ddc6893669dafacdf971ca8ae0b5ef75 Mon Sep 17 00:00:00 2001 From: John Logan Date: Mon, 23 Feb 2026 15:21:10 -0800 Subject: [PATCH] Put CLI and container-core-images in a keychain access group. - Closes #1253 (hopefully). - Requires apple/containerization#553. - We share a keychain between the CLI and the image helper, but the user needs to opt-in the image helper for access to entries created by the CLI. Since both apps are notarized with the same team ID, we should be able to use an access group to avoid this step for release builds. --- Makefile | 5 ++--- Package.resolved | 2 +- Sources/ContainerCommands/Registry/RegistryList.swift | 2 +- Sources/ContainerCommands/Registry/RegistryLogin.swift | 2 +- .../ContainerCommands/Registry/RegistryLogout.swift | 2 +- .../ContainerAPIService/Client/Constants.swift | 2 ++ .../ContainerImagesService/Server/ImagesService.swift | 2 +- signing/container-core-images.entitlements | 10 ++++++++++ signing/container.entitlements | 10 ++++++++++ 9 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 signing/container-core-images.entitlements create mode 100644 signing/container.entitlements diff --git a/Makefile b/Makefile index 0d22945ed..e4e826084 100644 --- a/Makefile +++ b/Makefile @@ -117,12 +117,11 @@ $(STAGING_DIR): .PHONY: installer-pkg installer-pkg: $(STAGING_DIR) @echo Signing container binaries... - @codesign $(CODESIGN_OPTS) --identifier com.apple.container.cli "$(join $(STAGING_DIR), bin/container)" + @codesign $(CODESIGN_OPTS) --identifier com.apple.container.cli --entitlements=signing/container.entitlements "$(join $(STAGING_DIR), bin/container)" @codesign $(CODESIGN_OPTS) --identifier com.apple.container.apiserver "$(join $(STAGING_DIR), bin/container-apiserver)" - @codesign $(CODESIGN_OPTS) --prefix=com.apple.container. "$(join $(STAGING_DIR), libexec/container/plugins/container-core-images/bin/container-core-images)" + @codesign $(CODESIGN_OPTS) --prefix=com.apple.container. --entitlements=signing/container-core-images.entitlements "$(join $(STAGING_DIR), libexec/container/plugins/container-core-images/bin/container-core-images)" @codesign $(CODESIGN_OPTS) --prefix=com.apple.container. --entitlements=signing/container-runtime-linux.entitlements "$(join $(STAGING_DIR), libexec/container/plugins/container-runtime-linux/bin/container-runtime-linux)" @codesign $(CODESIGN_OPTS) --prefix=com.apple.container. --entitlements=signing/container-network-vmnet.entitlements "$(join $(STAGING_DIR), libexec/container/plugins/container-network-vmnet/bin/container-network-vmnet)" - @echo Creating application installer @pkgbuild --root "$(STAGING_DIR)" --identifier com.apple.container-installer --install-location /usr/local --version ${RELEASE_VERSION} $(PKG_PATH) @rm -rf "$(STAGING_DIR)" diff --git a/Package.resolved b/Package.resolved index 970758983..173049b53 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "a0d9f0581740922266b0739fae8ec0998c7d5c7d98ff76cccb68a878f27e88ab", + "originHash" : "9d09d49729b014b3becca478d9303cabff31461a0f686e1c741d4ed7e5262906", "pins" : [ { "identity" : "async-http-client", diff --git a/Sources/ContainerCommands/Registry/RegistryList.swift b/Sources/ContainerCommands/Registry/RegistryList.swift index 9b83013f2..ef7285a1a 100644 --- a/Sources/ContainerCommands/Registry/RegistryList.swift +++ b/Sources/ContainerCommands/Registry/RegistryList.swift @@ -38,7 +38,7 @@ extension Application { aliases: ["ls"]) public func run() async throws { - let keychain = KeychainHelper(securityDomain: Constants.keychainID) + let keychain = KeychainHelper(securityDomain: Constants.keychainID, accessGroup: Constants.keychainGroup) let registries = try keychain.list() try printRegistries(registries: registries, format: format) } diff --git a/Sources/ContainerCommands/Registry/RegistryLogin.swift b/Sources/ContainerCommands/Registry/RegistryLogin.swift index 96d5919f5..917404729 100644 --- a/Sources/ContainerCommands/Registry/RegistryLogin.swift +++ b/Sources/ContainerCommands/Registry/RegistryLogin.swift @@ -57,7 +57,7 @@ extension Application { } password = String(decoding: passwordData, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines) } - let keychain = KeychainHelper(securityDomain: Constants.keychainID) + let keychain = KeychainHelper(securityDomain: Constants.keychainID, accessGroup: Constants.keychainGroup) if username == "" { username = try keychain.userPrompt(hostname: server) } diff --git a/Sources/ContainerCommands/Registry/RegistryLogout.swift b/Sources/ContainerCommands/Registry/RegistryLogout.swift index 2f5a2432b..ea3e14e11 100644 --- a/Sources/ContainerCommands/Registry/RegistryLogout.swift +++ b/Sources/ContainerCommands/Registry/RegistryLogout.swift @@ -34,7 +34,7 @@ extension Application { var registry: String public func run() async throws { - let keychain = KeychainHelper(securityDomain: Constants.keychainID) + let keychain = KeychainHelper(securityDomain: Constants.keychainID, accessGroup: Constants.keychainGroup) let r = Reference.resolveDomain(domain: registry) try keychain.delete(hostname: r) } diff --git a/Sources/Services/ContainerAPIService/Client/Constants.swift b/Sources/Services/ContainerAPIService/Client/Constants.swift index c5ab8fe63..1bbcf2b81 100644 --- a/Sources/Services/ContainerAPIService/Client/Constants.swift +++ b/Sources/Services/ContainerAPIService/Client/Constants.swift @@ -18,4 +18,6 @@ public enum Constants { /// The keychain ID to use for registry credentials. public static let keychainID = "com.apple.container.registry" + /// The application access group to use for registry credentials. + public static let keychainGroup = "com.apple.container.keychain" } diff --git a/Sources/Services/ContainerImagesService/Server/ImagesService.swift b/Sources/Services/ContainerImagesService/Server/ImagesService.swift index 2fb06ed1c..f72e87ec2 100644 --- a/Sources/Services/ContainerImagesService/Server/ImagesService.swift +++ b/Sources/Services/ContainerImagesService/Server/ImagesService.swift @@ -427,7 +427,7 @@ extension ImagesService { if let authentication { return try await body(authentication) } - let keychain = KeychainHelper(securityDomain: Constants.keychainID) + let keychain = KeychainHelper(securityDomain: Constants.keychainID, accessGroup: Constants.keychainGroup) do { authentication = try keychain.lookup(hostname: host) } catch let err as KeychainHelper.Error { diff --git a/signing/container-core-images.entitlements b/signing/container-core-images.entitlements new file mode 100644 index 000000000..5d24b49c3 --- /dev/null +++ b/signing/container-core-images.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.apple.container.keychain + + + diff --git a/signing/container.entitlements b/signing/container.entitlements new file mode 100644 index 000000000..5d24b49c3 --- /dev/null +++ b/signing/container.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.apple.container.keychain + + +