-
Notifications
You must be signed in to change notification settings - Fork 2
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
0 parents
commit 0163c9c
Showing
9 changed files
with
2,652 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,2 @@ | ||
coverage.txt | ||
dist |
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,11 @@ | ||
|
||
build: | ||
main: cmd/versent-bless/main.go | ||
binary: versent-bless | ||
goos: | ||
- darwin | ||
- linux | ||
- windows | ||
goarch: | ||
- amd64 | ||
- 386 |
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,22 @@ | ||
Copyright (c) 2017 Versent Pty. Ltd. | ||
|
||
Please consider promoting this project if you find it useful. | ||
|
||
Permission is hereby granted, free of charge, to any person | ||
obtaining a copy of this software and associated documentation | ||
files (the "Software"), to deal in the Software without restriction, | ||
including without limitation the rights to use, copy, modify, merge, | ||
publish, distribute, sublicense, and/or sell copies of the Software, | ||
and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included | ||
in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT | ||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | ||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
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 @@ | ||
SOURCE_FILES?=$$(go list ./... | grep -v /vendor/) | ||
TEST_PATTERN?=. | ||
TEST_OPTIONS?= | ||
|
||
GO ?= go | ||
|
||
# Install all the build and lint dependencies | ||
setup: | ||
go get -u github.com/alecthomas/gometalinter | ||
go get -u github.com/golang/dep/cmd/dep | ||
go get -u github.com/pierrre/gotestcover | ||
go get -u golang.org/x/tools/cmd/cover | ||
go get github.com/vektra/mockery/... | ||
gometalinter --install | ||
.PHONY: setup | ||
|
||
# Install from source. | ||
install: | ||
@echo "==> Installing up ${GOPATH}/bin/versent-bless" | ||
@$(GO) install ./... | ||
.PHONY: install | ||
|
||
# Run all the tests | ||
test: | ||
@gotestcover $(TEST_OPTIONS) -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m | ||
.PHONY: test | ||
|
||
# Run all the tests and opens the coverage report | ||
cover: test | ||
@$(GO) tool cover -html=coverage.txt | ||
.PHONY: cover | ||
|
||
# Release binaries to GitHub. | ||
release: | ||
@echo "==> Releasing" | ||
@goreleaser -p 1 --rm-dist -config .goreleaser.yml | ||
@echo "==> Complete" | ||
.PHONY: release | ||
|
||
generate-mocks: | ||
mockery -dir ../../aws/aws-sdk-go/service/lambda/lambdaiface --all | ||
.PHONY: generate-mocks | ||
|
||
.PHONY: setup test cover generate-mocks |
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,27 @@ | ||
# versent bless client | ||
|
||
This project is a port of the [netflix/bless/bless_client](https://github.com/Netflix/bless/tree/master/bless_client). | ||
|
||
# usage | ||
|
||
``` | ||
usage: versent-bless [<flags>] <command> [<args> ...] | ||
A command line client for netflix bless. | ||
Flags: | ||
--help Show context-sensitive help (also try --help-long and --help-man). | ||
--debug Enable debug mode. | ||
Commands: | ||
help [<command>...] | ||
Show help. | ||
login <region> <lambda_function_name> <bastion_user> <bastion_user_ip> <remote_usernames> <bastion_ips> <bastion_command> <public_key_to_sign> <certificate_filename> [<kmsauth_token>] | ||
Login and retrieve a key. | ||
``` | ||
|
||
# license | ||
|
||
This code is released under MIT License. |
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,113 @@ | ||
package bless | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
|
||
"github.com/aws/aws-sdk-go/service/lambda/lambdaiface" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/service/lambda" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
var ( | ||
lambdaSvc lambdaiface.LambdaAPI = lambda.New(session.New()) | ||
|
||
debug = false | ||
) | ||
|
||
// Payload used to encode request payload for bless lambda | ||
type Payload struct { | ||
BastionUser string `json:"bastion_user"` | ||
BastionUserIP string `json:"bastion_user_ip"` | ||
RemoteUsernames string `json:"remote_usernames"` | ||
BastionIps string `json:"bastion_ips"` | ||
BastionCommand string `json:"command"` | ||
PublicKeyToSign string `json:"public_key_to_sign"` | ||
KmsAuthToken string `json:"kms_auth_token,omitempty"` | ||
} | ||
|
||
// Result used to decode response from bless lambda | ||
type Result struct { | ||
Certificate string `json:"certificate,omitempty"` | ||
} | ||
|
||
// ConfigureAws enable override of the default aws sdk configuration | ||
func ConfigureAws(config *aws.Config) { | ||
lambdaSvc = lambda.New(session.New(config)) | ||
} | ||
|
||
// SetDebug enable or disable debugging | ||
func SetDebug(enable bool) { | ||
debug = enable | ||
} | ||
|
||
// LoadPublicKey load the public key from the supplied path | ||
func LoadPublicKey(publicKey string) ([]byte, error) { | ||
data, err := ioutil.ReadFile(publicKey) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "Failed to load public key") | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
// ValidatePublicKey validate the public key | ||
func ValidatePublicKey(publicKeyData []byte) (string, error) { | ||
if len(publicKeyData) == 0 { | ||
return "", errors.New("Empty public key supplied") | ||
} | ||
|
||
return string(publicKeyData), nil | ||
} | ||
|
||
// InvokeBlessLambda invoke the bless lambda function | ||
func InvokeBlessLambda(region, lambdaFunctionName *string, payloadJSON []byte) (*Result, error) { | ||
|
||
input := &lambda.InvokeInput{ | ||
FunctionName: lambdaFunctionName, | ||
InvocationType: aws.String("RequestResponse"), | ||
LogType: aws.String("None"), | ||
Payload: payloadJSON, | ||
} | ||
|
||
res, err := lambdaSvc.Invoke(input) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "Lambda invoke failed") | ||
} | ||
|
||
if aws.Int64Value(res.StatusCode) != 200 { | ||
return nil, errors.Wrapf(err, "Lambda Invoke failed: %v", res) | ||
} | ||
|
||
resultPayload := &Result{} | ||
|
||
err = json.Unmarshal(res.Payload, resultPayload) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "Failed to deserialise JSON result") | ||
} | ||
|
||
return resultPayload, nil | ||
} | ||
|
||
// WriteCertificate write the generated certificate out to a file | ||
func WriteCertificate(certificateFilename string, certificateContent string) error { | ||
err := ioutil.WriteFile(certificateFilename, bytes.NewBufferString(certificateContent).Bytes(), 0600) | ||
if err != nil { | ||
return errors.Wrap(err, "Failed to write certificate file") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Debug write debug messages if it is enabled | ||
func Debug(message string, args ...interface{}) { | ||
if debug { | ||
fmt.Fprintf(os.Stderr, message, args...) | ||
} | ||
} |
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,78 @@ | ||
package bless | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"testing" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/lambda" | ||
|
||
"github.com/stretchr/testify/mock" | ||
"github.com/stretchr/testify/require" | ||
"github.com/versent/bless/mocks" | ||
) | ||
|
||
func TestLoadPublicKey(t *testing.T) { | ||
|
||
tfile := mustWriteTempfile("data") | ||
|
||
data, err := LoadPublicKey(tfile.Name()) | ||
require.Nil(t, err) | ||
|
||
require.Len(t, data, 4) | ||
|
||
_, err = LoadPublicKey("badfile") | ||
require.Error(t, err) | ||
|
||
} | ||
|
||
func TestValidatePublicKey(t *testing.T) { | ||
data, err := ValidatePublicKey([]byte{0xa, 0xb, 0xc, 0xd}) | ||
require.Nil(t, err) | ||
require.Len(t, data, 4) | ||
|
||
_, err = ValidatePublicKey([]byte{}) | ||
require.Error(t, err) | ||
} | ||
|
||
func TestWriteCertificate(t *testing.T) { | ||
err := WriteCertificate(fmt.Sprintf("%s/%s-%d", os.TempDir(), "abc", time.Now().Unix()), "data") | ||
require.Nil(t, err) | ||
err = WriteCertificate(fmt.Sprintf("nothere/%s-%d", "abc", time.Now().Unix()), "data") | ||
require.Error(t, err) | ||
} | ||
|
||
func TestInvokeBlessLambda(t *testing.T) { | ||
|
||
lambdaMock := &mocks.LambdaAPI{} | ||
|
||
lambdaSvc = lambdaMock | ||
|
||
result := &lambda.InvokeOutput{ | ||
StatusCode: aws.Int64(200), | ||
Payload: []byte(`{"certificate":"data"}`), | ||
} | ||
|
||
lambdaMock.On("Invoke", mock.AnythingOfType("*lambda.InvokeInput")).Return(result, nil) | ||
|
||
res, err := InvokeBlessLambda(aws.String("us-west-2"), aws.String("whatever"), []byte("whatever")) | ||
require.Nil(t, err) | ||
require.Len(t, res.Certificate, 4) | ||
} | ||
|
||
func mustWriteTempfile(data string) *os.File { | ||
tfile, err := ioutil.TempFile(os.TempDir(), "test") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
_, err = tfile.WriteString("data") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return tfile | ||
} |
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,88 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"log" | ||
"os" | ||
|
||
"github.com/alecthomas/kingpin" | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/versent/bless" | ||
) | ||
|
||
var ( | ||
app = kingpin.New("versent-bless", "A command line client for netflix bless.") | ||
debug = app.Flag("debug", "Enable debug mode.").Bool() | ||
login = app.Command("login", "Login and retrieve a key.") | ||
|
||
region = login.Arg("region", "AWS Region.").Required().String() | ||
lambdaFunctionName = login.Arg("lambda_function_name", "Lambda function name.").Required().String() | ||
bastionUser = login.Arg("bastion_user", "Bastion user.").Required().String() | ||
bastionUserIP = login.Arg("bastion_user_ip", "Bastion user IP.").Required().String() | ||
remoteUsernames = login.Arg("remote_usernames", "Remote user names.").Required().String() | ||
bastionIps = login.Arg("bastion_ips", "Bastion IPs.").Required().String() | ||
bastionCommand = login.Arg("bastion_command", "Bastion command.").Required().String() | ||
publicKeyToSign = login.Arg("public_key_to_sign", "Public key to sign.").Required().String() | ||
certificateFilename = login.Arg("certificate_filename", "Certificate filename.").Required().String() | ||
kmsAuthToken = login.Arg("kmsauth_token", "KMS Auth Token.").String() | ||
) | ||
|
||
func main() { | ||
|
||
switch kingpin.MustParse(app.Parse(os.Args[1:])) { | ||
case login.FullCommand(): | ||
|
||
bless.ConfigureAws(&aws.Config{Region: region}) | ||
|
||
bless.SetDebug(*debug) | ||
|
||
publicKeyData, err := bless.LoadPublicKey(*publicKeyToSign) | ||
if err != nil { | ||
log.Fatalf("%+v\n", err) | ||
} | ||
|
||
bless.Debug("publicKeyData: %s", string(publicKeyData)) | ||
|
||
publicKey, err := bless.ValidatePublicKey(publicKeyData) | ||
if err != nil { | ||
log.Fatalf("%+v\n", err) | ||
} | ||
|
||
payload := &bless.Payload{ | ||
BastionUser: *bastionUser, | ||
BastionUserIP: *bastionUserIP, | ||
RemoteUsernames: *remoteUsernames, | ||
BastionIps: *bastionIps, | ||
BastionCommand: *bastionCommand, | ||
PublicKeyToSign: publicKey, | ||
} | ||
|
||
if kmsAuthToken != nil { | ||
payload.KmsAuthToken = *kmsAuthToken | ||
} | ||
|
||
bless.Debug("payload: %v", payload) | ||
|
||
payloadJSON, err := json.Marshal(payload) | ||
if err != nil { | ||
log.Fatalf("%+v\n", err) | ||
} | ||
|
||
log.Printf("payload_json is: %s", string(payloadJSON)) | ||
|
||
resultPayload, err := bless.InvokeBlessLambda(region, lambdaFunctionName, payloadJSON) | ||
if err != nil { | ||
log.Fatalf("%+v\n", err) | ||
} | ||
|
||
bless.Debug("resultPayload: %v", resultPayload) | ||
|
||
err = bless.WriteCertificate(*certificateFilename, resultPayload.Certificate) | ||
if err != nil { | ||
log.Fatalf("%+v\n", err) | ||
} | ||
|
||
log.Printf("Wrote Certificate to: %s", *certificateFilename) | ||
|
||
} | ||
} |
Oops, something went wrong.