From bc5c042429c7fa93f993cfef3a7ad2b5f4b1059c Mon Sep 17 00:00:00 2001 From: stevemenezes <45364637+stevemenezes@users.noreply.github.com> Date: Fri, 4 Aug 2023 04:20:59 -0700 Subject: [PATCH] connects guac with a given aws neptune cluster endpoint (#1126) Signed-off-by: stevemenezes --- cmd/guacgql/cmd/root.go | 18 +++++++- cmd/guacgql/cmd/server.go | 89 +++++++++++++++++++++++++++++++++++++-- go.mod | 4 +- go.sum | 5 ++- guac.yaml | 8 ++++ pkg/cli/store.go | 6 +++ 6 files changed, 121 insertions(+), 9 deletions(-) diff --git a/cmd/guacgql/cmd/root.go b/cmd/guacgql/cmd/root.go index 72aef44679..542e943911 100644 --- a/cmd/guacgql/cmd/root.go +++ b/cmd/guacgql/cmd/root.go @@ -44,6 +44,13 @@ var flags = struct { arangoAddr string arangoUser string arangoPass string + + // Needed only if using neptune backend + neptuneEndpoint string + neptunePort int + neptuneRegion string + neptuneUser string + neptuneRealm string }{} var rootCmd = &cobra.Command{ @@ -66,6 +73,12 @@ var rootCmd = &cobra.Command{ flags.arangoPass = viper.GetString("arango-pass") flags.arangoAddr = viper.GetString("arango-addr") + flags.neptuneEndpoint = viper.GetString("neptune-endpoint") + flags.neptunePort = viper.GetInt("neptune-port") + flags.neptuneRegion = viper.GetString("neptune-region") + flags.neptuneUser = viper.GetString("neptune-user") + flags.neptuneRealm = viper.GetString("neptune-realm") + startServer(cmd) }, } @@ -75,8 +88,9 @@ func init() { set, err := cli.BuildFlags([]string{ "arango-addr", "arango-user", "arango-pass", - "neo4j-addr", "neo4j-user", "neo4j-pass", "neo4j-realm", "gql-test-data", - "gql-listen-port", "gql-debug", "gql-backend", "gql-trace"}) + "neo4j-addr", "neo4j-user", "neo4j-pass", "neo4j-realm", + "neptune-endpoint", "neptune-port", "neptune-region", "neptune-user", "neptune-realm", + "gql-test-data", "gql-listen-port", "gql-debug", "gql-backend", "gql-trace"}) if err != nil { fmt.Fprintf(os.Stderr, "failed to setup flag: %v", err) os.Exit(1) diff --git a/cmd/guacgql/cmd/server.go b/cmd/guacgql/cmd/server.go index 091423d7b4..16c08ec2a9 100644 --- a/cmd/guacgql/cmd/server.go +++ b/cmd/guacgql/cmd/server.go @@ -17,6 +17,7 @@ package cmd import ( "context" + "encoding/json" "fmt" "net/http" "os" @@ -27,6 +28,9 @@ import ( "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/debug" "github.com/99designs/gqlgen/graphql/playground" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/spf13/cobra" "github.com/guacsec/guac/pkg/assembler/backends/arangodb" @@ -38,9 +42,12 @@ import ( ) const ( - arango = "arango" - neo4js = "neo4j" - inmems = "inmem" + arango = "arango" + neo4js = "neo4j" + inmems = "inmem" + neptune = "neptune" + + neptuneServiceName = "neptune-db" ) func startServer(cmd *cobra.Command) { @@ -104,7 +111,7 @@ func startServer(cmd *cobra.Command) { func validateFlags() error { if flags.backend != neo4js && - flags.backend != inmems && flags.backend != arango { + flags.backend != inmems && flags.backend != arango && flags.backend != neptune { return fmt.Errorf("invalid graphql backend specified: %v", flags.backend) } return nil @@ -149,6 +156,28 @@ func getGraphqlServer(ctx context.Context) (*handler.Server, error) { return nil, fmt.Errorf("error creating inmem backend: %w", err) } + topResolver = resolvers.Resolver{Backend: backend} + + case neptune: + // TODO: rename the neo4j config to something more generic since it would be used by Neptune as well. + neptuneRequestURL := fmt.Sprintf("https://%s:%d/opencypher", flags.neptuneEndpoint, flags.neptunePort) + neptuneToken, err := generateNeptuneToken(neptuneRequestURL, flags.neptuneRegion) + if err != nil { + return nil, fmt.Errorf("failed to create password for neptune: %w", err) + } + + neptuneDBAddr := fmt.Sprintf("bolt+s://%s:%d/opencypher", flags.neptuneEndpoint, flags.neptunePort) + args := neo4j.Neo4jConfig{ + User: flags.neptuneUser, + Pass: neptuneToken, + DBAddr: neptuneDBAddr, + Realm: flags.neptuneRealm, + } + backend, err := neo4j.GetBackend(&args) + if err != nil { + return nil, fmt.Errorf("error creating neptune backend: %w", err) + } + topResolver = resolvers.Resolver{Backend: backend} default: return nil, fmt.Errorf("invalid backend specified: %v", flags.backend) @@ -164,3 +193,55 @@ func healthHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = fmt.Fprint(w, "Server is healthy") } + +// generateNeptuneToken generates a token for neptune using the AWS SDK. +func generateNeptuneToken(neptuneURL string, region string) (string, error) { + req, err := http.NewRequest(http.MethodGet, neptuneURL, nil) + if err != nil { + return "", fmt.Errorf("error creating http request for neptune: %w", err) + } + + signer, err := getAWSRequestSigner() + if err != nil { + return "", fmt.Errorf("error creating AWS request signer: %w", err) + } + + if _, err := signer.Sign(req, nil, neptuneServiceName, region, time.Now()); err != nil { + return "", fmt.Errorf("error signing neptune request: %w", err) + } + + headers := []string{"Authorization", "X-Amz-Date", "X-Amz-Security-Token"} + hdrMap := make(map[string]string) + for _, h := range headers { + hdrMap[h] = req.Header.Get(h) + } + + hdrMap["Host"] = req.Host + hdrMap["HttpMethod"] = req.Method + password, err := json.Marshal(hdrMap) + if err != nil { + return "", fmt.Errorf("error marshalling header map: %w", err) + } + + return string(password), nil +} + +// This method returns the AWS signer to be used for signing the request to be sent to Neptune Cluster. +// It checks for the presence of AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN in the environment. +// If not found, it creates a new session and gets the credentials from the session. +func getAWSRequestSigner() (*v4.Signer, error) { + accessKeyID := os.Getenv("AWS_ACCESS_KEY_ID") + secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY") + sessionToken := os.Getenv("AWS_SESSION_TOKEN") + + if accessKeyID != "" && secretAccessKey != "" && sessionToken != "" { + return v4.NewSigner(credentials.NewEnvCredentials()), nil + } + + sess, err := session.NewSession() + if err != nil { + return nil, err + } + + return v4.NewSigner(sess.Config.Credentials), nil +} diff --git a/go.mod b/go.mod index 50a60ba5df..23b8706be5 100644 --- a/go.mod +++ b/go.mod @@ -81,7 +81,7 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-logr/logr v1.2.4 // indirect - github.com/goark/errs v1.1.0 // indirect + github.com/goark/errs v1.3.2 // indirect github.com/goark/go-cvss v1.6.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect @@ -99,6 +99,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect @@ -167,6 +168,7 @@ require ( github.com/Khan/genqlient v0.6.0 github.com/Masterminds/semver v1.5.0 github.com/arangodb/go-driver v1.6.0 + github.com/aws/aws-sdk-go v1.44.284 github.com/fsnotify/fsnotify v1.6.0 github.com/go-git/go-git/v5 v5.8.1 github.com/gobwas/glob v0.2.3 diff --git a/go.sum b/go.sum index 149b9ab193..0509bbdaaa 100644 --- a/go.sum +++ b/go.sum @@ -1320,8 +1320,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/goark/errs v1.1.0 h1:FKnyw4LVyRADIjM8Nj0Up6r0/y5cfADvZAd1E+tthXE= -github.com/goark/errs v1.1.0/go.mod h1:TtaPEoadm2mzqzfXdkkfpN2xuniCFm2q4JH+c1qzaqw= +github.com/goark/errs v1.3.2 h1:ifccNe1aK7Xezt4XVYwHUqalmnfhuphnEvh3FshCReQ= +github.com/goark/errs v1.3.2/go.mod h1:ZsQucxaDFVfSB8I99j4bxkDRfNOrlKINwg72QMuRWKw= github.com/goark/go-cvss v1.6.6 h1:WJFuIWqmAw1Ilb9USv0vuX+nYzOWJp8lIujseJ/y3sU= github.com/goark/go-cvss v1.6.6/go.mod h1:H3qbfUSUlV7XtA3EwWNunvXz6OySwWHOuO+R6ZPMQPI= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -1738,6 +1738,7 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= diff --git a/guac.yaml b/guac.yaml index 701068b26c..fde69533b9 100644 --- a/guac.yaml +++ b/guac.yaml @@ -9,6 +9,14 @@ neo4j-pass: s3cr3t neo4j-addr: neo4j://localhost:7687 neo4j-realm: neo4j +# Neptune details +# Populate neptune-endpoint, neptune-port and neptune-region with accurate values. +neptune-user: username +neptune-endpoint: localhost +neptune-port: 8182 +neptune-region: us-east-1 +neptune-realm: neptune + # Nats setup nats-addr: nats://localhost:4222 diff --git a/pkg/cli/store.go b/pkg/cli/store.go index 9838e3fa78..1ba5043b33 100644 --- a/pkg/cli/store.go +++ b/pkg/cli/store.go @@ -48,6 +48,12 @@ func init() { set.String("neo4j-pass", "", "neo4j password credential to connect to graph db") set.String("neo4j-realm", "neo4j", "realm to connect to graph db") + set.String("neptune-endpoint", "localhost", "address to neptune db") + set.Int("neptune-port", 8182, "port used for neptune db connection") + set.String("neptune-region", "us-east-1", "region to connect to neptune db") + set.String("neptune-user", "", "neptune user credential to connect to graph db") + set.String("neptune-realm", "neptune", "realm to connect to graph db") + set.String("arango-addr", "http://localhost:8529", "address to arango db") set.String("arango-user", "", "arango user to connect to graph db") set.String("arango-pass", "", "arango password to connect to graph db")