Historically Docker has relied upon the docker-credential-helper which ships with Docker Desktop.
The docker-credential-helper is a separate binary and is always invoked as a separate binary due to the limitations of the OS keystore. As far as I am aware, macOS has a limitation that when writing a credential to the keystore it is only retrievable by the same binary. So storing a credential in the Docker CLI will only be retrievable by the Docker CLI.
With what does it integrate?
Depending on the OS, the docker-credential-helper integrates with:
In the past the docker-credential-helper used to integrate directly with the macOS Security/Security.h C library, but was swapped for the keybase/go-keychain library in this commit docker/docker-credential-helpers@4cdcdc2 due to deprecated SDK functions in osx docker/docker-credential-helpers#280.
The credential helper also usually ships the pass implementation and not the libsecret implementation. The pass implementation was added here docker/docker-credential-helpers#81 to support use cases where the user does not have a GUI installed (e.g. server environments).
Why not just use the credential helper?
The credential helper is very tied to the concept of a registry. Each credential stored is using a ServerURL to indicate which registry is saving the credential. You can see this in our schema defined for the libsecret implementation over here
Over time we have started moving away from registry authentication to other types of authentication like OAuth (Docker Hub login). This means that a login is based on a realm or namespace and multiple users could be saved under a realm / namespace.
Doing an exec out to the binary is annoying. It gives little control to the library implementing the credential-helper - we even have to rely on the user to install this (in certain environments) and we rely on the config specifying the correct credential-helper.
- This is a confusing UX to the user
- Other software can change the config
- Requires installing another binary
Why in Secrets Engine?
We want a re-usable keystore library that can be consumed by tools within Docker. The Secrets engine will be the main point of contact for most tools within Docker as each tool will also have a provider interface with the Secrets Engine to retrieve secrets from a keystore.
We could do it in a separate repository, but keeping it here will enable us to be fast when iterating and we can always move it out when needed.
What do we want to use for MacOS?
Since we already know that keybase/go-keychain works with docker-credential-helper, possibly sticking with it would be OK. It is unclear if keybase/go-keychain is really maintained, but it is widely used, so we wouldn't be the only ones complaining if it breaks.
What do we want to use for Linux?
It seems like a safe bet is to use libsecret. It supports calling the freedesktop secrets service and falling back to an encrypted file if not available.
We should call org.freedesktop.secrets over DBUS using keybase/go-keychain. The fallback libsecret provides is still reliant on the user having a GUI because of the reliance on org.freedesktop.portal.
What do we want to use for Windows?
We'll use https://github.com/danieljoos/wincred
Fallback store
A fallback store should be self-contained, easy to use and secure. The current credential helper fails at providing a self-contained store, it relies on pass which relies on gpg and is only Linux based. Credentials should always be encrypted and shouldn't require any setup or init run to get started. Users should also not need any additional tools to use it.
A library that could fit this scenario is age. It is a well-known library for file encryption and can be extended to support more niche cases if we need to (e.g. support Yubikeys).
Prior Art
Some interesting issues on the Credential Helper
Historically Docker has relied upon the docker-credential-helper which ships with Docker Desktop.
The docker-credential-helper is a separate binary and is always invoked as a separate binary due to the limitations of the OS keystore. As far as I am aware, macOS has a limitation that when writing a credential to the keystore it is only retrievable by the same binary. So storing a credential in the Docker CLI will only be retrievable by the Docker CLI.
With what does it integrate?
Depending on the OS, the docker-credential-helper integrates with:
In the past the docker-credential-helper used to integrate directly with the macOS
Security/Security.hC library, but was swapped for the keybase/go-keychain library in this commit docker/docker-credential-helpers@4cdcdc2 due to deprecated SDK functions in osx docker/docker-credential-helpers#280.The credential helper also usually ships the
passimplementation and not thelibsecretimplementation. Thepassimplementation was added here docker/docker-credential-helpers#81 to support use cases where the user does not have a GUI installed (e.g. server environments).Why not just use the credential helper?
The credential helper is very tied to the concept of a registry. Each credential stored is using a
ServerURLto indicate which registry is saving the credential. You can see this in our schema defined for thelibsecretimplementation over hereOver time we have started moving away from registry authentication to other types of authentication like OAuth (Docker Hub login). This means that a login is based on a realm or namespace and multiple users could be saved under a realm / namespace.
Doing an
execout to the binary is annoying. It gives little control to the library implementing the credential-helper - we even have to rely on the user to install this (in certain environments) and we rely on the config specifying the correct credential-helper.Why in Secrets Engine?
We want a re-usable keystore library that can be consumed by tools within Docker. The Secrets engine will be the main point of contact for most tools within Docker as each tool will also have a provider interface with the Secrets Engine to retrieve secrets from a keystore.
We could do it in a separate repository, but keeping it here will enable us to be fast when iterating and we can always move it out when needed.
What do we want to use for MacOS?
Since we already know that keybase/go-keychain works with docker-credential-helper, possibly sticking with it would be OK. It is unclear if keybase/go-keychain is really maintained, but it is widely used, so we wouldn't be the only ones complaining if it breaks.
What do we want to use for Linux?
It seems like a safe bet is to use libsecret. It supports calling the freedesktop secrets service and falling back to an encrypted file if not available.We should call org.freedesktop.secrets over DBUS using keybase/go-keychain. The fallback
libsecretprovides is still reliant on the user having a GUI because of the reliance on org.freedesktop.portal.What do we want to use for Windows?
We'll use https://github.com/danieljoos/wincred
Fallback store
A fallback store should be self-contained, easy to use and secure. The current credential helper fails at providing a self-contained store, it relies on pass which relies on
gpgand is only Linux based. Credentials should always be encrypted and shouldn't require any setup orinitrun to get started. Users should also not need any additional tools to use it.A library that could fit this scenario is age. It is a well-known library for file encryption and can be extended to support more niche cases if we need to (e.g. support Yubikeys).
Prior Art
Some interesting issues on the Credential Helper