diff --git a/.travis.yml b/.travis.yml index d044064..d20b31c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,16 @@ language: go go: -- 1.x +- 1.11.x +- master +env: +- GO111MODULE=on git: depth: 1 -before_install: -- curl -L -s https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 - -o $GOPATH/bin/dep -- chmod +x $GOPATH/bin/dep -install: -- dep ensure --vendor-only notifications: email: false +install: true +before_script: +- go mod verify script: - go test -v ./... - mkdir bin @@ -26,4 +26,5 @@ deploy: - bin/assume-role-arn-osx on: repo: nordcloud/assume-role-arn - tags: true \ No newline at end of file + tags: true + diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 8cb8724..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,56 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:8aa343e08a3cdf0a4351794ec13031e61e61c1533fe879d952d56ee6f310ca91" - name = "github.com/aws/aws-sdk-go" - packages = [ - "aws", - "aws/awserr", - "aws/awsutil", - "aws/client", - "aws/client/metadata", - "aws/corehandlers", - "aws/credentials", - "aws/credentials/ec2rolecreds", - "aws/credentials/endpointcreds", - "aws/credentials/stscreds", - "aws/csm", - "aws/defaults", - "aws/ec2metadata", - "aws/endpoints", - "aws/request", - "aws/session", - "aws/signer/v4", - "internal/ini", - "internal/sdkio", - "internal/sdkrand", - "internal/sdkuri", - "internal/shareddefaults", - "private/protocol", - "private/protocol/query", - "private/protocol/query/queryutil", - "private/protocol/rest", - "private/protocol/xml/xmlutil", - "service/sts", - ] - pruneopts = "UT" - revision = "936c5266940725e6a728ea4b7241ede3f1616096" - version = "v1.15.82" - -[[projects]] - digest = "1:e22af8c7518e1eab6f2eab2b7d7558927f816262586cd6ed9f349c97a6c285c4" - name = "github.com/jmespath/go-jmespath" - packages = ["."] - pruneopts = "UT" - revision = "0b12d6b5" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/aws/aws-sdk-go/aws/session", - "github.com/aws/aws-sdk-go/service/sts", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index d7072c2..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,30 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[prune] - go-tests = true - unused-packages = true diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..15ef9f3 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +build: + GOOS=linux go build -o bin/assume-role-arn-linux cmd/assume-role-arn/main.go diff --git a/README.md b/README.md index 31b1cf5..8107597 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Available flags: * `-r role_arn` - required, role ARN * `-e external_id` - optional, if you need to specify external id * `-n role_session_name` - probably you don't need this +* `-m mfa_serial` - optional, the ARN of MFA virtual device +* `-mfatoken token` - optional, the MFA token * `-h` - help ## CI/CD pipeline example @@ -51,5 +53,13 @@ eval $(assume-role-arn -r arn:aws:iam::ACCOUNT_NUMBER_STG:role/Deployment) Now you should be able to execute AWS-related commands with your assumed role. +## MFA + +If your account is secured with MFA (multi-factor authentication) then you have to provide the ARN of MFA device +and the token: +``` +eval $(assume-role-arn -r arn:aws:iam:ACCOUNT_NUMBER_STG:role/Role -m arn:aws:iam::ACCOUNT:mfa/MFA_ID -mfatoken MFATOKEN) +``` + ## Authors * Jakub Woźniak, Nordcloud 🇵🇱 diff --git a/cmd/assume-role-arn/main.go b/cmd/assume-role-arn/main.go index d1c23d7..7b4ca11 100644 --- a/cmd/assume-role-arn/main.go +++ b/cmd/assume-role-arn/main.go @@ -1,10 +1,12 @@ package main import ( + "bufio" "flag" "fmt" "os" "os/exec" + "strings" "syscall" "github.com/aws/aws-sdk-go/aws" @@ -12,9 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/sts" ) -var roleARN string -var roleName string -var externalID string +var roleARN, roleName, externalID, mfa, mfaToken string func init() { flag.StringVar(&roleARN, "role", "", "role arn") @@ -26,6 +26,11 @@ func init() { flag.StringVar(&externalID, "extid", "", "external id") flag.StringVar(&externalID, "e", "", "external id (shorthand)") + flag.StringVar(&mfa, "mfaserial", "", "mfa serial") + flag.StringVar(&mfa, "m", "", "mfa serial (shorthand)") + + flag.StringVar(&mfaToken, "mfatoken", "", "mfa token") + flag.Parse() if roleARN == "" { @@ -43,9 +48,28 @@ func prepareAssumeInput() *sts.AssumeRoleInput { input.ExternalId = aws.String(externalID) } + if mfa != "" { + input.SerialNumber = aws.String(mfa) + input.TokenCode = aws.String(mfaToken) + if mfaToken == "" { + input.TokenCode = aws.String(askForMFAToken(roleARN)) + } + } + return input } +func askForMFAToken(roleARN string) string { + // ask for mfa token + reader := bufio.NewReader(os.Stdin) + fmt.Printf("Enter MFA for %s: ", roleARN) + mfaToken, err := reader.ReadString('\n') + if err != nil { + panic(err) + } + return strings.TrimRight(mfaToken, "\n") +} + func getSession() *session.Session { region := "us-east-1" sess, err := session.NewSessionWithOptions(session.Options{ @@ -62,13 +86,9 @@ func getSession() *session.Session { return sess } -func assumeRole(sess *session.Session, input *sts.AssumeRoleInput) *sts.AssumeRoleOutput { +func assumeRole(sess *session.Session, input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) { svc := sts.New(sess) - out, err := svc.AssumeRole(input) - if err != nil { - panic(err) - } - return out + return svc.AssumeRole(input) } func printExport(val *sts.AssumeRoleOutput) { @@ -95,9 +115,13 @@ func runCommand(args []string) error { } func main() { - toAssume := prepareAssumeInput() sess := getSession() - role := assumeRole(sess, toAssume) + toAssume := prepareAssumeInput() + + role, err := assumeRole(sess, toAssume) + if err != nil { + panic(err) + } if len(flag.Args()) > 0 { setEnv(role) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2b69620 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/nordcloud/assume-role-arn + +go 1.12 + +require ( + github.com/aws/aws-sdk-go v1.15.82 + github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 + github.com/sirupsen/logrus v1.4.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1d55f26 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/aws/aws-sdk-go v1.15.82 h1:tvOP/hcmpiUqtqJnU/IwJkqTEfnbsgja0xbPjvZuzbI= +github.com/aws/aws-sdk-go v1.15.82/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=