Updates the v1 search endpoint to also support v2 auth when an identity token is given. Only search v1 endpoint is supported since there is not v2 search currently defined to replace it. Signed-off-by: Derek McGowan <derek@mcgstyle.net> Upstream-commit: 19d48f0b8ba59eea9f2cac4ad1c7977712a6b7ac Component: engine
123 lines
3.7 KiB
Go
123 lines
3.7 KiB
Go
package distribution
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/docker/distribution"
|
|
distreference "github.com/docker/distribution/reference"
|
|
"github.com/docker/distribution/registry/client"
|
|
"github.com/docker/distribution/registry/client/auth"
|
|
"github.com/docker/distribution/registry/client/transport"
|
|
"github.com/docker/docker/dockerversion"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/engine-api/types"
|
|
"github.com/docker/go-connections/sockets"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// NewV2Repository returns a repository (v2 only). It creates an HTTP transport
|
|
// providing timeout settings and authentication support, and also verifies the
|
|
// remote API version.
|
|
func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) {
|
|
repoName := repoInfo.FullName()
|
|
// If endpoint does not support CanonicalName, use the RemoteName instead
|
|
if endpoint.TrimHostname {
|
|
repoName = repoInfo.RemoteName()
|
|
}
|
|
|
|
direct := &net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
DualStack: true,
|
|
}
|
|
|
|
// TODO(dmcgowan): Call close idle connections when complete, use keep alive
|
|
base := &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
Dial: direct.Dial,
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
TLSClientConfig: endpoint.TLSConfig,
|
|
// TODO(dmcgowan): Call close idle connections when complete and use keep alive
|
|
DisableKeepAlives: true,
|
|
}
|
|
|
|
proxyDialer, err := sockets.DialerFromEnvironment(direct)
|
|
if err == nil {
|
|
base.Dial = proxyDialer.Dial
|
|
}
|
|
|
|
modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), metaHeaders)
|
|
authTransport := transport.NewTransport(base, modifiers...)
|
|
|
|
challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
|
|
if err != nil {
|
|
transportOK := false
|
|
if responseErr, ok := err.(registry.PingResponseError); ok {
|
|
transportOK = true
|
|
err = responseErr.Err
|
|
}
|
|
return nil, foundVersion, fallbackError{
|
|
err: err,
|
|
confirmedV2: foundVersion,
|
|
transportOK: transportOK,
|
|
}
|
|
}
|
|
|
|
if authConfig.RegistryToken != "" {
|
|
passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
|
|
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
|
|
} else {
|
|
creds := registry.NewStaticCredentialStore(authConfig)
|
|
tokenHandlerOptions := auth.TokenHandlerOptions{
|
|
Transport: authTransport,
|
|
Credentials: creds,
|
|
Scopes: []auth.Scope{
|
|
auth.RepositoryScope{
|
|
Repository: repoName,
|
|
Actions: actions,
|
|
},
|
|
},
|
|
ClientID: registry.AuthClientID,
|
|
}
|
|
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
|
|
basicHandler := auth.NewBasicHandler(creds)
|
|
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
|
|
}
|
|
tr := transport.NewTransport(base, modifiers...)
|
|
|
|
repoNameRef, err := distreference.ParseNamed(repoName)
|
|
if err != nil {
|
|
return nil, foundVersion, fallbackError{
|
|
err: err,
|
|
confirmedV2: foundVersion,
|
|
transportOK: true,
|
|
}
|
|
}
|
|
|
|
repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr)
|
|
if err != nil {
|
|
err = fallbackError{
|
|
err: err,
|
|
confirmedV2: foundVersion,
|
|
transportOK: true,
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
type existingTokenHandler struct {
|
|
token string
|
|
}
|
|
|
|
func (th *existingTokenHandler) Scheme() string {
|
|
return "bearer"
|
|
}
|
|
|
|
func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
|
|
return nil
|
|
}
|