How Container Authenticates with Registries Using macOS Keychain
Container stores registry credentials in the macOS Keychain via the KeychainHelper wrapper, isolating secrets from plain-text files and enabling secure authentication through login, logout, and automatic retrieval during image pulls.
The apple/container tool leverages macOS native security infrastructure to handle OCI registry authentication. By using the Keychain as the credential store, Container ensures that registry passwords never reside in unencrypted configuration files, instead relying on the system's secure enclave for storage and retrieval.
The Keychain-Based Authentication Architecture
Container isolates registry credentials from its configuration by storing them as generic password items in the macOS Keychain. This architecture centers on the KeychainHelper class provided by the internal ContainerPersistence package, which acts as a thin wrapper around Apple's Security framework.
All Keychain entries are namespaced using a unique security domain defined by Constants.keychainID in Sources/ContainerCommands/Registry/Constants.swift. This prevents credential collisions with other applications and ensures that Container only accesses its own registry secrets.
Registry Login Workflow
The authentication flow begins with the container registry login command, implemented in Sources/ContainerCommands/Registry/RegistryLogin.swift.
Credential Prompting and Validation
The command parses optional --username and --password-stdin flags. If a username is not provided, KeychainHelper.userPrompt(hostname:) retrieves any previously saved username for that host or prompts the user interactively. For passwords, KeychainHelper.passwordPrompt() reads input securely from the terminal with echo disabled.
Before storage, Container validates the credentials by creating a RegistryClient with BasicAuthentication(username:password:) and invoking ping() to verify connectivity and authentication against the registry.
Secure Storage with KeychainHelper
Upon successful validation, the credentials persist via keychain.save(hostname:username:password:), which stores them as a generic password item keyed by the hostname.
// Simplified workflow from RegistryLogin.swift
let keychain = KeychainHelper(securityDomain: Constants.keychainID)
if username.isEmpty {
username = try keychain.userPrompt(hostname: server)
}
if password.isEmpty {
password = try keychain.passwordPrompt()
print() // newline after hidden password entry
}
// Validate before storing
let client = RegistryClient(
host: host,
scheme: scheme.rawValue,
port: url.port,
authentication: BasicAuthentication(username: username, password: password),
retryOptions: .default
)
try await client.ping()
try keychain.save(hostname: server, username: username, password: password)
Registry Logout and Credential Management
Container provides explicit commands to manage stored credentials without manual Keychain manipulation.
Removing Stored Credentials
The container registry logout command resolves the registry hostname and calls keychain.delete(hostname:), removing the entry from the Keychain entirely. This implementation resides in Sources/ContainerCommands/Registry/RegistryLogout.swift.
// From RegistryLogout.swift
let keychain = KeychainHelper(securityDomain: Constants.keychainID)
let host = Reference.resolveDomain(domain: registry)
try keychain.delete(hostname: host)
Listing Saved Registries
The container registry list command invokes KeychainHelper.list() to display all stored registry entries for the configured security domain. This allows users to audit which registries have active credentials without exposing the underlying passwords. The implementation is located in Sources/ContainerCommands/Registry/RegistryList.swift.
How KeychainHelper Works
KeychainHelper abstracts macOS Security framework interactions through a concise API:
init(securityDomain:)– Initializes the helper withConstants.keychainIDto namespace all entries.userPrompt(hostname:)– Retrieves stored usernames or prompts interactively.passwordPrompt()– Reads passwords securely from/dev/ttywith no echo.save(hostname:username:password:)– Creates or updates generic password items.delete(hostname:)– Removes specific entries by hostname.list()– Returns all stored credentials for the security domain.
Because credentials live in the system Keychain, they benefit from macOS security policies including automatic screen-lock protection, Touch ID integration, and hardware-backed encryption.
End-to-End Authentication Flow
-
Initial Login – When running
container registry login my.registry.com, the tool creates aKeychainHelperinstance, prompts for missing credentials, validates them viaRegistryClient.ping(), and stores them viakeychain.save(). -
Subsequent Operations – Commands like
container runorcontainer pullautomatically retrieve credentials from the Keychain usingkeychain.load(hostname:)(internal to the image-pull logic) without requiring user interaction. -
Explicit Logout – Running
container registry logoutensures stale credentials are removed viakeychain.delete(), preventing unauthorized access to private registries.
Summary
- Container uses the macOS Keychain via
KeychainHelperto store credentials securely outside of configuration files. - The
logincommand validates credentials usingRegistryClient.ping()before persisting them withkeychain.save(hostname:username:password:). - Credentials are namespaced using
Constants.keychainIDto prevent collisions with other applications. - The
logoutcommand removes entries usingkeychain.delete(hostname:), whilelistdisplays stored registries without exposing passwords. - Image pull operations automatically retrieve credentials from the Keychain, enabling seamless authentication workflows.
Frequently Asked Questions
How does Container store registry credentials securely?
Container stores credentials as generic password items in the macOS Keychain using the KeychainHelper class from the ContainerPersistence package. This ensures secrets are encrypted and protected by the user's login password or Touch ID, rather than being stored in plain-text configuration files like ~/.docker/config.json.
What happens when I run container registry login?
The RegistryLogin command creates a KeychainHelper instance with Constants.keychainID and prompts for credentials if not provided via --username or --password-stdin. It validates the credentials using RegistryClient.ping() before calling keychain.save(hostname:username:password:) to persist them in the Keychain under the Container security domain.
How can I check which registries have stored credentials?
Run container registry list, which invokes KeychainHelper.list() to retrieve all stored entries for the Container security domain. This displays all registries that currently have saved credentials without exposing the actual passwords, as implemented in Sources/ContainerCommands/Registry/RegistryList.swift.
Is the Keychain integration mandatory for Container?
Yes, the Keychain is the mandatory credential store for registry authentication in Container. The tool does not support unencrypted credential files, ensuring that all registry passwords benefit from macOS security policies including automatic locking and hardware-backed encryption through the Secure Enclave.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →