Skip to content

Commit c68c62c

Browse files
authored
Merge pull request #749 from fluxcd/docker-registry-host-mismatch
registry: repo URL and dockerconfig URL mismatch
2 parents fe31ff9 + f79fd03 commit c68c62c

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

internal/helm/registry/auth.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
Copyright 2022 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
package registry
218

319
import (
@@ -6,6 +22,7 @@ import (
622
"net/url"
723

824
"github.com/docker/cli/cli/config"
25+
"github.com/docker/cli/cli/config/credentials"
926
"helm.sh/helm/v3/pkg/registry"
1027
corev1 "k8s.io/api/core/v1"
1128
)
@@ -30,6 +47,14 @@ func LoginOptionFromSecret(registryURL string, secret corev1.Secret) (registry.L
3047
if err != nil {
3148
return nil, fmt.Errorf("unable to get authentication data from Secret '%s': %w", secret.Name, err)
3249
}
50+
51+
// Make sure that the obtained auth config is for the requested host.
52+
// When the docker config does not contain the credentials for a host,
53+
// the credential store returns an empty auth config.
54+
// Refer: https://github.com/docker/cli/blob/v20.10.16/cli/config/credentials/file_store.go#L44
55+
if credentials.ConvertToHostname(authConfig.ServerAddress) != parsedURL.Host {
56+
return nil, fmt.Errorf("no auth config for '%s' in the docker-registry Secret '%s'", parsedURL.Host, secret.Name)
57+
}
3358
username = authConfig.Username
3459
password = authConfig.Password
3560
} else {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright 2022 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package registry
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
corev1 "k8s.io/api/core/v1"
24+
)
25+
26+
func TestLoginOptionFromSecret(t *testing.T) {
27+
testURL := "oci://registry.example.com/foo/bar"
28+
testUser := "flux"
29+
testPassword := "somepassword"
30+
testDockerconfigjson := `{"auths":{"registry.example.com":{"username":"flux","password":"somepassword","auth":"Zmx1eDpzb21lcGFzc3dvcmQ="}}}`
31+
testDockerconfigjsonHTTPS := `{"auths":{"https://registry.example.com":{"username":"flux","password":"somepassword","auth":"Zmx1eDpzb21lcGFzc3dvcmQ="}}}`
32+
dockerconfigjsonKey := ".dockerconfigjson"
33+
34+
tests := []struct {
35+
name string
36+
url string
37+
secretType corev1.SecretType
38+
secretData map[string][]byte
39+
wantErr bool
40+
}{
41+
{
42+
name: "generic secret",
43+
url: testURL,
44+
secretType: corev1.SecretTypeOpaque,
45+
secretData: map[string][]byte{
46+
"username": []byte(testUser),
47+
"password": []byte(testPassword),
48+
},
49+
},
50+
{
51+
name: "generic secret without username",
52+
url: testURL,
53+
secretType: corev1.SecretTypeOpaque,
54+
secretData: map[string][]byte{
55+
"password": []byte(testPassword),
56+
},
57+
wantErr: true,
58+
},
59+
{
60+
name: "generic secret without password",
61+
url: testURL,
62+
secretType: corev1.SecretTypeOpaque,
63+
secretData: map[string][]byte{
64+
"username": []byte(testUser),
65+
},
66+
wantErr: true,
67+
},
68+
{
69+
name: "generic secret without username and password",
70+
url: testURL,
71+
secretType: corev1.SecretTypeOpaque,
72+
},
73+
{
74+
name: "docker-registry secret",
75+
url: testURL,
76+
secretType: corev1.SecretTypeDockerConfigJson,
77+
secretData: map[string][]byte{
78+
dockerconfigjsonKey: []byte(testDockerconfigjson),
79+
},
80+
},
81+
{
82+
name: "docker-registry secret host mismatch",
83+
url: "oci://registry.gitlab.com",
84+
secretType: corev1.SecretTypeDockerConfigJson,
85+
secretData: map[string][]byte{
86+
dockerconfigjsonKey: []byte(testDockerconfigjson),
87+
},
88+
wantErr: true,
89+
},
90+
{
91+
name: "docker-registry secret invalid host",
92+
url: "oci://registry .gitlab.com",
93+
secretType: corev1.SecretTypeDockerConfigJson,
94+
secretData: map[string][]byte{
95+
dockerconfigjsonKey: []byte(testDockerconfigjson),
96+
},
97+
wantErr: true,
98+
},
99+
{
100+
name: "docker-registry secret invalid docker config",
101+
url: testURL,
102+
secretType: corev1.SecretTypeDockerConfigJson,
103+
secretData: map[string][]byte{
104+
dockerconfigjsonKey: []byte("foo"),
105+
},
106+
wantErr: true,
107+
},
108+
{
109+
name: "docker-registry secret with URL scheme",
110+
url: testURL,
111+
secretType: corev1.SecretTypeDockerConfigJson,
112+
secretData: map[string][]byte{
113+
dockerconfigjsonKey: []byte(testDockerconfigjsonHTTPS),
114+
},
115+
},
116+
}
117+
118+
for _, tt := range tests {
119+
t.Run(tt.name, func(t *testing.T) {
120+
g := NewWithT(t)
121+
122+
secret := corev1.Secret{}
123+
secret.Name = "test-secret"
124+
secret.Data = tt.secretData
125+
secret.Type = tt.secretType
126+
127+
_, err := LoginOptionFromSecret(tt.url, secret)
128+
g.Expect(err != nil).To(Equal(tt.wantErr))
129+
})
130+
}
131+
}

0 commit comments

Comments
 (0)