From 255a2fab223ea317181288a4c887d057572ace47 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Thu, 20 May 2021 14:32:52 -0700 Subject: [PATCH 1/8] Add AWS Marketplace Entitlement verification --- cmd/nginx-ingress/aws.go | 108 +++++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ go.sum | 21 ++++++++ 3 files changed, 132 insertions(+) create mode 100644 cmd/nginx-ingress/aws.go diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go new file mode 100644 index 0000000000..6ae2b63972 --- /dev/null +++ b/cmd/nginx-ingress/aws.go @@ -0,0 +1,108 @@ +// +build aws + +package main + +import ( + "context" + "encoding/base64" + "errors" + "log" + "math/rand" + + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/marketplacemetering" + "github.com/aws/aws-sdk-go-v2/service/marketplacemetering/types" + + "github.com/dgrijalva/jwt-go/v4" +) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-" + +var ( + productCode string + pubKeyVersion int32 = 1 + pubKeyString string + nonce string +) + +func init() { + rand.Seed(jwt.Now().UnixNano()) + nonce = RandStringBytesRmndr(32) + + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Fatalf("Error loading AWS configuration: %v", err) + } + + mpm := marketplacemetering.New(marketplacemetering.Options{Region: cfg.Region, Credentials: cfg.Credentials}) + + var notEnt *types.CustomerNotEntitledException + var invalidRegion *types.InvalidRegionException + + out, err := mpm.RegisterUsage(context.TODO(), &marketplacemetering.RegisterUsageInput{ProductCode: &productCode, PublicKeyVersion: &pubKeyVersion, Nonce: &nonce}) + if err != nil { + if errors.As(err, ¬Ent) { + log.Fatalf("User not entitled: %v", err) + } else if errors.As(err, &invalidRegion) { + log.Fatalf("Invalid region: %v", err) + } + log.Fatal(err) + + } + + pk, err := base64.StdEncoding.DecodeString(pubKeyString) + if err != nil { + log.Fatal(err) + } + pubKey, err := jwt.ParseRSAPublicKeyFromPEM(pk) + if err != nil { + log.Fatal(err) + } + + token, err := jwt.ParseWithClaims(*out.Signature, &Claims{}, jwt.KnownKeyfunc(jwt.SigningMethodPS256, pubKey)) + if err != nil { + log.Fatal(err) + } + + if claims, ok := token.Claims.(*Claims); ok && token.Valid { + if claims.ProductCode == productCode && claims.PublicKeyVersion == pubKeyVersion && claims.Nonce == nonce { + log.Println("AWS verification successful") + } else { + log.Fatal("The claims in the JWT token don't match the request") + } + } else { + log.Fatal("Something is wrong with the JWT token") + } +} + +type Claims struct { + ProductCode string `json:"productCode,omitempty"` + PublicKeyVersion int32 `json:"publicKeyVersion,omitempty"` + IssuedAt *jwt.Time `json:"iat,omitempty"` + Nonce string `json:"nonce,omitempty"` +} + +func (c Claims) Valid(h *jwt.ValidationHelper) error { + if c.Nonce == "" { + return &jwt.InvalidClaimsError{Message: "The JWT token doesn't include the Nonce"} + } + if c.ProductCode == "" { + return &jwt.InvalidClaimsError{Message: "The JWT token doesn't include the ProductCode"} + } + if c.PublicKeyVersion == 0 { + return &jwt.InvalidClaimsError{Message: "The JWT token doesn't include the PublicKeyVersion"} + } + if h.Before(c.IssuedAt.Time) { + return &jwt.InvalidClaimsError{Message: "The JWT token has a wrong creation time"} + } + + return nil +} + +func RandStringBytesRmndr(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))] + } + return string(b) +} diff --git a/go.mod b/go.mod index 5ca7f4d0f3..737adfa9ca 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,9 @@ module github.com/nginxinc/kubernetes-ingress go 1.16 require ( + github.com/aws/aws-sdk-go-v2/config v1.2.0 + github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.0 + github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 github.com/emicklei/go-restful v2.15.0+incompatible // indirect github.com/go-openapi/spec v0.20.3 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b diff --git a/go.sum b/go.sum index 6fc13597a9..a3e8654941 100644 --- a/go.sum +++ b/go.sum @@ -100,6 +100,24 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +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/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/marketplacemetering v1.3.0 h1:Ab+EXeViBmZfegbNjQdLBm44vTLC2UXRoa9GFbLoMaM= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.0/go.mod h1:v763uumOE+WCzS9uhz+ivcZgHQQRb4nQbjl0ytPsV38= +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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -153,7 +171,10 @@ 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/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU= +github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= From e93855b845aff17edcdf3c0a91f48e4b4c7600d5 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Fri, 21 May 2021 12:24:24 -0700 Subject: [PATCH 2/8] PR feedback --- cmd/nginx-ingress/aws.go | 8 ++------ go.mod | 4 ++-- go.sum | 34 ++++++++++++++++++---------------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index 6ae2b63972..f34fc5674f 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -36,15 +36,11 @@ func init() { mpm := marketplacemetering.New(marketplacemetering.Options{Region: cfg.Region, Credentials: cfg.Credentials}) - var notEnt *types.CustomerNotEntitledException - var invalidRegion *types.InvalidRegionException - out, err := mpm.RegisterUsage(context.TODO(), &marketplacemetering.RegisterUsageInput{ProductCode: &productCode, PublicKeyVersion: &pubKeyVersion, Nonce: &nonce}) if err != nil { + var notEnt *types.CustomerNotEntitledException if errors.As(err, ¬Ent) { - log.Fatalf("User not entitled: %v", err) - } else if errors.As(err, &invalidRegion) { - log.Fatalf("Invalid region: %v", err) + log.Fatalf("User not entitled, code: %v, message: %v, fault: %v", notEnt.ErrorCode(), notEnt.ErrorMessage(), notEnt.ErrorFault().String()) } log.Fatal(err) diff --git a/go.mod b/go.mod index 737adfa9ca..15e66ba761 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/nginxinc/kubernetes-ingress go 1.16 require ( - github.com/aws/aws-sdk-go-v2/config v1.2.0 - github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.0 + github.com/aws/aws-sdk-go-v2/config v1.3.0 + github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.1 github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 github.com/emicklei/go-restful v2.15.0+incompatible // indirect github.com/go-openapi/spec v0.20.3 // indirect diff --git a/go.sum b/go.sum index a3e8654941..7bb91a2ac1 100644 --- a/go.sum +++ b/go.sum @@ -100,22 +100,24 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -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/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/marketplacemetering v1.3.0 h1:Ab+EXeViBmZfegbNjQdLBm44vTLC2UXRoa9GFbLoMaM= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.0/go.mod h1:v763uumOE+WCzS9uhz+ivcZgHQQRb4nQbjl0ytPsV38= -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/aws-sdk-go-v2 v1.6.0 h1:r20hdhm8wZmKkClREfacXrKfX0Y7/s0aOoeraFbf/sY= +github.com/aws/aws-sdk-go-v2 v1.6.0/go.mod h1:tI4KhsR5VkzlUa2DZAdwx7wCAYGwkZZ1H31PYrBFx1w= +github.com/aws/aws-sdk-go-v2/config v1.3.0 h1:0JAnp0WcsgKilFLiZEScUTKIvTKa2LkicadZADza+u0= +github.com/aws/aws-sdk-go-v2/config v1.3.0/go.mod h1:lOxzHWDt/k7MMidA/K8DgXL4+ynnZYsDq65Qhs/l3dg= +github.com/aws/aws-sdk-go-v2/credentials v1.2.1 h1:AqQ8PzWll1wegNUOfIKcbp/JspTbJl54gNonrO6VUsY= +github.com/aws/aws-sdk-go-v2/credentials v1.2.1/go.mod h1:Rfvim1eZTC9W5s8YJyYYtl1KMk6e8fHv+wMRQGO4Ru0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.1.1 h1:w1ocBIhQkLgupEB3d0uOuBddqVYl0xpubz7HSTzWG8A= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.1.1/go.mod h1:GTXAhrxHQOj9N+J5tYVjwt+rpRyy/42qLjlgw9pz1a0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.0.0 h1:k7I9E6tyVWBo7H9ffpnxDWudtjau6Qt9rnOYgV+ciEQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.0.0/go.mod h1:g3XMXuxvqSMUjnsXXp/960152w0wFS4CXVYgQaSVOHE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.1.1 h1:l7pDLsmOGrnR8LT+3gIv8NlHpUhs7220E457KEC2UM0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.1.1/go.mod h1:2+ehJPkdIdl46VCj67Emz/EH2hpebHZtaLdzqg+sWOI= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.1 h1:0rWDX2ToWh38xFUWAM+Mz2jcPfSXELfVYaaR0mYfnz4= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.3.1/go.mod h1:K0qzQa0qdpOqiA2tpL9XbSZXKnE95GJSHJj9YLsf7Zs= +github.com/aws/aws-sdk-go-v2/service/sso v1.2.1 h1:alpXc5UG7al7QnttHe/9hfvUfitV8r3w0onPpPkGzi0= +github.com/aws/aws-sdk-go-v2/service/sso v1.2.1/go.mod h1:VimPFPltQ/920i1X0Sb0VJBROLIHkDg2MNP10D46OGs= +github.com/aws/aws-sdk-go-v2/service/sts v1.4.1 h1:9Z00tExoaLutWVDmY6LyvIAcKjHetkbdmpRt4JN/FN0= +github.com/aws/aws-sdk-go-v2/service/sts v1.4.1/go.mod h1:G9osDWA52WQ38BDcj65VY1cNmcAQXAXTsE8IWH8j81w= 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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= From e1f60f01ed116c419c0543445a1f19560bc16039 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Fri, 21 May 2021 13:40:13 -0700 Subject: [PATCH 3/8] Use more secure random string, update errors --- cmd/nginx-ingress/aws.go | 49 +++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index f34fc5674f..99ab66dcf8 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -4,10 +4,11 @@ package main import ( "context" + "crypto/rand" "encoding/base64" "errors" "log" - "math/rand" + "math/big" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/marketplacemetering" @@ -16,8 +17,6 @@ import ( "github.com/dgrijalva/jwt-go/v4" ) -const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-" - var ( productCode string pubKeyVersion int32 = 1 @@ -26,12 +25,14 @@ var ( ) func init() { - rand.Seed(jwt.Now().UnixNano()) - nonce = RandStringBytesRmndr(32) + nonce, err := generateRandomString(255) + if err != nil { + log.Fatal(err) + } cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { - log.Fatalf("Error loading AWS configuration: %v", err) + log.Fatalf("error loading AWS configuration: %v", err) } mpm := marketplacemetering.New(marketplacemetering.Options{Region: cfg.Region, Credentials: cfg.Credentials}) @@ -40,7 +41,7 @@ func init() { if err != nil { var notEnt *types.CustomerNotEntitledException if errors.As(err, ¬Ent) { - log.Fatalf("User not entitled, code: %v, message: %v, fault: %v", notEnt.ErrorCode(), notEnt.ErrorMessage(), notEnt.ErrorFault().String()) + log.Fatalf("user not entitled, code: %v, message: %v, fault: %v", notEnt.ErrorCode(), notEnt.ErrorMessage(), notEnt.ErrorFault().String()) } log.Fatal(err) @@ -48,26 +49,26 @@ func init() { pk, err := base64.StdEncoding.DecodeString(pubKeyString) if err != nil { - log.Fatal(err) + log.Fatalf("error decoding Public Key string: %v", err) } pubKey, err := jwt.ParseRSAPublicKeyFromPEM(pk) if err != nil { - log.Fatal(err) + log.Fatalf("error parsing Public Key: %v", err) } token, err := jwt.ParseWithClaims(*out.Signature, &Claims{}, jwt.KnownKeyfunc(jwt.SigningMethodPS256, pubKey)) if err != nil { - log.Fatal(err) + log.Fatalf("error parsing the JWT token: %v", err) } if claims, ok := token.Claims.(*Claims); ok && token.Valid { if claims.ProductCode == productCode && claims.PublicKeyVersion == pubKeyVersion && claims.Nonce == nonce { log.Println("AWS verification successful") } else { - log.Fatal("The claims in the JWT token don't match the request") + log.Fatal("the claims in the JWT token don't match the request") } } else { - log.Fatal("Something is wrong with the JWT token") + log.Fatal("something is wrong with the JWT token") } } @@ -80,25 +81,31 @@ type Claims struct { func (c Claims) Valid(h *jwt.ValidationHelper) error { if c.Nonce == "" { - return &jwt.InvalidClaimsError{Message: "The JWT token doesn't include the Nonce"} + return &jwt.InvalidClaimsError{Message: "the JWT token doesn't include the Nonce"} } if c.ProductCode == "" { - return &jwt.InvalidClaimsError{Message: "The JWT token doesn't include the ProductCode"} + return &jwt.InvalidClaimsError{Message: "the JWT token doesn't include the ProductCode"} } if c.PublicKeyVersion == 0 { - return &jwt.InvalidClaimsError{Message: "The JWT token doesn't include the PublicKeyVersion"} + return &jwt.InvalidClaimsError{Message: "the JWT token doesn't include the PublicKeyVersion"} } if h.Before(c.IssuedAt.Time) { - return &jwt.InvalidClaimsError{Message: "The JWT token has a wrong creation time"} + return &jwt.InvalidClaimsError{Message: "the JWT token has a wrong creation time"} } return nil } -func RandStringBytesRmndr(n int) string { - b := make([]byte, n) - for i := range b { - b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))] +func generateRandomString(n int) (string, error) { + const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + ret := make([]byte, n) + for i := 0; i < n; i++ { + num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) + if err != nil { + return "", err + } + ret[i] = letters[num.Int64()] } - return string(b) + + return string(ret), nil } From 386bc5ca2057a20744c65a70ab28be13ba5bc9a0 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Tue, 25 May 2021 11:58:40 -0700 Subject: [PATCH 4/8] Update validate time function --- cmd/nginx-ingress/aws.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index 99ab66dcf8..a969fbecb5 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -89,8 +89,8 @@ func (c Claims) Valid(h *jwt.ValidationHelper) error { if c.PublicKeyVersion == 0 { return &jwt.InvalidClaimsError{Message: "the JWT token doesn't include the PublicKeyVersion"} } - if h.Before(c.IssuedAt.Time) { - return &jwt.InvalidClaimsError{Message: "the JWT token has a wrong creation time"} + if err := h.ValidateNotBefore(c.IssuedAt); err != nil { + return err } return nil From 6a712801efa40fab68eb9c7acec23fed8ac09da4 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Tue, 25 May 2021 12:02:38 -0700 Subject: [PATCH 5/8] unexport claims --- cmd/nginx-ingress/aws.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index a969fbecb5..ac92514dc2 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -56,12 +56,12 @@ func init() { log.Fatalf("error parsing Public Key: %v", err) } - token, err := jwt.ParseWithClaims(*out.Signature, &Claims{}, jwt.KnownKeyfunc(jwt.SigningMethodPS256, pubKey)) + token, err := jwt.ParseWithClaims(*out.Signature, &claims{}, jwt.KnownKeyfunc(jwt.SigningMethodPS256, pubKey)) if err != nil { log.Fatalf("error parsing the JWT token: %v", err) } - if claims, ok := token.Claims.(*Claims); ok && token.Valid { + if claims, ok := token.Claims.(*claims); ok && token.Valid { if claims.ProductCode == productCode && claims.PublicKeyVersion == pubKeyVersion && claims.Nonce == nonce { log.Println("AWS verification successful") } else { @@ -72,14 +72,14 @@ func init() { } } -type Claims struct { +type claims struct { ProductCode string `json:"productCode,omitempty"` PublicKeyVersion int32 `json:"publicKeyVersion,omitempty"` IssuedAt *jwt.Time `json:"iat,omitempty"` Nonce string `json:"nonce,omitempty"` } -func (c Claims) Valid(h *jwt.ValidationHelper) error { +func (c claims) Valid(h *jwt.ValidationHelper) error { if c.Nonce == "" { return &jwt.InvalidClaimsError{Message: "the JWT token doesn't include the Nonce"} } From aa47412e1f599667b2d57957b21f35bf9758e652 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Tue, 25 May 2021 12:40:34 -0700 Subject: [PATCH 6/8] add timeout --- cmd/nginx-ingress/aws.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index ac92514dc2..ba51a7e561 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -9,6 +9,7 @@ import ( "errors" "log" "math/big" + "time" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/marketplacemetering" @@ -25,19 +26,22 @@ var ( ) func init() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + nonce, err := generateRandomString(255) if err != nil { log.Fatal(err) } - cfg, err := config.LoadDefaultConfig(context.TODO()) + cfg, err := config.LoadDefaultConfig(ctx) if err != nil { log.Fatalf("error loading AWS configuration: %v", err) } mpm := marketplacemetering.New(marketplacemetering.Options{Region: cfg.Region, Credentials: cfg.Credentials}) - out, err := mpm.RegisterUsage(context.TODO(), &marketplacemetering.RegisterUsageInput{ProductCode: &productCode, PublicKeyVersion: &pubKeyVersion, Nonce: &nonce}) + out, err := mpm.RegisterUsage(ctx, &marketplacemetering.RegisterUsageInput{ProductCode: &productCode, PublicKeyVersion: &pubKeyVersion, Nonce: &nonce}) if err != nil { var notEnt *types.CustomerNotEntitledException if errors.As(err, ¬Ent) { From 3d4c3e1e87234f5a2e1fa5a94f0cceb8ce35ca39 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Wed, 26 May 2021 15:17:39 -0700 Subject: [PATCH 7/8] Remove log on success --- cmd/nginx-ingress/aws.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index ba51a7e561..0c538f9314 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -66,9 +66,7 @@ func init() { } if claims, ok := token.Claims.(*claims); ok && token.Valid { - if claims.ProductCode == productCode && claims.PublicKeyVersion == pubKeyVersion && claims.Nonce == nonce { - log.Println("AWS verification successful") - } else { + if claims.ProductCode != productCode || claims.PublicKeyVersion != pubKeyVersion || claims.Nonce != nonce { log.Fatal("the claims in the JWT token don't match the request") } } else { From 682cdcc9fb2d1dadeabfd62f43d8e1dc7399da18 Mon Sep 17 00:00:00 2001 From: Luca Comellini Date: Wed, 26 May 2021 17:14:46 -0700 Subject: [PATCH 8/8] PR feedback --- cmd/nginx-ingress/aws.go | 27 ++++++++++++++++----------- cmd/nginx-ingress/main.go | 9 +++++++++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cmd/nginx-ingress/aws.go b/cmd/nginx-ingress/aws.go index 0c538f9314..a153cb9e7f 100644 --- a/cmd/nginx-ingress/aws.go +++ b/cmd/nginx-ingress/aws.go @@ -7,7 +7,7 @@ import ( "crypto/rand" "encoding/base64" "errors" - "log" + "fmt" "math/big" "time" @@ -26,17 +26,21 @@ var ( ) func init() { + startupCheckFn = checkAWSEntitlement +} + +func checkAWSEntitlement() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() nonce, err := generateRandomString(255) if err != nil { - log.Fatal(err) + return err } cfg, err := config.LoadDefaultConfig(ctx) if err != nil { - log.Fatalf("error loading AWS configuration: %v", err) + return fmt.Errorf("error loading AWS configuration: %v", err) } mpm := marketplacemetering.New(marketplacemetering.Options{Region: cfg.Region, Credentials: cfg.Credentials}) @@ -45,33 +49,34 @@ func init() { if err != nil { var notEnt *types.CustomerNotEntitledException if errors.As(err, ¬Ent) { - log.Fatalf("user not entitled, code: %v, message: %v, fault: %v", notEnt.ErrorCode(), notEnt.ErrorMessage(), notEnt.ErrorFault().String()) + return fmt.Errorf("user not entitled, code: %v, message: %v, fault: %v", notEnt.ErrorCode(), notEnt.ErrorMessage(), notEnt.ErrorFault().String()) } - log.Fatal(err) - + return err } pk, err := base64.StdEncoding.DecodeString(pubKeyString) if err != nil { - log.Fatalf("error decoding Public Key string: %v", err) + return fmt.Errorf("error decoding Public Key string: %v", err) } pubKey, err := jwt.ParseRSAPublicKeyFromPEM(pk) if err != nil { - log.Fatalf("error parsing Public Key: %v", err) + return fmt.Errorf("error parsing Public Key: %v", err) } token, err := jwt.ParseWithClaims(*out.Signature, &claims{}, jwt.KnownKeyfunc(jwt.SigningMethodPS256, pubKey)) if err != nil { - log.Fatalf("error parsing the JWT token: %v", err) + return fmt.Errorf("error parsing the JWT token: %v", err) } if claims, ok := token.Claims.(*claims); ok && token.Valid { if claims.ProductCode != productCode || claims.PublicKeyVersion != pubKeyVersion || claims.Nonce != nonce { - log.Fatal("the claims in the JWT token don't match the request") + return fmt.Errorf("the claims in the JWT token don't match the request") } } else { - log.Fatal("something is wrong with the JWT token") + return fmt.Errorf("something is wrong with the JWT token") } + + return nil } type claims struct { diff --git a/cmd/nginx-ingress/main.go b/cmd/nginx-ingress/main.go index 1727df9ef7..8d7d9ea9e7 100644 --- a/cmd/nginx-ingress/main.go +++ b/cmd/nginx-ingress/main.go @@ -185,6 +185,8 @@ var ( enableLatencyMetrics = flag.Bool("enable-latency-metrics", false, "Enable collection of latency metrics for upstreams. Requires -enable-prometheus-metrics") + + startupCheckFn func() error ) func main() { @@ -201,6 +203,13 @@ func main() { os.Exit(0) } + if startupCheckFn != nil { + err := startupCheckFn() + if err != nil { + glog.Fatalf("Failed startup check: %v", err) + } + } + healthStatusURIValidationError := validateLocation(*healthStatusURI) if healthStatusURIValidationError != nil { glog.Fatalf("Invalid value for health-status-uri: %v", healthStatusURIValidationError)