Container Registry Authentication and Credential Storage in Apple's Container Tool

The container CLI stores registry credentials securely in the macOS Keychain under the identifier com.apple.container.registry, providing login, logout, and list commands via RegistryLogin, RegistryLogout, and RegistryList.

The apple/container project provides a Swift-based container management solution that integrates deeply with macOS security primitives. Unlike tools that cache credentials in plain-text configuration files, this implementation leverages the system Keychain for container registry authentication and credential storage, ensuring secrets remain encrypted and access-controlled by the operating system.

Keychain Integration and Security Domain

All credential operations are centralized through KeychainHelper, which abstracts macOS Keychain access using the repository-wide security domain defined in Sources/Services/ContainerAPIService/Client/Constants.swift. At line 20, the constant Constants.keychainID is set to "com.apple.container.registry", serving as the exclusive identifier for all stored entries. This design prevents credential leakage and ties authentication data to the user's system-level security policies.

The helper exposes three core methods for credential lifecycle management:

  • KeychainHelper.save(hostname:username:password:) – Persists credentials after validation.
  • KeychainHelper.delete(hostname:) – Removes entries during logout.
  • KeychainHelper.list() – Retrieves all stored RegistryInfo objects for enumeration.

Authentication Lifecycle Commands

Logging In via RegistryLogin

The container registry login command is implemented in Sources/ContainerCommands/Registry/RegistryLogin.swift (lines 63-97). The workflow first constructs a BasicAuthentication object from the provided username and password (optionally supplied via the --password-stdin flag). It then validates connectivity by pinging the registry endpoint. Upon successful verification, the credentials are persisted using KeychainHelper.save(hostname:username:password:).


# Log in to a registry (prompts for username/password or reads password from stdin)

container registry login myregistry.example.com \
    --username alice \
    --password-stdin < password.txt

Logging Out via RegistryLogout

The container registry logout command, found in Sources/ContainerCommands/Registry/RegistryLogout.swift (lines 36-40), performs a straightforward deletion. It invokes KeychainHelper.delete(hostname:) to remove the specific entry associated with the provided registry host, ensuring no residual authentication data remains on the system.


# Log out (remove credentials)

container registry logout myregistry.example.com

Listing Stored Credentials via RegistryList

To enumerate configured registries, the registry list command uses Sources/ContainerCommands/Registry/RegistryList.swift (lines 41-50). This command calls KeychainHelper.list() to fetch all entries, then maps each RegistryInfo instance to a RegistryResource object as defined in Sources/ContainerResource/Registry/RegistryResource.swift (lines 20-52). Output is rendered as a formatted table by default, or as a simple hostname list when the -q (quiet) flag is specified.


# Show stored registry credentials

container registry list           # table view

container registry list -q       # quiet – only hostnames

Runtime Credential Retrieval for Image Operations

When pulling or pushing images, the system retrieves stored credentials to construct a RegistryClient. In Sources/ContainerAPIService/Client/ClientImage.swift (lines 62-71), the code resolves the connection scheme—either http or https—based on command-line flags such as --scheme and internal DNS domain checks. It then instantiates BasicAuthentication with the username and password recovered from the Keychain, passing this object to the RegistryClient initializer.

// Programmatic login handling (simplified)
let keychain = KeychainHelper(securityDomain: Constants.keychainID)

// Prompt user if needed
let username = try keychain.userPrompt(hostname: server)
let password = try keychain.passwordPrompt()

// Save credentials
try keychain.save(hostname: server, username: username, password: password)

// Later – retrieve for image pull
let stored = try keychain.list().first { $0.hostname == server }
let auth = BasicAuthentication(username: stored?.username ?? "", password: stored?.password ?? "")
let client = RegistryClient(host: server, scheme: "https", port: nil, authentication: auth)

Key Source Files

The credential management subsystem spans the following critical paths:

Summary

  • Secure Storage: Credentials are stored exclusively in the macOS Keychain under the identifier "com.apple.container.registry", never in plain-text files.
  • Command Interface: Users interact with credentials through registry login, registry logout, and registry list commands.
  • Validation Flow: Login operations validate registry connectivity via ping before persisting secrets via KeychainHelper.save(hostname:username:password:).
  • Runtime Usage: Image operations retrieve credentials using KeychainHelper.list(), construct BasicAuthentication objects, and inject them into RegistryClient instances with configurable schemes (http/https).

Frequently Asked Questions

Where does the container CLI store registry credentials?

Credentials are stored in the macOS Keychain under the security domain identifier "com.apple.container.registry", defined in Sources/Services/ContainerAPIService/Client/Constants.swift. This ensures encryption at rest and aligns with system-wide access controls.

How can I log in to a registry non-interactively?

Use the --password-stdin flag with container registry login to pipe the password via standard input. This avoids exposing secrets in shell history and is handled by the RegistryLogin implementation in Sources/ContainerCommands/Registry/RegistryLogin.swift.

What authentication method is used for registry operations?

The tool utilizes BasicAuthentication objects constructed from Keychain-stored username/password pairs. These are passed to RegistryClient during instantiation, as seen in the credential retrieval flow within ClientImage.swift.

How do I configure the connection scheme for insecure registries?

Specify the --scheme flag (e.g., --scheme http) when executing commands. The ClientImage logic (lines 62-71) resolves the scheme based on this flag and internal DNS domain checks before initiating the connection.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →