@@ -34,6 +34,8 @@ const (
3434 dockerCfgFileName = "config.json"
3535 dockerCfgObsolete = ".dockercfg"
3636
37+ systemPerHostCertDirPath = "/etc/docker/certs.d"
38+
3739 resolvedPingV2URL = "%s://%s/v2/"
3840 resolvedPingV1URL = "%s://%s/v1/_ping"
3941 tagsPath = "/v2/%s/tags/list"
@@ -129,12 +131,29 @@ func newTransport() *http.Transport {
129131 return tr
130132}
131133
132- func setupCertificates (dir string , tlsc * tls.Config ) error {
133- if dir == "" {
134- return nil
134+ // dockerCertDir returns a path to a directory to be consumed by setupCertificates() depending on ctx and hostPort.
135+ func dockerCertDir (ctx * types.SystemContext , hostPort string ) string {
136+ if ctx != nil && ctx .DockerCertPath != "" {
137+ return ctx .DockerCertPath
135138 }
139+ var hostCertDir string
140+ if ctx != nil && ctx .DockerPerHostCertDirPath != "" {
141+ hostCertDir = ctx .DockerPerHostCertDirPath
142+ } else if ctx != nil && ctx .RootForImplicitAbsolutePaths != "" {
143+ hostCertDir = filepath .Join (ctx .RootForImplicitAbsolutePaths , systemPerHostCertDirPath )
144+ } else {
145+ hostCertDir = systemPerHostCertDirPath
146+ }
147+ return filepath .Join (hostCertDir , hostPort )
148+ }
149+
150+ func setupCertificates (dir string , tlsc * tls.Config ) error {
151+ logrus .Debugf ("Looking for TLS certificates and private keys in %s" , dir )
136152 fs , err := ioutil .ReadDir (dir )
137- if err != nil && ! os .IsNotExist (err ) {
153+ if err != nil {
154+ if os .IsNotExist (err ) {
155+ return nil
156+ }
138157 return err
139158 }
140159
@@ -146,7 +165,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
146165 return errors .Wrap (err , "unable to get system cert pool" )
147166 }
148167 tlsc .RootCAs = systemPool
149- logrus .Debugf ("crt: %s" , fullPath )
168+ logrus .Debugf (" crt: %s" , fullPath )
150169 data , err := ioutil .ReadFile (fullPath )
151170 if err != nil {
152171 return err
@@ -156,7 +175,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
156175 if strings .HasSuffix (f .Name (), ".cert" ) {
157176 certName := f .Name ()
158177 keyName := certName [:len (certName )- 5 ] + ".key"
159- logrus .Debugf ("cert: %s" , fullPath )
178+ logrus .Debugf (" cert: %s" , fullPath )
160179 if ! hasFile (fs , keyName ) {
161180 return errors .Errorf ("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt" , keyName , certName )
162181 }
@@ -169,7 +188,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
169188 if strings .HasSuffix (f .Name (), ".key" ) {
170189 keyName := f .Name ()
171190 certName := keyName [:len (keyName )- 4 ] + ".cert"
172- logrus .Debugf ("key: %s" , fullPath )
191+ logrus .Debugf (" key: %s" , fullPath )
173192 if ! hasFile (fs , certName ) {
174193 return errors .Errorf ("missing client certificate %s for key %s" , certName , keyName )
175194 }
@@ -199,18 +218,18 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool,
199218 return nil , err
200219 }
201220 tr := newTransport ()
202- if ctx != nil && ( ctx . DockerCertPath != "" || ctx . DockerInsecureSkipTLSVerify ) {
203- tlsc := & tls. Config {}
204-
205- if err := setupCertificates ( ctx . DockerCertPath , tlsc ); err != nil {
206- return nil , err
207- }
208-
209- tlsc . InsecureSkipVerify = ctx . DockerInsecureSkipTLSVerify
210- tr . TLSClientConfig = tlsc
221+ tr . TLSClientConfig = serverDefault ()
222+ // It is undefined whether the host[:port] string for dockerHostname should be dockerHostname or dockerRegistry,
223+ // because docker/docker does not read the certs.d subdirectory at all in that case. We use the user-visible
224+ // dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
225+ // generally the UI hides the existence of the different dockerRegistry. But note that this behavior is
226+ // undocumented and may change if docker/docker changes.
227+ certDir := dockerCertDir ( ctx , reference . Domain ( ref . ref ))
228+ if err := setupCertificates ( certDir , tr . TLSClientConfig ); err != nil {
229+ return nil , err
211230 }
212- if tr . TLSClientConfig == nil {
213- tr .TLSClientConfig = serverDefault ()
231+ if ctx != nil && ctx . DockerInsecureSkipTLSVerify {
232+ tr .TLSClientConfig . InsecureSkipVerify = true
214233 }
215234 client := & http.Client {Transport : tr }
216235
@@ -289,31 +308,36 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error {
289308 if len (c .challenges ) == 0 {
290309 return nil
291310 }
292- // assume just one...
293- challenge := c .challenges [0 ]
294- switch challenge .Scheme {
295- case "basic" :
296- req .SetBasicAuth (c .username , c .password )
297- return nil
298- case "bearer" :
299- if c .token == nil || time .Now ().After (c .tokenExpiration ) {
300- realm , ok := challenge .Parameters ["realm" ]
301- if ! ok {
302- return errors .Errorf ("missing realm in bearer auth challenge" )
311+ schemeNames := make ([]string , 0 , len (c .challenges ))
312+ for _ , challenge := range c .challenges {
313+ schemeNames = append (schemeNames , challenge .Scheme )
314+ switch challenge .Scheme {
315+ case "basic" :
316+ req .SetBasicAuth (c .username , c .password )
317+ return nil
318+ case "bearer" :
319+ if c .token == nil || time .Now ().After (c .tokenExpiration ) {
320+ realm , ok := challenge .Parameters ["realm" ]
321+ if ! ok {
322+ return errors .Errorf ("missing realm in bearer auth challenge" )
323+ }
324+ service , _ := challenge .Parameters ["service" ] // Will be "" if not present
325+ scope := fmt .Sprintf ("repository:%s:%s" , c .scope .remoteName , c .scope .actions )
326+ token , err := c .getBearerToken (realm , service , scope )
327+ if err != nil {
328+ return err
329+ }
330+ c .token = token
331+ c .tokenExpiration = token .IssuedAt .Add (time .Duration (token .ExpiresIn ) * time .Second )
303332 }
304- service , _ := challenge .Parameters ["service" ] // Will be "" if not present
305- scope := fmt .Sprintf ("repository:%s:%s" , c .scope .remoteName , c .scope .actions )
306- token , err := c .getBearerToken (realm , service , scope )
307- if err != nil {
308- return err
309- }
310- c .token = token
311- c .tokenExpiration = token .IssuedAt .Add (time .Duration (token .ExpiresIn ) * time .Second )
333+ req .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , c .token .Token ))
334+ return nil
335+ default :
336+ logrus .Debugf ("no handler for %s authentication" , challenge .Scheme )
312337 }
313- req .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , c .token .Token ))
314- return nil
315338 }
316- return errors .Errorf ("no handler for %s authentication" , challenge .Scheme )
339+ logrus .Infof ("None of the challenges sent by server (%s) are supported, trying an unauthenticated request anyway" , strings .Join (schemeNames , ", " ))
340+ return nil
317341}
318342
319343func (c * dockerClient ) getBearerToken (realm , service , scope string ) (* bearerToken , error ) {
0 commit comments