keychain

package
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 15, 2026 License: Apache-2.0 Imports: 10 Imported by: 2

README

Store Keychain

Keychain integrates with the OS keystore. It supports Linux, macOS and Windows and can be used directly with keychain.New.

For more design implementation see ../docs/keychain/design.md.

Quickstart

import (
	"context"

	"github.com/docker/secrets-engine/store"
	"github.com/docker/secrets-engine/store/keychain"
	"github.com/docker/secrets-engine/store/mocks"
)

func main() {
	ctx := context.Background()
	kc, err := keychain.New(
		ctx,
		"service-group",
		"service-name",
		func(_ context.Context, _ store.ID) *mocks.MockCredential {
			return &mocks.MockCredential{}
		},
	)
	if err != nil {
		// handle error (see Availability below for detecting an unusable host)
	}
	_ = kc
}
Availability

keychain.New eagerly verifies that the OS keychain backend is reachable before returning. On a host without a usable keychain — for example WSL or a headless machine with no D-Bus session bus, or a Linux desktop with no gnome-keyring/kwallet running — it returns an error that matches keychain.ErrKeychainUnavailable, so callers can detect this at construction time and fall back to another store instead of failing on the first operation:

st, err := keychain.New(ctx, group, name, factory)
if errors.Is(err, keychain.ErrKeychainUnavailable) {
    // keychain unreachable on this host — use a fallback store
}

The ctx bounds the availability probe. On Linux it bounds the probe's D-Bus connection handshake and its single NameHasOwner round-trip and lets you cancel construction; if you pass a context without a deadline, New applies a short internal default so it stays responsive on an unreachable host, and any deadline you set yourself always wins. The probe never launches a session bus (dbus-launch) and, as a fast path, checks that the session bus socket exists before dialing. The check is prompt-safe and side-effect-free: it asks the D-Bus daemon whether the Secret Service is registered and never touches your stored secrets. On macOS and Windows the check is a no-op (and ctx is unused). See ../docs/keychain/design.md for details.

Locked collections (Linux)

A reachable keychain can still hold a locked collection. This is the default state on headless Linux hosts with SSH key-only logins: PAM has no password to auto-unlock the login keyring, so it is locked after every keyring-daemon restart.

A store operation that finds the collection locked asks the Secret Service to unlock it. On a passwordless keyring this succeeds silently. On a password-protected keyring it opens the backend's unlock prompt. If the prompt is dismissed (gnome-keyring does this immediately when no prompter can be shown), times out, or the operation's context expires, the operation fails with an error matching keychain.ErrCollectionLocked:

_, err := st.Get(ctx, id)
if errors.Is(err, keychain.ErrCollectionLocked) {
    // The collection still holds the user's credentials. Tell the user how
    // to unlock it, for example by logging in to the desktop session or
    // running gnome-keyring-daemon --unlock. Do not fall back to another
    // store; that would split credentials across stores.
}

The operation's ctx bounds the prompt wait, so a caller can set its own deadline; an internal 30 second cap always applies. Unavailable means there is no keychain to use, so fall back. Locked means the keychain and credentials exist but need the user's help, so surface the remediation and do not fall back.

Secrets

The keychain assumes that any secret stored would conform to the store.Secret interface. This allows the keychain to store secrets of any type and leaves it up to the implementer to decide how they would like their secret parsed.

Example CLI

The keychain package also contains an example CLI tool to test out how a real application might interact with the host keychain.

You can build the CLI by running go build inside the store/ root directory.

$ go build -o keychain-cli ./keychain/cmd/
$ ./keychain-cli

Documentation

Overview

The keychain package for Linux uses the org.freedesktop.secret service API over dbus. For more information on the Secret Service API, see https://specifications.freedesktop.org/secret-service-spec/latest/index.html.

Index

Constants

This section is empty.

Variables

View Source
var ErrCollectionLocked = errors.New("keychain collection is locked")

ErrCollectionLocked is returned by store operations when the keychain collection is locked and could not be unlocked (prompt dismissed, timed out, or canceled). The collection still holds the user's credentials. Linux-only.

View Source
var ErrDuplicateItem = errors.New("keychain item already exists")

ErrDuplicateItem is returned by Save when an item with the same ID already exists; use Upsert to overwrite. macOS-only: Windows and Linux update the existing item in place.

View Source
var ErrKeychainUnavailable = errors.New("keychain backend unavailable")

ErrKeychainUnavailable is returned by New when the keychain backend is unreachable (no D-Bus session bus, or no Secret Service daemon), as opposed to ErrNoDefaultCollection where the backend is reachable. Linux-only.

View Source
var ErrNoDefaultCollection = errors.New("no default keychain collection available")

ErrNoDefaultCollection is returned when the secret service has no usable default collection, typically on headless hosts with an uninitialized keyring. Linux-only; declared here so callers can reference it without build tags.

Functions

func New

func New[T store.Secret](ctx context.Context, serviceGroup, serviceName string, factory store.Factory[T], opts ...Option) (store.Store, error)

New creates a new keychain store.

It takes ServiceGroup and ServiceName and a [Factory] as input.

A ServiceGroup is added to an item stored by the keychain under the item's attributes and label. Many applications can share the same serviceGroup.

On macOS it is important that the service group matches the Keychain Access Groups. This prevents access from other applications not inside the Keychain Access group. https://developer.apple.com/documentation/security/sharing-access-to-keychain-items-among-a-collection-of-apps#Set-your-apps-access-groups

On Linux the service group is added to the attributes of a secret to tag the item. The secrets service API does not have the concept of a scoped item per application inside the collection. Thus, adding a service group does not prevent other applications from accessing the secret.

A ServiceName is a unique name of the application storing credentials, it is important to keep the service name unchanged once the service has stored credentials. Changing the service name can be done, but would require migrating existing credentials.

[Factory] is a function used to instantiate new secrets of type T.

ctx bounds the eager backend-availability probe New performs before returning (see ErrKeychainUnavailable). On Linux it bounds the probe's D-Bus connection handshake and its NameHasOwner round-trip, and lets the caller cancel construction; if ctx carries no deadline, New applies a short internal default so construction stays responsive on an unreachable host, and a caller-supplied deadline always takes precedence. On macOS/Windows the probe is a no-op and ctx is unused. New does not retain ctx: it governs construction only, not later store operations.

Types

type DarwinOptions added in v0.0.17

type DarwinOptions optionFunc[darwinOptions]

func WithUseDataProtectionKeychain added in v0.0.17

func WithUseDataProtectionKeychain() DarwinOptions

WithUseDataProtectionKeychain forces the use of entitlements to share credentials stored in the keychain between applications

type Option added in v0.0.17

type Option interface {
	// contains filtered or unexported methods
}

func WithDarwinOptions added in v0.0.17

func WithDarwinOptions(opt DarwinOptions) Option

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL