-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Mikael Johansson
committed
Mar 18, 2016
0 parents
commit 7840779
Showing
10 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
awsu* | ||
target | ||
coverage.txt | ||
.vscode | ||
vendor/ | ||
.aws | ||
keys/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
language: go | ||
sudo: false | ||
os: | ||
- linux | ||
- osx | ||
go: | ||
- 1.6 | ||
|
||
# Deploy executables to Github release tags | ||
deploy: | ||
provider: releases | ||
api_key: | ||
secure: JotH2XgNOyR2Pi+5NMQRS2t7VhMamxzhlxWf+pXC3hUUzeIKSTszX0DVtfgjKkLkJzoai9Y62kEUhEvOHEqdQN8hsE89mjbBVCTPpO0Jl0NNJ8HKbIlg0iY5vgMesQTO5LrSIQoYfILhGFVUqVvRPw/7CX7Y5wSV6ca4SvvruSSzyG2dEyehCR5TcPFJR/aH4DmO/I4AumPEA82uuv3naaGmFl1y45Q51jgUWWq2WvJK/i0pECWzSc6HtVUAvFvrypZKRu4L+EFC0DS8mSUa6lgEsUnp714/YnYM2zXqXy2vBZR/ZNZVN3/67BGpqaZeGmoKEH0l5cXzBdPTiuihneziI6v3E+CP6E/bhVTSiDKJw1yeA8E5fdotnjhvXVHAxftA182yh3TVPnDinn/QKgTsdtS4ofs99AOyqtaMGdIGp5WRDJsP/40bsy9uRApdrxiKqpSxWcSQndqs3x506fVcd2VnuITSIA9q4lM6d1x1pP74qtcGx175iI1TLt06wu59mnXC8Ba+uZh6ycY37Zsiqdvr1J+hJCUJrTMXDghFtrHSUhxOvVWtehQbG2VL53/NhvwCauwZLVJiiNmqZcppyWDww6Y7+zSNBPm9MIY0o/bgr3nCHSDRiQ3mhmauO12hab6svmPIhcDQ/n1lMpF4OgXbdWwlPCoGh/sZAYg= | ||
file: "awsu-$(uname -s)-$(uname -m)" | ||
skip_cleanup: true | ||
on: | ||
tags: true | ||
|
||
# Code coverage for master branch using https://codecov.io/github/meltwater/awsu | ||
after_success: | ||
- if [ "$TRAVIS_TAG" == "" ] && [ "$TRAVIS_OS_NAME" == "linux" ]; then bash <(curl -s https://codecov.io/bash); fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
export GO15VENDOREXPERIMENT=1 | ||
|
||
all: tools deps fmt build test lint | ||
|
||
tools: | ||
go get -u golang.org/x/tools/cmd/cover | ||
go get -u github.com/golang/lint/golint | ||
go get -u github.com/Masterminds/glide | ||
|
||
deps: | ||
glide install | ||
|
||
# http://golang.org/cmd/go/#hdr-Run_gofmt_on_package_sources | ||
fmt: | ||
go fmt ./... | ||
|
||
build: | ||
CGO_ENABLED=0 go build -o "awsu-`uname -s`-`uname -m`" | ||
ln -sf "awsu-`uname -s`-`uname -m`" awsu | ||
|
||
test: | ||
go test -bench=. -v -coverprofile=coverage.txt -covermode=atomic | ||
|
||
lint: | ||
golint | ||
|
||
clean: | ||
rm -f ./awsu | ||
|
||
.PHONY: tools deps fmt build test lint clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Assume AWS IAM role | ||
[![Travis CI](https://img.shields.io/travis/meltwater/awsu/master.svg)](https://travis-ci.org/meltwater/awsu) | ||
|
||
Assumes an IAM role and passes the temporary credentials to another command or shell. | ||
|
||
If you manage multiple AWS accounts and use IAM role switching to perform work in them, this would | ||
allow you to use tools like Terraform, Docker Machine or Vagrant in the accounts. Cross account | ||
IAM role switching is described at | ||
|
||
* https://aws.amazon.com/blogs/aws/new-cross-account-access-in-the-aws-management-console/ | ||
|
||
## Usage | ||
|
||
``` | ||
Assume a AWS IAM role and execute a command or shell | ||
Usage: | ||
awsu IAMRoleARN [command] [args]... [flags] | ||
Flags: | ||
--duration int Expiration time in seconds for the temporary credentials (default 900) | ||
``` | ||
|
||
It could be useful to setup ~/.bash_aliases for roles in different accounts | ||
|
||
``` | ||
alias ondev='awsu arn:aws:iam::123456789:role/Developer' | ||
alias onprod='awsu arn:aws:iam::891234567:role/Developer' | ||
``` | ||
|
||
For example | ||
|
||
``` | ||
$ ondev terraform plan | ||
$ ondev docker-machine create --driver amazonec2 ... | ||
``` | ||
|
||
Inspired by | ||
|
||
* https://github.com/mlrobinson/aws-profile | ||
* https://github.com/jbuck/assume-aws-role | ||
* http://blog.sinica.me/aws_multi_account_with_terraform.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/service/sts" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"syscall" | ||
) | ||
|
||
func filterExistingCredentials(list []string) []string { | ||
result := make([]string, 0) | ||
for _, item := range list { | ||
if !strings.HasPrefix(item, "AWS_ACCESS_KEY_ID=") && | ||
!strings.HasPrefix(item, "AWS_SECRET_ACCESS_KEY=") && | ||
!strings.HasPrefix(item, "AWS_SESSION_TOKEN=") { | ||
result = append(result, item) | ||
} | ||
} | ||
|
||
return result | ||
} | ||
|
||
// Encrypts data from stdin and writes to stdout | ||
func executeCommand(durationSeconds int64, iamRole string, args []string) { | ||
hostname, err := os.Hostname() | ||
sessionName := fmt.Sprintf("%s-%s-%s", | ||
defaults(os.Getenv("USER"), "unknown"), | ||
defaults(hostname, os.Getenv("HOST"), os.Getenv("HOSTNAME"), "unknown"), | ||
randSeq(8)) | ||
|
||
svc := sts.New(session.New()) | ||
|
||
params := &sts.AssumeRoleInput{ | ||
RoleArn: aws.String(iamRole), // Required | ||
RoleSessionName: aws.String(sessionName), // Required | ||
DurationSeconds: aws.Int64(durationSeconds), | ||
// ExternalId: aws.String("externalIdType"), | ||
// Policy: aws.String("sessionPolicyDocumentType"), | ||
// SerialNumber: aws.String("serialNumberType"), | ||
// TokenCode: aws.String("tokenCodeType"), | ||
} | ||
|
||
resp, err := svc.AssumeRole(params) | ||
check(err) | ||
|
||
// Default to launch a subshell | ||
binary := defaults(os.Getenv("SHELL"), "/bin/sh") | ||
|
||
// Resolve absolute path of binary | ||
if len(args) >= 1 { | ||
binary, err = exec.LookPath(args[0]) | ||
check(err) | ||
} | ||
|
||
// Inject the temporary credentials | ||
env := append(filterExistingCredentials(os.Environ()), | ||
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", *resp.Credentials.AccessKeyId), | ||
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", *resp.Credentials.SecretAccessKey), | ||
fmt.Sprintf("AWS_SESSION_TOKEN=%s", *resp.Credentials.SessionToken)) | ||
|
||
// Execute subcommand | ||
err = syscall.Exec(binary, args, env) | ||
check(err) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package: github.com/meltwater/awsu | ||
import: | ||
- package: github.com/go-errors/errors | ||
- package: github.com/spf13/cobra | ||
- package: github.com/stretchr/testify | ||
subpackages: | ||
- assert | ||
- package: github.com/aws/aws-sdk-go | ||
subpackages: | ||
- aws/session |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"os" | ||
"time" | ||
|
||
"github.com/go-errors/errors" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func main() { | ||
rand.Seed(time.Now().UnixNano()) | ||
|
||
var durationSeconds int64 | ||
|
||
rootCmd := &cobra.Command{ | ||
Use: "awsu IAMRoleARN [command] [args]...", | ||
Short: "Assume a AWS IAM role and execute a command or shell", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
assertThat(len(args) >= 1, "Expected an IAM role") | ||
executeCommand(durationSeconds, args[0], args[1:]) | ||
}, | ||
} | ||
|
||
rootCmd.Flags().Int64VarP(&durationSeconds, "duration", "", int64(900), "Expiration time in seconds for the temporary credentials") | ||
|
||
// Handle checked errors nicely | ||
defer func() { | ||
if err := recover(); err != nil { | ||
switch err.(type) { | ||
case *CommandError: | ||
fmt.Fprintf(os.Stderr, "%s\n", err) | ||
default: | ||
fmt.Fprintf(os.Stderr, "%s\n", errors.Wrap(err, 2).ErrorStack()) | ||
} | ||
|
||
os.Exit(1) | ||
} | ||
}() | ||
|
||
rootCmd.Execute() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
) | ||
|
||
var randAlphabet = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") | ||
|
||
// CommandError is the checked exception thrown on runtime errors | ||
type CommandError struct { | ||
msg string // description of error | ||
err error // inner error | ||
} | ||
|
||
func (e *CommandError) Error() string { return e.msg } | ||
|
||
// Panics with a message if the given error isn't nil | ||
func check(err error, a ...interface{}) { | ||
if err != nil { | ||
var msg string | ||
if len(a) > 0 { | ||
msg = fmt.Sprintf("%s (%s)", fmt.Sprintf(a[0].(string), a[1:]...), err) | ||
} else { | ||
msg = fmt.Sprintf("%s", err) | ||
} | ||
|
||
panic(&CommandError{msg, err}) | ||
} | ||
} | ||
|
||
// Panics with a message if the given condition isn't true | ||
func assertThat(condition bool, msg string, a ...interface{}) { | ||
if !condition { | ||
panic(&CommandError{fmt.Sprintf(msg, a...), nil}) | ||
} | ||
} | ||
|
||
func defaults(a ...string) string { | ||
for _, item := range a { | ||
if len(item) > 0 { | ||
return item | ||
} | ||
} | ||
|
||
return "" | ||
} | ||
|
||
func randSeq(n int) string { | ||
b := make([]rune, n) | ||
for i := range b { | ||
b[i] = randAlphabet[rand.Intn(len(randAlphabet))] | ||
} | ||
|
||
return string(b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestCheck(t *testing.T) { | ||
// Handle checked errors nicely | ||
defer func() { | ||
if err := recover(); err != nil { | ||
switch err.(type) { | ||
case *CommandError: | ||
assert.Equal(t, "Test Error (Inner Error)", fmt.Sprintf("%s", err)) | ||
default: | ||
t.Errorf("Expected to catch a CommandError but got %v", err) | ||
} | ||
} | ||
}() | ||
|
||
check(errors.New("Inner Error"), "Test Error") | ||
} | ||
|
||
func TestAssert(t *testing.T) { | ||
// Handle checked errors nicely | ||
defer func() { | ||
if err := recover(); err != nil { | ||
switch err.(type) { | ||
case *CommandError: | ||
assert.Equal(t, "Test Error", fmt.Sprintf("%s", err)) | ||
default: | ||
t.Errorf("Expected to catch a CommandError but got %v", err) | ||
} | ||
} | ||
}() | ||
|
||
assertThat(false, "Test Error") | ||
} | ||
|
||
func TestDefaults(t *testing.T) { | ||
assert.Equal(t, "abc", defaults("abc", "123")) | ||
assert.Equal(t, "123", defaults("", "123")) | ||
assert.Equal(t, "", defaults("", "")) | ||
assert.Equal(t, "", defaults("")) | ||
assert.Equal(t, "", defaults()) | ||
} |