github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/config/auth.go (about) 1 // Copyright 2015 The rkt Authors 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 config 16 17 import ( 18 "encoding/base64" 19 "encoding/json" 20 "fmt" 21 "github.com/aws/aws-sdk-go/aws" 22 "github.com/aws/aws-sdk-go/aws/client/metadata" 23 "github.com/aws/aws-sdk-go/aws/credentials" 24 "github.com/aws/aws-sdk-go/aws/request" 25 "github.com/aws/aws-sdk-go/private/signer/v4" 26 "io" 27 "net/http" 28 "strings" 29 "time" 30 ) 31 32 const ( 33 authHeader string = "Authorization" 34 defaultAWSRegion string = "us-east-1" 35 awsS3Service string = "s3" 36 ) 37 38 type authV1JsonParser struct{} 39 40 type authV1 struct { 41 Domains []string `json:"domains"` 42 Type string `json:"type"` 43 Credentials json.RawMessage `json:"credentials"` 44 } 45 46 type basicV1 struct { 47 User string `json:"user"` 48 Password string `json:"password"` 49 } 50 51 type oauthV1 struct { 52 Token string `json:"token"` 53 } 54 55 type awsV1 struct { 56 AccessKeyID string `json:"accessKeyID"` 57 SecretAccessKey string `json:"secretAccessKey"` 58 Region string `json:"awsRegion"` 59 } 60 61 type dockerAuthV1JsonParser struct{} 62 63 type dockerAuthV1 struct { 64 Registries []string `json:"registries"` 65 Credentials basicV1 `json:"credentials"` 66 } 67 68 func init() { 69 addParser("auth", "v1", &authV1JsonParser{}) 70 addParser("dockerAuth", "v1", &dockerAuthV1JsonParser{}) 71 registerSubDir("auth.d", []string{"auth", "dockerAuth"}) 72 } 73 74 type basicAuthHeaderer struct { 75 auth basicV1 76 } 77 78 func (h *basicAuthHeaderer) GetHeader() http.Header { 79 headers := make(http.Header) 80 creds := []byte(fmt.Sprintf("%s:%s", h.auth.User, h.auth.Password)) 81 encodedCreds := base64.StdEncoding.EncodeToString(creds) 82 headers.Add(authHeader, "Basic "+encodedCreds) 83 84 return headers 85 } 86 87 func (h *basicAuthHeaderer) SignRequest(r *http.Request) *http.Request { 88 r.Header.Set(authHeader, h.GetHeader().Get(authHeader)) 89 90 return r 91 } 92 93 type oAuthBearerTokenHeaderer struct { 94 auth oauthV1 95 } 96 97 func (h *oAuthBearerTokenHeaderer) GetHeader() http.Header { 98 headers := make(http.Header) 99 headers.Add(authHeader, "Bearer "+h.auth.Token) 100 101 return headers 102 } 103 104 func (h *oAuthBearerTokenHeaderer) SignRequest(r *http.Request) *http.Request { 105 r.Header.Set(authHeader, h.GetHeader().Get(authHeader)) 106 107 return r 108 } 109 110 type awsAuthHeaderer struct { 111 auth awsV1 112 } 113 114 func (h *awsAuthHeaderer) GetHeader() http.Header { 115 return make(http.Header) 116 } 117 118 func (h *awsAuthHeaderer) SignRequest(r *http.Request) *http.Request { 119 region := h.auth.Region 120 121 var body io.ReadSeeker 122 if r.Body != nil { 123 body = r.Body.(io.ReadSeeker) 124 } 125 126 if len(region) == 0 { 127 region = guessAWSRegion(r.URL.Host) 128 } 129 v4.Sign(&request.Request{ 130 ClientInfo: metadata.ClientInfo{ 131 SigningRegion: region, 132 SigningName: awsS3Service, 133 }, 134 Config: aws.Config{ 135 Credentials: credentials.NewStaticCredentials(h.auth.AccessKeyID, h.auth.SecretAccessKey, ""), 136 }, 137 HTTPRequest: r, 138 Body: body, 139 Time: time.Now(), 140 }) 141 142 return r 143 } 144 145 func guessAWSRegion(host string) string { 146 // Separate out potential :<port> 147 hostParts := strings.Split(host, ":") 148 host = strings.ToLower(hostParts[0]) 149 150 parts := strings.Split(host, ".") 151 152 if len(parts) < 3 || parts[len(parts)-2] != "amazonaws" { 153 return defaultAWSRegion 154 } 155 156 // Try to guess region based on url, but if nothing 157 // matches, just fall back to defaultAWSRegion. 158 if strings.HasSuffix(host, "s3.amazonaws.com") || strings.HasSuffix(host, "s3-external-1.amazonaws.com") { 159 return "us-east-1" 160 } else if len(parts) > 3 && strings.HasPrefix(parts[len(parts)-3], "s3-") { 161 return parts[len(parts)-3][3:] 162 } else if len(parts) > 4 && parts[len(parts)-4] == "s3" { 163 return parts[len(parts)-3] 164 } else { 165 return defaultAWSRegion 166 } 167 } 168 169 func (p *authV1JsonParser) parse(config *Config, raw []byte) error { 170 var auth authV1 171 if err := json.Unmarshal(raw, &auth); err != nil { 172 return err 173 } 174 if len(auth.Domains) == 0 { 175 return fmt.Errorf("no domains specified") 176 } 177 if len(auth.Type) == 0 { 178 return fmt.Errorf("no auth type specified") 179 } 180 var ( 181 err error 182 headerer Headerer 183 ) 184 switch auth.Type { 185 case "basic": 186 headerer, err = p.getBasicV1Headerer(auth.Credentials) 187 case "oauth": 188 headerer, err = p.getOAuthV1Headerer(auth.Credentials) 189 case "aws": 190 headerer, err = p.getAWSV1Headerer(auth.Credentials) 191 default: 192 err = fmt.Errorf("unknown auth type: %q", auth.Type) 193 } 194 if err != nil { 195 return err 196 } 197 for _, domain := range auth.Domains { 198 if _, ok := config.AuthPerHost[domain]; ok { 199 return fmt.Errorf("auth for domain %q is already specified", domain) 200 } 201 config.AuthPerHost[domain] = headerer 202 } 203 return nil 204 } 205 206 func (p *authV1JsonParser) getBasicV1Headerer(raw json.RawMessage) (Headerer, error) { 207 var basic basicV1 208 if err := json.Unmarshal(raw, &basic); err != nil { 209 return nil, err 210 } 211 if err := validateBasicV1(&basic); err != nil { 212 return nil, err 213 } 214 return &basicAuthHeaderer{ 215 auth: basic, 216 }, nil 217 } 218 219 func (p *authV1JsonParser) getOAuthV1Headerer(raw json.RawMessage) (Headerer, error) { 220 var oauth oauthV1 221 if err := json.Unmarshal(raw, &oauth); err != nil { 222 return nil, err 223 } 224 if len(oauth.Token) == 0 { 225 return nil, fmt.Errorf("no oauth bearer token specified") 226 } 227 return &oAuthBearerTokenHeaderer{ 228 auth: oauth, 229 }, nil 230 } 231 232 func (p *authV1JsonParser) getAWSV1Headerer(raw json.RawMessage) (Headerer, error) { 233 var aws awsV1 234 if err := json.Unmarshal(raw, &aws); err != nil { 235 return nil, err 236 } 237 if len(aws.AccessKeyID) == 0 { 238 return nil, fmt.Errorf("no AWS Access Key ID specified") 239 } 240 if len(aws.SecretAccessKey) == 0 { 241 return nil, fmt.Errorf("no AWS Secret Access Key specified") 242 } 243 return &awsAuthHeaderer{ 244 auth: aws, 245 }, nil 246 } 247 248 func (p *dockerAuthV1JsonParser) parse(config *Config, raw []byte) error { 249 var auth dockerAuthV1 250 if err := json.Unmarshal(raw, &auth); err != nil { 251 return err 252 } 253 if len(auth.Registries) == 0 { 254 return fmt.Errorf("no registries specified") 255 } 256 if err := validateBasicV1(&auth.Credentials); err != nil { 257 return err 258 } 259 basic := BasicCredentials{ 260 User: auth.Credentials.User, 261 Password: auth.Credentials.Password, 262 } 263 for _, registry := range auth.Registries { 264 if _, ok := config.DockerCredentialsPerRegistry[registry]; ok { 265 return fmt.Errorf("credentials for docker registry %q are already specified", registry) 266 } 267 config.DockerCredentialsPerRegistry[registry] = basic 268 } 269 return nil 270 } 271 272 func validateBasicV1(basic *basicV1) error { 273 if basic == nil { 274 return fmt.Errorf("no credentials") 275 } 276 if len(basic.User) == 0 { 277 return fmt.Errorf("user not specified") 278 } 279 if len(basic.Password) == 0 { 280 return fmt.Errorf("password not specified") 281 } 282 return nil 283 }