1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
/*
* Copyright (c) 2020. Ant Group. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package auth
import (
"encoding/base64"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/containerd/containerd/reference/docker"
"github.com/containerd/nydus-snapshotter/pkg/label"
"github.com/google/go-containerregistry/pkg/authn"
)
const (
sep = ":"
)
var (
emptyPassKeyChain = PassKeyChain{}
)
// PassKeyChain is user/password based key chain
type PassKeyChain struct {
Username string
Password string
}
func FromBase64(str string) (PassKeyChain, error) {
decoded, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return emptyPassKeyChain, err
}
pair := strings.Split(string(decoded), sep)
if len(pair) != 2 {
return emptyPassKeyChain, errors.New("invalid registry auth token")
}
return PassKeyChain{
Username: pair[0],
Password: pair[1],
}, nil
}
func (kc PassKeyChain) ToBase64() string {
if kc.Username == "" && kc.Password == "" {
return ""
}
return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", kc.Username, kc.Password)))
}
// TokenBase check if PassKeyChain is token based, when username is empty and password is not empty
// then password is registry token
func (kc PassKeyChain) TokenBase() bool {
return kc.Username == "" && kc.Password != ""
}
// FromLabels finds image pull username and secret from snapshot labels.
// Returned `nil` means no valid username and secret is passed, it should
// not override input nydusd configuration.
func FromLabels(labels map[string]string) *PassKeyChain {
u, found := labels[label.NydusImagePullUsername]
if !found || u == "" {
return nil
}
p, found := labels[label.NydusImagePullSecret]
if !found || p == "" {
return nil
}
return &PassKeyChain{
Username: u,
Password: p,
}
}
// GetRegistryKeyChain get image pull keychain from (ordered):
// 1. username and secrets labels
// 2. cri request
// 3. docker config
// 4. k8s docker config secret
func GetRegistryKeyChain(host, ref string, labels map[string]string) *PassKeyChain {
kc := FromLabels(labels)
if kc != nil {
return kc
}
// TODO: Handle error
kc, _ = FromCRI(host, ref)
if kc != nil {
return kc
}
kc = FromDockerConfig(host)
if kc != nil {
return kc
}
return FromKubeSecretDockerConfig(host)
}
func GetKeyChainByRef(ref string, labels map[string]string) (*PassKeyChain, error) {
named, err := docker.ParseDockerRef(ref)
if err != nil {
return nil, errors.Wrapf(err, "parse ref %s", ref)
}
host := docker.Domain(named)
keychain := GetRegistryKeyChain(host, ref, labels)
return keychain, nil
}
func (kc PassKeyChain) Resolve(target authn.Resource) (authn.Authenticator, error) {
return authn.FromConfig(kc.toAuthConfig()), nil
}
// toAuthConfig convert PassKeyChain to authn.AuthConfig when kc is token based,
// RegistryToken is preferred to
func (kc PassKeyChain) toAuthConfig() authn.AuthConfig {
if kc.TokenBase() {
return authn.AuthConfig{
RegistryToken: kc.Password,
}
}
return authn.AuthConfig{
Username: kc.Username,
Password: kc.Password,
}
}
|