github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/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 "io" 21 "net/http" 22 "net/url" 23 "os" 24 "path/filepath" 25 "strings" 26 "time" 27 28 "github.com/docker/distribution/registry/client/auth" 29 "github.com/docker/distribution/registry/client/transport" 30 "github.com/docker/docker/api/types" 31 "github.com/docker/docker/dockerversion" 32 dockerRegistry "github.com/docker/docker/registry" 33 "github.com/pkg/errors" 34 "github.com/sirupsen/logrus" 35 ) 36 37 func Login(ctx context.Context, authConfig *types.AuthConfig) error { 38 domain := authConfig.ServerAddress 39 if !strings.HasPrefix(domain, "http://") && !strings.HasPrefix(domain, "https://") { 40 domain = "https://" + domain 41 } 42 endpointURL, err := url.Parse(domain) 43 if err != nil { 44 return err 45 } 46 47 modifiers := dockerRegistry.Headers(dockerversion.DockerUserAgent(ctx), nil) 48 base := dockerRegistry.NewTransport(nil) 49 base.TLSClientConfig.InsecureSkipVerify = os.Getenv("SKIP_TLS_VERIFY") == "true" 50 if err := dockerRegistry.ReadCertsDirectory(base.TLSClientConfig, filepath.Join(dockerRegistry.CertsDir(), endpointURL.Host)); err != nil { 51 return err 52 } 53 authTransport := transport.NewTransport(base, modifiers...) 54 55 credentialAuthConfig := *authConfig 56 creds := loginCredentialStore{ 57 authConfig: &credentialAuthConfig, 58 } 59 loginClient, err := authHTTPClient(endpointURL, authTransport, modifiers, creds, nil) 60 if err != nil { 61 return err 62 } 63 64 endpointStr := strings.TrimRight(endpointURL.String(), "/") + "/v2/" 65 req, err := http.NewRequest("GET", endpointStr, nil) 66 if err != nil { 67 return err 68 } 69 70 resp, err := loginClient.Do(req) 71 if err != nil { 72 if strings.Contains(err.Error(), "x509") { 73 return fmt.Errorf("%v, if you want to skip TLS verification, set the environment variable 'SKIP_TLS_VERIFY=true' ", err) 74 } 75 return err 76 } 77 defer func(Body io.ReadCloser) { 78 err := Body.Close() 79 if err != nil { 80 logrus.Warnf("failed to close http reader") 81 } 82 }(resp.Body) 83 84 if resp.StatusCode == http.StatusOK { 85 return nil 86 } 87 88 // TODO(dmcgowan): Attempt to further interpret result, status code and error code string 89 err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) 90 return err 91 } 92 93 func authHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) { 94 challengeManager, _, err := dockerRegistry.PingV2Registry(endpoint, authTransport) 95 if err != nil { 96 return nil, err 97 } 98 99 tokenHandlerOptions := auth.TokenHandlerOptions{ 100 Transport: authTransport, 101 Credentials: creds, 102 OfflineAccess: true, 103 ClientID: dockerRegistry.AuthClientID, 104 Scopes: scopes, 105 } 106 tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) 107 basicHandler := auth.NewBasicHandler(creds) 108 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) 109 tr := transport.NewTransport(authTransport, modifiers...) 110 111 return &http.Client{ 112 Transport: tr, 113 Timeout: 15 * time.Second, 114 }, nil 115 } 116 117 type loginCredentialStore struct { 118 authConfig *types.AuthConfig 119 } 120 121 func (lcs loginCredentialStore) Basic(*url.URL) (string, string) { 122 return lcs.authConfig.Username, lcs.authConfig.Password 123 } 124 125 func (lcs loginCredentialStore) RefreshToken(*url.URL, string) string { 126 return lcs.authConfig.IdentityToken 127 } 128 129 func (lcs loginCredentialStore) SetRefreshToken(u *url.URL, service, token string) { 130 lcs.authConfig.IdentityToken = token 131 }