Skip to content

Commit 20158e2

Browse files
authored
Merge pull request #6 from stackrox/mc/quay-auth
Restructure dockerauth code
2 parents 06d03ec + 7f039c8 commit 20158e2

3 files changed

Lines changed: 98 additions & 69 deletions

File tree

pkg/deployer/deployer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func New(log *logger.Logger, overrideFile string, overrideSetExpressions []strin
8282
overrideSetExpressions: overrideSetExpressions,
8383
}
8484

85-
d.dockerAuth = dockerauth.New(log, true)
85+
d.dockerAuth = dockerauth.New(log)
8686
d.imageCache = imagecache.New(log, "", 20)
8787
d.portForward = portforward.New(kubectl, log)
8888
d.clusterDefaults = clusterdefaults.NewManager(log)

pkg/dockerauth/dockerauth.go

Lines changed: 95 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,64 +13,79 @@ import (
1313
"github.com/stackrox/roxie/pkg/logger"
1414
)
1515

16-
// DockerAuth handles Docker authentication and pull secret management
16+
const (
17+
acsImageRegistry = "quay.io"
18+
)
19+
20+
// DockerAuth handles Docker authentication and pull secret management.
1721
type DockerAuth struct {
18-
logger *logger.Logger
19-
cacheEnabled bool
20-
authCache map[string]string
22+
logger *logger.Logger
2123
}
2224

23-
// DockerConfig represents Docker configuration structure
25+
// DockerConfig represents Docker configuration structure.
2426
type DockerConfig struct {
2527
Auths map[string]AuthEntry `json:"auths,omitempty"`
2628
CredHelpers map[string]string `json:"credHelpers,omitempty"`
29+
CredsStore string `json:"credsStore,omitempty"`
2730
}
2831

29-
// AuthEntry represents a single auth entry in Docker config
32+
// AuthEntry represents a single auth entry in Docker config.
3033
type AuthEntry struct {
3134
Auth string `json:"auth,omitempty"`
3235
}
3336

34-
// CredentialData represents credential data from credential helper
37+
// CredentialData represents credential data from credential helper.
3538
type CredentialData struct {
3639
Username string `json:"Username"`
3740
Secret string `json:"Secret"`
3841
}
3942

40-
// New creates a new DockerAuth instance
41-
func New(log *logger.Logger, cacheEnabled bool) *DockerAuth {
43+
// New creates a new DockerAuth instance.
44+
func New(log *logger.Logger) *DockerAuth {
4245
return &DockerAuth{
43-
logger: log,
44-
cacheEnabled: cacheEnabled,
45-
authCache: make(map[string]string),
46+
logger: log,
4647
}
4748
}
4849

4950
// GetDockerAuthString generates Docker authentication string for image pull secrets
5051
func (d *DockerAuth) GetDockerAuthString(_, _ string) (string, error) {
51-
// Try environment variables first
52-
username := os.Getenv("REGISTRY_USERNAME")
53-
password := os.Getenv("REGISTRY_PASSWORD")
54-
55-
if username != "" && password != "" {
56-
// Use credentials from environment
57-
} else {
58-
// Try to get from Docker config file
52+
var username, password string
53+
54+
// Try environment variables first.
55+
username = os.Getenv("REGISTRY_USERNAME")
56+
password = os.Getenv("REGISTRY_PASSWORD")
57+
58+
if username != "" && password == "" {
59+
return "", errors.New("REGISTRY_USERNAME set but REGISTRY_PASSWORD is empty")
60+
}
61+
if username == "" && password != "" {
62+
return "", errors.New("REGISTRY_PASSWORD set but REGISTRY_USERNAME is empty")
63+
}
64+
65+
if username == "" {
66+
// Try to get from Docker config file.
5967
dockerConfigPath := filepath.Join(os.Getenv("HOME"), ".docker", "config.json")
68+
d.logger.Dimf("REGISTRY_USERNAME/REGISTRY_PASSWORD unset. Trying to obtain Docker credentials from config file: %s", dockerConfigPath)
6069
if _, err := os.Stat(dockerConfigPath); err == nil {
61-
return d.getDockerConfigAuth(dockerConfigPath)
70+
var err error
71+
username, password, err = d.getCredentialsFromDockerConfig(dockerConfigPath)
72+
if err != nil {
73+
return "", err
74+
}
6275
}
76+
}
6377

78+
if username == "" || password == "" {
6479
return "", errors.New("no Docker credentials found")
6580
}
6681

67-
// Create auth string
82+
// Create auth string.
6883
authString := fmt.Sprintf("%s:%s", username, password)
6984
encodedAuth := base64.StdEncoding.EncodeToString([]byte(authString))
7085

7186
dockerConfig := DockerConfig{
7287
Auths: map[string]AuthEntry{
73-
"quay.io": {Auth: encodedAuth},
88+
acsImageRegistry: {Auth: encodedAuth},
7489
},
7590
}
7691

@@ -82,68 +97,82 @@ func (d *DockerAuth) GetDockerAuthString(_, _ string) (string, error) {
8297
return string(jsonData), nil
8398
}
8499

85-
// getDockerConfigAuth extracts auth from existing Docker config
86-
func (d *DockerAuth) getDockerConfigAuth(configPath string) (string, error) {
100+
// getCredentialsFromDockerConfig extracts credentials from existing Docker config.
101+
func (d *DockerAuth) getCredentialsFromDockerConfig(configPath string) (string, string, error) {
87102
data, err := os.ReadFile(configPath)
88103
if err != nil {
89-
return "", fmt.Errorf("failed to read Docker config: %w", err)
104+
return "", "", fmt.Errorf("failed to read Docker config: %w", err)
90105
}
91106

92107
var config DockerConfig
93108
if err := json.Unmarshal(data, &config); err != nil {
94-
return "", fmt.Errorf("failed to parse Docker config: %w", err)
109+
return "", "", fmt.Errorf("failed to parse Docker config: %w", err)
95110
}
96111

97-
// Check for existing auths
98-
if len(config.Auths) > 0 {
99-
result := DockerConfig{Auths: config.Auths}
100-
jsonData, err := json.Marshal(result)
112+
// Check for existing auths for the ACS image registry.
113+
if authEntry, ok := config.Auths[acsImageRegistry]; ok && authEntry.Auth != "" {
114+
// Decode the base64 auth string to get username:password
115+
decoded, err := base64.StdEncoding.DecodeString(authEntry.Auth)
101116
if err != nil {
102-
return "", fmt.Errorf("failed to marshal auths: %w", err)
117+
return "", "", fmt.Errorf("failed to decode auth string: %w", err)
103118
}
104-
return string(jsonData), nil
119+
parts := bytes.SplitN(decoded, []byte(":"), 2)
120+
if len(parts) != 2 {
121+
return "", "", errors.New("invalid auth format")
122+
}
123+
return string(parts[0]), string(parts[1]), nil
105124
}
106125

107-
// Check for credential helpers
108-
if len(config.CredHelpers) > 0 {
109-
for registry, helper := range config.CredHelpers {
110-
cmd := exec.Command(fmt.Sprintf("docker-credential-%s", helper), "get")
111-
cmd.Stdin = bytes.NewBufferString(registry)
126+
// Try credential helper specifically configured for the ACS image registry
127+
helper := d.lookupCredentialHelperForRegistry(&config, acsImageRegistry)
128+
if helper == "" {
129+
return "", "", fmt.Errorf("no Docker credentials found in config for ACS image registry (%s)", acsImageRegistry)
130+
}
112131

113-
output, err := cmd.Output()
114-
if err != nil {
115-
d.logger.Warningf("Credential helper '%s' for '%s' failed: %v", helper, registry, err)
116-
continue
117-
}
132+
credData, err := d.getCredentialFromHelper(helper, acsImageRegistry)
133+
if err != nil {
134+
return "", "", fmt.Errorf("failed to get credentials from helper '%s' for '%s': %w", helper, acsImageRegistry, err)
135+
}
118136

119-
var credData CredentialData
120-
if err := json.Unmarshal(output, &credData); err != nil {
121-
continue
122-
}
137+
return credData.Username, credData.Secret, nil
138+
}
123139

124-
if credData.Username != "" && credData.Secret != "" {
125-
authString := fmt.Sprintf("%s:%s", credData.Username, credData.Secret)
126-
encodedAuth := base64.StdEncoding.EncodeToString([]byte(authString))
127-
128-
result := DockerConfig{
129-
Auths: map[string]AuthEntry{
130-
registry: {Auth: encodedAuth},
131-
},
132-
}
133-
134-
jsonData, err := json.Marshal(result)
135-
if err != nil {
136-
return "", fmt.Errorf("failed to marshal credential helper result: %w", err)
137-
}
138-
return string(jsonData), nil
139-
}
140-
}
140+
// lookupCredentialHelperForRegistry returns the credential helper name for a given registry
141+
// by checking registry-specific credHelpers first, then falling back to the global credsStore.
142+
// Returns empty string if no helper is configured.
143+
func (d *DockerAuth) lookupCredentialHelperForRegistry(config *DockerConfig, registry string) string {
144+
// First check for registry-specific credential helper
145+
if helper, ok := config.CredHelpers[registry]; ok {
146+
return helper
147+
}
148+
149+
// Fall back to global credential store
150+
return config.CredsStore
151+
}
152+
153+
// getCredentialFromHelper retrieves credentials from a credential helper.
154+
func (d *DockerAuth) getCredentialFromHelper(helperName, registry string) (*CredentialData, error) {
155+
cmd := exec.Command(fmt.Sprintf("docker-credential-%s", helperName), "get")
156+
cmd.Stdin = bytes.NewBufferString(registry)
157+
158+
output, err := cmd.Output()
159+
if err != nil {
160+
return nil, fmt.Errorf("credential helper '%s' for '%s' failed: %w", helperName, registry, err)
161+
}
162+
163+
var credData CredentialData
164+
if err := json.Unmarshal(output, &credData); err != nil {
165+
return nil, fmt.Errorf("failed to parse credential helper output: %w", err)
166+
}
167+
168+
if credData.Username == "" || credData.Secret == "" {
169+
return nil, errors.New("credential helper returned empty credentials")
141170
}
142171

143-
return "", errors.New("no Docker credentials found in config")
172+
return &credData, nil
144173
}
145174

146-
// CreatePullSecretYAML creates Kubernetes pull secret YAML
175+
// CreatePullSecretYAML creates Kubernetes pull secret YAML.
147176
func (d *DockerAuth) CreatePullSecretYAML(namespace string) (string, error) {
148177
dockerConfigJSON, err := d.GetDockerAuthString("", "")
149178
if err != nil {

pkg/dockerauth/dockerauth_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestCreatePullSecretYAMLFromEnv(t *testing.T) {
2020
}()
2121

2222
log := logger.New()
23-
da := New(log, true)
23+
da := New(log)
2424

2525
yamlText, err := da.CreatePullSecretYAML("ns")
2626
if err != nil {
@@ -94,7 +94,7 @@ func TestCreatePullSecretYAMLNoCredentials(t *testing.T) {
9494
}()
9595

9696
log := logger.New()
97-
da := New(log, true)
97+
da := New(log)
9898

9999
_, err := da.CreatePullSecretYAML("ns")
100100
if err == nil {

0 commit comments

Comments
 (0)