credentialprovider: track kube secrets as creds sources in DockerKeyrings

This commit is contained in:
Stanislav Láznička 2024-10-09 14:13:34 +02:00
parent e549eeb796
commit 09284d926c
No known key found for this signature in database
GPG Key ID: F8D8054395A1D157
5 changed files with 293 additions and 126 deletions

View File

@ -17,6 +17,9 @@ limitations under the License.
package credentialprovider
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"net"
"net/url"
"path/filepath"
@ -24,7 +27,9 @@ import (
"strings"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features"
)
// DockerKeyring tracks a set of docker registry credentials, maintaining a
@ -35,13 +40,13 @@ import (
// most specific match for a given image
// - iterating a map does not yield predictable results
type DockerKeyring interface {
Lookup(image string) ([]AuthConfig, bool)
Lookup(image string) ([]TrackedAuthConfig, bool)
}
// BasicDockerKeyring is a trivial map-backed implementation of DockerKeyring
type BasicDockerKeyring struct {
index []string
creds map[string][]AuthConfig
creds map[string][]TrackedAuthConfig
}
// providersDockerKeyring is an implementation of DockerKeyring that
@ -50,6 +55,47 @@ type providersDockerKeyring struct {
Providers []DockerConfigProvider
}
// TrackedAuthConfig wraps the AuthConfig and adds information about the source
// of the credentials.
type TrackedAuthConfig struct {
AuthConfig
AuthConfigHash string
Source *CredentialSource
}
// NewTrackedAuthConfig initializes the TrackedAuthConfig structure by adding
// the source information to the supplied AuthConfig. It also counts a hash of the
// AuthConfig and keeps it in the returned structure.
//
// The supplied CredentialSource is only used when the "KubeletEnsureSecretPulledImages"
// is enabled, the same applies for counting the hash.
func NewTrackedAuthConfig(c *AuthConfig, src *CredentialSource) *TrackedAuthConfig {
if c == nil {
panic("cannot construct TrackedAuthConfig with a nil AuthConfig")
}
authConfig := &TrackedAuthConfig{
AuthConfig: *c,
}
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletEnsureSecretPulledImages) {
authConfig.Source = src
authConfig.AuthConfigHash = hashAuthConfig(c)
}
return authConfig
}
type CredentialSource struct {
Secret SecretCoordinates
}
type SecretCoordinates struct {
UID string
Namespace string
Name string
}
// AuthConfig contains authorization information for connecting to a Registry
// This type mirrors "github.com/docker/docker/api/types.AuthConfig"
type AuthConfig struct {
@ -72,11 +118,13 @@ type AuthConfig struct {
RegistryToken string `json:"registrytoken,omitempty"`
}
// Add add some docker config in basic docker keyring
func (dk *BasicDockerKeyring) Add(cfg DockerConfig) {
// Add inserts the docker config `cfg` into the basic docker keyring. It attaches
// the `src` information that describes where the docker config `cfg` comes from.
// `src` is nil if the docker config is globally available on the node.
func (dk *BasicDockerKeyring) Add(src *CredentialSource, cfg DockerConfig) {
if dk.index == nil {
dk.index = make([]string, 0)
dk.creds = make(map[string][]AuthConfig)
dk.creds = make(map[string][]TrackedAuthConfig)
}
for loc, ident := range cfg {
creds := AuthConfig{
@ -111,7 +159,9 @@ func (dk *BasicDockerKeyring) Add(cfg DockerConfig) {
} else {
key = parsed.Host
}
dk.creds[key] = append(dk.creds[key], creds)
trackedCreds := NewTrackedAuthConfig(&creds, src)
dk.creds[key] = append(dk.creds[key], *trackedCreds)
dk.index = append(dk.index, key)
}
@ -235,9 +285,9 @@ func URLsMatch(globURL *url.URL, targetURL *url.URL) (bool, error) {
// Lookup implements the DockerKeyring method for fetching credentials based on image name.
// Multiple credentials may be returned if there are multiple potentially valid credentials
// available. This allows for rotation.
func (dk *BasicDockerKeyring) Lookup(image string) ([]AuthConfig, bool) {
func (dk *BasicDockerKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) {
// range over the index as iterating over a map does not provide a predictable ordering
ret := []AuthConfig{}
ret := []TrackedAuthConfig{}
for _, k := range dk.index {
// both k and image are schemeless URLs because even though schemes are allowed
// in the credential configurations, we remove them in Add.
@ -257,16 +307,18 @@ func (dk *BasicDockerKeyring) Lookup(image string) ([]AuthConfig, bool) {
}
}
return []AuthConfig{}, false
return []TrackedAuthConfig{}, false
}
// Lookup implements the DockerKeyring method for fetching credentials
// based on image name.
func (dk *providersDockerKeyring) Lookup(image string) ([]AuthConfig, bool) {
func (dk *providersDockerKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) {
keyring := &BasicDockerKeyring{}
for _, p := range dk.Providers {
keyring.Add(p.Provide(image))
// TODO: the source should probably change once we depend on service accounts (KEP-4412).
// Perhaps `Provide()` should return the source modified to accommodate this?
keyring.Add(nil, p.Provide(image))
}
return keyring.Lookup(image)
@ -274,13 +326,13 @@ func (dk *providersDockerKeyring) Lookup(image string) ([]AuthConfig, bool) {
// FakeKeyring a fake config credentials
type FakeKeyring struct {
auth []AuthConfig
auth []TrackedAuthConfig
ok bool
}
// Lookup implements the DockerKeyring method for fetching credentials based on image name
// return fake auth and ok
func (f *FakeKeyring) Lookup(image string) ([]AuthConfig, bool) {
func (f *FakeKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) {
return f.auth, f.ok
}
@ -289,8 +341,8 @@ type UnionDockerKeyring []DockerKeyring
// Lookup implements the DockerKeyring method for fetching credentials based on image name.
// return each credentials
func (k UnionDockerKeyring) Lookup(image string) ([]AuthConfig, bool) {
authConfigs := []AuthConfig{}
func (k UnionDockerKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) {
authConfigs := []TrackedAuthConfig{}
for _, subKeyring := range k {
if subKeyring == nil {
continue
@ -302,3 +354,14 @@ func (k UnionDockerKeyring) Lookup(image string) ([]AuthConfig, bool) {
return authConfigs, (len(authConfigs) > 0)
}
func hashAuthConfig(creds *AuthConfig) string {
credBytes, err := json.Marshal(creds)
if err != nil {
return ""
}
hash := sha256.New()
hash.Write([]byte(credBytes))
return hex.EncodeToString(hash.Sum(nil))
}

View File

@ -21,6 +21,10 @@ import (
"fmt"
"reflect"
"testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/features"
)
func TestURLsMatch(t *testing.T) {
@ -222,7 +226,7 @@ func TestDockerKeyringForGlob(t *testing.T) {
if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
} else {
keyring.Add(cfg)
keyring.Add(nil, cfg)
}
creds, ok := keyring.Lookup(test.targetURL + "/foo/bar")
@ -290,7 +294,7 @@ func TestKeyringMiss(t *testing.T) {
if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
} else {
keyring.Add(cfg)
keyring.Add(nil, cfg)
}
_, ok := keyring.Lookup(test.lookupURL + "/foo/bar")
@ -318,7 +322,7 @@ func TestKeyringMissWithDockerHubCredentials(t *testing.T) {
if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
} else {
keyring.Add(cfg)
keyring.Add(nil, cfg)
}
val, ok := keyring.Lookup("world.mesos.org/foo/bar")
@ -344,7 +348,7 @@ func TestKeyringHitWithUnqualifiedDockerHub(t *testing.T) {
if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
} else {
keyring.Add(cfg)
keyring.Add(nil, cfg)
}
creds, ok := keyring.Lookup("google/docker-registry")
@ -353,7 +357,7 @@ func TestKeyringHitWithUnqualifiedDockerHub(t *testing.T) {
return
}
if len(creds) > 1 {
t.Errorf("Got more hits than expected: %s", creds)
t.Errorf("Got more hits than expected: %v", creds)
}
val := creds[0]
@ -385,7 +389,7 @@ func TestKeyringHitWithUnqualifiedLibraryDockerHub(t *testing.T) {
if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
} else {
keyring.Add(cfg)
keyring.Add(nil, cfg)
}
creds, ok := keyring.Lookup("jenkins")
@ -394,7 +398,7 @@ func TestKeyringHitWithUnqualifiedLibraryDockerHub(t *testing.T) {
return
}
if len(creds) > 1 {
t.Errorf("Got more hits than expected: %s", creds)
t.Errorf("Got more hits than expected: %v", creds)
}
val := creds[0]
@ -426,7 +430,7 @@ func TestKeyringHitWithQualifiedDockerHub(t *testing.T) {
if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
} else {
keyring.Add(cfg)
keyring.Add(nil, cfg)
}
creds, ok := keyring.Lookup(url + "/google/docker-registry")
@ -435,7 +439,7 @@ func TestKeyringHitWithQualifiedDockerHub(t *testing.T) {
return
}
if len(creds) > 2 {
t.Errorf("Got more hits than expected: %s", creds)
t.Errorf("Got more hits than expected: %v", creds)
}
val := creds[0]
@ -498,20 +502,24 @@ func TestProvidersDockerKeyring(t *testing.T) {
}
func TestDockerKeyringLookup(t *testing.T) {
// turn on the ensure secret pulled images feature to get the hashes with the creds
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletEnsureSecretPulledImages, true)
ada := AuthConfig{
Username: "ada",
Password: "smash", // Fake value for testing.
Email: "ada@example.com",
}
adaHash := "353258b53f5e9a57b059eab3f05312fc35bbeb874f08ce101e7bf0bf46977423"
grace := AuthConfig{
Username: "grace",
Password: "squash", // Fake value for testing.
Email: "grace@example.com",
}
graceHash := "f949b3837a1eb733a951b6aeda0b3327c09ec50c917de9ca35818e8fbf567e29"
dk := &BasicDockerKeyring{}
dk.Add(DockerConfig{
dk.Add(nil, DockerConfig{
"bar.example.com/pong": DockerConfigEntry{
Username: grace.Username,
Password: grace.Password,
@ -526,27 +534,27 @@ func TestDockerKeyringLookup(t *testing.T) {
tests := []struct {
image string
match []AuthConfig
match []TrackedAuthConfig
ok bool
}{
// direct match
{"bar.example.com", []AuthConfig{ada}, true},
{"bar.example.com", []TrackedAuthConfig{{AuthConfig: ada, AuthConfigHash: adaHash}}, true},
// direct match deeper than other possible matches
{"bar.example.com/pong", []AuthConfig{grace, ada}, true},
{"bar.example.com/pong", []TrackedAuthConfig{{AuthConfig: grace, AuthConfigHash: graceHash}, {AuthConfig: ada, AuthConfigHash: adaHash}}, true},
// no direct match, deeper path ignored
{"bar.example.com/ping", []AuthConfig{ada}, true},
{"bar.example.com/ping", []TrackedAuthConfig{{AuthConfig: ada, AuthConfigHash: adaHash}}, true},
// match first part of path token
{"bar.example.com/pongz", []AuthConfig{grace, ada}, true},
{"bar.example.com/pongz", []TrackedAuthConfig{{AuthConfig: grace, AuthConfigHash: graceHash}, {AuthConfig: ada, AuthConfigHash: adaHash}}, true},
// match regardless of sub-path
{"bar.example.com/pong/pang", []AuthConfig{grace, ada}, true},
{"bar.example.com/pong/pang", []TrackedAuthConfig{{AuthConfig: grace, AuthConfigHash: graceHash}, {AuthConfig: ada, AuthConfigHash: adaHash}}, true},
// no host match
{"example.com", []AuthConfig{}, false},
{"foo.example.com", []AuthConfig{}, false},
{"example.com", []TrackedAuthConfig{}, false},
{"foo.example.com", []TrackedAuthConfig{}, false},
}
for i, tt := range tests {
@ -565,14 +573,17 @@ func TestDockerKeyringLookup(t *testing.T) {
// by images that only match the hostname.
// NOTE: the above covers the case of a more specific match trumping just hostname.
func TestIssue3797(t *testing.T) {
// turn on the ensure secret pulled images feature to get the hashes with the creds
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletEnsureSecretPulledImages, true)
rex := AuthConfig{
Username: "rex",
Password: "tiny arms", // Fake value for testing.
Email: "rex@example.com",
}
rexHash := "899748fec74c8dd761845fca727f4249b05be275ff24026676fcd4351f656363"
dk := &BasicDockerKeyring{}
dk.Add(DockerConfig{
dk.Add(nil, DockerConfig{
"https://quay.io/v1/": DockerConfigEntry{
Username: rex.Username,
Password: rex.Password,
@ -582,15 +593,15 @@ func TestIssue3797(t *testing.T) {
tests := []struct {
image string
match []AuthConfig
match []TrackedAuthConfig
ok bool
}{
// direct match
{"quay.io", []AuthConfig{rex}, true},
{"quay.io", []TrackedAuthConfig{{AuthConfig: rex, AuthConfigHash: rexHash}}, true},
// partial matches
{"quay.io/foo", []AuthConfig{rex}, true},
{"quay.io/foo/bar", []AuthConfig{rex}, true},
{"quay.io/foo", []TrackedAuthConfig{{AuthConfig: rex, AuthConfigHash: rexHash}}, true},
{"quay.io/foo/bar", []TrackedAuthConfig{{AuthConfig: rex, AuthConfigHash: rexHash}}, true},
}
for i, tt := range tests {

View File

@ -27,32 +27,52 @@ import (
// then a DockerKeyring is built based on every hit and unioned with the defaultKeyring.
// If they do not, then the default keyring is returned
func MakeDockerKeyring(passedSecrets []v1.Secret, defaultKeyring credentialprovider.DockerKeyring) (credentialprovider.DockerKeyring, error) {
passedCredentials := []credentialprovider.DockerConfig{}
for _, passedSecret := range passedSecrets {
providerFromSecrets, err := secretsToTrackedDockerConfigs(passedSecrets)
if err != nil {
return nil, err
}
if providerFromSecrets == nil {
return defaultKeyring, nil
}
return credentialprovider.UnionDockerKeyring{providerFromSecrets, defaultKeyring}, nil
}
func secretsToTrackedDockerConfigs(secrets []v1.Secret) (credentialprovider.DockerKeyring, error) {
provider := &credentialprovider.BasicDockerKeyring{}
validSecretsFound := 0
for _, passedSecret := range secrets {
if dockerConfigJSONBytes, dockerConfigJSONExists := passedSecret.Data[v1.DockerConfigJsonKey]; (passedSecret.Type == v1.SecretTypeDockerConfigJson) && dockerConfigJSONExists && (len(dockerConfigJSONBytes) > 0) {
dockerConfigJSON := credentialprovider.DockerConfigJSON{}
if err := json.Unmarshal(dockerConfigJSONBytes, &dockerConfigJSON); err != nil {
return nil, err
}
passedCredentials = append(passedCredentials, dockerConfigJSON.Auths)
coords := credentialprovider.SecretCoordinates{
UID: string(passedSecret.UID),
Namespace: passedSecret.Namespace,
Name: passedSecret.Name}
provider.Add(&credentialprovider.CredentialSource{Secret: coords}, dockerConfigJSON.Auths)
validSecretsFound++
} else if dockercfgBytes, dockercfgExists := passedSecret.Data[v1.DockerConfigKey]; (passedSecret.Type == v1.SecretTypeDockercfg) && dockercfgExists && (len(dockercfgBytes) > 0) {
dockercfg := credentialprovider.DockerConfig{}
if err := json.Unmarshal(dockercfgBytes, &dockercfg); err != nil {
return nil, err
}
passedCredentials = append(passedCredentials, dockercfg)
coords := credentialprovider.SecretCoordinates{
UID: string(passedSecret.UID),
Namespace: passedSecret.Namespace,
Name: passedSecret.Name}
provider.Add(&credentialprovider.CredentialSource{Secret: coords}, dockercfg)
validSecretsFound++
}
}
if len(passedCredentials) > 0 {
basicKeyring := &credentialprovider.BasicDockerKeyring{}
for _, currCredentials := range passedCredentials {
basicKeyring.Add(currCredentials)
if validSecretsFound == 0 {
return nil, nil
}
return credentialprovider.UnionDockerKeyring{basicKeyring, defaultKeyring}, nil
}
return defaultKeyring, nil
return provider, nil
}

View File

@ -20,243 +20,316 @@ import (
"reflect"
"testing"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/credentialprovider"
"k8s.io/kubernetes/pkg/features"
)
// fakeKeyring is a fake docker auth config keyring
type fakeKeyring struct {
auth []credentialprovider.AuthConfig
// FakeKeyring a fake config credentials
type FakeKeyring struct {
auth []credentialprovider.TrackedAuthConfig
ok bool
}
// Lookup implements the DockerKeyring method for fetching credentials based on image name.
// Returns fake results based on the auth and ok fields in fakeKeyring
func (f *fakeKeyring) Lookup(image string) ([]credentialprovider.AuthConfig, bool) {
// Lookup implements the DockerKeyring method for fetching credentials based on image name
// return fake auth and ok
func (f *FakeKeyring) Lookup(image string) ([]credentialprovider.TrackedAuthConfig, bool) {
return f.auth, f.ok
}
func Test_MakeDockerKeyring(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletEnsureSecretPulledImages, true)
testcases := []struct {
name string
image string
defaultKeyring credentialprovider.DockerKeyring
pullSecrets []v1.Secret
authConfigs []credentialprovider.AuthConfig
expectedAuthConfigs []credentialprovider.TrackedAuthConfig
found bool
}{
{
name: "with .dockerconfigjson and auth field",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
Source: &credentialprovider.CredentialSource{
Secret: credentialprovider.SecretCoordinates{
Name: "s1", Namespace: "ns1", UID: "uid1"},
},
AuthConfig: credentialprovider.AuthConfig{
Username: "user",
Password: "password",
},
AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d",
},
},
found: true,
},
{
name: "with .dockerconfig and auth field",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockercfg,
Data: map[string][]byte{
v1.DockerConfigKey: []byte(`{"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
Source: &credentialprovider.CredentialSource{
Secret: credentialprovider.SecretCoordinates{
Name: "s1", Namespace: "ns1", UID: "uid1"},
},
AuthConfig: credentialprovider.AuthConfig{
Username: "user",
Password: "password",
},
AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d",
},
},
found: true,
},
{
name: "with .dockerconfigjson and username/password fields",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"username": "user", "password": "password"}}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
Source: &credentialprovider.CredentialSource{
Secret: credentialprovider.SecretCoordinates{
Name: "s1", Namespace: "ns1", UID: "uid1"},
},
AuthConfig: credentialprovider.AuthConfig{
Username: "user",
Password: "password",
},
AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d",
},
},
found: true,
},
{
name: "with .dockerconfig and username/password fields",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockercfg,
Data: map[string][]byte{
v1.DockerConfigKey: []byte(`{"test.registry.io": {"username": "user", "password": "password"}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
Source: &credentialprovider.CredentialSource{
Secret: credentialprovider.SecretCoordinates{
Name: "s1", Namespace: "ns1", UID: "uid1"},
},
AuthConfig: credentialprovider.AuthConfig{
Username: "user",
Password: "password",
},
AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d",
},
},
found: true,
},
{
name: "with .dockerconfigjson but with wrong Secret Type",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{},
defaultKeyring: &FakeKeyring{},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockercfg,
Data: map[string][]byte{
v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`),
},
},
},
authConfigs: nil,
found: false,
},
{
name: "with .dockerconfig but with wrong Secret Type",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{},
defaultKeyring: &FakeKeyring{},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
v1.DockerConfigKey: []byte(`{"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}`),
},
},
},
authConfigs: nil,
found: false,
},
{
name: "with not matcing .dockerconfigjson and default keyring",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{
auth: []credentialprovider.AuthConfig{
defaultKeyring: &FakeKeyring{
auth: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
},
},
},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
v1.DockerConfigJsonKey: []byte(`{"auths": {"foobar.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
AuthConfigHash: "",
},
},
found: true,
},
{
name: "with not matching .dockerconfig and default keyring",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{
auth: []credentialprovider.AuthConfig{
defaultKeyring: &FakeKeyring{
auth: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
},
},
},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockercfg,
Data: map[string][]byte{
v1.DockerConfigKey: []byte(`{"foobar.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
AuthConfigHash: "",
},
},
found: true,
},
{
name: "with no pull secrets but has default keyring",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{
auth: []credentialprovider.AuthConfig{
defaultKeyring: &FakeKeyring{
auth: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
},
},
},
pullSecrets: []v1.Secret{},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
AuthConfigHash: "",
},
},
found: false,
},
{
name: "with pull secrets and has default keyring",
image: "test.registry.io",
defaultKeyring: &fakeKeyring{
auth: []credentialprovider.AuthConfig{
defaultKeyring: &FakeKeyring{
auth: []credentialprovider.TrackedAuthConfig{
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
},
},
},
pullSecrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "s1", Namespace: "ns1", UID: "uid1"},
Type: v1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`),
},
},
},
authConfigs: []credentialprovider.AuthConfig{
expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{
{
Source: &credentialprovider.CredentialSource{
Secret: credentialprovider.SecretCoordinates{
Name: "s1", Namespace: "ns1", UID: "uid1"},
},
AuthConfig: credentialprovider.AuthConfig{
Username: "user",
Password: "password",
},
AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d",
},
{
AuthConfig: credentialprovider.AuthConfig{
Username: "default-user",
Password: "default-password",
},
AuthConfigHash: "",
},
},
found: true,
},
@ -276,9 +349,9 @@ func Test_MakeDockerKeyring(t *testing.T) {
t.Errorf("unexpected lookup for image: %s", testcase.image)
}
if !reflect.DeepEqual(authConfigs, testcase.authConfigs) {
if !reflect.DeepEqual(authConfigs, testcase.expectedAuthConfigs) { // TODO: we may need better comparison as the result is unordered
t.Logf("actual auth configs: %#v", authConfigs)
t.Logf("expected auth configs: %#v", testcase.authConfigs)
t.Logf("expected auth configs: %#v", testcase.expectedAuthConfigs)
t.Error("auth configs did not match")
}
})

View File

@ -320,7 +320,7 @@ func TestPullWithSecrets(t *testing.T) {
}
for description, test := range tests {
builtInKeyRing := &credentialprovider.BasicDockerKeyring{}
builtInKeyRing.Add(test.builtInDockerConfig)
builtInKeyRing.Add(nil, test.builtInDockerConfig)
_, fakeImageService, fakeManager, err := customTestRuntimeManager(builtInKeyRing)
require.NoError(t, err)