github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/image/distributionutil/login.go (about) 1 // Copyright © 2021 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package distributionutil 16 17 import ( 18 "context" 19 "fmt" 20 "net/http" 21 "net/url" 22 "os" 23 "path/filepath" 24 "strings" 25 "time" 26 27 "github.com/docker/distribution/registry/client/auth" 28 "github.com/docker/distribution/registry/client/transport" 29 "github.com/docker/docker/api/types" 30 "github.com/docker/docker/dockerversion" 31 dockerRegistry "github.com/docker/docker/registry" 32 "github.com/pkg/errors" 33 ) 34 35 func Login(ctx context.Context, authConfig *types.AuthConfig) error { 36 domain := authConfig.ServerAddress 37 if !strings.HasPrefix(domain, "http://") && !strings.HasPrefix(domain, "https://") { 38 domain = "https://" + domain 39 } 40 endpointURL, err := url.Parse(domain) 41 if err != nil { 42 return err 43 } 44 45 modifiers := dockerRegistry.Headers(dockerversion.DockerUserAgent(ctx), nil) 46 base := dockerRegistry.NewTransport(nil) 47 base.TLSClientConfig.InsecureSkipVerify = os.Getenv("SKIP_TLS_VERIFY") == "true" 48 if err := dockerRegistry.ReadCertsDirectory(base.TLSClientConfig, filepath.Join(dockerRegistry.CertsDir(), endpointURL.Host)); err != nil { 49 return err 50 } 51 authTransport := transport.NewTransport(base, modifiers...) 52 53 credentialAuthConfig := *authConfig 54 creds := loginCredentialStore{ 55 authConfig: &credentialAuthConfig, 56 } 57 loginClient, err := authHTTPClient(endpointURL, authTransport, modifiers, creds, nil) 58 if err != nil { 59 return err 60 } 61 62 endpointStr := strings.TrimRight(endpointURL.String(), "/") + "/v2/" 63 req, err := http.NewRequest("GET", endpointStr, nil) 64 if err != nil { 65 return err 66 } 67 68 resp, err := loginClient.Do(req) 69 if err != nil { 70 if strings.Contains(err.Error(), "x509") { 71 return fmt.Errorf("%v, if you want to skip TLS verification, set the environment variable 'SKIP_TLS_VERIFY=true' ", err) 72 } 73 return err 74 } 75 defer resp.Body.Close() 76 77 if resp.StatusCode == http.StatusOK { 78 return nil 79 } 80 81 // TODO(dmcgowan): Attempt to further interpret result, status code and error code string 82 err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) 83 return err 84 } 85 86 func authHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) { 87 challengeManager, err := dockerRegistry.PingV2Registry(endpoint, authTransport) 88 if err != nil { 89 return nil, err 90 } 91 92 tokenHandlerOptions := auth.TokenHandlerOptions{ 93 Transport: authTransport, 94 Credentials: creds, 95 OfflineAccess: true, 96 ClientID: dockerRegistry.AuthClientID, 97 Scopes: scopes, 98 } 99 tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) 100 basicHandler := auth.NewBasicHandler(creds) 101 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) 102 tr := transport.NewTransport(authTransport, modifiers...) 103 104 return &http.Client{ 105 Transport: tr, 106 Timeout: 15 * time.Second, 107 }, nil 108 } 109 110 type loginCredentialStore struct { 111 authConfig *types.AuthConfig 112 } 113 114 func (lcs loginCredentialStore) Basic(*url.URL) (string, string) { 115 return lcs.authConfig.Username, lcs.authConfig.Password 116 } 117 118 func (lcs loginCredentialStore) RefreshToken(*url.URL, string) string { 119 return lcs.authConfig.IdentityToken 120 } 121 122 func (lcs loginCredentialStore) SetRefreshToken(u *url.URL, service, token string) { 123 lcs.authConfig.IdentityToken = token 124 }