github.com/enmand/kubernetes@v1.2.0-alpha.0/pkg/credentialprovider/gcp/metadata.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package gcp_credentials 18 19 import ( 20 "encoding/json" 21 "net/http" 22 "strings" 23 "time" 24 25 "github.com/golang/glog" 26 "k8s.io/kubernetes/pkg/credentialprovider" 27 ) 28 29 const ( 30 metadataUrl = "http://metadata.google.internal./computeMetadata/v1/" 31 metadataAttributes = metadataUrl + "instance/attributes/" 32 dockerConfigKey = metadataAttributes + "google-dockercfg" 33 dockerConfigUrlKey = metadataAttributes + "google-dockercfg-url" 34 metadataScopes = metadataUrl + "instance/service-accounts/default/scopes" 35 metadataToken = metadataUrl + "instance/service-accounts/default/token" 36 metadataEmail = metadataUrl + "instance/service-accounts/default/email" 37 storageScopePrefix = "https://www.googleapis.com/auth/devstorage" 38 cloudPlatformScopePrefix = "https://www.googleapis.com/auth/cloud-platform" 39 ) 40 41 // For these urls, the parts of the host name can be glob, for example '*.gcr.io" will match 42 // "foo.gcr.io" and "bar.gcr.io". 43 var containerRegistryUrls = []string{"container.cloud.google.com", "gcr.io", "*.gcr.io"} 44 45 var metadataHeader = &http.Header{ 46 "Metadata-Flavor": []string{"Google"}, 47 } 48 49 // A DockerConfigProvider that reads its configuration from Google 50 // Compute Engine metadata. 51 type metadataProvider struct { 52 Client *http.Client 53 } 54 55 // A DockerConfigProvider that reads its configuration from a specific 56 // Google Compute Engine metadata key: 'google-dockercfg'. 57 type dockerConfigKeyProvider struct { 58 metadataProvider 59 } 60 61 // A DockerConfigProvider that reads its configuration from a URL read from 62 // a specific Google Compute Engine metadata key: 'google-dockercfg-url'. 63 type dockerConfigUrlKeyProvider struct { 64 metadataProvider 65 } 66 67 // A DockerConfigProvider that provides a dockercfg with: 68 // Username: "_token" 69 // Password: "{access token from metadata}" 70 type containerRegistryProvider struct { 71 metadataProvider 72 } 73 74 // init registers the various means by which credentials may 75 // be resolved on GCP. 76 func init() { 77 credentialprovider.RegisterCredentialProvider("google-dockercfg", 78 &credentialprovider.CachingDockerConfigProvider{ 79 Provider: &dockerConfigKeyProvider{ 80 metadataProvider{Client: http.DefaultClient}, 81 }, 82 Lifetime: 60 * time.Second, 83 }) 84 85 credentialprovider.RegisterCredentialProvider("google-dockercfg-url", 86 &credentialprovider.CachingDockerConfigProvider{ 87 Provider: &dockerConfigUrlKeyProvider{ 88 metadataProvider{Client: http.DefaultClient}, 89 }, 90 Lifetime: 60 * time.Second, 91 }) 92 93 credentialprovider.RegisterCredentialProvider("google-container-registry", 94 // Never cache this. The access token is already 95 // cached by the metadata service. 96 &containerRegistryProvider{ 97 metadataProvider{Client: http.DefaultClient}, 98 }) 99 } 100 101 // Enabled implements DockerConfigProvider for all of the Google implementations. 102 func (g *metadataProvider) Enabled() bool { 103 _, err := credentialprovider.ReadUrl(metadataUrl, g.Client, metadataHeader) 104 return err == nil 105 } 106 107 // Provide implements DockerConfigProvider 108 func (g *dockerConfigKeyProvider) Provide() credentialprovider.DockerConfig { 109 // Read the contents of the google-dockercfg metadata key and 110 // parse them as an alternate .dockercfg 111 if cfg, err := credentialprovider.ReadDockerConfigFileFromUrl(dockerConfigKey, g.Client, metadataHeader); err != nil { 112 glog.Errorf("while reading 'google-dockercfg' metadata: %v", err) 113 } else { 114 return cfg 115 } 116 117 return credentialprovider.DockerConfig{} 118 } 119 120 // Provide implements DockerConfigProvider 121 func (g *dockerConfigUrlKeyProvider) Provide() credentialprovider.DockerConfig { 122 // Read the contents of the google-dockercfg-url key and load a .dockercfg from there 123 if url, err := credentialprovider.ReadUrl(dockerConfigUrlKey, g.Client, metadataHeader); err != nil { 124 glog.Errorf("while reading 'google-dockercfg-url' metadata: %v", err) 125 } else { 126 if strings.HasPrefix(string(url), "http") { 127 if cfg, err := credentialprovider.ReadDockerConfigFileFromUrl(string(url), g.Client, nil); err != nil { 128 glog.Errorf("while reading 'google-dockercfg-url'-specified url: %s, %v", string(url), err) 129 } else { 130 return cfg 131 } 132 } else { 133 // TODO(mattmoor): support reading alternate scheme URLs (e.g. gs:// or s3://) 134 glog.Errorf("Unsupported URL scheme: %s", string(url)) 135 } 136 } 137 138 return credentialprovider.DockerConfig{} 139 } 140 141 // Enabled implements a special metadata-based check, which verifies the 142 // storage scope is available on the GCE VM. 143 func (g *containerRegistryProvider) Enabled() bool { 144 value, err := credentialprovider.ReadUrl(metadataScopes+"?alt=json", g.Client, metadataHeader) 145 if err != nil { 146 return false 147 } 148 var scopes []string 149 if err := json.Unmarshal([]byte(value), &scopes); err != nil { 150 return false 151 } 152 153 for _, v := range scopes { 154 // cloudPlatformScope implies storage scope. 155 if strings.HasPrefix(v, storageScopePrefix) || strings.HasPrefix(v, cloudPlatformScopePrefix) { 156 return true 157 } 158 } 159 glog.Warningf("Google container registry is disabled, no storage scope is available: %s", value) 160 return false 161 } 162 163 // tokenBlob is used to decode the JSON blob containing an access token 164 // that is returned by GCE metadata. 165 type tokenBlob struct { 166 AccessToken string `json:"access_token"` 167 } 168 169 // Provide implements DockerConfigProvider 170 func (g *containerRegistryProvider) Provide() credentialprovider.DockerConfig { 171 cfg := credentialprovider.DockerConfig{} 172 173 tokenJsonBlob, err := credentialprovider.ReadUrl(metadataToken, g.Client, metadataHeader) 174 if err != nil { 175 glog.Errorf("while reading access token endpoint: %v", err) 176 return cfg 177 } 178 179 email, err := credentialprovider.ReadUrl(metadataEmail, g.Client, metadataHeader) 180 if err != nil { 181 glog.Errorf("while reading email endpoint: %v", err) 182 return cfg 183 } 184 185 var parsedBlob tokenBlob 186 if err := json.Unmarshal([]byte(tokenJsonBlob), &parsedBlob); err != nil { 187 glog.Errorf("while parsing json blob %s: %v", tokenJsonBlob, err) 188 return cfg 189 } 190 191 entry := credentialprovider.DockerConfigEntry{ 192 Username: "_token", 193 Password: parsedBlob.AccessToken, 194 Email: string(email), 195 } 196 197 // Add our entry for each of the supported container registry URLs 198 for _, k := range containerRegistryUrls { 199 cfg[k] = entry 200 } 201 return cfg 202 }