Skip to content

Commit

Permalink
Auth configs in registries.conf
Browse files Browse the repository at this point in the history
Allow credential-helpers = [] and credential-helper = "" configuration in registries.conf as defautl store for credentials.

Signed-off-by: Qi Wang <[email protected]>
  • Loading branch information
QiWang19 committed Jul 2, 2020
1 parent 4c0f8a7 commit d7049e0
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 18 deletions.
136 changes: 119 additions & 17 deletions pkg/docker/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"runtime"
"strings"

"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/image/v5/types"
helperclient "github.com/docker/docker-credential-helpers/client"
"github.com/docker/docker-credential-helpers/credentials"
Expand Down Expand Up @@ -53,9 +54,23 @@ var (
ErrNotSupported = errors.New("not supported")
)

// SetAuthentication stores the username and password in the auth.json file
// SetAuthentication stores the username and password in the credential helper or file
func SetAuthentication(sys *types.SystemContext, registry, username, password string) error {
helpers, err := sysregistriesv2.CredentialHelpersForRegistry(sys, registry)
if err != nil {
return err
}
for _, helper := range helpers {
if err = setAuthToCredHelper(helper, registry, username, password); err != nil {
logrus.Warnf("error storing credentials for %s to the credential helper %s", registry, helper)
continue
}
logrus.Debugf("credentials for %s were stored in the credential helper %s", registry, helper)
return nil
}

return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {

if ch, exists := auths.CredHelpers[registry]; exists {
return false, setAuthToCredHelper(ch, registry, username, password)
}
Expand Down Expand Up @@ -104,21 +119,41 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon

// Credential helpers may override credentials from the auth file.
for registry, credHelper := range auths.CredHelpers {
username, password, err := getAuthFromCredHelper(credHelper, registry)
conf, err := getAuthFromCredHelper(credHelper, registry)
if err != nil {
if credentials.IsErrCredentialsNotFoundMessage(err.Error()) {
continue
}
return nil, err
}

conf := types.DockerAuthConfig{Username: username, Password: password}
authConfigs[normalizeRegistry(registry)] = conf
}
}

// TODO(keyring): if we ever reenable the keyring support, we had to
// query all credentials from the keyring here.
helpers, err := sysregistriesv2.AllConfiguredCredentialHelpers(sys)
if err != nil {
return nil, err
}
for _, helper := range helpers {
creds, err := listAuthFromCredHelper(helper)
if err != nil {
return nil, err
}
for registry := range creds {
conf, err := getAuthFromCredHelper(helper, registry)
if err != nil {
return nil, err
}
value, ok := authConfigs[normalizeRegistry(registry)]
if ok && value != conf {
return nil, err
}
if !ok {
authConfigs[normalizeRegistry(registry)] = conf
}
}
}

return authConfigs, nil
}
Expand Down Expand Up @@ -152,6 +187,23 @@ func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuth
return *sys.DockerAuthConfig, nil
}

helpers, err := sysregistriesv2.CredentialHelpersForRegistry(sys, registry)
if err != nil {
return types.DockerAuthConfig{}, err
}
for _, helper := range helpers {
creds, err := getAuthFromCredHelper(helper, registry)
if err != nil {
if credentials.IsErrCredentialsNotFoundMessage(err.Error()) {
logrus.Debugf("credentials not found in %s", helper)
continue
}
return creds, err
}
logrus.Debugf("Returning credentials from credential helper %s", helper)
return creds, nil
}

if enableKeyring {
username, password, err := getAuthFromKernelKeyring(registry)
if err == nil {
Expand Down Expand Up @@ -201,6 +253,24 @@ func GetAuthentication(sys *types.SystemContext, registry string) (string, strin

// RemoveAuthentication deletes the credentials stored in auth.json
func RemoveAuthentication(sys *types.SystemContext, registry string) error {
helpers, err := sysregistriesv2.CredentialHelpersForRegistry(sys, registry)
if err != nil {
return err
}
for _, helper := range helpers {
if _, err = getAuthFromCredHelper(helper, registry); err != nil {
if credentials.IsErrCredentialsNotFoundMessage(err.Error()) {
logrus.Warnf("Not logged in to %s with credential helper %s", registry, helper)
}
continue
}
if err = deleteAuthFromCredHelper(helper, registry); err != nil {
return err
}
logrus.Debugf("Credentials were deleted from credential helper %s", helper)
return nil
}

return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
// First try cred helpers.
if ch, exists := auths.CredHelpers[registry]; exists {
Expand Down Expand Up @@ -230,6 +300,24 @@ func RemoveAuthentication(sys *types.SystemContext, registry string) error {

// RemoveAllAuthentication deletes all the credentials stored in auth.json and kernel keyring
func RemoveAllAuthentication(sys *types.SystemContext) error {
helpers, err := sysregistriesv2.AllConfiguredCredentialHelpers(sys)
if err != nil {
return err
}
for _, helper := range helpers {
creds, err := listAuthFromCredHelper(helper)
if err != nil {
return err
}
for registry := range creds {
if err = deleteAuthFromCredHelper(helper, registry); err != nil {
logrus.Warningf("error deleting credentials for %s from %s: %v", registry, helper, err)
} else {
logrus.Debugf("Credentials for %s were deleted from credential helper %s", registry, helper)
}
}
}

return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
if enableKeyring {
err := removeAllAuthFromKernelKeyring()
Expand All @@ -239,12 +327,31 @@ func RemoveAllAuthentication(sys *types.SystemContext) error {
}
logrus.Debugf("error removing credentials from kernel keyring")
}
for _, helper := range auths.CredHelpers {
creds, err := listAuthFromCredHelper(helper)
if err != nil {
return false, nil
}
for registry := range creds {
if err = deleteAuthFromCredHelper(helper, registry); err != nil {
logrus.Warningf("error deleting credentials for %s from %s: %v", registry, helper, err)
} else {
logrus.Debugf("Credentials for %s were deleted from credential helper %s", registry, helper)
}
}
}
auths.CredHelpers = make(map[string]string)
auths.AuthConfigs = make(map[string]dockerAuthConfig)
return true, nil
})
}

func listAuthFromCredHelper(credHelper string) (map[string]string, error) {
helperName := fmt.Sprintf("docker-credential-%s", credHelper)
p := helperclient.NewShellProgramFunc(helperName)
return helperclient.List(p)
}

// getPathToAuth gets the path of the auth.json file used for reading and writting credentials
// returns the path, and a bool specifies whether the file is in legacy format
func getPathToAuth(sys *types.SystemContext) (string, bool, error) {
Expand Down Expand Up @@ -353,14 +460,17 @@ func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) (
return nil
}

func getAuthFromCredHelper(credHelper, registry string) (string, string, error) {
func getAuthFromCredHelper(credHelper, registry string) (types.DockerAuthConfig, error) {
helperName := fmt.Sprintf("docker-credential-%s", credHelper)
p := helperclient.NewShellProgramFunc(helperName)
creds, err := helperclient.Get(p, registry)
if err != nil {
return "", "", err
return types.DockerAuthConfig{}, err
}
return creds.Username, creds.Secret, nil
return types.DockerAuthConfig{
Username: creds.Username,
Password: creds.Secret,
}, nil
}

func setAuthToCredHelper(credHelper, registry, username, password string) error {
Expand Down Expand Up @@ -389,15 +499,7 @@ func findAuthentication(registry, path string, legacyFormat bool) (types.DockerA

// First try cred helpers. They should always be normalized.
if ch, exists := auths.CredHelpers[registry]; exists {
username, password, err := getAuthFromCredHelper(ch, registry)
if err != nil {
return types.DockerAuthConfig{}, err
}

return types.DockerAuthConfig{
Username: username,
Password: password,
}, nil
return getAuthFromCredHelper(ch, registry)
}

// I'm feeling lucky
Expand Down
58 changes: 57 additions & 1 deletion pkg/sysregistriesv2/system_registries_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ type Registry struct {
// effectively be pulled from "example.com/foo/bar/myimage:latest".
// If no Prefix is specified, it defaults to the specified location.
Prefix string `toml:"prefix"`
// The credential helper for specified registry
// global default CredentialHelpers is used if CredentialHelper is not specified
CredentialHelper string `toml:"credential-helper,omitempty"`
// A registry is an Endpoint too
Endpoint
// The registry's mirrors.
Expand Down Expand Up @@ -151,7 +154,9 @@ func (config *V1RegistriesConf) Nonempty() bool {

// V2RegistriesConf is the sysregistries v2 configuration format.
type V2RegistriesConf struct {
Registries []Registry `toml:"registry"`
// The credential store for registries
CredentialHelpers []string `toml:"credential-helpers"`
Registries []Registry `toml:"registry"`
// An array of host[:port] (not prefix!) entries to use for resolving unqualified image references
UnqualifiedSearchRegistries []string `toml:"unqualified-search-registries"`
}
Expand Down Expand Up @@ -275,6 +280,12 @@ func (config *V2RegistriesConf) postProcess() error {
}
}

// ignore the credHelper if it is set for inner namespace
if reg.CredentialHelper != "" && strings.Contains(reg.Prefix, "/") {
reg.CredentialHelper = ""
logrus.Errorf("credential-helper can only be configured for registry domain, not for inner namespaces %s", reg.Prefix)
}

// make sure mirrors are valid
for _, mir := range reg.Mirrors {
mir.Location, err = parseLocation(mir.Location)
Expand Down Expand Up @@ -570,6 +581,51 @@ func UnqualifiedSearchRegistries(ctx *types.SystemContext) ([]string, error) {
return config.UnqualifiedSearchRegistries, nil
}

// AllConfiguredCredentialHelpers returns a list of all credential helpers that are configred for some registry
// Almost all caller should use CredentialHelpersForRegistry to get configuration applicable to a specific registry.
func AllConfiguredCredentialHelpers(sys *types.SystemContext) ([]string, error) {
helpers := make(map[string]bool)
config, err := getConfig(sys)
if err != nil {
return nil, err
}
for _, store := range config.CredentialHelpers {
helpers[store] = true
}
for _, reg := range config.Registries {
if reg.CredentialHelper != "" {
if ok := helpers[reg.CredentialHelper]; !ok {
helpers[reg.CredentialHelper] = true
}
}
}

var result []string
for cred := range helpers {
result = append(result, cred)
}

return result, nil
}

// CredentialHelpersForRegistry returns a list of credential helpers to try to use for the given registry,
// in the order to try.
func CredentialHelpersForRegistry(sys *types.SystemContext, registry string) ([]string, error) {
config, err := getConfig(sys)
if err != nil {
return nil, err
}
for _, reg := range config.Registries {
if reg.Prefix == registry && reg.CredentialHelper != "" {
return []string{reg.CredentialHelper}, nil
}
}
if len(config.CredentialHelpers) > 0 {
return config.CredentialHelpers, nil
}
return nil, nil
}

// refMatchesPrefix returns true iff ref,
// which is a registry, repository namespace, repository or image reference (as formatted by
// reference.Domain(), reference.Named.Name() or reference.Reference.String()
Expand Down

0 comments on commit d7049e0

Please sign in to comment.