From 345548a03967b20d5758a4a4fc609fbd69c4f06e Mon Sep 17 00:00:00 2001 From: zhaoyonghe Date: Tue, 2 Apr 2024 12:13:25 -0400 Subject: [PATCH] Support podman auth file REGISTRY_AUTH_FILE. --- pkg/authn/keychain.go | 20 ++++++++--- pkg/authn/keychain_test.go | 70 ++++++++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/pkg/authn/keychain.go b/pkg/authn/keychain.go index 99e0b81c8..c16bb1611 100644 --- a/pkg/authn/keychain.go +++ b/pkg/authn/keychain.go @@ -86,8 +86,8 @@ func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) { // config.Load, which may fail if the config can't be parsed. // // If neither was found, look for Podman's auth at - // $XDG_RUNTIME_DIR/containers/auth.json and attempt to load it as a - // Docker config. + // $REGISTRY_AUTH_FILE or $XDG_RUNTIME_DIR/containers/auth.json + // and attempt to load it as a Docker config. // // If neither are found, fallback to Anonymous. var cf *configfile.ConfigFile @@ -96,16 +96,28 @@ func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) { if err != nil { return nil, err } - } else { + } else if fileExists(os.Getenv("REGISTRY_AUTH_FILE")) { + f, err := os.Open(os.Getenv("REGISTRY_AUTH_FILE")) + if err != nil { + return nil, err + } + defer f.Close() + cf, err = config.LoadFromReader(f) + if err != nil { + return nil, err + } + } else if fileExists(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json")) { f, err := os.Open(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json")) if err != nil { - return Anonymous, nil + return nil, err } defer f.Close() cf, err = config.LoadFromReader(f) if err != nil { return nil, err } + } else { + return Anonymous, nil } // See: diff --git a/pkg/authn/keychain_test.go b/pkg/authn/keychain_test.go index fa3a75155..5a79db9bf 100644 --- a/pkg/authn/keychain_test.go +++ b/pkg/authn/keychain_test.go @@ -92,27 +92,31 @@ func TestNoConfig(t *testing.T) { } } +func writeConfig(t *testing.T, dir, file, content string) { + if err := os.MkdirAll(dir, 0777); err != nil { + t.Fatalf("mkdir %s: %v", dir, err) + } + if err := os.WriteFile(filepath.Join(dir, file), []byte(content), 0600); err != nil { + t.Fatalf("write %q: %v", file, err) + } +} + func TestPodmanConfig(t *testing.T) { tmpdir := os.Getenv("TEST_TMPDIR") if tmpdir == "" { tmpdir = t.TempDir() } fresh++ - p := filepath.Join(tmpdir, fmt.Sprintf("%d", fresh)) - t.Setenv("XDG_RUNTIME_DIR", p) - os.Unsetenv("DOCKER_CONFIG") - if err := os.MkdirAll(filepath.Join(p, "containers"), 0777); err != nil { - t.Fatalf("mkdir %s/containers: %v", p, err) - } - cfg := filepath.Join(p, "containers/auth.json") - content := fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, encode("foo", "bar")) - if err := os.WriteFile(cfg, []byte(content), 0600); err != nil { - t.Fatalf("write %q: %v", cfg, err) - } + os.Unsetenv("DOCKER_CONFIG") // At first, $DOCKER_CONFIG is unset and $HOME/.docker/config.json isn't - // found, but Podman auth is configured. This should return Podman's - // auth. + // found, but Podman auth $XDG_RUNTIME_DIR/containers/auth.json is configured. + // This should return Podman's auth $XDG_RUNTIME_DIR/containers/auth.json. + p := filepath.Join(tmpdir, fmt.Sprintf("%d", fresh)) + t.Setenv("XDG_RUNTIME_DIR", p) + writeConfig(t, filepath.Join(p, "containers"), "auth.json", + fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, + encode("XDG_RUNTIME_DIR-foo", "XDG_RUNTIME_DIR-bar"))) auth, err := DefaultKeychain.Resolve(testRegistry) if err != nil { t.Fatalf("Resolve() = %v", err) @@ -122,24 +126,40 @@ func TestPodmanConfig(t *testing.T) { t.Fatal(err) } want := &AuthConfig{ - Username: "foo", - Password: "bar", + Username: "XDG_RUNTIME_DIR-foo", + Password: "XDG_RUNTIME_DIR-bar", } if !reflect.DeepEqual(got, want) { t.Errorf("got %+v, want %+v", got, want) } - // Now, configure $HOME/.docker/config.json, which should override - // Podman auth and be used. - if err := os.MkdirAll(filepath.Join(os.Getenv("HOME"), ".docker"), 0777); err != nil { - t.Fatalf("mkdir $HOME/.docker: %v", err) + // Then, configure Podman auth $REGISTRY_AUTH_FILE. + // This demonstrates that $REGISTRY_AUTH_FILE is preferred over $XDG_RUNTIME_DIR/containers/auth.json. + t.Setenv("REGISTRY_AUTH_FILE", filepath.Join(p, "auth.json")) + writeConfig(t, p, "auth.json", + fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, + encode("REGISTRY_AUTH_FILE-foo", "REGISTRY_AUTH_FILE-bar"))) + auth, err = DefaultKeychain.Resolve(testRegistry) + if err != nil { + t.Fatalf("Resolve() = %v", err) + } + got, err = auth.Authorization() + if err != nil { + t.Fatal(err) } - cfg = filepath.Join(os.Getenv("HOME"), ".docker/config.json") - content = fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, encode("home-foo", "home-bar")) - if err := os.WriteFile(cfg, []byte(content), 0600); err != nil { - t.Fatalf("write %q: %v", cfg, err) + want = &AuthConfig{ + Username: "REGISTRY_AUTH_FILE-foo", + Password: "REGISTRY_AUTH_FILE-bar", + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) } - defer func() { os.Remove(cfg) }() + + // Now, configure $HOME/.docker/config.json, which should override + // Podman auth and be used. + writeConfig(t, filepath.Join(os.Getenv("HOME"), ".docker"), "config.json", + fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, encode("home-foo", "home-bar"))) + defer func() { os.Remove(filepath.Join(os.Getenv("HOME"), ".docker/config.json")) }() auth, err = DefaultKeychain.Resolve(testRegistry) if err != nil { t.Fatalf("Resolve() = %v", err) @@ -160,7 +180,7 @@ func TestPodmanConfig(t *testing.T) { // auth configured. // This demonstrates that DOCKER_CONFIG is preferred over Podman auth // and $HOME/.docker/config.json. - content = fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, encode("another-foo", "another-bar")) + content := fmt.Sprintf(`{"auths": {"test.io": {"auth": %q}}}`, encode("another-foo", "another-bar")) cd := setupConfigFile(t, content) defer os.RemoveAll(filepath.Dir(cd))