Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(connect): Fetch password from Secrets Manager #56

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 11 additions & 31 deletions cmd/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cmd
import (
"flag"
"fmt"
"log"

"github.com/birdiecare/dbc/handler"
"github.com/common-nighthawk/go-figure"
Expand All @@ -15,9 +16,7 @@ import (
var host string
var port string
var region string
var user string
var localport string
var iam bool

// connectCmd represents the connect command
var connectCmd = &cobra.Command{
Expand All @@ -29,7 +28,7 @@ Opens a connection with the given database @ the given port at localhost:5432
dbc connect

Password Authentication:
Use an existing database user password to authenticate against the dtaabase once the connection is open.
Use an existing database user password to authenticate against the database once the connection is open.

dbc connect -h $host -u $user

Expand All @@ -38,43 +37,26 @@ Then connect to the DB using the password for $user
psql -h localhost -p 5432 -U $user -d $database --password

Then paste your password for $user.

IAM Authentication:
Use IAM Authentication to authenticate as a User to the DB

dbc connect -h $host -u $user --iam

This command will output a Token to use as a password when connecting to your database.

psql -h localhost -p ${localport} -U ${user} -d ${database} --password

Then paste the token`,
`,

Run: func(cmd *cobra.Command, args []string) {
flag.Parse()

// Assert AWS Creds
handler.AssertCredentials()

if host == "" {
host = handler.FuzzEndpoints(iam)
}
dbname, host := handler.FuzzEndpoints()

user, password := handler.FuzzUsers(dbname)

if iam {
fmt.Println("")
myFigure := figure.NewFigure("DBC Connect IAM", "", true)
myFigure.Print()
myFigure := figure.NewFigure("DBC Connect", "", true)
myFigure.Print()

fmt.Println("")
handler.GenerateToken(host, port, region, user)
} else {
myFigure := figure.NewFigure("DBC Connect", "", true)
myFigure.Print()
fmt.Println("")
log.Println("Connect with user " + user + " and password " + password)

fmt.Println("")
}
// Start Port-Forwarding Session
handler.Handler(region, host, port, localport)
handler.PortForward(region, host, port, localport)
},
}

Expand All @@ -85,7 +67,5 @@ func init() {
connectCmd.Flags().StringVarP(&host, "host", "H", "", "Hostname of the Database to open a connection to. If a hostname is not provided, a fuzzyfind list with be presented to select a database, and subsequently, a user to connect to`")
connectCmd.Flags().StringVarP(&port, "port", "p", "5432", "Port of the Datbase to open a connection to (default 5432)")
connectCmd.Flags().StringVarP(&region, "region", "r", "eu-west-2", "Region of the Datbase to open a connection to (default eu-west-2)")
connectCmd.Flags().StringVarP(&user, "user", "u", "", "The DB User to open a connection with")
connectCmd.Flags().StringVarP(&localport, "localport", "l", "5432", "Local Port to forward database connection to (default 5432)")
connectCmd.Flags().BoolVarP(&iam, "iam", "I", false, "Bool: Use IAM Authentication for Database Connection - Generates a password token using IAM Authentication")
}
36 changes: 11 additions & 25 deletions handler/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ type db struct {
IAM bool
}

var endpoints []db

// Return DB Type list of Database ClusterId's, InstanceId's and Endpoints.
func getEndpoints() {
func getEndpoints() []db {

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
Expand All @@ -44,6 +42,8 @@ func getEndpoints() {
log.Fatal(err)
}

var endpoints []db

// Get clusters
for _, i := range cluster_list.DBClusters {
endpoints = append(endpoints, db{
Expand All @@ -65,25 +65,13 @@ func getEndpoints() {
})
}
}
}

func FuzzEndpoints(iam bool) string {

var returnEndpoint string

getEndpoints()
return endpoints
}

// if IAM - remove non-IAM enabled db endpoints from selection list
if iam {
var endpoints_iam []db
for _, i := range endpoints {
if i.IAM == true {
endpoints_iam = append(endpoints_iam, i)
}
}
func FuzzEndpoints() (string, string) {

endpoints = endpoints_iam
}
endpoints := getEndpoints()

idx, err := fuzzyfinder.Find(
endpoints,
Expand All @@ -105,12 +93,10 @@ func FuzzEndpoints(iam bool) string {
}

if len(endpoints[idx].Endpoints) > 1 {
returnEndpoint = fuzzCluster(endpoints[idx].Endpoints)
} else {
returnEndpoint = endpoints[idx].Endpoints[0]
return endpoints[idx].DBId, fuzzCluster(endpoints[idx].Endpoints)
}

return returnEndpoint
return endpoints[idx].DBId, endpoints[idx].Endpoints[0]
}

func fuzzCluster(e []string) string {
Expand All @@ -121,7 +107,7 @@ func fuzzCluster(e []string) string {
if i == -1 {
return ""
}
return fmt.Sprintf("Role: %s", isRole(e, i))
return fmt.Sprintf("Role: %s", getRole(e, i))
}))
if err != nil {
log.Fatal(err)
Expand All @@ -130,7 +116,7 @@ func fuzzCluster(e []string) string {
return e[idx]
}

func isRole(e []string, i int) string {
func getRole(e []string, i int) string {
if e[i] == e[0] {
return "Reader"
} else {
Expand Down
2 changes: 1 addition & 1 deletion handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package handler
import "log"

// Handler package entrypoint
func Handler(r string, h string, p string, lp string) {
func PortForward(r string, h string, p string, lp string) {

log.Println("Opening connection for:", h)
createSession(getBastion(), h, p, lp)
Expand Down
98 changes: 98 additions & 0 deletions handler/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package handler

import (
"context"
"encoding/json"
"log"

"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
"github.com/ktr0731/go-fuzzyfinder"
)

type SecretContent struct {
User string
Password string
}

func getUserSecretsForHost(host string) []types.SecretListEntry {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Panic("configuration error: " + err.Error())
}

svc := secretsmanager.NewFromConfig(cfg)

l_params := &secretsmanager.ListSecretsInput{
Filters: []types.Filter{
types.Filter{
Key: "tag-key",
Values: []string{"birdie:database_name"},
},
types.Filter{
Key: "tag-value",
Values: []string{host},
},
},
}

secrets, err := svc.ListSecrets(context.Background(), l_params)

if err != nil {
log.Fatal(err)
}

return secrets.SecretList
}

func getPasswordForUser(secretId string) (string, string) {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Panic("configuration error: " + err.Error())
}

svc := secretsmanager.NewFromConfig(cfg)

res, err := svc.GetSecretValue(context.Background(), &secretsmanager.GetSecretValueInput{
SecretId: &secretId,
})

if err != nil {
log.Fatal(err)
}

var secretContent SecretContent

err = json.Unmarshal([]byte(*res.SecretString), &secretContent)

if err != nil {
log.Fatal(err)
}

return secretContent.User, secretContent.Password

}

func FuzzUsers(host string) (string, string) {
userSecrets := getUserSecretsForHost(host)

idx, err := fuzzyfinder.Find(
userSecrets,
func(i int) string {
for _, tag := range userSecrets[i].Tags {
if *tag.Key == "birdie:role_name" {
return *tag.Value
}
}
return "Unknown"
},
fuzzyfinder.WithHeader("Select a user to connect with:"),
)

if err != nil {
log.Fatal(err)
}

return getPasswordForUser(*userSecrets[idx].Name)
}