Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate AWS SDK to V2 and add AWS SSO support #92

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 60 additions & 48 deletions aws-es-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"bytes"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
Expand All @@ -19,16 +21,20 @@ import (
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
"context"

"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson/primitive"
)


var ESURL_REGEXP = regexp.MustCompile(`http(?:s?)://.+\..+\.es.amazonaws.com`)

func logger(debug bool) {

formatFilePath := func(path string) string {
Expand Down Expand Up @@ -80,14 +86,15 @@ type proxy struct {
nosignreq bool
fileRequest *os.File
fileResponse *os.File
credentials *credentials.Credentials
credentials aws.Credentials
httpClient *http.Client
auth bool
username string
password string
realm string
remoteTerminate bool
assumeRole string
sso bool
}

func newProxy(args ...interface{}) *proxy {
Expand All @@ -114,6 +121,7 @@ func newProxy(args ...interface{}) *proxy {
realm: args[9].(string),
remoteTerminate: args[10].(bool),
assumeRole: args[11].(string),
sso: args[12].(bool),
}
}

Expand Down Expand Up @@ -159,70 +167,69 @@ func (p *proxy) parseEndpoint() error {
logrus.Debugln("Endpoint split is less than 2")
}

awsEndpoints := []string{}
for _, partition := range endpoints.DefaultPartitions() {
for region := range partition.Regions() {
awsEndpoints = append(awsEndpoints, fmt.Sprintf("%s.es.%s", region, partition.DNSSuffix()))
}
}

isAWSEndpoint = false
for _, v := range awsEndpoints {
if split[1] == v {
logrus.Debugln("Provided endpoint is a valid AWS Elasticsearch endpoint")
isAWSEndpoint = true
break
}
}

isAWSEndpoint = ESURL_REGEXP.MatchString(p.endpoint)
if isAWSEndpoint {
// Extract region and service from link. This should be save now
parts := strings.Split(link.Host, ".")
p.region, p.service = parts[1], "es"
logrus.Debugln("AWS Region", p.region)
}

}

return nil
}

func (p *proxy) getSigner() *v4.Signer {
// Refresh credentials after expiration. Required for STS
if p.credentials == nil {
sess, err := session.NewSession(
&aws.Config{
Region: aws.String(p.region),
CredentialsChainVerboseErrors: aws.Bool(true),
},
)

if p.credentials == (aws.Credentials{}) {
var cfg aws.Config
var err error

if p.sso {
profile := os.Getenv("AWS_PROFILE")
logrus.Debugf("Using profile: %s", profile)
cfg, err = config.LoadDefaultConfig(
context.TODO(),
config.WithSharedConfigProfile(profile),
)
} else {
cfg, err = config.LoadDefaultConfig(context.TODO(),
config.WithRegion(p.region),
)
}
if err != nil {
logrus.Debugln(err)
logrus.Infoln(err)
}

awsRoleARN := os.Getenv("AWS_ROLE_ARN")
awsWebIdentityTokenFile := os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE")

var creds *credentials.Credentials
if awsRoleARN != "" && awsWebIdentityTokenFile != "" {
logrus.Infof("Using web identity credentials with role %s", awsRoleARN)
creds = stscreds.NewWebIdentityCredentials(sess, awsRoleARN, "", awsWebIdentityTokenFile)
credsProvider := stscreds.NewWebIdentityRoleProvider(sts.NewFromConfig(cfg), awsRoleARN, stscreds.IdentityTokenFile(awsWebIdentityTokenFile), func(o *stscreds.WebIdentityRoleOptions) {
o.RoleSessionName = ""
})
cfg.Credentials = aws.NewCredentialsCache(credsProvider)
} else if p.assumeRole != "" {
logrus.Infof("Assuming credentials from %s", p.assumeRole)
creds = stscreds.NewCredentials(sess, p.assumeRole, func(provider *stscreds.AssumeRoleProvider) {
provider.Duration = 17 * time.Minute
provider.ExpiryWindow = 13 * time.Minute
provider.MaxJitterFrac = 0.1
client := sts.NewFromConfig(cfg)
credsProvider := stscreds.NewAssumeRoleProvider(client, p.assumeRole, func(assumeRoleOptions *stscreds.AssumeRoleOptions) {
assumeRoleOptions.Duration = 17 * time.Minute
})
cfg.Credentials = aws.NewCredentialsCache(credsProvider, func(options *aws.CredentialsCacheOptions) {
options.ExpiryWindow = 13 * time.Minute
options.ExpiryWindowJitterFrac = 0.1
})
} else {
logrus.Infoln("Using default credentials")
creds = sess.Config.Credentials
}

creds, err := cfg.Credentials.Retrieve(context.Background())
if err != nil {
logrus.Fatalf("Unable to retrieve credentials %s", err)
}
p.credentials = creds
logrus.Infoln("Generated fresh AWS Credentials object")
}

return v4.NewSigner(p.credentials)
return v4.NewSigner()
}

func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -276,11 +283,13 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Start AWS session from ENV, Shared Creds or EC2Role
signer := p.getSigner()


// Sign the request with AWSv4
payload := bytes.NewReader(replaceBody(req))
_, err := signer.Sign(req, payload, p.service, p.region, time.Now())
payload := replaceBody(req)
payloadHash := sha256.Sum256(payload)
err := signer.SignHTTP(context.TODO(), p.credentials, req, hex.EncodeToString(payloadHash[:]), p.service, p.region, time.Now())
if err != nil {
p.credentials = nil
p.credentials = aws.Credentials{}
logrus.Errorln("Failed to sign", err)
http.Error(w, "Failed to sign", http.StatusForbidden)
return
Expand All @@ -298,7 +307,7 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// AWS credentials expired, need to generate fresh ones
if resp.StatusCode == 403 {
logrus.Errorln("Received 403 from AWSAuth, invalidating credentials for retrial")
p.credentials = nil
p.credentials = aws.Credentials{}

logrus.Debugln("Received Status code from AWS:", resp.StatusCode)
b := bytes.Buffer{}
Expand Down Expand Up @@ -464,6 +473,7 @@ func main() {
timeout int
remoteTerminate bool
assumeRole string
sso bool
)

flag.StringVar(&endpoint, "endpoint", "", "Amazon ElasticSearch Endpoint (e.g: https://dummy-host.eu-west-1.es.amazonaws.com)")
Expand All @@ -481,6 +491,7 @@ func main() {
flag.StringVar(&realm, "realm", "", "Authentication Required")
flag.BoolVar(&remoteTerminate, "remote-terminate", false, "Allow HTTP remote termination")
flag.StringVar(&assumeRole, "assume", "", "Optionally specify role to assume")
flag.BoolVar(&sso, "sso", false, "Use AWS SSO for auth")
flag.Parse()

if endpoint == "" {
Expand Down Expand Up @@ -528,6 +539,7 @@ func main() {
realm,
remoteTerminate,
assumeRole,
sso,
)

if err = p.parseEndpoint(); err != nil {
Expand Down
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ module github.com/abutaha/aws-es-proxy
go 1.14

require (
github.com/aws/aws-sdk-go v1.30.4
github.com/aws/aws-sdk-go-v2 v1.5.0
github.com/aws/aws-sdk-go-v2/config v1.2.0
github.com/aws/aws-sdk-go-v2/credentials v1.2.0
github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.3.0
github.com/aws/aws-sdk-go-v2/service/sts v1.4.0
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.5.1 // indirect
go.mongodb.org/mongo-driver v1.3.1
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
36 changes: 28 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aws/aws-sdk-go v1.30.4 h1:dpQgypC3rld2Uuz+/2u+0nbfmmyEWxau6v1hdAlvoc8=
github.com/aws/aws-sdk-go v1.30.4/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go-v2 v1.5.0 h1:o0TprBiEkqtMGD2Ira1VCq3zwWej256zHLSRcd+cxUA=
github.com/aws/aws-sdk-go-v2 v1.5.0/go.mod h1:tI4KhsR5VkzlUa2DZAdwx7wCAYGwkZZ1H31PYrBFx1w=
github.com/aws/aws-sdk-go-v2/config v1.2.0 h1:3JVWs+ilru3/5Zq6KbuQj8aqp7DW+Uw/wv6gCYZ2UnI=
github.com/aws/aws-sdk-go-v2/config v1.2.0/go.mod h1:JgPbg7YzzczkGu1Zi0hHVKYXVzx4OTKnNSD+h+qlpLw=
github.com/aws/aws-sdk-go-v2/credentials v1.2.0 h1:NxD//04/Y4nid+Slj8JjouisY/DAYjjXW4lqWNkBaO8=
github.com/aws/aws-sdk-go-v2/credentials v1.2.0/go.mod h1:3Xxgc7WsldLnLnPSRcNOT5eVRgb55Kkgp8mE5kAmLrU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.1.0 h1:EVNLR3OULDnvp92ISADQwZwVsdz7dasl1MCneUVJQnQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.1.0/go.mod h1:GOKx1449nzMoUdTKrP41RsPn1hogOaxb+5MaoOiZgqc=
github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.3.0 h1:2ImoK9L81UfpeN2fEI/NrdqTp5Z3WL1FPtS1oiX1hc0=
github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.3.0/go.mod h1:N80Lc+oYpuUcWiQ3EhhAuqzEXn5DVj7uEGWWvfDX+D8=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.1.0 h1:i+cyzgQbk02N3pbwBTwjOChoDuIxsGdE6ucD2ZLVnJQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.1.0/go.mod h1:mruB7K2oMCoU0WhUeTV1CxpqoP7Q0N1uo5TXH4r2dZA=
github.com/aws/aws-sdk-go-v2/service/sso v1.2.0 h1:6cTVa8anc914VgJzca8Jd0ewA7Y5fbEFheXqidum0tg=
github.com/aws/aws-sdk-go-v2/service/sso v1.2.0/go.mod h1:5qnaL4AtNElFr+a5mdkvD+89jGwpTVyWWX5W/eLzmes=
github.com/aws/aws-sdk-go-v2/service/sts v1.4.0 h1:pcnLXm4eMWUgqfbjsRyeSz90CKunJUmre+trKJx/FAk=
github.com/aws/aws-sdk-go-v2/service/sts v1.4.0/go.mod h1:VJE1MpZYuhpWfOVmz7xFou78H5uJc7RX+8CKpbRaG9k=
github.com/aws/smithy-go v1.4.0 h1:3rsQpgRe+OoQgJhEwGNpIkosl0fJLdmQqF4gSFRjg+4=
github.com/aws/smithy-go v1.4.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
Expand Down Expand Up @@ -33,9 +48,13 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
Expand All @@ -55,7 +74,6 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down Expand Up @@ -84,8 +102,6 @@ golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaE
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -107,10 +123,14 @@ golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=