From af0f21d7440551f4b57c2d7effc5b639af3efff6 Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Tue, 19 May 2020 18:42:12 -0400 Subject: [PATCH 1/8] added support for IMDSv2 API --- authority/provisioner/aws.go | 40 +++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 3f2c8873..221634a4 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -29,6 +29,19 @@ const awsIdentityURL = "http://169.254.169.254/latest/dynamic/instance-identity/ // awsSignatureURL is the url used to retrieve the instance identity signature. const awsSignatureURL = "http://169.254.169.254/latest/dynamic/instance-identity/signature" +// awsAPITokenURL is the url used to get the IMDSv2 API token +const awsAPITokenURL = "http://169.254.169.254/latest/api/token" + +// awsAPITokenTTL is the default TTL to use when requesting IMDSv2 API tokens +// -- we keep this short-lived since we get a new token with every call to readURL() +const awsAPITokenTTL = "30" + +// awsMetadataTokenHeader is the header that must be passed with every IMDSv2 request +const awsMetadataTokenHeader = "X-aws-ec2-metadata-token" + +// awsMetadataTokenTTLHeader is the header used to indicate the token TTL requested +const awsMetadataTokenTTLHeader = "X-aws-ec2-metadata-token-ttl-seconds" + // awsCertificate is the certificate used to validate the instance identity // signature. const awsCertificate = `-----BEGIN CERTIFICATE----- @@ -332,7 +345,15 @@ func (p *AWS) checkSignature(signed, signature []byte) error { // using pkg/errors to avoid verbose errors, the caller should use it and write // the appropriate error. func (p *AWS) readURL(url string) ([]byte, error) { - r, err := http.Get(url) + client := &http.Client{} + + // get authorization token + req, err := http.NewRequest(http.MethodPut, awsAPITokenURL, nil) + if err != nil { + return nil, err + } + req.Header.Set(awsMetadataTokenTTLHeader, awsAPITokenTTL) + r, err := client.Do(req) if err != nil { return nil, err } @@ -341,6 +362,23 @@ func (p *AWS) readURL(url string) ([]byte, error) { if err != nil { return nil, err } + token := string(b) + + // now get the data + req, err = http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + req.Header.Set(awsMetadataTokenHeader, token) + r, err = client.Do(req) + if err != nil { + return nil, err + } + defer r.Body.Close() + b, err = ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } return b, nil } From bbbe4738c77fc6c8a7845267f5a816fdb766ca22 Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Tue, 19 May 2020 23:57:09 -0400 Subject: [PATCH 2/8] Added status code checking --- authority/provisioner/aws.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 221634a4..c2c1fab9 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -196,14 +196,14 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { var idoc awsInstanceIdentityDocument doc, err := p.readURL(p.config.identityURL) if err != nil { - return "", errors.Wrap(err, "error retrieving identity document, are you in an AWS VM?") + return "", errors.Wrap(err, "error retrieving identity document, are you in an AWS VM with IMDSv2 enabled?") } if err := json.Unmarshal(doc, &idoc); err != nil { return "", errors.Wrap(err, "error unmarshaling identity document") } sig, err := p.readURL(p.config.signatureURL) if err != nil { - return "", errors.Wrap(err, "error retrieving identity document signature, are you in an AWS VM?") + return "", errors.Wrap(err, "error retrieving identity document signature, are you in an AWS VM with IMDSv2 enabled?") } signature, err := base64.StdEncoding.DecodeString(string(sig)) if err != nil { @@ -358,6 +358,9 @@ func (p *AWS) readURL(url string) ([]byte, error) { return nil, err } defer r.Body.Close() + if r.StatusCode >= 400 { + return nil, fmt.Errorf("HTTP request returned non-successful status code %d", r.StatusCode) + } b, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err @@ -375,6 +378,9 @@ func (p *AWS) readURL(url string) ([]byte, error) { return nil, err } defer r.Body.Close() + if r.StatusCode >= 400 { + return nil, fmt.Errorf("HTTP request returned non-successful status code %d", r.StatusCode) + } b, err = ioutil.ReadAll(r.Body) if err != nil { return nil, err From dd27901b12e32defd01346e2525db0f623179439 Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Wed, 20 May 2020 09:03:35 -0400 Subject: [PATCH 3/8] Moved token URL and TTL to config values --- authority/provisioner/aws.go | 8 ++++++-- authority/provisioner/utils_test.go | 14 ++++++++++++++ go.mod | 2 +- go.sum | 11 +++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index c2c1fab9..f3b539d6 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -71,6 +71,8 @@ const awsSignatureAlgorithm = x509.SHA256WithRSA type awsConfig struct { identityURL string signatureURL string + tokenURL string + tokenTTL string certificate *x509.Certificate signatureAlgorithm x509.SignatureAlgorithm } @@ -87,6 +89,8 @@ func newAWSConfig() (*awsConfig, error) { return &awsConfig{ identityURL: awsIdentityURL, signatureURL: awsSignatureURL, + tokenURL: awsAPITokenURL, + tokenTTL: awsAPITokenTTL, certificate: cert, signatureAlgorithm: awsSignatureAlgorithm, }, nil @@ -348,11 +352,11 @@ func (p *AWS) readURL(url string) ([]byte, error) { client := &http.Client{} // get authorization token - req, err := http.NewRequest(http.MethodPut, awsAPITokenURL, nil) + req, err := http.NewRequest(http.MethodPut, p.config.tokenURL, nil) if err != nil { return nil, err } - req.Header.Set(awsMetadataTokenTTLHeader, awsAPITokenTTL) + req.Header.Set(awsMetadataTokenTTLHeader, p.config.tokenTTL) r, err := client.Do(req) if err != nil { return nil, err diff --git a/authority/provisioner/utils_test.go b/authority/provisioner/utils_test.go index 418e6a5b..0ef80314 100644 --- a/authority/provisioner/utils_test.go +++ b/authority/provisioner/utils_test.go @@ -457,12 +457,25 @@ func generateAWSWithServer() (*AWS, *httptest.Server, error) { if err != nil { return nil, nil, errors.Wrap(err, "error signing document") } + token := "AQAEAEEO9-7Z88ewKFpboZuDlFYWz9A3AN-wMOVzjEhfAyXW31BvVw==" srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/latest/dynamic/instance-identity/document": + // check for API token + if r.Header.Get("X-aws-ec2-metadata-token") != token { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("401 Unauthorized")) + } w.Write(doc) case "/latest/dynamic/instance-identity/signature": + // check for API token + if r.Header.Get("X-aws-ec2-metadata-token") != token { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("401 Unauthorized")) + } w.Write([]byte(base64.StdEncoding.EncodeToString(signature))) + case "/latest/api/token": + w.Write([]byte(token)) case "/bad-document": w.Write([]byte("{}")) case "/bad-signature": @@ -475,6 +488,7 @@ func generateAWSWithServer() (*AWS, *httptest.Server, error) { })) aws.config.identityURL = srv.URL + "/latest/dynamic/instance-identity/document" aws.config.signatureURL = srv.URL + "/latest/dynamic/instance-identity/signature" + aws.config.tokenURL = srv.URL + "/latest/api/token" return aws, srv, nil } diff --git a/go.mod b/go.mod index c81244cf..8cf58e89 100644 --- a/go.mod +++ b/go.mod @@ -26,5 +26,5 @@ require ( gopkg.in/square/go-jose.v2 v2.4.0 ) -//replace github.com/smallstep/cli => ../cli +replace github.com/smallstep/cli => ../cli //replace github.com/smallstep/nosql => ../nosql diff --git a/go.sum b/go.sum index 7a9caaef..2f95d40a 100644 --- a/go.sum +++ b/go.sum @@ -21,12 +21,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk= github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g= github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= +github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -52,6 +56,7 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bombsimon/wsl/v2 v2.0.0 h1:+Vjcn+/T5lSrO8Bjzhk4v14Un/2UyCA1E3V5j9nwTkQ= github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -65,6 +70,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -80,6 +86,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -268,6 +275,7 @@ github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -282,6 +290,7 @@ github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= @@ -364,6 +373,7 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU= github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= +github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= @@ -778,6 +788,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= From 226cc6ab48eef50900144188d9f0f8b77b930a5a Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Wed, 20 May 2020 09:32:04 -0400 Subject: [PATCH 4/8] reverted --- go.mod | 2 +- go.sum | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 8cf58e89..c81244cf 100644 --- a/go.mod +++ b/go.mod @@ -26,5 +26,5 @@ require ( gopkg.in/square/go-jose.v2 v2.4.0 ) -replace github.com/smallstep/cli => ../cli +//replace github.com/smallstep/cli => ../cli //replace github.com/smallstep/nosql => ../nosql diff --git a/go.sum b/go.sum index 2f95d40a..7a9caaef 100644 --- a/go.sum +++ b/go.sum @@ -21,16 +21,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk= github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g= github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= -github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -56,7 +52,6 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bombsimon/wsl/v2 v2.0.0 h1:+Vjcn+/T5lSrO8Bjzhk4v14Un/2UyCA1E3V5j9nwTkQ= github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -70,7 +65,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -86,7 +80,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -275,7 +268,6 @@ github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -290,7 +282,6 @@ github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= @@ -373,7 +364,6 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU= github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= -github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= @@ -788,7 +778,6 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= From 8c6a46887baf700592dbca69f236749057c02002 Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Wed, 20 May 2020 09:39:19 -0400 Subject: [PATCH 5/8] Added token URL fixes to tests --- authority/provisioner/aws_test.go | 6 ++++++ authority/provisioner/utils_test.go | 2 ++ 2 files changed, 8 insertions(+) diff --git a/authority/provisioner/aws_test.go b/authority/provisioner/aws_test.go index 9e8fc7ad..21620053 100644 --- a/authority/provisioner/aws_test.go +++ b/authority/provisioner/aws_test.go @@ -103,36 +103,42 @@ func TestAWS_GetIdentityToken(t *testing.T) { p2.Accounts = p1.Accounts p2.config.identityURL = srv.URL + "/bad-document" p2.config.signatureURL = p1.config.signatureURL + p2.config.tokenURL = p1.config.tokenURL p3, err := generateAWS() assert.FatalError(t, err) p3.Accounts = p1.Accounts p3.config.signatureURL = srv.URL p3.config.identityURL = p1.config.identityURL + p3.config.tokenURL = p1.config.tokenURL p4, err := generateAWS() assert.FatalError(t, err) p4.Accounts = p1.Accounts p4.config.signatureURL = srv.URL + "/bad-signature" p4.config.identityURL = p1.config.identityURL + p4.config.tokenURL = p1.config.tokenURL p5, err := generateAWS() assert.FatalError(t, err) p5.Accounts = p1.Accounts p5.config.identityURL = "https://1234.1234.1234.1234" p5.config.signatureURL = p1.config.signatureURL + p5.config.tokenURL = p1.config.tokenURL p6, err := generateAWS() assert.FatalError(t, err) p6.Accounts = p1.Accounts p6.config.identityURL = p1.config.identityURL p6.config.signatureURL = "https://1234.1234.1234.1234" + p6.config.tokenURL = p1.config.tokenURL p7, err := generateAWS() assert.FatalError(t, err) p7.Accounts = p1.Accounts p7.config.identityURL = srv.URL + "/bad-json" p7.config.signatureURL = p1.config.signatureURL + p7.config.tokenURL = p1.config.tokenURL caURL := "https://ca.smallstep.com" u, err := url.Parse(caURL) diff --git a/authority/provisioner/utils_test.go b/authority/provisioner/utils_test.go index 0ef80314..23e8d86d 100644 --- a/authority/provisioner/utils_test.go +++ b/authority/provisioner/utils_test.go @@ -416,6 +416,8 @@ func generateAWS() (*AWS, error) { config: &awsConfig{ identityURL: awsIdentityURL, signatureURL: awsSignatureURL, + tokenURL: awsAPITokenURL, + tokenTTL: awsAPITokenTTL, certificate: cert, signatureAlgorithm: awsSignatureAlgorithm, }, From 18ac5c07e222b3e8af7bd765b1bc8c084ef3d68d Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Wed, 20 May 2020 13:15:51 -0400 Subject: [PATCH 6/8] Added support for specifying IMDS version preference --- authority/provisioner/aws.go | 81 ++++++++++++++++++++++------- authority/provisioner/utils_test.go | 11 ++-- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index f3b539d6..9da6a9f3 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -147,6 +147,7 @@ type AWS struct { Accounts []string `json:"accounts"` DisableCustomSANs bool `json:"disableCustomSANs"` DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` + IMDSVersions []string `json:"imdsVersions"` InstanceAge Duration `json:"instanceAge,omitempty"` Claims *Claims `json:"claims,omitempty"` claimer *Claimer @@ -200,14 +201,14 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { var idoc awsInstanceIdentityDocument doc, err := p.readURL(p.config.identityURL) if err != nil { - return "", errors.Wrap(err, "error retrieving identity document, are you in an AWS VM with IMDSv2 enabled?") + return "", errors.Wrap(err, "error retrieving identity document, are you in an AWS VM using the proper IMDS version?") } if err := json.Unmarshal(doc, &idoc); err != nil { return "", errors.Wrap(err, "error unmarshaling identity document") } sig, err := p.readURL(p.config.signatureURL) if err != nil { - return "", errors.Wrap(err, "error retrieving identity document signature, are you in an AWS VM with IMDSv2 enabled?") + return "", errors.Wrap(err, "error retrieving identity document signature, are you in an AWS VM using the proper IMDS version?") } signature, err := base64.StdEncoding.DecodeString(string(sig)) if err != nil { @@ -349,43 +350,87 @@ func (p *AWS) checkSignature(signed, signature []byte) error { // using pkg/errors to avoid verbose errors, the caller should use it and write // the appropriate error. func (p *AWS) readURL(url string) ([]byte, error) { - client := &http.Client{} + var resp *http.Response + var err error - // get authorization token + for _, v := range p.IMDSVersions { + switch v { + case "v1": + resp, err = p.readURLv1(url) + if err == nil && resp.StatusCode < 400 { + return p.readResponseBody(resp) + } + case "v2": + resp, err = p.readURLv2(url) + if err == nil && resp.StatusCode < 400 { + return p.readResponseBody(resp) + } + default: + return nil, fmt.Errorf("%s: not a supported AWS Instance Metadata Service version", v) + } + } + + // all versions have been exhausted and we haven't returned successfully yet so pass + // the error on to the caller + if err != nil { + return nil, err + } + return nil, fmt.Errorf("Request for metadata returned non-successful status code %d", + resp.StatusCode) +} + +func (p *AWS) readURLv1(url string) (*http.Response, error) { + client := http.Client{} + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + resp, err := client.Do(req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (p *AWS) readURLv2(url string) (*http.Response, error) { + client := http.Client{} + + // first get the token req, err := http.NewRequest(http.MethodPut, p.config.tokenURL, nil) if err != nil { return nil, err } req.Header.Set(awsMetadataTokenTTLHeader, p.config.tokenTTL) - r, err := client.Do(req) + resp, err := client.Do(req) if err != nil { return nil, err } - defer r.Body.Close() - if r.StatusCode >= 400 { - return nil, fmt.Errorf("HTTP request returned non-successful status code %d", r.StatusCode) + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("Request for API token returned non-successful status code %d", resp.StatusCode) } - b, err := ioutil.ReadAll(r.Body) + token, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } - token := string(b) - // now get the data + // now make the request req, err = http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } - req.Header.Set(awsMetadataTokenHeader, token) - r, err = client.Do(req) + req.Header.Set(awsMetadataTokenHeader, string(token)) + resp, err = client.Do(req) if err != nil { return nil, err } - defer r.Body.Close() - if r.StatusCode >= 400 { - return nil, fmt.Errorf("HTTP request returned non-successful status code %d", r.StatusCode) - } - b, err = ioutil.ReadAll(r.Body) + return resp, nil +} + +func (p *AWS) readResponseBody(resp *http.Response) ([]byte, error) { + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } diff --git a/authority/provisioner/utils_test.go b/authority/provisioner/utils_test.go index 23e8d86d..84283631 100644 --- a/authority/provisioner/utils_test.go +++ b/authority/provisioner/utils_test.go @@ -408,11 +408,12 @@ func generateAWS() (*AWS, error) { return nil, errors.Wrap(err, "error parsing AWS certificate") } return &AWS{ - Type: "AWS", - Name: name, - Accounts: []string{accountID}, - Claims: &globalProvisionerClaims, - claimer: claimer, + Type: "AWS", + Name: name, + Accounts: []string{accountID}, + Claims: &globalProvisionerClaims, + IMDSVersions: []string{"v2", "v1"}, + claimer: claimer, config: &awsConfig{ identityURL: awsIdentityURL, signatureURL: awsSignatureURL, From 044d00045a14026fa66d796c4fc7144047a46f96 Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Wed, 20 May 2020 13:24:45 -0400 Subject: [PATCH 7/8] Fixed missing initialization of IMDS versions --- authority/provisioner/aws.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 9da6a9f3..52c8a40d 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -282,6 +282,22 @@ func (p *AWS) Init(config Config) (err error) { return err } p.audiences = config.Audiences.WithFragment(p.GetID()) + + // validate IMDS versions + if len(p.IMDSVersions) == 0 { + p.IMDSVersions = []string{"v2", "v1"} + } + for _, v := range p.IMDSVersions { + switch v { + case "v1": + // valid + case "v2": + // valid + default: + return errors.Errorf("%s: not a supported AWS Instance Metadata Service version", v) + } + } + return nil } From e9b500daf2185110f49b2eaa4a3a0f9e17020753 Mon Sep 17 00:00:00 2001 From: Josh Hogle Date: Wed, 20 May 2020 14:43:25 -0400 Subject: [PATCH 8/8] Updated error message --- authority/provisioner/aws.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 52c8a40d..fd03d0c3 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -201,14 +201,14 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { var idoc awsInstanceIdentityDocument doc, err := p.readURL(p.config.identityURL) if err != nil { - return "", errors.Wrap(err, "error retrieving identity document, are you in an AWS VM using the proper IMDS version?") + return "", errors.Wrap(err, "error retrieving identity document:\n Are you in an AWS VM?\n Is the metadata service enabled?\n Are you using the proper metadata service version?") } if err := json.Unmarshal(doc, &idoc); err != nil { return "", errors.Wrap(err, "error unmarshaling identity document") } sig, err := p.readURL(p.config.signatureURL) if err != nil { - return "", errors.Wrap(err, "error retrieving identity document signature, are you in an AWS VM using the proper IMDS version?") + return "", errors.Wrap(err, "error retrieving identity document:\n Are you in an AWS VM?\n Is the metadata service enabled?\n Are you using the proper metadata service version?") } signature, err := base64.StdEncoding.DecodeString(string(sig)) if err != nil {