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  }