@@ -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.
1721type 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.
2426type 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.
3033type 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.
3538type 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
5051func (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.
147176func (d * DockerAuth ) CreatePullSecretYAML (namespace string ) (string , error ) {
148177 dockerConfigJSON , err := d .GetDockerAuthString ("" , "" )
149178 if err != nil {
0 commit comments