diff --git a/.gitignore b/.gitignore index e69de29..eef5814 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,3 @@ +CLI/cli +.idea +CLI/.env \ No newline at end of file diff --git a/CLI/go.mod b/CLI/go.mod new file mode 100644 index 0000000..83c6157 --- /dev/null +++ b/CLI/go.mod @@ -0,0 +1,27 @@ +module cli + +go 1.20 + +require ( + github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/api v1.9.1 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/peterbourgon/ff/v3 v3.3.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect +) diff --git a/CLI/go.sum b/CLI/go.sum new file mode 100644 index 0000000..1aa5911 --- /dev/null +++ b/CLI/go.sum @@ -0,0 +1,66 @@ +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38= +github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/peterbourgon/ff/v3 v3.3.0 h1:PaKe7GW8orVFh8Unb5jNHS+JZBwWUMa2se0HM6/BI24= +github.com/peterbourgon/ff/v3 v3.3.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/CLI/main.go b/CLI/main.go new file mode 100644 index 0000000..07e7de9 --- /dev/null +++ b/CLI/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "cli/parse_file" + "cli/vault_actions" + "context" + "flag" + "fmt" + "log" + "os" + + vault "github.com/hashicorp/vault/api" + "github.com/peterbourgon/ff/v3/ffcli" +) + +func main() { + + rootFlagSet := flag.NewFlagSet("textctl", flag.ExitOnError) + + secure_env := parse_file.Parsefile() + + config := vault.DefaultConfig() + config.Address = secure_env.Host + client, err := vault.NewClient(config) + + if err != nil { + log.Fatalf("unable to initialize Vault client: %v", err) + } + client.SetToken(secure_env.Token) + + root := &ffcli.Command{ + ShortUsage: "textctl [flags] ", + FlagSet: rootFlagSet, + Subcommands: []*ffcli.Command{vault_actions.Sdelete_var(client), vault_actions.Screate_var(client), + vault_actions.Sget_var(client), vault_actions.Spush_var(client)}, + Exec: func(context.Context, []string) error { + return flag.ErrHelp + }, + } + + if err := root.ParseAndRun(context.Background(), os.Args[1:]); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} diff --git a/CLI/parse_file/parse_file.go b/CLI/parse_file/parse_file.go new file mode 100644 index 0000000..2bfc3d4 --- /dev/null +++ b/CLI/parse_file/parse_file.go @@ -0,0 +1,30 @@ +package parse_file + +import ( + "fmt" + "os" + "strconv" + + "github.com/joho/godotenv" +) + +type Configuration struct { + Project string + Host string + Port int + Token string +} + +func Parsefile() Configuration { + var config Configuration + err := godotenv.Load(".env") + if err != nil { + fmt.Println("File reading error", err) + return config + } + config.Project = os.Getenv("SECURE_ENV_PROJECT") + config.Host = os.Getenv("SECURE_ENV_HOST") + config.Port, err = strconv.Atoi(os.Getenv("SECURE_ENV_PORT")) + config.Token = os.Getenv("SECURE_ENV_TOKEN") + return config +} diff --git a/CLI/vault_actions/push.go b/CLI/vault_actions/push.go new file mode 100644 index 0000000..e8ac3fe --- /dev/null +++ b/CLI/vault_actions/push.go @@ -0,0 +1,69 @@ +package vault_actions + +import ( + "bufio" + "context" + "fmt" + "os" + "strings" + + vault "github.com/hashicorp/vault/api" + "github.com/peterbourgon/ff/v3/ffcli" +) + +type Variable struct { + Key string + Value string +} + +func linesInFile(fileName string) []string { + f, _ := os.Open(fileName) + scanner := bufio.NewScanner(f) + result := []string{} + for scanner.Scan() { + line := scanner.Text() + result = append(result, line) + } + return result +} + +func create_var(data string) Variable { + var variable Variable + variable.Key = strings.Split(data, "=")[0] + variable.Value = strings.Split(data, "=")[1] + variable.Value = strings.Trim(variable.Value, "\"") + return variable +} + +func isVariableSecureEnvLocal(variable Variable) bool { + return strings.Contains(variable.Key, "SECURE_ENV_") +} + +func Spush_var(client *vault.Client) *ffcli.Command { + + push := &ffcli.Command{ + Name: "push", + ShortUsage: "push", + ShortHelp: "Push a file to Vault.", + Exec: func(_ context.Context, args []string) error { + push(client) + return nil + }, + } + return push +} + +func push(client *vault.Client) bool { + + lines := linesInFile(".env") + secretData := map[string]interface{}{} + for _, line := range lines { + variable := create_var(line) + if !isVariableSecureEnvLocal(variable) { + secretData[variable.Key] = variable.Value + } + } + Screate("secure-env", secretData, client) + fmt.Println("Pushed successfully.") + return true +} diff --git a/CLI/vault_actions/screate.go b/CLI/vault_actions/screate.go new file mode 100644 index 0000000..d1e5b7b --- /dev/null +++ b/CLI/vault_actions/screate.go @@ -0,0 +1,67 @@ +package vault_actions + +import ( + "context" + "fmt" + "log" + + vault "github.com/hashicorp/vault/api" + "github.com/peterbourgon/ff/v3/ffcli" +) + +func Screate_var(client *vault.Client) *ffcli.Command { + + screate := &ffcli.Command{ + Name: "screate", + ShortUsage: "screate [ ...]", + ShortHelp: "Create a new secret.", + Exec: func(_ context.Context, args []string) error { + + if n := len(args); n != 3 { + return fmt.Errorf("create requires 3 arguments, name, key and content") + } + + secret, err := client.KVv2("secret").Get(context.Background(), args[0]) + if err != nil { + log.Fatalf("unable to read secret: %v", err) + } + + secretData := secret.Data + secretData[args[1]] = args[2] + fmt.Println(secretData) + Screate(args[0], secretData, client) + return nil + }, + } + return screate +} + +func Screate(name string, secretData map[string]interface{}, client *vault.Client) bool { + existingSecret, err := client.KVv2("secret").Get(context.Background(), name) + if err != nil { + log.Fatalf("unable to read secret: %v", err) + } + + if existingSecret != nil { + existingData := existingSecret.Data + for k, v := range secretData { + existingData[k] = v + } + + _, err = client.KVv2("secret").Put(context.Background(), name, existingData) + if err != nil { + log.Fatalf("unable to write secret: %v", err) + } + fmt.Println("deja un secret existant") + + } else { + _, err = client.KVv2("secret").Put(context.Background(), name, secretData) + if err != nil { + log.Fatalf("unable to write secret: %v", err) + } + fmt.Println("pas de secret existant") + } + + fmt.Println("Secret written successfully.") + return true +} diff --git a/CLI/vault_actions/sdelete.go b/CLI/vault_actions/sdelete.go new file mode 100644 index 0000000..2ce4fd4 --- /dev/null +++ b/CLI/vault_actions/sdelete.go @@ -0,0 +1,44 @@ +package vault_actions + +import ( + "context" + "fmt" + "log" + + vault "github.com/hashicorp/vault/api" + "github.com/peterbourgon/ff/v3/ffcli" +) + +func Sdelete_var(client *vault.Client) *ffcli.Command { + + sdelete := &ffcli.Command{ + Name: "sdelete", + ShortUsage: "sdelete [ ...]", + ShortHelp: "Delete a secret.", + Exec: func(_ context.Context, args []string) error { + + if n := len(args); n != 1 { + return fmt.Errorf("create requires 1 arguments, name and key but you provided %d", n) + } + sdelete(args[0], client) + return nil + }, + } + return sdelete +} + +func sdelete(name string, client *vault.Client) { + + _, err := client.KVv2("secret").Get(context.Background(), name) + + if err != nil { + log.Fatalf("%v", err) + } + + err = client.KVv2("secret").Delete(context.Background(), name) + if err != nil { + log.Fatalf("unable to delete secret: %v", err) + } else { + log.Printf("secret %s deleted", name) + } +} diff --git a/CLI/vault_actions/sget.go b/CLI/vault_actions/sget.go new file mode 100644 index 0000000..ba8c90f --- /dev/null +++ b/CLI/vault_actions/sget.go @@ -0,0 +1,44 @@ +package vault_actions + +import ( + "context" + "fmt" + "log" + + vault "github.com/hashicorp/vault/api" + "github.com/peterbourgon/ff/v3/ffcli" +) + +func Sget_var(client *vault.Client) *ffcli.Command { + + sget := &ffcli.Command{ + Name: "sget", + ShortUsage: "sget [ ...]", + ShortHelp: "get the content of a secret.", + Exec: func(_ context.Context, args []string) error { + + if n := len(args); n != 2 { + return fmt.Errorf("create requires 2 arguments, name and key") + } + sget(args[0], args[1], client) + return nil + }, + } + return sget +} + +func sget(name string, key string, client *vault.Client) { + + secret, err := client.KVv2("secret").Get(context.Background(), name) + + if err != nil { + log.Fatalf("unable to read secret: %v", err) + } + + value, ok := secret.Data[key].(string) + if !ok { + log.Fatalf("value type assertion failed: %T %#v", secret.Data[key], secret.Data[key]) + } + + fmt.Println("Access granted! the value of the secret is: ", value) +}