mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fix registry auth by storing the string passed on the command line, and allowing for credential selection by normalizing on hostname. Also, remove remote ping calls from CmdPush and CmdPull.
Docker-DCO-1.1-Signed-off-by: Jake Moshenko <jake@devtable.com> (github: jakedt)
This commit is contained in:
parent
70771ad2ed
commit
90b0cce07b
6 changed files with 60 additions and 57 deletions
|
@ -977,13 +977,13 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
||||||
|
|
||||||
cli.LoadConfigFile()
|
cli.LoadConfigFile()
|
||||||
|
|
||||||
// Resolve the Repository name from fqn to endpoint + name
|
// Resolve the Repository name from fqn to hostname + name
|
||||||
endpoint, _, err := registry.ResolveRepositoryName(name)
|
hostname, _, err := registry.ResolveRepositoryName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Resolve the Auth config relevant for this server
|
// Resolve the Auth config relevant for this server
|
||||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||||
// If we're not using a custom registry, we know the restrictions
|
// If we're not using a custom registry, we know the restrictions
|
||||||
// applied to repository names and can warn the user in advance.
|
// applied to repository names and can warn the user in advance.
|
||||||
// Custom repositories can have different rules, and we must also
|
// Custom repositories can have different rules, and we must also
|
||||||
|
@ -1014,10 +1014,10 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
||||||
if err := push(authConfig); err != nil {
|
if err := push(authConfig); err != nil {
|
||||||
if strings.Contains(err.Error(), "Status 401") {
|
if strings.Contains(err.Error(), "Status 401") {
|
||||||
fmt.Fprintln(cli.out, "\nPlease login prior to push:")
|
fmt.Fprintln(cli.out, "\nPlease login prior to push:")
|
||||||
if err := cli.CmdLogin(endpoint); err != nil {
|
if err := cli.CmdLogin(hostname); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||||
return push(authConfig)
|
return push(authConfig)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -1042,8 +1042,8 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
||||||
*tag = parsedTag
|
*tag = parsedTag
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the Repository name from fqn to endpoint + name
|
// Resolve the Repository name from fqn to hostname + name
|
||||||
endpoint, _, err := registry.ResolveRepositoryName(remote)
|
hostname, _, err := registry.ResolveRepositoryName(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1051,7 +1051,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
||||||
cli.LoadConfigFile()
|
cli.LoadConfigFile()
|
||||||
|
|
||||||
// Resolve the Auth config relevant for this server
|
// Resolve the Auth config relevant for this server
|
||||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("fromImage", remote)
|
v.Set("fromImage", remote)
|
||||||
v.Set("tag", *tag)
|
v.Set("tag", *tag)
|
||||||
|
@ -1073,10 +1073,10 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
||||||
if err := pull(authConfig); err != nil {
|
if err := pull(authConfig); err != nil {
|
||||||
if strings.Contains(err.Error(), "Status 401") {
|
if strings.Contains(err.Error(), "Status 401") {
|
||||||
fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
|
fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
|
||||||
if err := cli.CmdLogin(endpoint); err != nil {
|
if err := cli.CmdLogin(hostname); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||||
return pull(authConfig)
|
return pull(authConfig)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -1753,8 +1753,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
v.Set("fromImage", repos)
|
v.Set("fromImage", repos)
|
||||||
v.Set("tag", tag)
|
v.Set("tag", tag)
|
||||||
|
|
||||||
// Resolve the Repository name from fqn to endpoint + name
|
// Resolve the Repository name from fqn to hostname + name
|
||||||
endpoint, _, err := registry.ResolveRepositoryName(repos)
|
hostname, _, err := registry.ResolveRepositoryName(repos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1763,7 +1763,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
cli.LoadConfigFile()
|
cli.LoadConfigFile()
|
||||||
|
|
||||||
// Resolve the Auth config relevant for this server
|
// Resolve the Auth config relevant for this server
|
||||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||||
buf, err := json.Marshal(authConfig)
|
buf, err := json.Marshal(authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
65
auth/auth.go
65
auth/auth.go
|
@ -252,50 +252,39 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method matches a auth configuration to a server address or a url
|
// this method matches a auth configuration to a server address or a url
|
||||||
func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig {
|
func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
|
||||||
if registry == IndexServerAddress() || len(registry) == 0 {
|
if hostname == IndexServerAddress() || len(hostname) == 0 {
|
||||||
// default to the index server
|
// default to the index server
|
||||||
return config.Configs[IndexServerAddress()]
|
return config.Configs[IndexServerAddress()]
|
||||||
}
|
}
|
||||||
// if it's not the index server there are three cases:
|
|
||||||
//
|
|
||||||
// 1. a full config url -> it should be used as is
|
|
||||||
// 2. a full url, but with the wrong protocol
|
|
||||||
// 3. a hostname, with an optional port
|
|
||||||
//
|
|
||||||
// as there is only one auth entry which is fully qualified we need to start
|
|
||||||
// parsing and matching
|
|
||||||
|
|
||||||
swapProtocol := func(url string) string {
|
// First try the happy case
|
||||||
if strings.HasPrefix(url, "http:") {
|
if c, found := config.Configs[hostname]; found {
|
||||||
return strings.Replace(url, "http:", "https:", 1)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(url, "https:") {
|
|
||||||
return strings.Replace(url, "https:", "http:", 1)
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
resolveIgnoringProtocol := func(url string) AuthConfig {
|
|
||||||
if c, found := config.Configs[url]; found {
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
registrySwappedProtocol := swapProtocol(url)
|
|
||||||
// now try to match with the different protocol
|
convertToHostname := func(url string) string {
|
||||||
if c, found := config.Configs[registrySwappedProtocol]; found {
|
stripped := url
|
||||||
return c
|
if strings.HasPrefix(url, "http://") {
|
||||||
|
stripped = strings.Replace(url, "http://", "", 1)
|
||||||
|
} else if strings.HasPrefix(url, "https://") {
|
||||||
|
stripped = strings.Replace(url, "https://", "", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nameParts := strings.SplitN(stripped, "/", 2)
|
||||||
|
|
||||||
|
return nameParts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe they have a legacy config file, we will iterate the keys converting
|
||||||
|
// them to the new format and testing
|
||||||
|
normalizedHostename := convertToHostname(hostname)
|
||||||
|
for registry, config := range config.Configs {
|
||||||
|
if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When all else fails, return an empty auth config
|
||||||
return AuthConfig{}
|
return AuthConfig{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// match both protocols as it could also be a server name like httpfoo
|
|
||||||
if strings.HasPrefix(registry, "http:") || strings.HasPrefix(registry, "https:") {
|
|
||||||
return resolveIgnoringProtocol(registry)
|
|
||||||
}
|
|
||||||
|
|
||||||
url := "https://" + registry
|
|
||||||
if !strings.Contains(registry, "/") {
|
|
||||||
url = url + "/v1/"
|
|
||||||
}
|
|
||||||
return resolveIgnoringProtocol(url)
|
|
||||||
}
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
configFile.Configs["https://registry.example.com/v1/"] = registryAuth
|
configFile.Configs["https://registry.example.com/v1/"] = registryAuth
|
||||||
configFile.Configs["http://localhost:8000/v1/"] = localAuth
|
configFile.Configs["http://localhost:8000/v1/"] = localAuth
|
||||||
|
configFile.Configs["registry.com"] = registryAuth
|
||||||
|
|
||||||
validRegistries := map[string][]string{
|
validRegistries := map[string][]string{
|
||||||
"https://registry.example.com/v1/": {
|
"https://registry.example.com/v1/": {
|
||||||
|
@ -122,6 +123,12 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
|
||||||
"localhost:8000",
|
"localhost:8000",
|
||||||
"localhost:8000/v1/",
|
"localhost:8000/v1/",
|
||||||
},
|
},
|
||||||
|
"registry.com": {
|
||||||
|
"https://registry.com/v1/",
|
||||||
|
"http://registry.com/v1/",
|
||||||
|
"registry.com",
|
||||||
|
"registry.com/v1/",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for configKey, registries := range validRegistries {
|
for configKey, registries := range validRegistries {
|
||||||
|
|
|
@ -91,7 +91,7 @@ func validateRepositoryName(repositoryName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves a repository name to a endpoint + name
|
// Resolves a repository name to a hostname + name
|
||||||
func ResolveRepositoryName(reposName string) (string, string, error) {
|
func ResolveRepositoryName(reposName string) (string, string, error) {
|
||||||
if strings.Contains(reposName, "://") {
|
if strings.Contains(reposName, "://") {
|
||||||
// It cannot contain a scheme!
|
// It cannot contain a scheme!
|
||||||
|
@ -117,11 +117,8 @@ func ResolveRepositoryName(reposName string) (string, string, error) {
|
||||||
if err := validateRepositoryName(reposName); err != nil {
|
if err := validateRepositoryName(reposName); err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
endpoint, err := ExpandAndVerifyRegistryUrl(hostname)
|
|
||||||
if err != nil {
|
return hostname, reposName, nil
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return endpoint, reposName, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method expands the registry name as used in the prefix of a repo
|
// this method expands the registry name as used in the prefix of a repo
|
||||||
|
|
|
@ -145,7 +145,7 @@ func TestResolveRepositoryName(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assertEqual(t, ep, "http://"+u+"/v1/", "Expected endpoint to be "+u)
|
assertEqual(t, ep, u, "Expected endpoint to be "+u)
|
||||||
assertEqual(t, repo, "private/moonbase", "Expected endpoint to be private/moonbase")
|
assertEqual(t, repo, "private/moonbase", "Expected endpoint to be private/moonbase")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
server.go
14
server.go
|
@ -1336,7 +1336,12 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
|
||||||
defer srv.poolRemove("pull", localName+":"+tag)
|
defer srv.poolRemove("pull", localName+":"+tag)
|
||||||
|
|
||||||
// Resolve the Repository name from fqn to endpoint + name
|
// Resolve the Repository name from fqn to endpoint + name
|
||||||
endpoint, remoteName, err := registry.ResolveRepositoryName(localName)
|
hostname, remoteName, err := registry.ResolveRepositoryName(localName)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint, err := registry.ExpandAndVerifyRegistryUrl(hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -1538,7 +1543,12 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
|
||||||
defer srv.poolRemove("push", localName)
|
defer srv.poolRemove("push", localName)
|
||||||
|
|
||||||
// Resolve the Repository name from fqn to endpoint + name
|
// Resolve the Repository name from fqn to endpoint + name
|
||||||
endpoint, remoteName, err := registry.ResolveRepositoryName(localName)
|
hostname, remoteName, err := registry.ResolveRepositoryName(localName)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint, err := registry.ExpandAndVerifyRegistryUrl(hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue