registry.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Package registry contains client primitives to interact with a remote Docker registry.
  2. package registry // import "github.com/docker/docker/registry"
  3. import (
  4. "context"
  5. "crypto/tls"
  6. "net"
  7. "net/http"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "time"
  12. "github.com/containerd/log"
  13. "github.com/docker/distribution/registry/client/transport"
  14. "github.com/docker/go-connections/tlsconfig"
  15. )
  16. // HostCertsDir returns the config directory for a specific host.
  17. func HostCertsDir(hostname string) string {
  18. return filepath.Join(CertsDir(), cleanPath(hostname))
  19. }
  20. // newTLSConfig constructs a client TLS configuration based on server defaults
  21. func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
  22. // PreferredServerCipherSuites should have no effect
  23. tlsConfig := tlsconfig.ServerDefault()
  24. tlsConfig.InsecureSkipVerify = !isSecure
  25. if isSecure && CertsDir() != "" {
  26. hostDir := HostCertsDir(hostname)
  27. log.G(context.TODO()).Debugf("hostDir: %s", hostDir)
  28. if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
  29. return nil, err
  30. }
  31. }
  32. return tlsConfig, nil
  33. }
  34. func hasFile(files []os.DirEntry, name string) bool {
  35. for _, f := range files {
  36. if f.Name() == name {
  37. return true
  38. }
  39. }
  40. return false
  41. }
  42. // ReadCertsDirectory reads the directory for TLS certificates
  43. // including roots and certificate pairs and updates the
  44. // provided TLS configuration.
  45. func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
  46. fs, err := os.ReadDir(directory)
  47. if err != nil && !os.IsNotExist(err) {
  48. return invalidParam(err)
  49. }
  50. for _, f := range fs {
  51. if strings.HasSuffix(f.Name(), ".crt") {
  52. if tlsConfig.RootCAs == nil {
  53. systemPool, err := tlsconfig.SystemCertPool()
  54. if err != nil {
  55. return invalidParamWrapf(err, "unable to get system cert pool")
  56. }
  57. tlsConfig.RootCAs = systemPool
  58. }
  59. log.G(context.TODO()).Debugf("crt: %s", filepath.Join(directory, f.Name()))
  60. data, err := os.ReadFile(filepath.Join(directory, f.Name()))
  61. if err != nil {
  62. return err
  63. }
  64. tlsConfig.RootCAs.AppendCertsFromPEM(data)
  65. }
  66. if strings.HasSuffix(f.Name(), ".cert") {
  67. certName := f.Name()
  68. keyName := certName[:len(certName)-5] + ".key"
  69. log.G(context.TODO()).Debugf("cert: %s", filepath.Join(directory, f.Name()))
  70. if !hasFile(fs, keyName) {
  71. return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName)
  72. }
  73. cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
  74. if err != nil {
  75. return err
  76. }
  77. tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
  78. }
  79. if strings.HasSuffix(f.Name(), ".key") {
  80. keyName := f.Name()
  81. certName := keyName[:len(keyName)-4] + ".cert"
  82. log.G(context.TODO()).Debugf("key: %s", filepath.Join(directory, f.Name()))
  83. if !hasFile(fs, certName) {
  84. return invalidParamf("missing client certificate %s for key %s", certName, keyName)
  85. }
  86. }
  87. }
  88. return nil
  89. }
  90. // Headers returns request modifiers with a User-Agent and metaHeaders
  91. func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModifier {
  92. modifiers := []transport.RequestModifier{}
  93. if userAgent != "" {
  94. modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{
  95. "User-Agent": []string{userAgent},
  96. }))
  97. }
  98. if metaHeaders != nil {
  99. modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
  100. }
  101. return modifiers
  102. }
  103. // newTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
  104. // default TLS configuration.
  105. func newTransport(tlsConfig *tls.Config) *http.Transport {
  106. if tlsConfig == nil {
  107. tlsConfig = tlsconfig.ServerDefault()
  108. }
  109. direct := &net.Dialer{
  110. Timeout: 30 * time.Second,
  111. KeepAlive: 30 * time.Second,
  112. }
  113. return &http.Transport{
  114. Proxy: http.ProxyFromEnvironment,
  115. DialContext: direct.DialContext,
  116. TLSHandshakeTimeout: 10 * time.Second,
  117. TLSClientConfig: tlsConfig,
  118. // TODO(dmcgowan): Call close idle connections when complete and use keep alive
  119. DisableKeepAlives: true,
  120. }
  121. }