1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/registry/service.go

273 lines
7.3 KiB
Go
Raw Normal View History

package registry
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/pkg/tlsconfig"
)
// Service is a registry service. It tracks configuration data such as a list
// of mirrors.
type Service struct {
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
Config *ServiceConfig
}
// NewService returns a new instance of Service ready to be
// installed into an engine.
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
func NewService(options *Options) *Service {
return &Service{
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
Config: NewServiceConfig(options),
}
}
// Auth contacts the public registry with the provided credentials,
// and returns OK if authentication was sucessful.
// It can be used to verify the validity of a client's credentials.
func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
addr := authConfig.ServerAddress
if addr == "" {
// Use the official registry address if not specified.
addr = IndexServer
}
index, err := s.ResolveIndex(addr)
if err != nil {
return "", err
}
endpoint, err := NewEndpoint(index, nil)
if err != nil {
return "", err
}
authConfig.ServerAddress = endpoint.String()
return Login(authConfig, endpoint)
}
// Search queries the public registry for images matching the specified
// search terms, and returns the results.
func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
repoInfo, err := s.ResolveRepository(term)
if err != nil {
return nil, err
}
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
// *TODO: Search multiple indexes.
endpoint, err := repoInfo.GetEndpoint(http.Header(headers))
if err != nil {
return nil, err
}
r, err := NewSession(endpoint.client, authConfig, endpoint)
if err != nil {
return nil, err
}
return r.SearchRepositories(repoInfo.GetSearchTerm())
}
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
// ResolveRepository splits a repository name into its components
// and configuration of the associated registry.
func (s *Service) ResolveRepository(name string) (*RepositoryInfo, error) {
return s.Config.NewRepositoryInfo(name)
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
}
// ResolveIndex takes indexName and returns index info
func (s *Service) ResolveIndex(name string) (*IndexInfo, error) {
return s.Config.NewIndexInfo(name)
Deprecating ResolveRepositoryName Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository Moving --registry-mirror configuration to registry config Created resolve_repository job Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name. Adding test for RepositoryInfo Adding tests for opts.StringSetOpts and registry.ValidateMirror Fixing search term use of repoInfo Adding integration tests for registry mirror configuration Normalizing LookupImage image name to match LocalName parsing rules Normalizing repository LocalName to avoid multiple references to an official image Removing errorOut use in tests Removing TODO comment gofmt changes golint comments cleanup. renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig Splitting out builtins.Registry and registry.NewService calls Stray whitespace cleanup Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test Factoring out ValidateRepositoryName from NewRepositoryInfo Removing unused IndexServerURL Allowing json marshaling of ServiceConfig. Exposing ServiceConfig in /info Switching to CamelCase for json marshaling PR cleanup; removing 'Is' prefix from boolean members. Removing unneeded json tags. Removing non-cleanup related fix for 'localhost:[port]' in splitReposName Merge fixes for gh9735 Fixing integration test Reapplying #9754 Adding comment on config.IndexConfigs use from isSecureIndex Remove unused error return value from isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com> Adding back comment in isSecureIndex Signed-off-by: Don Kjer <don.kjer@gmail.com>
2014-10-06 21:54:52 -04:00
}
// APIEndpoint represents a remote API endpoint
type APIEndpoint struct {
Mirror bool
URL string
Version APIVersion
Official bool
TrimHostname bool
TLSConfig *tls.Config
VersionHeader string
Versions []auth.APIVersion
}
// ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint
func (e APIEndpoint) ToV1Endpoint(metaHeaders http.Header) (*Endpoint, error) {
return newEndpoint(e.URL, e.TLSConfig, metaHeaders)
}
// TLSConfig constructs a client TLS configuration based on server defaults
func (s *Service) TLSConfig(hostname string) (*tls.Config, error) {
// PreferredServerCipherSuites should have no effect
tlsConfig := tlsconfig.ServerDefault
isSecure := s.Config.isSecureIndex(hostname)
tlsConfig.InsecureSkipVerify = !isSecure
if isSecure {
hasFile := func(files []os.FileInfo, name string) bool {
for _, f := range files {
if f.Name() == name {
return true
}
}
return false
}
hostDir := filepath.Join(CertsDir, hostname)
logrus.Debugf("hostDir: %s", hostDir)
fs, err := ioutil.ReadDir(hostDir)
if err != nil && !os.IsNotExist(err) {
return nil, err
}
for _, f := range fs {
if strings.HasSuffix(f.Name(), ".crt") {
if tlsConfig.RootCAs == nil {
// TODO(dmcgowan): Copy system pool
tlsConfig.RootCAs = x509.NewCertPool()
}
logrus.Debugf("crt: %s", filepath.Join(hostDir, f.Name()))
data, err := ioutil.ReadFile(filepath.Join(hostDir, f.Name()))
if err != nil {
return nil, err
}
tlsConfig.RootCAs.AppendCertsFromPEM(data)
}
if strings.HasSuffix(f.Name(), ".cert") {
certName := f.Name()
keyName := certName[:len(certName)-5] + ".key"
logrus.Debugf("cert: %s", filepath.Join(hostDir, f.Name()))
if !hasFile(fs, keyName) {
return nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
}
cert, err := tls.LoadX509KeyPair(filepath.Join(hostDir, certName), filepath.Join(hostDir, keyName))
if err != nil {
return nil, err
}
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
}
if strings.HasSuffix(f.Name(), ".key") {
keyName := f.Name()
certName := keyName[:len(keyName)-4] + ".cert"
logrus.Debugf("key: %s", filepath.Join(hostDir, f.Name()))
if !hasFile(fs, certName) {
return nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
}
}
}
}
return &tlsConfig, nil
}
func (s *Service) tlsConfigForMirror(mirror string) (*tls.Config, error) {
mirrorURL, err := url.Parse(mirror)
if err != nil {
return nil, err
}
return s.TLSConfig(mirrorURL.Host)
}
// LookupEndpoints creates an list of endpoints to try, in order of preference.
// It gives preference to v2 endpoints over v1, mirrors over the actual
// registry, and HTTPS over plain HTTP.
func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
var cfg = tlsconfig.ServerDefault
tlsConfig := &cfg
if strings.HasPrefix(repoName, DefaultNamespace+"/") {
// v2 mirrors
for _, mirror := range s.Config.Mirrors {
mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
if err != nil {
return nil, err
}
endpoints = append(endpoints, APIEndpoint{
URL: mirror,
// guess mirrors are v2
Version: APIVersion2,
Mirror: true,
TrimHostname: true,
TLSConfig: mirrorTLSConfig,
})
}
// v2 registry
endpoints = append(endpoints, APIEndpoint{
URL: DefaultV2Registry,
Version: APIVersion2,
Official: true,
TrimHostname: true,
TLSConfig: tlsConfig,
})
// v1 registry
endpoints = append(endpoints, APIEndpoint{
URL: DefaultV1Registry,
Version: APIVersion1,
Official: true,
TrimHostname: true,
TLSConfig: tlsConfig,
})
return endpoints, nil
}
slashIndex := strings.IndexRune(repoName, '/')
if slashIndex <= 0 {
return nil, fmt.Errorf("invalid repo name: missing '/': %s", repoName)
}
hostname := repoName[:slashIndex]
tlsConfig, err = s.TLSConfig(hostname)
if err != nil {
return nil, err
}
isSecure := !tlsConfig.InsecureSkipVerify
v2Versions := []auth.APIVersion{
{
Type: "registry",
Version: "2.0",
},
}
endpoints = []APIEndpoint{
{
URL: "https://" + hostname,
Version: APIVersion2,
TrimHostname: true,
TLSConfig: tlsConfig,
VersionHeader: DefaultRegistryVersionHeader,
Versions: v2Versions,
},
{
URL: "https://" + hostname,
Version: APIVersion1,
TrimHostname: true,
TLSConfig: tlsConfig,
},
}
if !isSecure {
endpoints = append(endpoints, APIEndpoint{
URL: "http://" + hostname,
Version: APIVersion2,
TrimHostname: true,
// used to check if supposed to be secure via InsecureSkipVerify
TLSConfig: tlsConfig,
VersionHeader: DefaultRegistryVersionHeader,
Versions: v2Versions,
}, APIEndpoint{
URL: "http://" + hostname,
Version: APIVersion1,
TrimHostname: true,
// used to check if supposed to be secure via InsecureSkipVerify
TLSConfig: tlsConfig,
})
}
return endpoints, nil
}