Skip to content

Commit 13010cc

Browse files
authored
empty cred check failed to fall back (#2020)
* empty cred check failed to fall back Signed-off-by: grokspawn <jordan@nimblewidget.com> * utest CI fixes Signed-off-by: grokspawn <jordan@nimblewidget.com> --------- Signed-off-by: grokspawn <jordan@nimblewidget.com>
1 parent ab08265 commit 13010cc

2 files changed

Lines changed: 124 additions & 5 deletions

File tree

pkg/image/containersimageregistry/registry.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,14 @@ func (r *Registry) Pull(ctx context.Context, ref orimage.Reference) error {
160160
return err
161161
}
162162

163-
sourceCtx := r.sourceCtx
164-
authFile := getAuthFile(r.sourceCtx, namedRef.String())
163+
sysCtx := *r.sourceCtx
164+
authFile := getAuthFile(r.sourceCtx, namedRef.Name())
165165
if authFile != "" {
166-
sourceCtx.AuthFilePath = authFile
166+
sysCtx.AuthFilePath = authFile
167167
}
168168

169169
if _, err := copy.Image(ctx, policyContext, ociLayoutRef, dockerRef, &copy.Options{
170-
SourceCtx: sourceCtx,
170+
SourceCtx: &sysCtx,
171171
DestinationCtx: r.cache.getSystemContext(),
172172
OptimizeDestinationImageAlreadyExists: true,
173173

@@ -290,7 +290,12 @@ func getAuthFile(sourceCtx *types.SystemContext, ref string) string {
290290
// the auth config file we derived above, if it exists. If we find a matching credential
291291
// in this file, we'll use this file.
292292
if stat, statErr := os.Stat(authFile); statErr == nil && stat.Mode().IsRegular() {
293-
if _, err := config.GetCredentials(&types.SystemContext{AuthFilePath: authFile}, ref); err == nil {
293+
credCtx := &types.SystemContext{AuthFilePath: authFile}
294+
if sourceCtx != nil {
295+
credCtx.SystemRegistriesConfPath = sourceCtx.SystemRegistriesConfPath
296+
credCtx.SystemRegistriesConfDirPath = sourceCtx.SystemRegistriesConfDirPath
297+
}
298+
if cred, err := config.GetCredentials(credCtx, ref); err == nil && cred != (types.DockerAuthConfig{}) {
294299
return authFile
295300
}
296301
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package containersimageregistry
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
"go.podman.io/image/v5/types"
12+
)
13+
14+
func writeAuthFile(t *testing.T, dir, filename string, auths map[string]interface{}) string { //nolint:unparam
15+
t.Helper()
16+
data, err := json.Marshal(map[string]interface{}{"auths": auths})
17+
require.NoError(t, err)
18+
path := filepath.Join(dir, filename)
19+
require.NoError(t, os.WriteFile(path, data, 0600))
20+
return path
21+
}
22+
23+
func authEntry(user, pass string) map[string]string { //nolint:unparam
24+
return map[string]string{
25+
"auth": base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)),
26+
}
27+
}
28+
29+
// hermeticCtx returns a SystemContext that isolates credential lookups
30+
// from the host's /etc/containers/registries.conf.
31+
func hermeticCtx(t *testing.T) *types.SystemContext {
32+
t.Helper()
33+
emptyDir := t.TempDir()
34+
regConf := filepath.Join(emptyDir, "registries.conf")
35+
require.NoError(t, os.WriteFile(regConf, []byte{}, 0600))
36+
return &types.SystemContext{
37+
SystemRegistriesConfPath: regConf,
38+
SystemRegistriesConfDirPath: emptyDir,
39+
}
40+
}
41+
42+
func TestGetAuthFile(t *testing.T) {
43+
const ref = "registry.example.com/ns/image"
44+
45+
t.Run("REGISTRY_AUTH_FILE with matching creds", func(t *testing.T) {
46+
dir := t.TempDir()
47+
authPath := writeAuthFile(t, dir, "auth.json", map[string]interface{}{
48+
"registry.example.com": authEntry("user", "pass"),
49+
})
50+
t.Setenv("REGISTRY_AUTH_FILE", authPath)
51+
t.Setenv("DOCKER_CONFIG", t.TempDir())
52+
53+
got := getAuthFile(hermeticCtx(t), ref)
54+
require.Equal(t, authPath, got)
55+
})
56+
57+
t.Run("REGISTRY_AUTH_FILE without matching creds returns empty for fallback", func(t *testing.T) {
58+
dir := t.TempDir()
59+
authPath := writeAuthFile(t, dir, "auth.json", map[string]interface{}{
60+
"other.registry.io": authEntry("user", "pass"),
61+
})
62+
t.Setenv("REGISTRY_AUTH_FILE", authPath)
63+
t.Setenv("DOCKER_CONFIG", t.TempDir())
64+
65+
got := getAuthFile(hermeticCtx(t), ref)
66+
require.Empty(t, got)
67+
})
68+
69+
t.Run("REGISTRY_AUTH_FILE empty file returns empty for fallback", func(t *testing.T) {
70+
dir := t.TempDir()
71+
authPath := writeAuthFile(t, dir, "auth.json", map[string]interface{}{})
72+
t.Setenv("REGISTRY_AUTH_FILE", authPath)
73+
t.Setenv("DOCKER_CONFIG", t.TempDir())
74+
75+
got := getAuthFile(hermeticCtx(t), ref)
76+
require.Empty(t, got)
77+
})
78+
79+
t.Run("no env vars and no auth file returns empty", func(t *testing.T) {
80+
t.Setenv("REGISTRY_AUTH_FILE", "")
81+
t.Setenv("DOCKER_CONFIG", t.TempDir())
82+
83+
got := getAuthFile(hermeticCtx(t), ref)
84+
require.Empty(t, got)
85+
})
86+
87+
t.Run("falls back to sourceCtx.AuthFilePath", func(t *testing.T) {
88+
dir := t.TempDir()
89+
authPath := writeAuthFile(t, dir, "auth.json", map[string]interface{}{
90+
"registry.example.com": authEntry("user", "pass"),
91+
})
92+
t.Setenv("REGISTRY_AUTH_FILE", "")
93+
t.Setenv("DOCKER_CONFIG", t.TempDir())
94+
95+
sysCtx := hermeticCtx(t)
96+
sysCtx.AuthFilePath = authPath
97+
got := getAuthFile(sysCtx, ref)
98+
require.Equal(t, authPath, got)
99+
})
100+
101+
t.Run("REGISTRY_AUTH_FILE nonexistent file falls back to sourceCtx", func(t *testing.T) {
102+
dir := t.TempDir()
103+
authPath := writeAuthFile(t, dir, "auth.json", map[string]interface{}{
104+
"registry.example.com": authEntry("user", "pass"),
105+
})
106+
t.Setenv("REGISTRY_AUTH_FILE", "/nonexistent/auth.json")
107+
t.Setenv("DOCKER_CONFIG", t.TempDir())
108+
109+
sysCtx := hermeticCtx(t)
110+
sysCtx.AuthFilePath = authPath
111+
got := getAuthFile(sysCtx, ref)
112+
require.Equal(t, authPath, got)
113+
})
114+
}

0 commit comments

Comments
 (0)