Skip to content

Commit

Permalink
Merge pull request #6 from duplocloud/release/0.4.0
Browse files Browse the repository at this point in the history
Release v0.4.0
  • Loading branch information
joek-duplo committed Mar 18, 2022
2 parents dbd323a + f4902e5 commit a62d194
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 6 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=0.3.2
VERSION=0.3.3

default: all

Expand All @@ -14,4 +14,4 @@ test: all
all: duplo-aws-credential-process

duplo-aws-credential-process: Makefile duplocloud/*.go cmd/duplo-aws-credential-process/*.go
go build -ldflags "-X main.version=v$(VERSION)-dev -X main.commit=$(shell git rev-parse HEAD)" ./cmd/duplo-aws-credential-process/
go build -ldflags "-X main.version=$(VERSION)-dev -X main.commit=$(shell git rev-parse HEAD)" ./cmd/duplo-aws-credential-process/
100 changes: 100 additions & 0 deletions cmd/duplo-aws-credential-process/interactive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

import (
"errors"
"fmt"
"io"
"net"
"net/http"
"time"

"github.com/skratchdot/open-golang/open"
)

type tokenResult struct {
token string
err error
}

func tokenViaPost(baseUrl string, timeout time.Duration) (string, error) {

// Create the listener on a random port.
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return "", err
}

// Get the port being listened to.
localPort := listener.Addr().(*net.TCPAddr).Port

// Run the HTTP server on localhost.
done := make(chan tokenResult)
go func() {
mux := http.NewServeMux()

mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
var bytes []byte
var err error
completed := false
status := "ok"

// Only allow the specified Duplo to give us creds.
res.Header().Add("Access-Control-Allow-Origin", baseUrl)

// A POST means we are done, whether good or bad.
if req.Method == "POST" {
defer req.Body.Close()

completed = true

// Authorize the origin, and get the POST body.
origin := req.Header.Get("Origin")
if origin != baseUrl {
err = fmt.Errorf("unauthorized origin: %s", origin)
} else {
bytes, err = io.ReadAll(req.Body)
}
}

// Send the proper response.
if completed {
if err != nil {
res.WriteHeader(500)
status = "failed"
} else {
status = "done"
if len(bytes) == 0 {
err = errors.New("canceled")
}
}
}
_, _ = fmt.Fprintf(res, "\"%s\"\n", status)

// If we are done, send the result to the channel.
if completed {
done <- tokenResult{token: string(bytes), err: err}
}
})
_ = http.Serve(listener, mux)
}()

// Open the browser.
url := fmt.Sprintf("%s/app/user/verify-token?localAppName=duplo-aws-credential-process&localPort=%d", baseUrl, localPort)
err = open.Run(url)
dieIf(err, "failed to open interactive browser session")

// Wait for the token result, and return it.
select {
case tokenResult := <-done:
return tokenResult.token, tokenResult.err
case <-time.After(timeout):
return "", errors.New("timed out")
}
}

func mustTokenInteractive(host string) string {
token, err := tokenViaPost(host, 180*time.Second)
dieIf(err, "failed to get token from interactive browser session")

return token
}
24 changes: 20 additions & 4 deletions cmd/duplo-aws-credential-process/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@ func outputCreds(creds *AwsConfigOutput, cacheKey string) {
os.Stdout.WriteString("\n")
}

func mustDuploClient(host, token string, interactive bool) *duplocloud.Client {
// Possibly get a token from an interactive process.
if token == "" {
if !interactive {
log.Fatalf("%s: --token not specified and --interactive mode is disabled", os.Args[0])
}

token = mustTokenInteractive(host)
}

// Create the client.
client, err := duplocloud.NewClient(host, token)
dieIf(err, "invalid arguments")

return client
}

var commit string
var version string

Expand All @@ -76,6 +93,7 @@ func main() {
tenantID := flag.String("tenant", "", "Get credentials for the given tenant")
debug := flag.Bool("debug", false, "Turn on verbose (debugging) output")
noCache = flag.Bool("no-cache", false, "Disable caching (not recommended)")
interactive := flag.Bool("interactive", false, "Allow getting Duplo credentials via an interactive browser session (experimental)")
showVersion := flag.Bool("version", false, "Output version information and exit")
flag.Parse()

Expand Down Expand Up @@ -103,10 +121,6 @@ func main() {
duplocloud.LogLevel = duplocloud.TRACE
}

// Prepare the connection to the duplo API.
client, err := duplocloud.NewClient(*host, *token)
dieIf(err, "invalid arguments")

// Prepare the cache directory
mustInitCache()

Expand All @@ -123,6 +137,7 @@ func main() {

// Otherwise, get the credentials from Duplo.
if creds == nil {
client := mustDuploClient(*host, *token, *interactive)
result, err := client.AdminGetJITAwsCredentials()
dieIf(err, "failed to get credentials")
creds = convertCreds(result)
Expand All @@ -143,6 +158,7 @@ func main() {

// Otherwise, get the credentials from Duplo.
if creds == nil {
client := mustDuploClient(*host, *token, *interactive)

// If it doesn't look like a UUID, get the tenant ID from the name.
if len(*tenantID) < 32 {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/duplocloud/duplo-aws-jit

go 1.16

require github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=

0 comments on commit a62d194

Please sign in to comment.