Login against private registry
To improve the use of docker with a private registry the login command is extended with a parameter for the server address. While implementing i noticed that two problems hindered authentication to a private registry: 1. the resolve of the authentication did not match during push because the looked up key was for example localhost:8080 but the stored one would have been https://localhost:8080 Besides The lookup needs to still work if the https->http fallback is used 2. During pull of an image no authentication is sent, which means all repositories are expected to be private. These points are fixed now. The changes are implemented in a way to be compatible to existing behavior both in the API as also with the private registry. Update: - login does not require the full url any more, you can login to the repository prefix: example: docker logon localhost:8080 Fixed corner corner cases: - When login is done during pull and push the registry endpoint is used and not the central index - When Remote sends a 401 during pull, it is now correctly delegating to CmdLogin - After a Login is done pull and push are using the newly entered login data, and not the previous ones. This one seems to be also broken in master, too. - Auth config is now transfered in a parameter instead of the body when /images/create is called.
This commit is contained in:
parent
037e1bb1a3
commit
ee38e49093
1 changed files with 29 additions and 4 deletions
|
@ -22,6 +22,7 @@ import (
|
||||||
var (
|
var (
|
||||||
ErrAlreadyExists = errors.New("Image already exists")
|
ErrAlreadyExists = errors.New("Image already exists")
|
||||||
ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
|
ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
|
||||||
|
ErrLoginRequired = errors.New("Authentication is required.")
|
||||||
)
|
)
|
||||||
|
|
||||||
func pingRegistryEndpoint(endpoint string) error {
|
func pingRegistryEndpoint(endpoint string) error {
|
||||||
|
@ -102,17 +103,38 @@ 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 "", "", err
|
||||||
|
}
|
||||||
|
return endpoint, reposName, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this method expands the registry name as used in the prefix of a repo
|
||||||
|
// to a full url. if it already is a url, there will be no change.
|
||||||
|
// The registry is pinged to test if it http or https
|
||||||
|
func ExpandAndVerifyRegistryUrl(hostname string) (string, error) {
|
||||||
|
if strings.HasPrefix(hostname, "http:") || strings.HasPrefix(hostname, "https:") {
|
||||||
|
// if there is no slash after https:// (8 characters) then we have no path in the url
|
||||||
|
if strings.LastIndex(hostname, "/") < 9 {
|
||||||
|
// there is no path given. Expand with default path
|
||||||
|
hostname = hostname + "/v1/"
|
||||||
|
}
|
||||||
|
if err := pingRegistryEndpoint(hostname); err != nil {
|
||||||
|
return "", errors.New("Invalid Registry endpoint: " + err.Error())
|
||||||
|
}
|
||||||
|
return hostname, nil
|
||||||
|
}
|
||||||
endpoint := fmt.Sprintf("https://%s/v1/", hostname)
|
endpoint := fmt.Sprintf("https://%s/v1/", hostname)
|
||||||
if err := pingRegistryEndpoint(endpoint); err != nil {
|
if err := pingRegistryEndpoint(endpoint); err != nil {
|
||||||
utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
|
utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
|
||||||
endpoint = fmt.Sprintf("http://%s/v1/", hostname)
|
endpoint = fmt.Sprintf("http://%s/v1/", hostname)
|
||||||
if err = pingRegistryEndpoint(endpoint); err != nil {
|
if err = pingRegistryEndpoint(endpoint); err != nil {
|
||||||
//TODO: triggering highland build can be done there without "failing"
|
//TODO: triggering highland build can be done there without "failing"
|
||||||
return "", "", errors.New("Invalid Registry endpoint: " + err.Error())
|
return "", errors.New("Invalid Registry endpoint: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := validateRepositoryName(reposName)
|
return endpoint, nil
|
||||||
return endpoint, reposName, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
|
func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
|
||||||
|
@ -139,6 +161,9 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s
|
||||||
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
|
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
|
||||||
res, err := doWithCookies(r.client, req)
|
res, err := doWithCookies(r.client, req)
|
||||||
if err != nil || res.StatusCode != 200 {
|
if err != nil || res.StatusCode != 200 {
|
||||||
|
if res.StatusCode == 401 {
|
||||||
|
return nil, ErrLoginRequired
|
||||||
|
}
|
||||||
if res != nil {
|
if res != nil {
|
||||||
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
|
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
|
||||||
}
|
}
|
||||||
|
@ -282,7 +307,7 @@ func (r *Registry) GetRepositoryData(indexEp, remote string) (*RepositoryData, e
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode == 401 {
|
if res.StatusCode == 401 {
|
||||||
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Please login first (HTTP code %d)", res.StatusCode), res)
|
return nil, ErrLoginRequired
|
||||||
}
|
}
|
||||||
// TODO: Right now we're ignoring checksums in the response body.
|
// TODO: Right now we're ignoring checksums in the response body.
|
||||||
// In the future, we need to use them to check image validity.
|
// In the future, we need to use them to check image validity.
|
||||||
|
|
Loading…
Reference in a new issue