mirror of https://github.com/docker/docs.git
232 lines
6.0 KiB
Go
232 lines
6.0 KiB
Go
// Package awsauth implements AWS request signing using Signed Signature Version 2,
|
|
// Signed Signature Version 3, and Signed Signature Version 4. Supports S3 and STS.
|
|
package awsauth
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Credentials stores the information necessary to authorize with AWS and it
|
|
// is from this information that requests are signed.
|
|
type Credentials struct {
|
|
AccessKeyID string
|
|
SecretAccessKey string
|
|
SecurityToken string `json:"Token"`
|
|
Expiration time.Time
|
|
}
|
|
|
|
// Sign signs a request bound for AWS. It automatically chooses the best
|
|
// authentication scheme based on the service the request is going to.
|
|
func Sign(req *http.Request, cred ...Credentials) *http.Request {
|
|
service, _ := serviceAndRegion(req.URL.Host)
|
|
sigVersion := awsSignVersion[service]
|
|
|
|
switch sigVersion {
|
|
case 2:
|
|
return Sign2(req, cred...)
|
|
case 3:
|
|
return Sign3(req, cred...)
|
|
case 4:
|
|
return Sign4(req, cred...)
|
|
case -1:
|
|
return SignS3(req, cred...)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Sign4 signs a request with Signed Signature Version 4.
|
|
func Sign4(req *http.Request, cred ...Credentials) *http.Request {
|
|
signMutex.Lock()
|
|
defer signMutex.Unlock()
|
|
keys := chooseKeys(cred)
|
|
|
|
// Add the X-Amz-Security-Token header when using STS
|
|
if keys.SecurityToken != "" {
|
|
req.Header.Set("X-Amz-Security-Token", keys.SecurityToken)
|
|
}
|
|
|
|
prepareRequestV4(req)
|
|
meta := new(metadata)
|
|
|
|
// Task 1
|
|
hashedCanonReq := hashedCanonicalRequestV4(req, meta)
|
|
|
|
// Task 2
|
|
stringToSign := stringToSignV4(req, hashedCanonReq, meta)
|
|
|
|
// Task 3
|
|
signingKey := signingKeyV4(keys.SecretAccessKey, meta.date, meta.region, meta.service)
|
|
signature := signatureV4(signingKey, stringToSign)
|
|
|
|
req.Header.Set("Authorization", buildAuthHeaderV4(signature, meta, keys))
|
|
|
|
return req
|
|
}
|
|
|
|
// Sign3 signs a request with Signed Signature Version 3.
|
|
// If the service you're accessing supports Version 4, use that instead.
|
|
func Sign3(req *http.Request, cred ...Credentials) *http.Request {
|
|
signMutex.Lock()
|
|
defer signMutex.Unlock()
|
|
keys := chooseKeys(cred)
|
|
|
|
// Add the X-Amz-Security-Token header when using STS
|
|
if keys.SecurityToken != "" {
|
|
req.Header.Set("X-Amz-Security-Token", keys.SecurityToken)
|
|
}
|
|
|
|
prepareRequestV3(req)
|
|
|
|
// Task 1
|
|
stringToSign := stringToSignV3(req)
|
|
|
|
// Task 2
|
|
signature := signatureV3(stringToSign, keys)
|
|
|
|
// Task 3
|
|
req.Header.Set("X-Amzn-Authorization", buildAuthHeaderV3(signature, keys))
|
|
|
|
return req
|
|
}
|
|
|
|
// Sign2 signs a request with Signed Signature Version 2.
|
|
// If the service you're accessing supports Version 4, use that instead.
|
|
func Sign2(req *http.Request, cred ...Credentials) *http.Request {
|
|
signMutex.Lock()
|
|
defer signMutex.Unlock()
|
|
keys := chooseKeys(cred)
|
|
|
|
// Add the SecurityToken parameter when using STS
|
|
// This must be added before the signature is calculated
|
|
if keys.SecurityToken != "" {
|
|
v := url.Values{}
|
|
v.Set("SecurityToken", keys.SecurityToken)
|
|
augmentRequestQuery(req, v)
|
|
|
|
}
|
|
|
|
prepareRequestV2(req, keys)
|
|
|
|
stringToSign := stringToSignV2(req)
|
|
signature := signatureV2(stringToSign, keys)
|
|
|
|
values := url.Values{}
|
|
values.Set("Signature", signature)
|
|
|
|
augmentRequestQuery(req, values)
|
|
|
|
return req
|
|
}
|
|
|
|
// SignS3 signs a request bound for Amazon S3 using their custom
|
|
// HTTP authentication scheme.
|
|
func SignS3(req *http.Request, cred ...Credentials) *http.Request {
|
|
signMutex.Lock()
|
|
defer signMutex.Unlock()
|
|
keys := chooseKeys(cred)
|
|
|
|
// Add the X-Amz-Security-Token header when using STS
|
|
if keys.SecurityToken != "" {
|
|
req.Header.Set("X-Amz-Security-Token", keys.SecurityToken)
|
|
}
|
|
|
|
prepareRequestS3(req)
|
|
|
|
stringToSign := stringToSignS3(req)
|
|
signature := signatureS3(stringToSign, keys)
|
|
|
|
authHeader := "AWS " + keys.AccessKeyID + ":" + signature
|
|
req.Header.Set("Authorization", authHeader)
|
|
|
|
return req
|
|
}
|
|
|
|
// SignS3Url signs a GET request for a resource on Amazon S3 by appending
|
|
// query string parameters containing credentials and signature. You must
|
|
// specify an expiration date for these signed requests. After that date,
|
|
// a request signed with this method will be rejected by S3.
|
|
func SignS3Url(req *http.Request, expire time.Time, cred ...Credentials) *http.Request {
|
|
signMutex.Lock()
|
|
defer signMutex.Unlock()
|
|
keys := chooseKeys(cred)
|
|
|
|
stringToSign := stringToSignS3Url("GET", expire, req.URL.Path)
|
|
signature := signatureS3(stringToSign, keys)
|
|
|
|
qs := req.URL.Query()
|
|
qs.Set("AWSAccessKeyId", keys.AccessKeyID)
|
|
qs.Set("Signature", signature)
|
|
qs.Set("Expires", timeToUnixEpochString(expire))
|
|
req.URL.RawQuery = qs.Encode()
|
|
|
|
return req
|
|
}
|
|
|
|
// expired checks to see if the temporary credentials from an IAM role are
|
|
// within 4 minutes of expiration (The IAM documentation says that new keys
|
|
// will be provisioned 5 minutes before the old keys expire). Credentials
|
|
// that do not have an Expiration cannot expire.
|
|
func (k *Credentials) expired() bool {
|
|
if k.Expiration.IsZero() {
|
|
// Credentials with no expiration can't expire
|
|
return false
|
|
}
|
|
expireTime := k.Expiration.Add(-4 * time.Minute)
|
|
// if t - 4 mins is before now, true
|
|
if expireTime.Before(time.Now()) {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
type metadata struct {
|
|
algorithm string
|
|
credentialScope string
|
|
signedHeaders string
|
|
date string
|
|
region string
|
|
service string
|
|
}
|
|
|
|
const (
|
|
envAccessKeyID = "AWS_ACCESS_KEY_ID"
|
|
envSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
|
|
envSecurityToken = "AWS_SECURITY_TOKEN"
|
|
)
|
|
|
|
var (
|
|
awsSignVersion = map[string]int{
|
|
"autoscaling": 4,
|
|
"cloudfront": 4,
|
|
"cloudformation": 4,
|
|
"cloudsearch": 4,
|
|
"monitoring": 4,
|
|
"dynamodb": 4,
|
|
"ec2": 2,
|
|
"elasticmapreduce": 4,
|
|
"elastictranscoder": 4,
|
|
"elasticache": 2,
|
|
"glacier": 4,
|
|
"kinesis": 4,
|
|
"redshift": 4,
|
|
"rds": 4,
|
|
"sdb": 2,
|
|
"sns": 4,
|
|
"sqs": 4,
|
|
"s3": 4,
|
|
"elasticbeanstalk": 4,
|
|
"importexport": 2,
|
|
"iam": 4,
|
|
"route53": 3,
|
|
"elasticloadbalancing": 4,
|
|
"email": 3,
|
|
}
|
|
|
|
signMutex sync.Mutex
|
|
)
|