From 14f2a7c0ce174b1ad6bfd44142a001f0ae632235 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Wed, 24 Nov 2021 16:37:04 -0800 Subject: [PATCH 01/22] Started implementing the list feature --- config.yaml | 4 ++ main.go | 100 +++++++++++++++++++++++++++++----------- main_test.go | 12 ++--- pkg/helpers/helpers.go | 2 +- pkg/platform/linux.go | 4 ++ pkg/platform/mac.go | 6 ++- pkg/platform/windows.go | 4 ++ pkg/vault/aws.go | 4 ++ pkg/vault/azure.go | 4 ++ pkg/vault/hashicorp.go | 4 ++ pkg/vault/vault.go | 1 + 11 files changed, 110 insertions(+), 35 deletions(-) diff --git a/config.yaml b/config.yaml index ea9c9f2..7f1ccc4 100644 --- a/config.yaml +++ b/config.yaml @@ -1,6 +1,10 @@ +### Example Config File logging: enabled: true path: C:/temp/ +### This example shows all parameters available for currently supported +### vaults and is not meant to be used as shown. +### Only one external vault provider can be used at a time! aws: description: my_secret_name region: us-west-2 diff --git a/main.go b/main.go index 7e12772..0a7e06b 100644 --- a/main.go +++ b/main.go @@ -16,18 +16,20 @@ import ( "github.com/tonedefdev/terracreds/pkg/vault" ) -// Terracreds interface implements these methods for a credential's lifecycle -type Terracreds interface { - // Create or store an API token in a vault +// TerraCreds interface implements these methods for a credential's lifecycle +type TerraCreds interface { + // Create or store a secret in a vault Create(cfg api.Config, hostname string, token interface{}, user *user.User, vault vault.TerraVault) error - // Delete or forget an API token in a vault + // Delete or forget a secret in a vault Delete(cfg api.Config, command string, hostname string, user *user.User, vault vault.TerraVault) error - // Get or retrieve an API token in a vault + // Get or retrieve a secret in a vault Get(cfg api.Config, hostname string, user *user.User, vault vault.TerraVault) ([]byte, error) + // List the secrets from within a vault + List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) } -// returnProvider returns the correct struct for the specific operating system -func returnProvider(os string) Terracreds { +// NewTerraCreds is the constructor to create a TerraCreds interface +func NewTerraCreds(os string) TerraCreds { switch os { case "darwin": return &platform.Mac{} @@ -40,9 +42,9 @@ func returnProvider(os string) Terracreds { } } -// returnsVaultProvider handles returning the correct vault provider based on the -// api.Config struct -func returnVaultProvider(cfg *api.Config, hostname string) vault.TerraVault { +// NewTerrVault is the constructor to create a TerraVault interface +// for the vault provider defined in the cfg +func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { if cfg.Aws.Region != "" { vault := &vault.AwsSecretsManager{ Description: cfg.Aws.Description, @@ -50,7 +52,6 @@ func returnVaultProvider(cfg *api.Config, hostname string) vault.TerraVault { SecretName: hostname, } - // Fallback to the cfg's secret name if it isn't an empty string if cfg.Aws.SecretName != "" { vault.SecretName = cfg.Aws.SecretName } @@ -65,7 +66,6 @@ func returnVaultProvider(cfg *api.Config, hostname string) vault.TerraVault { VaultUri: cfg.Azure.VaultUri, } - // Fallback to the cfg's secret name if it isn't an empty string if cfg.Azure.SecretName != "" { vault.SecretName = cfg.Azure.SecretName } @@ -94,16 +94,16 @@ func returnVaultProvider(cfg *api.Config, hostname string) vault.TerraVault { func main() { var cfg api.Config - version := "2.0.0" + version := "2.0.1" err := helpers.LoadConfig(&cfg) if err != nil { helpers.CheckError(err) } - provider := returnProvider(runtime.GOOS) - if provider == nil { - fmt.Fprintf(color.Output, "%s: Terracreds cannot run on this platform: '%s'\n", color.RedString("ERROR"), runtime.GOOS) + terraCreds := NewTerraCreds(runtime.GOOS) + if terraCreds == nil { + fmt.Fprintf(color.Output, "%s: terracreds cannot run on this platform: '%s'\n", color.RedString("ERROR"), runtime.GOOS) return } @@ -136,12 +136,13 @@ func main() { return nil } - vaultProvider := returnVaultProvider(&cfg, c.String("hostname")) + terraVault := NewTerraVault(&cfg, c.String("hostname")) hostname := helpers.GetSecretName(&cfg, c.String("hostname")) user, err := user.Current() helpers.CheckError(err) - err = Terracreds.Create(provider, cfg, hostname, c.String("apiToken"), user, vaultProvider) + + err = terraCreds.Create(cfg, hostname, c.String("apiToken"), user, terraVault) if err != nil { helpers.CheckError(err) } @@ -173,12 +174,14 @@ func main() { return nil } - vaultProvider := returnVaultProvider(&cfg, c.String("hostname")) + terraVault := NewTerraVault(&cfg, c.String("hostname")) hostname := helpers.GetSecretName(&cfg, c.String("hostname")) + method := os.Args[1] user, err := user.Current() helpers.CheckError(err) - err = Terracreds.Delete(provider, cfg, os.Args[1], hostname, user, vaultProvider) + + err = terraCreds.Delete(cfg, method, hostname, user, terraVault) if err != nil { helpers.CheckError(err) } @@ -195,12 +198,14 @@ func main() { return nil } - vaultProvider := returnVaultProvider(&cfg, os.Args[2]) + terraVault := NewTerraVault(&cfg, os.Args[2]) hostname := helpers.GetSecretName(&cfg, os.Args[2]) + method := os.Args[1] user, err := user.Current() helpers.CheckError(err) - err = Terracreds.Delete(provider, cfg, os.Args[1], hostname, user, vaultProvider) + + err = terraCreds.Delete(cfg, method, hostname, user, terraVault) if err != nil { helpers.CheckError(err) } @@ -219,7 +224,7 @@ func main() { }, }, Action: func(c *cli.Context) error { - helpers.GenerateTerracreds(c) + helpers.GenerateTerraCreds(c) return nil }, }, @@ -231,13 +236,14 @@ func main() { user, err := user.Current() helpers.CheckError(err) - vaultProvider := returnVaultProvider(&cfg, os.Args[2]) + terraVault := NewTerraVault(&cfg, os.Args[2]) hostname := helpers.GetSecretName(&cfg, os.Args[2]) - token, err := Terracreds.Get(provider, cfg, hostname, user, vaultProvider) + token, err := terraCreds.Get(cfg, hostname, user, terraVault) if err != nil { helpers.CheckError(err) } + fmt.Println(string(token)) return nil } @@ -248,6 +254,45 @@ func main() { return nil }, }, + { + Name: "list", + Usage: "List the credentials stored in a vault using a list provided", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "secret-names", + Aliases: []string{"s"}, + Value: "", + Usage: "A comma separated list of secret names to retrieve to retrieved", + }, + &cli.StringFlag{ + Name: "input-file", + Aliases: []string{"f"}, + Value: "", + Usage: "The path to the file that provides the list of secrets to be retrieved", + }, + &cli.BoolFlag{ + Name: "export-as-tfvars", + Value: false, + Usage: "Exports the secret keys and values as 'TF_VARS_secret_key=secret_value' for the given operating system", + }, + &cli.BoolFlag{ + Name: "export-as-env", + Value: false, + Usage: "Exports the secret values and exposes them as environment variables for the given operating system", + }, + }, + Action: func(c *cli.Context) error { + if len(os.Args) > 1 { + + return nil + } + + msg := "A hostname was expected after the 'get' command but no argument was provided" + helpers.Logging(cfg, msg, "ERROR") + fmt.Fprintf(color.Output, "%s: %s\n", color.RedString("ERROR"), msg) + return nil + }, + }, { Name: "store", Usage: "(Terraform Only) Store or update a credential object in your vault provider of choice when 'terraform login' has been called", @@ -257,12 +302,13 @@ func main() { return nil } - vaultProvider := returnVaultProvider(&cfg, os.Args[2]) + terraVault := NewTerraVault(&cfg, os.Args[2]) hostname := helpers.GetSecretName(&cfg, os.Args[2]) user, err := user.Current() helpers.CheckError(err) - err = Terracreds.Create(provider, cfg, hostname, nil, user, vaultProvider) + + err = terraCreds.Create(cfg, hostname, nil, user, terraVault) if err != nil { helpers.CheckError(err) } diff --git a/main_test.go b/main_test.go index 612e4a3..67282eb 100644 --- a/main_test.go +++ b/main_test.go @@ -91,7 +91,7 @@ func TestGenerateTerracreds(t *testing.T) { path := t.TempDir() tfUser := path + "\\terraform.d" helpers.NewDirectory(tfUser) - helpers.GenerateTerracreds(c) + helpers.GenerateTerraCreds(c) } func TestTerracreds(t *testing.T) { @@ -100,17 +100,17 @@ func TestTerracreds(t *testing.T) { const apiToken = "9ZWRa0Ge0iQCtA.atlasv1.HpZAd8426rHFskeEFo3AzimnkfR1ldYy69zz0op0NJZ79et8nrgjw3lQfi0FyJ1o8iw" const command = "delete" - provider := returnProvider(runtime.GOOS) - vaultProvider := returnVaultProvider(&cfg, hostname) + terraCreds := NewTerraCreds(runtime.GOOS) + terraVault := NewTerraVault(&cfg, hostname) user, err := user.Current() helpers.CheckError(err) - Terracreds.Create(provider, cfg, hostname, apiToken, user, vaultProvider) - token, err := Terracreds.Get(provider, cfg, hostname, user, vaultProvider) + terraCreds.Create(cfg, hostname, apiToken, user, terraVault) + token, err := terraCreds.Get(cfg, hostname, user, terraVault) if err != nil { helpers.CheckError(err) } fmt.Println(string(token)) - Terracreds.Delete(provider, cfg, command, hostname, user, vaultProvider) + terraCreds.Delete(cfg, command, hostname, user, terraVault) } diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 556c5d4..86327d8 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -188,7 +188,7 @@ func LogLevel(level string) string { // GenerateTerracreds creates the binary to use this package as a credential helper // and optionally the terraform.rc file -func GenerateTerracreds(c *cli.Context) { +func GenerateTerraCreds(c *cli.Context) { var cliConfig string var tfPlugins string var binary string diff --git a/pkg/platform/linux.go b/pkg/platform/linux.go index 00b5b8d..1cbcfe5 100644 --- a/pkg/platform/linux.go +++ b/pkg/platform/linux.go @@ -162,3 +162,7 @@ func (l *Linux) Get(cfg api.Config, hostname string, user *user.User, vault vaul fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) return nil, err } + +func (l *Linux) List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) { + return nil, nil +} diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index f4c5a81..4f584bb 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -24,7 +24,7 @@ func (m *Mac) Create(cfg api.Config, hostname string, token interface{}, user *u method = "Updated" if cfg.Aws.Region != "" || cfg.Azure.VaultUri != "" { - return errors.New("Terracreds doesn't currently support using the AWS or Azure provider on macOS") + return errors.New("terracreds doesn't currently support using the AWS or Azure provider on macOS") } if vault != nil && cfg.HashiVault.VaultUri != "" { @@ -166,3 +166,7 @@ func (m *Mac) Get(cfg api.Config, hostname string, user *user.User, vault vault. fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) return nil, err } + +func (m *Mac) List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) { + return nil, nil +} diff --git a/pkg/platform/windows.go b/pkg/platform/windows.go index 2c65a3f..1a8bddc 100644 --- a/pkg/platform/windows.go +++ b/pkg/platform/windows.go @@ -165,3 +165,7 @@ func (w *Windows) Get(cfg api.Config, hostname string, user *user.User, vault va fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) return nil, err } + +func (w *Windows) List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) { + return nil, nil +} diff --git a/pkg/vault/aws.go b/pkg/vault/aws.go index 3bdbcd9..8ba3c76 100644 --- a/pkg/vault/aws.go +++ b/pkg/vault/aws.go @@ -181,3 +181,7 @@ func (asm *AwsSecretsManager) Get() ([]byte, error) { return []byte(*result.SecretString), err } + +func (asm *AwsSecretsManager) List(secretNames []string) ([]string, error) { + return nil, nil +} diff --git a/pkg/vault/azure.go b/pkg/vault/azure.go index 0c494dc..605994d 100644 --- a/pkg/vault/azure.go +++ b/pkg/vault/azure.go @@ -72,3 +72,7 @@ func (akv *AzureKeyVault) Get() ([]byte, error) { get, err := client.GetSecret(ctx, akv.VaultUri, secret, "") return []byte(*get.Value), err } + +func (akv *AzureKeyVault) List(secretNames []string) ([]string, error) { + return nil, nil +} diff --git a/pkg/vault/hashicorp.go b/pkg/vault/hashicorp.go index d73e89c..c8cac0f 100644 --- a/pkg/vault/hashicorp.go +++ b/pkg/vault/hashicorp.go @@ -83,3 +83,7 @@ func (hc *HashiVault) Get() ([]byte, error) { return []byte(value), err } + +func (hc *HashiVault) List(secretNames []string) ([]string, error) { + return nil, nil +} diff --git a/pkg/vault/vault.go b/pkg/vault/vault.go index 1bc980c..cef95dd 100644 --- a/pkg/vault/vault.go +++ b/pkg/vault/vault.go @@ -6,4 +6,5 @@ type TerraVault interface { Create(secretValue string, method string) error Delete() error Get() ([]byte, error) + List(secretNames []string) ([]string, error) } From 966dace605b0713bfed10bcf86e4867c6770a818 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 31 May 2022 15:13:24 -0700 Subject: [PATCH 02/22] Fixed issue where terraform login was passing empty value. Adding ability to list credentials --- config.yaml | 19 +----------- go.mod | 1 + main.go | 67 +++++++++++++++++++++++++++++++++++------ pkg/platform/linux.go | 28 ++++++++++++++--- pkg/platform/mac.go | 10 ++++-- pkg/platform/windows.go | 35 ++++++++++++++++++--- pkg/vault/azure.go | 3 +- 7 files changed, 124 insertions(+), 39 deletions(-) diff --git a/config.yaml b/config.yaml index 7f1ccc4..b0732c8 100644 --- a/config.yaml +++ b/config.yaml @@ -1,21 +1,4 @@ ### Example Config File logging: enabled: true - path: C:/temp/ -### This example shows all parameters available for currently supported -### vaults and is not meant to be used as shown. -### Only one external vault provider can be used at a time! -aws: - description: my_secret_name - region: us-west-2 - secretName: my-secret -azure: - secretName: my-secret-name - useMSI: true - vaultUri: https://keyvault.azure.net -hcvault: - environmentTokenName: HASHI_TOKEN - keyVaultPath: kv - secretName: something-dumb - secretPath: tfe - vaultUri: http://localhost:8200 \ No newline at end of file + path: C:/temp/ \ No newline at end of file diff --git a/go.mod b/go.mod index 43dd662..53b9ed0 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/fatih/color v1.9.0 github.com/google/go-cmp v0.5.6 // indirect github.com/hashicorp/vault/api v1.1.1 + github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 github.com/urfave/cli/v2 v2.2.0 github.com/zalando/go-keyring v0.1.0 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect diff --git a/main.go b/main.go index 0a7e06b..e9a8e4b 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ type TerraCreds interface { // Get or retrieve a secret in a vault Get(cfg api.Config, hostname string, user *user.User, vault vault.TerraVault) ([]byte, error) // List the secrets from within a vault - List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) + List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) } // NewTerraCreds is the constructor to create a TerraCreds interface @@ -94,7 +94,7 @@ func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { func main() { var cfg api.Config - version := "2.0.1" + version := "2.0.2" err := helpers.LoadConfig(&cfg) if err != nil { @@ -113,6 +113,43 @@ func main() { UsageText: "Directly store credentials from Terraform using 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -t myAPItoken'", Version: version, Commands: []*cli.Command{ + { + Name: "config", + Usage: "View or modify the Terracreds configuration file", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "hostname", + Aliases: []string{"n"}, + Value: "place_holder", + Usage: "The name of the Terraform Cloud/Enterprise server's hostname or the name of the secret. This is also the display name of the credential object", + }, + &cli.StringFlag{ + Name: "apiToken", + Aliases: []string{"t"}, + Value: "", + Usage: "The Terraform Cloud/Enterprise API authorization token or other secret value to be securely stored in your vault provider of choice", + }, + }, + Action: func(c *cli.Context) error { + if len(os.Args) == 2 { + fmt.Fprintf(color.Output, "%s: No hostname or token was specified. Use 'terracreds create -h' to print help info\n", color.RedString("ERROR")) + return nil + } + + terraVault := NewTerraVault(&cfg, c.String("hostname")) + hostname := helpers.GetSecretName(&cfg, c.String("hostname")) + + user, err := user.Current() + helpers.CheckError(err) + + err = terraCreds.Create(cfg, hostname, c.String("apiToken"), user, terraVault) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, { Name: "create", Usage: "Manually create or update a credential object in the vault provider of your choice that contains the Terraform Cloud/Enterprise authorization token or another secret", @@ -256,13 +293,13 @@ func main() { }, { Name: "list", - Usage: "List the credentials stored in a vault using a list provided", + Usage: "List the credentials stored in a vault using a provided set of secret names", Flags: []cli.Flag{ &cli.StringFlag{ Name: "secret-names", Aliases: []string{"s"}, Value: "", - Usage: "A comma separated list of secret names to retrieve to retrieved", + Usage: "A comma separated list of secret names to be retrieved", }, &cli.StringFlag{ Name: "input-file", @@ -275,14 +312,26 @@ func main() { Value: false, Usage: "Exports the secret keys and values as 'TF_VARS_secret_key=secret_value' for the given operating system", }, - &cli.BoolFlag{ - Name: "export-as-env", - Value: false, - Usage: "Exports the secret values and exposes them as environment variables for the given operating system", - }, }, Action: func(c *cli.Context) error { if len(os.Args) > 1 { + terraVault := NewTerraVault(&cfg, os.Args[2]) + secretNames := strings.Split(c.String("secret-names"), ",") + + user, err := user.Current() + if err != nil { + helpers.CheckError(err) + } + + list, err := terraCreds.List(c, cfg, secretNames, user, terraVault) + if err != nil { + helpers.CheckError(err) + } + + for _, secret := range list { + value := fmt.Sprintf("%s\n", secret) + print(value) + } return nil } diff --git a/pkg/platform/linux.go b/pkg/platform/linux.go index 1cbcfe5..fa96e68 100644 --- a/pkg/platform/linux.go +++ b/pkg/platform/linux.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/fatih/color" + "github.com/urfave/cli/v2" "github.com/zalando/go-keyring" "github.com/tonedefdev/terracreds/api" @@ -45,11 +46,14 @@ func (l *Linux) Create(cfg api.Config, hostname string, token interface{}, user } if token == nil { - err = json.NewDecoder(os.Stdin).Decode(&api.CredentialResponse{}) + var response api.CredentialResponse + + err = json.NewDecoder(os.Stdin).Decode(&response) if err != nil { helpers.CheckError(err) } - err = keyring.Set(hostname, string(user.Username), api.CredentialResponse{}.Token) + + err = keyring.Set(hostname, string(user.Username), response.Token) return err } @@ -163,6 +167,22 @@ func (l *Linux) Get(cfg api.Config, hostname string, user *user.User, vault vaul return nil, err } -func (l *Linux) List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) { - return nil, nil +func (l *Linux) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { + var secretValues []string + + for _, secret := range secretNames { + cred, err := keyring.Get(secret, string(user.Username)) + if err == nil { + value := string(cred) + + if c.Bool("export-as-tfvars") { + fmt.Printf("TF_VAR_%s=%s\n", secret, value) + continue + } + + secretValues = append(secretValues, value) + } + } + + return secretValues, nil } diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index 4f584bb..97c0fd9 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/fatih/color" + "github.com/urfave/cli/v2" "github.com/zalando/go-keyring" "github.com/tonedefdev/terracreds/api" @@ -50,11 +51,14 @@ func (m *Mac) Create(cfg api.Config, hostname string, token interface{}, user *u } if token == nil { - err = json.NewDecoder(os.Stdin).Decode(&api.CredentialResponse{}) + var response api.CredentialResponse + + err = json.NewDecoder(os.Stdin).Decode(&response) if err != nil { helpers.CheckError(err) } - err = keyring.Set(hostname, string(user.Username), api.CredentialResponse{}.Token) + + err = keyring.Set(hostname, string(user.Username), response.Token) return err } @@ -167,6 +171,6 @@ func (m *Mac) Get(cfg api.Config, hostname string, user *user.User, vault vault. return nil, err } -func (m *Mac) List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) { +func (m *Mac) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { return nil, nil } diff --git a/pkg/platform/windows.go b/pkg/platform/windows.go index 1a8bddc..dc7a304 100644 --- a/pkg/platform/windows.go +++ b/pkg/platform/windows.go @@ -9,6 +9,7 @@ import ( "github.com/danieljoos/wincred" "github.com/fatih/color" + "github.com/urfave/cli/v2" "github.com/tonedefdev/terracreds/api" "github.com/tonedefdev/terracreds/pkg/helpers" @@ -46,8 +47,16 @@ func (w *Windows) Create(cfg api.Config, hostname string, token interface{}, use cred := wincred.NewGenericCredential(hostname) if token == nil { - err = json.NewDecoder(os.Stdin).Decode(&api.CredentialResponse{}) - cred.CredentialBlob = []byte(api.CredentialResponse{}.Token) + var response api.CredentialResponse + + err = json.NewDecoder(os.Stdin).Decode(&response) + if err != nil { + helpers.CheckError(err) + } + + cred.CredentialBlob = []byte(response.Token) + cred.UserName = string(user.Username) + err = cred.Write() return err } @@ -166,6 +175,24 @@ func (w *Windows) Get(cfg api.Config, hostname string, user *user.User, vault va return nil, err } -func (w *Windows) List(cfg api.Config, secretNames []byte, vault vault.TerraVault) ([]string, error) { - return nil, nil +func (w *Windows) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { + var secretValues []string + + for _, secret := range secretNames { + cred, err := wincred.GetGenericCredential(secret) + if err == nil && cred.UserName == user.Username { + value := string(cred.CredentialBlob) + + if c.Bool("export-as-tfvars") { + fmt.Printf("TF_VAR_%s=%s\n", secret, value) + continue + } + + secretValues = append(secretValues, value) + } else { + return nil, err + } + } + + return secretValues, nil } diff --git a/pkg/vault/azure.go b/pkg/vault/azure.go index 605994d..d9a54d7 100644 --- a/pkg/vault/azure.go +++ b/pkg/vault/azure.go @@ -18,9 +18,10 @@ type AzureKeyVault struct { // getVaultClientMSI returns a keyvault.BaseClient with an MSI authorizer for an Azure Key Vault resource func getVaultClientMSI() keyvault.BaseClient { + const vaultUri = "https://vault.azure.net" vaultClient := keyvault.New() msiConfig := auth.NewMSIConfig() - msiConfig.Resource = "https://vault.azure.net" + msiConfig.Resource = vaultUri authorizer, err := msiConfig.Authorizer() if err != nil { From f1f96bff80581c847e4db9bf6f978533a8cfe44a Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 31 May 2022 19:21:11 -0700 Subject: [PATCH 03/22] Updating to go 1.17 --- go.mod | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 53b9ed0..315b346 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,58 @@ module github.com/tonedefdev/terracreds -go 1.15 +go 1.17 require ( github.com/Azure/azure-sdk-for-go v58.1.0+incompatible github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.4.0 - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 github.com/aws/aws-sdk-go v1.41.0 github.com/danieljoos/wincred v1.1.0 github.com/fatih/color v1.9.0 - github.com/google/go-cmp v0.5.6 // indirect github.com/hashicorp/vault/api v1.1.1 - github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 github.com/urfave/cli/v2 v2.2.0 github.com/zalando/go-keyring v0.1.0 - golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect gopkg.in/yaml.v2 v2.3.0 ) + +require ( + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.17 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.11 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect + github.com/Azure/go-autorest/logger v0.2.0 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect + github.com/godbus/dbus v4.1.0+incompatible // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-multierror v1.1.0 // indirect + github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/sdk v0.2.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.3.2 // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect + golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect + golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect + golang.org/x/text v0.3.6 // indirect + golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect +) From 16fa9c62bcbf53a9327b432958b4267a1b101c32 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 31 May 2022 23:10:13 -0700 Subject: [PATCH 04/22] Allowing environment variable for config file location. Ability to create setup and view all available vaults from the CLI. Generalized the name and secret value since store more than just API tokens can be stored with the tool --- api/api.go | 2 +- main.go | 309 ++++++++++++++++++++++++++++++++++------- pkg/helpers/helpers.go | 51 ++++--- pkg/platform/mac.go | 18 ++- 4 files changed, 301 insertions(+), 79 deletions(-) diff --git a/api/api.go b/api/api.go index 7e92e56..d0af541 100644 --- a/api/api.go +++ b/api/api.go @@ -20,7 +20,7 @@ type Azure struct { SecretName string `yaml:"secretName,omitempty"` // UseMSI (Required) A flag to indicate if the Managed Identity of the Azure VM should be used for authentication - UseMSI bool `yaml:"useMSI,omitempty"` + UseMSI bool `yaml:"useMSI"` // VaultUri (Required) The FQDN of the Azure Key Vault resource VaultUri string `yaml:"vaultUri,omitempty"` diff --git a/main.go b/main.go index e9a8e4b..4425332 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "github.com/fatih/color" "github.com/urfave/cli/v2" + "gopkg.in/yaml.v2" "github.com/tonedefdev/terracreds/api" "github.com/tonedefdev/terracreds/pkg/helpers" @@ -16,6 +17,11 @@ import ( "github.com/tonedefdev/terracreds/pkg/vault" ) +var ( + cfg api.Config + configFilePath string +) + // TerraCreds interface implements these methods for a credential's lifecycle type TerraCreds interface { // Create or store a secret in a vault @@ -93,10 +99,22 @@ func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { } func main() { - var cfg api.Config - version := "2.0.2" + version := "2.1.0" + + fileEnvVar := os.Getenv("TC_CONFIG_PATH") + if fileEnvVar != "" { + configFilePath = fileEnvVar + "config.yaml" + } else { + binPath := helpers.GetBinaryPath(os.Args[0], runtime.GOOS) + configFilePath = binPath + "config.yaml" + } - err := helpers.LoadConfig(&cfg) + err := helpers.CreateConfigFile(configFilePath) + if err != nil { + helpers.CheckError(err) + } + + err = helpers.LoadConfig(configFilePath, &cfg) if err != nil { helpers.CheckError(err) } @@ -117,34 +135,217 @@ func main() { Name: "config", Usage: "View or modify the Terracreds configuration file", Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "hostname", - Aliases: []string{"n"}, - Value: "place_holder", - Usage: "The name of the Terraform Cloud/Enterprise server's hostname or the name of the secret. This is also the display name of the credential object", + &cli.BoolFlag{ + Name: "use-local-vault", + Usage: "WARNING: Resets configuration to only use the local operating system's credential vault. This will delete all configuration values for cloud provider vaults from the config file", + Required: false, }, - &cli.StringFlag{ - Name: "apiToken", - Aliases: []string{"t"}, - Value: "", - Usage: "The Terraform Cloud/Enterprise API authorization token or other secret value to be securely stored in your vault provider of choice", + }, + Subcommands: []*cli.Command{ + { + Name: "aws", + Usage: "AWS Secret Managers provider configuration settings", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "description", + Usage: "A description to provide to the secret", + Required: false, + }, + &cli.StringFlag{ + Name: "region", + Usage: "The region where AWS Secrets Manager is hosted", + Required: true, + }, + &cli.StringFlag{ + Name: "secret-name", + Usage: "The friendly name of the secret stored in AWS Secrets Manager. If omitted Terracreds will use the hostname value instead", + Required: true, + }, + }, + Action: func(c *cli.Context) error { + cfg.Aws.Description = c.String("description") + cfg.Aws.Region = c.String("region") + cfg.Aws.SecretName = c.String("secret-name") + + // Set all other config values to empty + cfg.Azure.SecretName = "" + cfg.Azure.UseMSI = false + cfg.Azure.VaultUri = "" + + cfg.HashiVault.EnvironmentTokenName = "" + cfg.HashiVault.KeyVaultPath = "" + cfg.HashiVault.SecretName = "" + cfg.HashiVault.SecretPath = "" + cfg.HashiVault.VaultUri = "" + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, + { + Name: "azure", + Usage: "Azure Key Vault provider configuration settings", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "secret-name", + Usage: "The name of the secret stored in Azure Key Vault. If omitted Terracreds will use the hostname value instead", + Required: false, + }, + &cli.BoolFlag{ + Name: "use-msi", + Usage: "A flag to indicate if the Managed Identity of the Azure VM should be used for authentication", + Required: true, + }, + &cli.StringFlag{ + Name: "vault-uri", + Usage: "The FQDN of the Azure Key Vault resource", + Required: true, + }, + }, + Action: func(c *cli.Context) error { + cfg.Azure.SecretName = c.String("secret-name") + cfg.Azure.UseMSI = c.Bool("use-msi") + cfg.Azure.VaultUri = c.String("vault-uri") + + // Set all other config values to empty + cfg.Aws.Description = "" + cfg.Aws.Region = "" + cfg.Aws.SecretName = "" + + cfg.HashiVault.EnvironmentTokenName = "" + cfg.HashiVault.KeyVaultPath = "" + cfg.HashiVault.SecretName = "" + cfg.HashiVault.SecretPath = "" + cfg.HashiVault.VaultUri = "" + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, + { + Name: "hashicorp", + Usage: "HashiCorp Vault provider configuration settings", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "environment-token-name", + Usage: "The name of the environment variable that currently holds the Vault token", + Required: true, + }, + &cli.StringFlag{ + Name: "key-vault-path", + Usage: "The name of the Key Vault store inside of Vault", + Required: true, + }, + &cli.StringFlag{ + Name: "secret-name", + Usage: "The name of the secret stored inside of Vault. If omitted Terracreds will use the hostname value instead", + Required: false, + }, + &cli.StringFlag{ + Name: "secret-path", + Usage: "The path of the secret itself inside of the vault", + Required: true, + }, + &cli.StringFlag{ + Name: "vault-uri", + Usage: "The URL of the Vault instance including its port", + Required: true, + }, + }, + Action: func(c *cli.Context) error { + cfg.HashiVault.EnvironmentTokenName = c.String("environment-token-name") + cfg.HashiVault.KeyVaultPath = c.String("key-vault-path") + cfg.HashiVault.SecretName = c.String("secret-name") + cfg.HashiVault.SecretPath = c.String("secret-path") + cfg.HashiVault.VaultUri = c.String("vault-uri") + + // Set all other config values to empty + cfg.Aws.Description = "" + cfg.Aws.Region = "" + cfg.Aws.SecretName = "" + + cfg.Azure.SecretName = "" + cfg.Azure.UseMSI = false + cfg.Azure.VaultUri = "" + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, + { + Name: "logging", + Usage: "Configure the Terracreds logging settings", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "enable", + Usage: "Enable logging", + Required: true, + }, + &cli.StringFlag{ + Name: "path", + Aliases: []string{"p"}, + Usage: "The path on the file system where the log file is stored", + Required: true, + }, + }, + Action: func(c *cli.Context) error { + cfg.Logging.Enabled = c.Bool("enable") + cfg.Logging.Path = c.String("path") + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, + { + Name: "view", + Usage: "Print the current configuration to the screen", + Action: func(c *cli.Context) error { + bytes, err := yaml.Marshal(&cfg) + if err != nil { + helpers.CheckError(err) + } + + print(string(bytes)) + return nil + }, }, }, Action: func(c *cli.Context) error { - if len(os.Args) == 2 { - fmt.Fprintf(color.Output, "%s: No hostname or token was specified. Use 'terracreds create -h' to print help info\n", color.RedString("ERROR")) - return nil - } - - terraVault := NewTerraVault(&cfg, c.String("hostname")) - hostname := helpers.GetSecretName(&cfg, c.String("hostname")) - - user, err := user.Current() - helpers.CheckError(err) - - err = terraCreds.Create(cfg, hostname, c.String("apiToken"), user, terraVault) - if err != nil { - helpers.CheckError(err) + if c.Bool("use-local-vault") == true { + cfg.Aws.Description = "" + cfg.Aws.Region = "" + cfg.Aws.SecretName = "" + + cfg.Azure.SecretName = "" + cfg.Azure.UseMSI = false + cfg.Azure.VaultUri = "" + + cfg.HashiVault.EnvironmentTokenName = "" + cfg.HashiVault.KeyVaultPath = "" + cfg.HashiVault.SecretName = "" + cfg.HashiVault.SecretPath = "" + cfg.HashiVault.VaultUri = "" + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } } return nil @@ -155,31 +356,31 @@ func main() { Usage: "Manually create or update a credential object in the vault provider of your choice that contains the Terraform Cloud/Enterprise authorization token or another secret", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "hostname", + Name: "name", Aliases: []string{"n"}, Value: "place_holder", Usage: "The name of the Terraform Cloud/Enterprise server's hostname or the name of the secret. This is also the display name of the credential object", }, &cli.StringFlag{ - Name: "apiToken", - Aliases: []string{"t"}, + Name: "secret", + Aliases: []string{"s"}, Value: "", Usage: "The Terraform Cloud/Enterprise API authorization token or other secret value to be securely stored in your vault provider of choice", }, }, Action: func(c *cli.Context) error { if len(os.Args) == 2 { - fmt.Fprintf(color.Output, "%s: No hostname or token was specified. Use 'terracreds create -h' to print help info\n", color.RedString("ERROR")) + fmt.Fprintf(color.Output, "%s: No secret name or secret was specified. Use 'terracreds create -h' to print help info\n", color.RedString("ERROR")) return nil } - terraVault := NewTerraVault(&cfg, c.String("hostname")) - hostname := helpers.GetSecretName(&cfg, c.String("hostname")) + terraVault := NewTerraVault(&cfg, c.String("name")) + name := helpers.GetSecretName(&cfg, c.String("name")) user, err := user.Current() helpers.CheckError(err) - err = terraCreds.Create(cfg, hostname, c.String("apiToken"), user, terraVault) + err = terraCreds.Create(cfg, name, c.String("secret"), user, terraVault) if err != nil { helpers.CheckError(err) } @@ -192,7 +393,7 @@ func main() { Usage: "Delete a stored credential in the vault provider of your choice", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "hostname", + Name: "name", Aliases: []string{"n"}, Value: "place_holder", Usage: "The name of the Terraform Cloud/Enterprise server's hostname or the name of the secret. This is also the display name of the credential object.", @@ -200,25 +401,25 @@ func main() { }, Action: func(c *cli.Context) error { if len(os.Args) == 2 { - fmt.Fprintf(color.Output, "%s: No hostname was specified. Use 'terracreds delete -h' for help info\n", color.RedString("ERROR")) + fmt.Fprintf(color.Output, "%s: No secret name was specified. Use 'terracreds delete -h' for help info\n", color.RedString("ERROR")) return nil } - if !strings.Contains(os.Args[2], "-n") && !strings.Contains(os.Args[2], "--hostname") { - msg := fmt.Sprintf("A hostname was not expected here: '%s'", os.Args[2]) + if !strings.Contains(os.Args[2], "-n") && !strings.Contains(os.Args[2], "--name") { + msg := fmt.Sprintf("A secret name was not expected here: '%s'", os.Args[2]) helpers.Logging(cfg, msg, "WARNING") - fmt.Fprintf(color.Output, "%s: %s Did you mean `terracreds delete --hostname/-n %s'?\n", color.YellowString("WARNING"), msg, os.Args[2]) + fmt.Fprintf(color.Output, "%s: %s Did you mean `terracreds delete --name/-n %s'?\n", color.YellowString("WARNING"), msg, os.Args[2]) return nil } - terraVault := NewTerraVault(&cfg, c.String("hostname")) - hostname := helpers.GetSecretName(&cfg, c.String("hostname")) + terraVault := NewTerraVault(&cfg, c.String("name")) + name := helpers.GetSecretName(&cfg, c.String("name")) method := os.Args[1] user, err := user.Current() helpers.CheckError(err) - err = terraCreds.Delete(cfg, method, hostname, user, terraVault) + err = terraCreds.Delete(cfg, method, name, user, terraVault) if err != nil { helpers.CheckError(err) } @@ -231,18 +432,18 @@ func main() { Usage: "(Terraform Only) Forget a stored credential in your vault provider of choice when 'terraform logout' has been called", Action: func(c *cli.Context) error { if len(os.Args) == 2 { - fmt.Fprintf(color.Output, "%s: No hostname was specified. Use 'terracreds forget -h' for help info\n", color.RedString("ERROR")) + fmt.Fprintf(color.Output, "%s: No secret name was specified. Use 'terracreds forget -h' for help info\n", color.RedString("ERROR")) return nil } terraVault := NewTerraVault(&cfg, os.Args[2]) - hostname := helpers.GetSecretName(&cfg, os.Args[2]) + name := helpers.GetSecretName(&cfg, os.Args[2]) method := os.Args[1] user, err := user.Current() helpers.CheckError(err) - err = terraCreds.Delete(cfg, method, hostname, user, terraVault) + err = terraCreds.Delete(cfg, method, name, user, terraVault) if err != nil { helpers.CheckError(err) } @@ -261,7 +462,7 @@ func main() { }, }, Action: func(c *cli.Context) error { - helpers.GenerateTerraCreds(c) + helpers.GenerateTerraCreds(c, version) return nil }, }, @@ -274,9 +475,9 @@ func main() { helpers.CheckError(err) terraVault := NewTerraVault(&cfg, os.Args[2]) - hostname := helpers.GetSecretName(&cfg, os.Args[2]) + name := helpers.GetSecretName(&cfg, os.Args[2]) - token, err := terraCreds.Get(cfg, hostname, user, terraVault) + token, err := terraCreds.Get(cfg, name, user, terraVault) if err != nil { helpers.CheckError(err) } @@ -285,7 +486,7 @@ func main() { return nil } - msg := "A hostname was expected after the 'get' command but no argument was provided" + msg := "A secret name was expected after the 'get' command but no argument was provided" helpers.Logging(cfg, msg, "ERROR") fmt.Fprintf(color.Output, "%s: %s\n", color.RedString("ERROR"), msg) return nil @@ -305,7 +506,7 @@ func main() { Name: "input-file", Aliases: []string{"f"}, Value: "", - Usage: "The path to the file that provides the list of secrets to be retrieved", + Usage: "The path to the file that provides the list of secrets to be retrieved. Each secret name should be on its own line", }, &cli.BoolFlag{ Name: "export-as-tfvars", @@ -336,7 +537,7 @@ func main() { return nil } - msg := "A hostname was expected after the 'get' command but no argument was provided" + msg := "A hostname or secret name was expected after the 'get' command but no argument was provided" helpers.Logging(cfg, msg, "ERROR") fmt.Fprintf(color.Output, "%s: %s\n", color.RedString("ERROR"), msg) return nil @@ -347,17 +548,17 @@ func main() { Usage: "(Terraform Only) Store or update a credential object in your vault provider of choice when 'terraform login' has been called", Action: func(c *cli.Context) error { if len(os.Args) == 2 { - fmt.Fprintf(color.Output, "%s: No hostname or token was specified. Use 'terracreds store -h' to print help info\n", color.RedString("ERROR")) + fmt.Fprintf(color.Output, "%s: No hostname or secret name was specified. Use 'terracreds store -h' to print help info\n", color.RedString("ERROR")) return nil } terraVault := NewTerraVault(&cfg, os.Args[2]) - hostname := helpers.GetSecretName(&cfg, os.Args[2]) + name := helpers.GetSecretName(&cfg, os.Args[2]) user, err := user.Current() helpers.CheckError(err) - err = terraCreds.Create(cfg, hostname, nil, user, terraVault) + err = terraCreds.Create(cfg, name, nil, user, terraVault) if err != nil { helpers.CheckError(err) } diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 86327d8..80a3329 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -3,6 +3,7 @@ package helpers import ( "fmt" "io" + "io/ioutil" "log" "os" "runtime" @@ -128,37 +129,41 @@ func WriteToLog(path string, data string, level string) error { } // CreateConfigFile creates a default terracreds config file if one does not -// exist in the same path as the binary -func CreateConfigFile() error { - bin := GetBinaryPath(os.Args[0], runtime.GOOS) - path := bin + "config.yaml" - +// exist in the specified file path +func CreateConfigFile(path string) error { if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { - doc := heredoc.Doc(` -logging: - enabled: false - path:`) - WriteToFile(path, doc) + cfgFile := api.Config{ + Logging: api.Logging{ + Enabled: false, + }, + } + + bytes, err := yaml.Marshal(&cfgFile) + err = ioutil.WriteFile(path, bytes, 0755) + CheckError(err) + fmt.Fprintf(color.Output, "%s: Created file '%s'\n", color.CyanString("INFO"), path) + return err } } return nil } // LoadConfig loads the config file if it exists -// and creates the config file if doesn't exist -func LoadConfig(cfg *api.Config) error { - CreateConfigFile() - - bin := GetBinaryPath(os.Args[0], runtime.GOOS) - path := bin + "config.yaml" - f, err := os.Open(string(path)) +func LoadConfig(path string, cfg *api.Config) error { + bytes, err := ioutil.ReadFile(path) CheckError(err) - defer f.Close() + err = yaml.Unmarshal(bytes, &cfg) + return err +} - decoder := yaml.NewDecoder(f) - err = decoder.Decode(cfg) +// WriteConfig makes requested changes to config file +func WriteConfig(path string, cfg *api.Config) error { + bytes, err := yaml.Marshal(&cfg) + CheckError(err) + err = ioutil.WriteFile(path, bytes, 0755) CheckError(err) + fmt.Fprintf(color.Output, "%s: Modified config file '%s'\n", color.GreenString("SUCCESS"), path) return err } @@ -188,7 +193,7 @@ func LogLevel(level string) string { // GenerateTerracreds creates the binary to use this package as a credential helper // and optionally the terraform.rc file -func GenerateTerraCreds(c *cli.Context) { +func GenerateTerraCreds(c *cli.Context, version string) { var cliConfig string var tfPlugins string var binary string @@ -197,14 +202,14 @@ func GenerateTerraCreds(c *cli.Context) { userProfile := os.Getenv("USERPROFILE") cliConfig = userProfile + "\\AppData\\Roaming\\terraform.rc" tfPlugins = userProfile + "\\AppData\\Roaming\\terraform.d\\plugins" - binary = tfPlugins + "\\terraform-credentials-terracreds.exe" + binary = fmt.Sprintf("%s\\terraform-credentials-terracreds_%s.exe", tfPlugins, version) } if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { userProfile := os.Getenv("HOME") cliConfig = userProfile + "/.terraform.d/.terraformrc" tfPlugins = userProfile + "/.terraform.d/plugins" - binary = tfPlugins + "/terraform-credentials-terracreds" + binary = fmt.Sprintf("%s/terraform-credentials-terracreds_%s", tfPlugins, version) } NewDirectory(tfPlugins) diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index 97c0fd9..5718e8e 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -172,5 +172,21 @@ func (m *Mac) Get(cfg api.Config, hostname string, user *user.User, vault vault. } func (m *Mac) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { - return nil, nil + var secretValues []string + + for _, secret := range secretNames { + cred, err := keyring.Get(secret, string(user.Username)) + if err == nil { + value := string(cred) + + if c.Bool("export-as-tfvars") { + fmt.Printf("TF_VAR_%s=%s\n", secret, value) + continue + } + + secretValues = append(secretValues, value) + } + } + + return secretValues, nil } From 41b8c4f4648dd622d57cf872b822228967f38995 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 4 Jun 2022 20:10:19 -0700 Subject: [PATCH 05/22] Added list functionality for vault providers. Added warning and user input to confirm any actions that may delete configuration settings. Changed some verbiage --- config.yaml | 3 +-- main.go | 50 ++++++++++++++++++++++++----------------- pkg/helpers/helpers.go | 19 +++++++++++----- pkg/platform/linux.go | 9 ++++++++ pkg/platform/mac.go | 11 ++++++++- pkg/platform/windows.go | 11 ++++++++- pkg/vault/aws.go | 38 ++++++++++++++++++++++++++++++- pkg/vault/azure.go | 17 +++++++++++++- pkg/vault/hashicorp.go | 26 ++++++++++++++++++++- 9 files changed, 150 insertions(+), 34 deletions(-) diff --git a/config.yaml b/config.yaml index b0732c8..8bbf065 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,3 @@ -### Example Config File logging: enabled: true - path: C:/temp/ \ No newline at end of file + path: C:/temp/ diff --git a/main.go b/main.go index 4425332..26f82d6 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ import ( var ( cfg api.Config configFilePath string + confirm string ) // TerraCreds interface implements these methods for a credential's lifecycle @@ -136,7 +137,7 @@ func main() { Usage: "View or modify the Terracreds configuration file", Flags: []cli.Flag{ &cli.BoolFlag{ - Name: "use-local-vault", + Name: "use-local-vault-only", Usage: "WARNING: Resets configuration to only use the local operating system's credential vault. This will delete all configuration values for cloud provider vaults from the config file", Required: false, }, @@ -327,24 +328,31 @@ func main() { }, }, Action: func(c *cli.Context) error { - if c.Bool("use-local-vault") == true { - cfg.Aws.Description = "" - cfg.Aws.Region = "" - cfg.Aws.SecretName = "" - - cfg.Azure.SecretName = "" - cfg.Azure.UseMSI = false - cfg.Azure.VaultUri = "" - - cfg.HashiVault.EnvironmentTokenName = "" - cfg.HashiVault.KeyVaultPath = "" - cfg.HashiVault.SecretName = "" - cfg.HashiVault.SecretPath = "" - cfg.HashiVault.VaultUri = "" - - err := helpers.WriteConfig(configFilePath, &cfg) - if err != nil { - helpers.CheckError(err) + if c.Bool("use-local-vault-only") == true { + const verbiage = "This will reset the configuration to only use the local operating system's credential vault. Any configuration values for a cloud provider vault will be permanently lost!" + fmt.Fprintf(color.Output, "%s: %s\n\n Enter 'yes' to continue or press 'enter' or 'return' to cancel: ", color.YellowString("WARNING"), verbiage) + fmt.Scanln(&confirm) + fmt.Print("\n") + + if confirm == "yes" { + cfg.Aws.Description = "" + cfg.Aws.Region = "" + cfg.Aws.SecretName = "" + + cfg.Azure.SecretName = "" + cfg.Azure.UseMSI = false + cfg.Azure.VaultUri = "" + + cfg.HashiVault.EnvironmentTokenName = "" + cfg.HashiVault.KeyVaultPath = "" + cfg.HashiVault.SecretName = "" + cfg.HashiVault.SecretPath = "" + cfg.HashiVault.VaultUri = "" + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } } } @@ -462,7 +470,7 @@ func main() { }, }, Action: func(c *cli.Context) error { - helpers.GenerateTerraCreds(c, version) + helpers.GenerateTerraCreds(c, version, confirm) return nil }, }, @@ -509,7 +517,7 @@ func main() { Usage: "The path to the file that provides the list of secrets to be retrieved. Each secret name should be on its own line", }, &cli.BoolFlag{ - Name: "export-as-tfvars", + Name: "as-tfvars", Value: false, Usage: "Exports the secret keys and values as 'TF_VARS_secret_key=secret_value' for the given operating system", }, diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 80a3329..0a00107 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -193,7 +193,7 @@ func LogLevel(level string) string { // GenerateTerracreds creates the binary to use this package as a credential helper // and optionally the terraform.rc file -func GenerateTerraCreds(c *cli.Context, version string) { +func GenerateTerraCreds(c *cli.Context, version string, confirm string) { var cliConfig string var tfPlugins string var binary string @@ -215,11 +215,18 @@ func GenerateTerraCreds(c *cli.Context, version string) { NewDirectory(tfPlugins) CopyTerraCreds(binary) if c.Bool("create-cli-config") == true { - doc := heredoc.Doc(` - credentials_helper "terracreds" { - args = [] - }`) - WriteToFile(cliConfig, doc) + const verbiage = "This command will delete any settings in your .terraformrc file\n\n Enter 'yes' to coninue or press 'enter' or 'return' to cancel: " + fmt.Fprintf(color.Output, "%s: %s", color.YellowString("WARNING"), verbiage) + fmt.Scanln(&confirm) + fmt.Print("\n") + + if confirm == "yes" { + doc := heredoc.Doc(` + credentials_helper "terracreds" { + args = [] + }`) + WriteToFile(cliConfig, doc) + } } } diff --git a/pkg/platform/linux.go b/pkg/platform/linux.go index fa96e68..704e813 100644 --- a/pkg/platform/linux.go +++ b/pkg/platform/linux.go @@ -170,6 +170,15 @@ func (l *Linux) Get(cfg api.Config, hostname string, user *user.User, vault vaul func (l *Linux) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { var secretValues []string + if vault != nil { + secrets, err := vault.List(secretNames) + if err != nil { + return nil, err + } + + return secrets, nil + } + for _, secret := range secretNames { cred, err := keyring.Get(secret, string(user.Username)) if err == nil { diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index 5718e8e..fe20ce1 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -174,12 +174,21 @@ func (m *Mac) Get(cfg api.Config, hostname string, user *user.User, vault vault. func (m *Mac) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { var secretValues []string + if vault != nil { + secrets, err := vault.List(secretNames) + if err != nil { + return nil, err + } + + return secrets, nil + } + for _, secret := range secretNames { cred, err := keyring.Get(secret, string(user.Username)) if err == nil { value := string(cred) - if c.Bool("export-as-tfvars") { + if c.Bool("as-tfvars") { fmt.Printf("TF_VAR_%s=%s\n", secret, value) continue } diff --git a/pkg/platform/windows.go b/pkg/platform/windows.go index dc7a304..b6b4eef 100644 --- a/pkg/platform/windows.go +++ b/pkg/platform/windows.go @@ -178,12 +178,21 @@ func (w *Windows) Get(cfg api.Config, hostname string, user *user.User, vault va func (w *Windows) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { var secretValues []string + if vault != nil { + secrets, err := vault.List(secretNames) + if err != nil { + return nil, err + } + + return secrets, nil + } + for _, secret := range secretNames { cred, err := wincred.GetGenericCredential(secret) if err == nil && cred.UserName == user.Username { value := string(cred.CredentialBlob) - if c.Bool("export-as-tfvars") { + if c.Bool("as-tfvars") { fmt.Printf("TF_VAR_%s=%s\n", secret, value) continue } diff --git a/pkg/vault/aws.go b/pkg/vault/aws.go index 8ba3c76..91f9129 100644 --- a/pkg/vault/aws.go +++ b/pkg/vault/aws.go @@ -183,5 +183,41 @@ func (asm *AwsSecretsManager) Get() ([]byte, error) { } func (asm *AwsSecretsManager) List(secretNames []string) ([]string, error) { - return nil, nil + var secretValues []string + svc := asm.getAwsSecetsManager() + + for _, secret := range secretNames { + input := &secretsmanager.GetSecretValueInput{ + SecretId: aws.String(secret), + } + + result, err := svc.GetSecretValue(input) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case secretsmanager.ErrCodeResourceNotFoundException: + return nil, err + case secretsmanager.ErrCodeInvalidParameterException: + fmt.Println(secretsmanager.ErrCodeInvalidParameterException, aerr.Error()) + case secretsmanager.ErrCodeInvalidRequestException: + fmt.Println(secretsmanager.ErrCodeInvalidRequestException, aerr.Error()) + case secretsmanager.ErrCodeDecryptionFailure: + fmt.Println(secretsmanager.ErrCodeDecryptionFailure, aerr.Error()) + case secretsmanager.ErrCodeInternalServiceError: + fmt.Println(secretsmanager.ErrCodeInternalServiceError, aerr.Error()) + default: + fmt.Println(aerr.Error()) + } + } else { + // Print the error, cast err to awserr.Error to get the Code and + // Message from an error. + fmt.Println(err.Error()) + } + return nil, err + } + + secretValues = append(secretValues, *result.SecretString) + } + + return secretValues, nil } diff --git a/pkg/vault/azure.go b/pkg/vault/azure.go index d9a54d7..aa7eddd 100644 --- a/pkg/vault/azure.go +++ b/pkg/vault/azure.go @@ -11,6 +11,7 @@ import ( ) type AzureKeyVault struct { + SecretList []string SecretName string UseMSI bool VaultUri string @@ -75,5 +76,19 @@ func (akv *AzureKeyVault) Get() ([]byte, error) { } func (akv *AzureKeyVault) List(secretNames []string) ([]string, error) { - return nil, nil + var secretValues []string + ctx := context.Background() + client := getVaultClientMSI() + + for _, secret := range secretNames { + get, err := client.GetSecret(ctx, akv.VaultUri, secret, "") + + if err != nil { + return nil, err + } + + secretValues = append(secretValues, *get.Value) + } + + return secretValues, nil } diff --git a/pkg/vault/hashicorp.go b/pkg/vault/hashicorp.go index c8cac0f..8f10e71 100644 --- a/pkg/vault/hashicorp.go +++ b/pkg/vault/hashicorp.go @@ -85,5 +85,29 @@ func (hc *HashiVault) Get() ([]byte, error) { } func (hc *HashiVault) List(secretNames []string) ([]string, error) { - return nil, nil + var secretValues []string + client := hc.newHashiVaultClient() + + kvPath := fmt.Sprintf("%s/data/%s", hc.KeyVaultPath, hc.SecretPath) + secret, err := client.Logical().Read(kvPath) + if secret == nil { + return nil, err + } + + data, ok := secret.Data["data"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("data type assertion failed: %T %#v", secret.Data["data"], secret.Data["data"]) + } + + for _, secret := range secretNames { + key := secret + value, ok := data[key].(string) + if !ok { + return nil, fmt.Errorf("value type assertion failed: %T %#v", data[key], data[key]) + } + + secretValues = append(secretValues, value) + } + + return secretValues, nil } From 627199163fe89f8f14081f77160fbb4479e8587f Mon Sep 17 00:00:00 2001 From: Tone Def Date: Sat, 4 Jun 2022 23:18:13 -0700 Subject: [PATCH 06/22] Added ability to set secrets in config file. Changed verbiage to generalize usage with all TACOS. Changed logging flag to enabled to match config setting name. Put finishing touches on list --- api/api.go | 9 +++-- main.go | 109 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 83 insertions(+), 35 deletions(-) diff --git a/api/api.go b/api/api.go index d0af541..98f0ac9 100644 --- a/api/api.go +++ b/api/api.go @@ -48,10 +48,11 @@ type HCVault struct { // Config struct for terracreds custom configuration type Config struct { - Logging Logging `yaml:"logging"` - Aws Aws `yaml:"aws,omitempty"` - Azure Azure `yaml:"azure,omitempty"` - HashiVault HCVault `yaml:"hcvault,omitempty"` + Logging Logging `yaml:"logging"` + Aws Aws `yaml:"aws,omitempty"` + Azure Azure `yaml:"azure,omitempty"` + HashiVault HCVault `yaml:"hcvault,omitempty"` + Secrets []string `yaml:"secrets,omitempty"` } // Logging struct defines the parameters for logging diff --git a/main.go b/main.go index 26f82d6..a819f94 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ var ( cfg api.Config configFilePath string confirm string + secretNames []string ) // TerraCreds interface implements these methods for a credential's lifecycle @@ -128,8 +129,8 @@ func main() { app := &cli.App{ Name: "terracreds", - Usage: "a credential helper for Terraform Cloud/Enterprise that leverages your vault provider of choice for securely storing your API tokens or other secrets.\n\n Visit https://github.com/tonedefdev/terracreds for more information", - UsageText: "Directly store credentials from Terraform using 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -t myAPItoken'", + Usage: "a credential helper for Terraform Automation and Collaboration Software (TACOS) that leverages your vault provider of choice for securely storing API tokens or other secrets.\n\n Visit https://github.com/tonedefdev/terracreds for more information", + UsageText: "Store Terraform API tokens by running 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -s myAPItoken'", Version: version, Commands: []*cli.Command{ { @@ -138,7 +139,7 @@ func main() { Flags: []cli.Flag{ &cli.BoolFlag{ Name: "use-local-vault-only", - Usage: "WARNING: Resets configuration to only use the local operating system's credential vault. This will delete all configuration values for cloud provider vaults from the config file", + Usage: "Resets configuration to only use the local operating system's credential vault. This will delete all configuration values for cloud provider vaults from the config file", Required: false, }, }, @@ -290,20 +291,48 @@ func main() { Usage: "Configure the Terracreds logging settings", Flags: []cli.Flag{ &cli.BoolFlag{ - Name: "enable", + Name: "enabled", Usage: "Enable logging", - Required: true, + Required: false, }, &cli.StringFlag{ Name: "path", Aliases: []string{"p"}, Usage: "The path on the file system where the log file is stored", + Required: false, + }, + }, + Action: func(c *cli.Context) error { + if c.Bool("enabled") { + cfg.Logging.Enabled = c.Bool("enabled") + } + + if c.String("path") != "" { + cfg.Logging.Path = c.String("path") + } + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, + { + Name: "secrets", + Usage: "Add a list of secret names to the configuration file", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "secret-list", + Aliases: []string{"l"}, + Usage: "Add a comma separated list of secret names to be stored in the configuration file to use with the 'list' command", Required: true, }, }, Action: func(c *cli.Context) error { - cfg.Logging.Enabled = c.Bool("enable") - cfg.Logging.Path = c.String("path") + secretValues := strings.Split(c.String("secret-list"), ",") + cfg.Secrets = secretValues err := helpers.WriteConfig(configFilePath, &cfg) if err != nil { @@ -361,19 +390,19 @@ func main() { }, { Name: "create", - Usage: "Manually create or update a credential object in the vault provider of your choice that contains the Terraform Cloud/Enterprise authorization token or another secret", + Usage: "Manually create or update a credential object in the vault provider of your choice that contains either the Terraform Automation and Collaboration Software API's authorization token or another secret", Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", Aliases: []string{"n"}, Value: "place_holder", - Usage: "The name of the Terraform Cloud/Enterprise server's hostname or the name of the secret. This is also the display name of the credential object", + Usage: "The name of the Terraform Automation and Collaboration Software server's hostname or the name of the secret. This is also the display name of the credential object", }, &cli.StringFlag{ Name: "secret", - Aliases: []string{"s"}, + Aliases: []string{"s", "t", "v"}, Value: "", - Usage: "The Terraform Cloud/Enterprise API authorization token or other secret value to be securely stored in your vault provider of choice", + Usage: "The Terraform Automation and Collaboration Software API authorization token or other secret value to be securely stored in your vault provider of choice", }, }, Action: func(c *cli.Context) error { @@ -404,7 +433,7 @@ func main() { Name: "name", Aliases: []string{"n"}, Value: "place_holder", - Usage: "The name of the Terraform Cloud/Enterprise server's hostname or the name of the secret. This is also the display name of the credential object.", + Usage: "The name of the Terraform Automation and Collaboration Software server's hostname or the name of the secret. This is also the display name of the credential object", }, }, Action: func(c *cli.Context) error { @@ -476,7 +505,7 @@ func main() { }, { Name: "get", - Usage: "Get the credential object value by passing the hostname of the Terraform Cloud/Enterprise server as an argument or the name of the secret. The credential is returned as a JSON object and formatted for consumption by Terraform", + Usage: "Get the credential object value by passing the hostname of the Terraform Automation and Collaboration Software server's hostname or the name of the secret as an argument. The credential is returned as a JSON object and formatted for consumption by Terraform", Action: func(c *cli.Context) error { if len(os.Args) > 2 { user, err := user.Current() @@ -505,27 +534,48 @@ func main() { Usage: "List the credentials stored in a vault using a provided set of secret names", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "secret-names", - Aliases: []string{"s"}, - Value: "", - Usage: "A comma separated list of secret names to be retrieved", + Name: "secret-names", + Aliases: []string{"s"}, + Value: "", + Usage: "A comma separated list of secret names to be retrieved", + Required: false, }, - &cli.StringFlag{ - Name: "input-file", - Aliases: []string{"f"}, - Value: "", - Usage: "The path to the file that provides the list of secrets to be retrieved. Each secret name should be on its own line", + &cli.BoolFlag{ + Name: "from-config", + Aliases: []string{"f"}, + Value: false, + Usage: "Get the secrets from the 'secrets' list in the configuration file", + Required: false, }, &cli.BoolFlag{ - Name: "as-tfvars", - Value: false, - Usage: "Exports the secret keys and values as 'TF_VARS_secret_key=secret_value' for the given operating system", + Name: "as-tfvars", + Value: false, + Usage: "Exports the secret keys and values as 'TF_VARS_secret_key=secret_value' for the given operating system", + Required: false, }, }, Action: func(c *cli.Context) error { + if len(os.Args) == 2 { + fmt.Fprintf(color.Output, "%s: No list command was specified. Use 'terracreds create -h' to print help info\n", color.RedString("ERROR")) + return nil + } + if len(os.Args) > 1 { terraVault := NewTerraVault(&cfg, os.Args[2]) - secretNames := strings.Split(c.String("secret-names"), ",") + + if len(cfg.Secrets) > 0 { + secretNames = cfg.Secrets + } + + if c.String("secret-names") != "" { + secretNames = strings.Split(c.String("secret-names"), ",") + } + + if len(cfg.Secrets) < 1 && c.String("secret-names") == "" { + verbiage := "A list of secrets must be provided. Use '--secret-names' and pass it a comma separated list of secrets, or setup the 'secrets' block in the terracreds config file to use this command" + fmt.Fprintf(color.Output, "%s: %s", color.RedString("ERROR"), verbiage) + return nil + } user, err := user.Current() if err != nil { @@ -545,18 +595,15 @@ func main() { return nil } - msg := "A hostname or secret name was expected after the 'get' command but no argument was provided" - helpers.Logging(cfg, msg, "ERROR") - fmt.Fprintf(color.Output, "%s: %s\n", color.RedString("ERROR"), msg) return nil }, }, { Name: "store", - Usage: "(Terraform Only) Store or update a credential object in your vault provider of choice when 'terraform login' has been called", + Usage: "(Terraform Only) Store or update a Terraform Automation and Collaboration Software API token in your vault provider of choice when 'terraform login' has been called", Action: func(c *cli.Context) error { if len(os.Args) == 2 { - fmt.Fprintf(color.Output, "%s: No hostname or secret name was specified. Use 'terracreds store -h' to print help info\n", color.RedString("ERROR")) + fmt.Fprintf(color.Output, "%s: No hostname was specified. Use 'terracreds store -h' to print help info\n", color.RedString("ERROR")) return nil } From cf2fcbca0d9685725c7af693d4213252f2febd84 Mon Sep 17 00:00:00 2001 From: Tone Def Date: Sat, 4 Jun 2022 23:28:47 -0700 Subject: [PATCH 07/22] Moved as-tfvars to higher level in processing to allow more flexibility --- main.go | 9 +++++++++ pkg/platform/linux.go | 6 ------ pkg/platform/mac.go | 6 ------ pkg/platform/windows.go | 6 ------ 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index a819f94..630d856 100644 --- a/main.go +++ b/main.go @@ -587,6 +587,15 @@ func main() { helpers.CheckError(err) } + if c.Bool("as-tfvars") { + for i, name := range secretNames { + fmt.Printf("TF_VAR_%s=%s\n", name, list[i]) + continue + } + + return nil + } + for _, secret := range list { value := fmt.Sprintf("%s\n", secret) print(value) diff --git a/pkg/platform/linux.go b/pkg/platform/linux.go index 704e813..9461e8d 100644 --- a/pkg/platform/linux.go +++ b/pkg/platform/linux.go @@ -183,12 +183,6 @@ func (l *Linux) List(c *cli.Context, cfg api.Config, secretNames []string, user cred, err := keyring.Get(secret, string(user.Username)) if err == nil { value := string(cred) - - if c.Bool("export-as-tfvars") { - fmt.Printf("TF_VAR_%s=%s\n", secret, value) - continue - } - secretValues = append(secretValues, value) } } diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index fe20ce1..adad2c3 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -187,12 +187,6 @@ func (m *Mac) List(c *cli.Context, cfg api.Config, secretNames []string, user *u cred, err := keyring.Get(secret, string(user.Username)) if err == nil { value := string(cred) - - if c.Bool("as-tfvars") { - fmt.Printf("TF_VAR_%s=%s\n", secret, value) - continue - } - secretValues = append(secretValues, value) } } diff --git a/pkg/platform/windows.go b/pkg/platform/windows.go index b6b4eef..87d449d 100644 --- a/pkg/platform/windows.go +++ b/pkg/platform/windows.go @@ -191,12 +191,6 @@ func (w *Windows) List(c *cli.Context, cfg api.Config, secretNames []string, use cred, err := wincred.GetGenericCredential(secret) if err == nil && cred.UserName == user.Username { value := string(cred.CredentialBlob) - - if c.Bool("as-tfvars") { - fmt.Printf("TF_VAR_%s=%s\n", secret, value) - continue - } - secretValues = append(secretValues, value) } else { return nil, err From 8b0dedd227b1684273ceaa419d3bc41c7a6b3865 Mon Sep 17 00:00:00 2001 From: Tone Def Date: Sat, 4 Jun 2022 23:33:09 -0700 Subject: [PATCH 08/22] Added alias for create called update to make it more user friendly --- main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 630d856..9a7b348 100644 --- a/main.go +++ b/main.go @@ -130,7 +130,7 @@ func main() { app := &cli.App{ Name: "terracreds", Usage: "a credential helper for Terraform Automation and Collaboration Software (TACOS) that leverages your vault provider of choice for securely storing API tokens or other secrets.\n\n Visit https://github.com/tonedefdev/terracreds for more information", - UsageText: "Store Terraform API tokens by running 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -s myAPItoken'", + UsageText: "Store Terraform Automation and Collaboration Software API tokens by running 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -s myAPItoken'", Version: version, Commands: []*cli.Command{ { @@ -389,8 +389,9 @@ func main() { }, }, { - Name: "create", - Usage: "Manually create or update a credential object in the vault provider of your choice that contains either the Terraform Automation and Collaboration Software API's authorization token or another secret", + Name: "create", + Aliases: []string{"update"}, + Usage: "Manually create or update a credential object in the vault provider of your choice that contains either the Terraform Automation and Collaboration Software API's authorization token or another secret", Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", From ff602f3946d291f346e64c5d360f70c88b0b83dc Mon Sep 17 00:00:00 2001 From: Tone Def Date: Sat, 4 Jun 2022 23:34:07 -0700 Subject: [PATCH 09/22] Added alias for secret-names to also be -l for list --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 9a7b348..a643885 100644 --- a/main.go +++ b/main.go @@ -536,7 +536,7 @@ func main() { Flags: []cli.Flag{ &cli.StringFlag{ Name: "secret-names", - Aliases: []string{"s"}, + Aliases: []string{"s", "l"}, Value: "", Usage: "A comma separated list of secret names to be retrieved", Required: false, From 3516b34717961aa7fa7a34226b7a19796a8a0b52 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sun, 5 Jun 2022 14:40:47 -0700 Subject: [PATCH 10/22] Added Google Secrets Manager as an available vault provider --- api/api.go | 10 + config.yaml | 2 +- go.mod | 24 ++- go.sum | 501 +++++++++++++++++++++++++++++++++++++++++++++++ main.go | 13 ++ pkg/vault/gcp.go | 112 +++++++++++ 6 files changed, 656 insertions(+), 6 deletions(-) create mode 100644 pkg/vault/gcp.go diff --git a/api/api.go b/api/api.go index 98f0ac9..c3dc49c 100644 --- a/api/api.go +++ b/api/api.go @@ -26,6 +26,15 @@ type Azure struct { VaultUri string `yaml:"vaultUri,omitempty"` } +// GCP is the configuration structure for the Goocle Cloud Secrets Manager provider +type GCP struct { + // ProjectId (Required) The name of the GCP project where the Secrets Manager instance has been created + ProjectId string `yaml:"projectId,omitempty"` + + // SecretId (Required) The name of the secret to create + SecretId string `yaml:"secretId,omitempty"` +} + // HCVault is the configuration structure for the Hashicorp Vault provider type HCVault struct { // EnvironmentTokenName (Required) The name of the environment variable that currently holds @@ -52,6 +61,7 @@ type Config struct { Aws Aws `yaml:"aws,omitempty"` Azure Azure `yaml:"azure,omitempty"` HashiVault HCVault `yaml:"hcvault,omitempty"` + GCP GCP `yaml:"gcp,omitempty"` Secrets []string `yaml:"secrets,omitempty"` } diff --git a/config.yaml b/config.yaml index 8bbf065..cc50913 100644 --- a/config.yaml +++ b/config.yaml @@ -1,3 +1,3 @@ logging: enabled: true - path: C:/temp/ + path: C:\Temp\ diff --git a/go.mod b/go.mod index 315b346..d505974 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,10 @@ require ( ) require ( + cloud.google.com/go v0.100.2 // indirect + cloud.google.com/go/compute v1.5.0 // indirect + cloud.google.com/go/iam v0.3.0 // indirect + cloud.google.com/go/secretmanager v1.4.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.17 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.11 // indirect @@ -30,8 +34,11 @@ require ( github.com/dimchansky/utfbom v1.1.1 // indirect github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect github.com/godbus/dbus v4.1.0+incompatible // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/google/go-cmp v0.5.6 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/googleapis/gax-go/v2 v2.2.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-multierror v1.1.0 // indirect @@ -49,10 +56,17 @@ require ( github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + go.opencensus.io v0.23.0 // indirect golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect - golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect - golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect + golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect + golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect + google.golang.org/api v0.74.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect + google.golang.org/grpc v1.46.2 // indirect + google.golang.org/protobuf v1.28.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect ) diff --git a/go.sum b/go.sum index df85545..0900456 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,60 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/secretmanager v1.4.0 h1:Cl+kDYvKHjPQ1l2DZDr2FG/cXUzNGCZkh05BARgddo8= +cloud.google.com/go/secretmanager v1.4.0/go.mod h1:h2VZz7Svt1W9/YVl7mfcX9LddvS6SOLOvMoOXBhYT1k= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v58.1.0+incompatible h1:WFsr3Efy7uweykOAEfOHO3ACtuwIv+rrFmSn9K48VnA= github.com/Azure/azure-sdk-for-go v58.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -27,15 +82,18 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -51,11 +109,23 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -87,6 +157,12 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= @@ -97,7 +173,11 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= @@ -116,11 +196,25 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -128,17 +222,61 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -183,6 +321,8 @@ github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= @@ -192,6 +332,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -271,6 +413,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -282,6 +426,7 @@ github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvH github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -293,53 +438,156 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 h1:MCfT24H3f//U5+UCrZp1/riVO3B50BovxtDiNn0XKkk= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zalando/go-keyring v0.1.0 h1:ffq972Aoa4iHNzBlUHgK5Y+k8+r/8GvcGd80/OFZb/k= github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -348,30 +596,84 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -380,28 +682,210 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf h1:JTjwKJX9erVpsw17w+OIPP7iAgEkN/r8urhWSunEDTs= +google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM= +google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -410,13 +894,20 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -425,12 +916,22 @@ gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/main.go b/main.go index a643885..3444c31 100644 --- a/main.go +++ b/main.go @@ -81,6 +81,19 @@ func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { return vault } + if cfg.GCP.ProjectId != "" { + vault := &vault.GCPSecretsManager{ + ProjectId: cfg.GCP.ProjectId, + SecretId: hostname, + } + + if cfg.GCP.SecretId != "" { + vault.SecretId = cfg.GCP.SecretId + } + + return vault + } + if cfg.HashiVault.VaultUri != "" { vault := &vault.HashiVault{ EnvTokenName: cfg.HashiVault.EnvironmentTokenName, diff --git a/pkg/vault/gcp.go b/pkg/vault/gcp.go new file mode 100644 index 0000000..49065a4 --- /dev/null +++ b/pkg/vault/gcp.go @@ -0,0 +1,112 @@ +package vault + +import ( + "context" + "fmt" + "strings" + + secretmanager "cloud.google.com/go/secretmanager/apiv1" + secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" + + "github.com/tonedefdev/terracreds/pkg/helpers" +) + +var ctx = context.Background() + +type GCPSecretsManager struct { + ProjectId string + SecretId string + SecretList []string +} + +func (gcp *GCPSecretsManager) getClient() *secretmanager.Client { + client, err := secretmanager.NewClient(ctx) + if err != nil { + helpers.CheckError(err) + } + defer client.Close() + return client +} + +// formatSecretName replaces the periods from the hostname with dashes +// since GCP can't store secrets that contain periods +func formatGcpSecretName(secretName string) string { + hostname := strings.Replace(secretName, ".", "-", -1) + return hostname +} + +func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { + client := gcp.getClient() + secretId := formatGcpSecretName(gcp.SecretId) + + createSecretReq := &secretmanagerpb.CreateSecretRequest{ + Parent: fmt.Sprintf("projects/%s", gcp.ProjectId), + SecretId: secretId, + Secret: &secretmanagerpb.Secret{ + Replication: &secretmanagerpb.Replication{ + Replication: &secretmanagerpb.Replication_Automatic_{ + Automatic: &secretmanagerpb.Replication_Automatic{}, + }, + }, + }, + } + + secret, err := client.CreateSecret(ctx, createSecretReq) + if err != nil { + return err + } + + payload := []byte(secretValue) + + addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{ + Parent: secret.Name, + Payload: &secretmanagerpb.SecretPayload{ + Data: payload, + }, + } + + print(secret.Name) + + _, err = client.AddSecretVersion(ctx, addSecretVersionReq) + if err != nil { + return err + } + + return err +} + +func (gcp *GCPSecretsManager) Delete() error { + client := gcp.getClient() + secretId := formatGcpSecretName(gcp.SecretId) + + destroySecretVersionReq := secretmanagerpb.DestroySecretVersionRequest{ + Name: fmt.Sprintf("/projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), + } + + _, err := client.DestroySecretVersion(ctx, &destroySecretVersionReq) + if err != nil { + return err + } + + return err +} + +func (gcp *GCPSecretsManager) Get() ([]byte, error) { + client := gcp.getClient() + secretId := formatGcpSecretName(gcp.SecretId) + + accessRequest := &secretmanagerpb.AccessSecretVersionRequest{ + Name: fmt.Sprintf("/projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), + } + + result, err := client.AccessSecretVersion(ctx, accessRequest) + if err != nil { + return nil, err + } + + return result.Payload.Data, err +} + +func (gcp *GCPSecretsManager) List(secretNames []string) ([]string, error) { + return nil, nil +} From a9403bf94dbe265057f2eeda5daa78142d65e608 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sun, 5 Jun 2022 15:51:43 -0700 Subject: [PATCH 11/22] Added list for gcp. Fixed issue where gcp keeps a single secret object and only updates values. Changed some verbiage. Started to update readme in prep for new release --- README.md | 31 ++++++++++++++++++++++- api/api.go | 6 ++--- config.yaml | 2 ++ go.mod | 4 +-- main.go | 62 +++++++++++++++++++++++++++++++++++++++++++-- pkg/vault/gcp.go | 66 ++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 155 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6a4e37d..15c0467 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ We all know storing secrets in plain text can pose major security threats, and T #### Currently supported Vault providers: - [x] AWS Secrets Manager - [x] Azure Key Vault -- [ ] Google Secret Manager +- [x] Google Secret Manager - [x] HashiCorp Vault ## Windows Install via Chocolatey @@ -165,6 +165,8 @@ If the token was updated successfully the following message is returned: Success! Terraform has obtained and saved an API token. ``` +You can also run `terracreds update -n my-secret -v my-secret-value` to update a secret value. + Additionally, you can check the `terracreds.log` if logging is enabled for more information ## Forgetting Credentials @@ -185,6 +187,17 @@ Success! Terraform has removed the stored API token for app.terraform.io. Additionally, you can check the `terracreds.log` if logging is enabled for more information +## List Credentials +> New in version `2.1.0` + +You can pass in a comma separated list of secrets via `terracreds list -l mysecret,mysecret2` to print out the secret values to the screen. + +You can also setup a list of secrets in the configuration file by using `terracreds config secrets -l mysecret,mysecret2` and then calling `terracreds list --from-config` to print out the secrets. + +There's a helper flag `terracreds list --as-tfvars` which will return the secret values formatted for use with `terraform`. Depending on the shell calling this command will determine how you can readily use these values. + +For instance on Linux/macOS you can simply call `eval` to evaluate the output and use those as variables in your current shell. + ## Setting Up a Vault Provider > You can reference example configs in our [repo](https://github.com/tonedefdev/terracreds/blob/main/config.yaml) plus we have example [terraform](https://github.com/tonedefdev/terracreds/tree/main/terraform) code you can reference in order to setup your `AWS` or `Azure` VMs to use `terracreds` for a CI/CD piepline agent or a development workstation @@ -242,6 +255,22 @@ secret_permissions = [ ``` > Since `Azure Key Vault` doesn't support the period character in a secret name a helper function will replace any periods with dashes so they can be successfully stored. This means a `terraform` API token name that would usually be `app.terraform.io` will become `app-terraform-io` +### Google Secret Manager +In to leverage `terracreds` to manage secrets in `Google Secret Manager` the following block needs to be provided in the configuration file: +```yaml +gcp: + projectId: my-gcp-project + secretId: my-secret-id +``` + +The `Google IAM` role `secretmanager.admin` is suggested in order to fully manage the secrets with `terracreds` + +| Value | Description | Required | +| ----- | ----------- | -------- | +| `projectId` | The name of the `GCP` project ID where the `Secret Manager` API has been enabled | `yes` | +| `secretId` | The name of the secret ID | `no` | + + ### HashiCorp Vault In order to leverage `terracreds` to manage secrets in `HashiCorp Vault` the following block needs to be provided in the configuration file: ```yaml diff --git a/api/api.go b/api/api.go index c3dc49c..dcb9b57 100644 --- a/api/api.go +++ b/api/api.go @@ -26,12 +26,12 @@ type Azure struct { VaultUri string `yaml:"vaultUri,omitempty"` } -// GCP is the configuration structure for the Goocle Cloud Secrets Manager provider +// GCP is the configuration structure for the Goocle Cloud Secret Manager provider type GCP struct { - // ProjectId (Required) The name of the GCP project where the Secrets Manager instance has been created + // ProjectId (Required) The name of the GCP project where the Secret Manager API has been enabled ProjectId string `yaml:"projectId,omitempty"` - // SecretId (Required) The name of the secret to create + // SecretId (Optional) The name of the secret to create SecretId string `yaml:"secretId,omitempty"` } diff --git a/config.yaml b/config.yaml index cc50913..6eccaf3 100644 --- a/config.yaml +++ b/config.yaml @@ -1,3 +1,5 @@ logging: enabled: true path: C:\Temp\ +gcp: + projectId: rekrootu diff --git a/go.mod b/go.mod index d505974..d8176f5 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( cloud.google.com/go v0.100.2 // indirect cloud.google.com/go/compute v1.5.0 // indirect cloud.google.com/go/iam v0.3.0 // indirect - cloud.google.com/go/secretmanager v1.4.0 // indirect + cloud.google.com/go/secretmanager v1.4.0 github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.17 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.11 // indirect @@ -65,7 +65,7 @@ require ( golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect google.golang.org/api v0.74.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect + google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 google.golang.org/grpc v1.46.2 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect diff --git a/main.go b/main.go index 3444c31..626687b 100644 --- a/main.go +++ b/main.go @@ -174,7 +174,8 @@ func main() { &cli.StringFlag{ Name: "secret-name", Usage: "The friendly name of the secret stored in AWS Secrets Manager. If omitted Terracreds will use the hostname value instead", - Required: true, + Value: "", + Required: false, }, }, Action: func(c *cli.Context) error { @@ -187,6 +188,9 @@ func main() { cfg.Azure.UseMSI = false cfg.Azure.VaultUri = "" + cfg.GCP.ProjectId = "" + cfg.GCP.SecretId = "" + cfg.HashiVault.EnvironmentTokenName = "" cfg.HashiVault.KeyVaultPath = "" cfg.HashiVault.SecretName = "" @@ -208,6 +212,7 @@ func main() { &cli.StringFlag{ Name: "secret-name", Usage: "The name of the secret stored in Azure Key Vault. If omitted Terracreds will use the hostname value instead", + Value: "", Required: false, }, &cli.BoolFlag{ @@ -231,6 +236,52 @@ func main() { cfg.Aws.Region = "" cfg.Aws.SecretName = "" + cfg.GCP.ProjectId = "" + cfg.GCP.SecretId = "" + + cfg.HashiVault.EnvironmentTokenName = "" + cfg.HashiVault.KeyVaultPath = "" + cfg.HashiVault.SecretName = "" + cfg.HashiVault.SecretPath = "" + cfg.HashiVault.VaultUri = "" + + err := helpers.WriteConfig(configFilePath, &cfg) + if err != nil { + helpers.CheckError(err) + } + + return nil + }, + }, + { + Name: "gcp", + Usage: "Google Cloud Provider Secret Managers configuration settings", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project-id", + Usage: "The name of the GCP project where the Secrets Manager has been created", + Required: true, + }, + &cli.StringFlag{ + Name: "secret-id", + Usage: "The name of the secret identifier in GCP Secret Manager. If omitted Terracreds will use the hostname value instead", + Value: "", + Required: false, + }, + }, + Action: func(c *cli.Context) error { + cfg.GCP.ProjectId = c.String("project-id") + cfg.GCP.SecretId = c.String("secret-id") + + // Set all other config values to empty + cfg.Azure.SecretName = "" + cfg.Azure.UseMSI = false + cfg.Azure.VaultUri = "" + + cfg.Aws.Description = "" + cfg.Aws.Region = "" + cfg.Aws.SecretName = "" + cfg.HashiVault.EnvironmentTokenName = "" cfg.HashiVault.KeyVaultPath = "" cfg.HashiVault.SecretName = "" @@ -262,6 +313,7 @@ func main() { &cli.StringFlag{ Name: "secret-name", Usage: "The name of the secret stored inside of Vault. If omitted Terracreds will use the hostname value instead", + Value: "", Required: false, }, &cli.StringFlag{ @@ -291,6 +343,9 @@ func main() { cfg.Azure.UseMSI = false cfg.Azure.VaultUri = "" + cfg.GCP.ProjectId = "" + cfg.GCP.SecretId = "" + err := helpers.WriteConfig(configFilePath, &cfg) if err != nil { helpers.CheckError(err) @@ -385,6 +440,9 @@ func main() { cfg.Azure.UseMSI = false cfg.Azure.VaultUri = "" + cfg.GCP.ProjectId = "" + cfg.GCP.SecretId = "" + cfg.HashiVault.EnvironmentTokenName = "" cfg.HashiVault.KeyVaultPath = "" cfg.HashiVault.SecretName = "" @@ -586,7 +644,7 @@ func main() { } if len(cfg.Secrets) < 1 && c.String("secret-names") == "" { - verbiage := "A list of secrets must be provided. Use '--secret-names' and pass it a comma separated list of secrets, or setup the 'secrets' block in the terracreds config file to use this command" + verbiage := "A list of secrets must be provided. Use '--secret-names' and pass it a comma separated list of secrets, or setup the 'secrets' block in the terracreds config file to use this command\n" fmt.Fprintf(color.Output, "%s: %s", color.RedString("ERROR"), verbiage) return nil } diff --git a/pkg/vault/gcp.go b/pkg/vault/gcp.go index 49065a4..6c27cac 100644 --- a/pkg/vault/gcp.go +++ b/pkg/vault/gcp.go @@ -24,7 +24,6 @@ func (gcp *GCPSecretsManager) getClient() *secretmanager.Client { if err != nil { helpers.CheckError(err) } - defer client.Close() return client } @@ -37,7 +36,30 @@ func formatGcpSecretName(secretName string) string { func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { client := gcp.getClient() + defer client.Close() + secretId := formatGcpSecretName(gcp.SecretId) + accessRequest := &secretmanagerpb.GetSecretRequest{ + Name: fmt.Sprintf("projects/%s/secrets/%s", gcp.ProjectId, secretId), + } + + get, err := client.GetSecret(ctx, accessRequest) + if err == nil { + payload := []byte(secretValue) + addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{ + Parent: get.Name, + Payload: &secretmanagerpb.SecretPayload{ + Data: payload, + }, + } + + _, err = client.AddSecretVersion(ctx, addSecretVersionReq) + if err != nil { + return err + } + + return err + } createSecretReq := &secretmanagerpb.CreateSecretRequest{ Parent: fmt.Sprintf("projects/%s", gcp.ProjectId), @@ -65,8 +87,6 @@ func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { }, } - print(secret.Name) - _, err = client.AddSecretVersion(ctx, addSecretVersionReq) if err != nil { return err @@ -77,13 +97,24 @@ func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { func (gcp *GCPSecretsManager) Delete() error { client := gcp.getClient() + defer client.Close() + secretId := formatGcpSecretName(gcp.SecretId) + accessRequest := &secretmanagerpb.AccessSecretVersionRequest{ + Name: fmt.Sprintf("projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), + } + + result, err := client.AccessSecretVersion(ctx, accessRequest) + if err != nil { + return err + } + destroySecretVersionReq := secretmanagerpb.DestroySecretVersionRequest{ - Name: fmt.Sprintf("/projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), + Name: result.Name, } - _, err := client.DestroySecretVersion(ctx, &destroySecretVersionReq) + _, err = client.DestroySecretVersion(ctx, &destroySecretVersionReq) if err != nil { return err } @@ -93,10 +124,11 @@ func (gcp *GCPSecretsManager) Delete() error { func (gcp *GCPSecretsManager) Get() ([]byte, error) { client := gcp.getClient() - secretId := formatGcpSecretName(gcp.SecretId) + defer client.Close() + secretId := formatGcpSecretName(gcp.SecretId) accessRequest := &secretmanagerpb.AccessSecretVersionRequest{ - Name: fmt.Sprintf("/projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), + Name: fmt.Sprintf("projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), } result, err := client.AccessSecretVersion(ctx, accessRequest) @@ -108,5 +140,23 @@ func (gcp *GCPSecretsManager) Get() ([]byte, error) { } func (gcp *GCPSecretsManager) List(secretNames []string) ([]string, error) { - return nil, nil + var secretValues []string + client := gcp.getClient() + defer client.Close() + + for _, secret := range secretNames { + secretId := formatGcpSecretName(secret) + accessRequest := &secretmanagerpb.AccessSecretVersionRequest{ + Name: fmt.Sprintf("projects/%s/secrets/%s/versions/latest", gcp.ProjectId, secretId), + } + + result, err := client.AccessSecretVersion(ctx, accessRequest) + if err != nil { + return nil, err + } + + secretValues = append(secretValues, string(result.Payload.Data)) + } + + return secretValues, nil } From c4824c4b67033d02212cb80f61407d9f75b11c27 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sun, 5 Jun 2022 16:08:41 -0700 Subject: [PATCH 12/22] Almost finalized readme --- .gitignore | 3 ++- README.md | 56 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 955cd48..509f970 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ terraform/.terraform/* terraform/.terraform.tfstate.lock.info terraform/terraform.tfstate terraform/terraform.tfstate.backup -terracreds \ No newline at end of file +terracreds +terracreds.json \ No newline at end of file diff --git a/README.md b/README.md index 15c0467..6ba5454 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Terracreds -A credential helper for Terraform Cloud/Enterprise, or to store other secrets, securely in the operating system's credential vault or through a third party vault. No longer keep secrets in a plain text configuration file! +A credential helper for Terraform Automation and Collaboration Software, or to store any other secrets, securely in the operating system's credential vault or through a third party vault provider. No longer keep secrets in a plain text configuration file! We all know storing secrets in plain text can pose major security threats, and Terraform doesn't come pre-packaged with a credential helper, so we decided to create one and to share it with the greater Terraform/DevOps community to help enable stronger security practices @@ -18,10 +18,17 @@ We all know storing secrets in plain text can pose major security threats, and T - [x] Google Secret Manager - [x] HashiCorp Vault +#### Currently Supported Terraform Automation and Collaboration Software +- [x] env0 +- [x] Scalr +- [x] Spacelift +- [x] Terraform Cloud +- [x] Terraform Enterprise + ## Windows Install via Chocolatey The fastest way to install `terracreds` on Windows is via our Chocolatey package: ```powershell -choco install terracreds --version "2.0.0" -y +choco install terracreds --version "2.1.0" -y ``` Once installed run the following command to verify `terracreds` was installed properly: @@ -31,7 +38,7 @@ terracreds -v To upgrade `terracreds` to the latest version with Chocolatey run the the following command: ```powershell -choco upgrade terracreds --version "2.0.0" -y +choco upgrade terracreds --version "2.1.0" -y ``` ## macOS Install @@ -42,10 +49,10 @@ extract the package, and then place it in a directory available on `$HOME` You'll need to download the latest binary from our release page and place it anywhere on `$PATH` of your system. You can also copy and run the following commands: ```bash -wget https://github.com/tonedefdev/terracreds/releases/download/v2.0.0/terracreds_2.0.0_linux_amd64.tar.gz && \ -tar -xvf terracreds_2.0.0_linux_amd64.tar.gz && \ +wget https://github.com/tonedefdev/terracreds/releases/download/v2.1.0/terracreds_2.1.0_linux_amd64.tar.gz && \ +tar -xvf terracreds_2.1.0_linux_amd64.tar.gz && \ sudo mv -f terracreds /usr/bin/terracreds && \ -rm -f terracreds_2.0.0_linux_amd64.tar.gz README.md +rm -f terracreds_2.1.0_linux_amd64.tar.gz README.md ``` The `terracreds` Linux implementation uses `gnome-keyring` in conjunction with `gnome-keyring-daemon` @@ -121,13 +128,13 @@ credentials_helper "terracreds" { Once you have moved all of your tokens from this file to the `Windows Credential Manager` or `KeyChain` via `terracreds` you can remove the tokens from the file. If you don't remove the tokens, and you add the `credentials_helper` block to this file, Terraform will still use the tokens instead of `terracreds` to retreive the tokens, so be sure to remove your tokens from this file once you have used the `create` or `terraform login` command to create the credentials in `terracreds` so you can actually leverage the credential helper ## Storing Credentials -For Terraform to properly use the credentials stored in your credential manager they need to be stored a specific way. The name of the credential object must be the domain name of the Terraform Cloud or Enterprise server. For instance `app.terraform.io` which is the default name `terraform login` will use +For Terraform to properly use the credentials stored in your credential manager they need to be stored a specific way. The name of the credential object must be the domain name of the Terraform Automation and Collaboration server. For instance `app.terraform.io` which is the default name `terraform login` will use -The value for the password will correspond to the API token associated for that specific Terraform Cloud or Enterprise server +The value for the password will correspond to the API token associated for that specific Terraform Automation and Collaboration server -The entire process is kicked off directly from the Terraform CLI. Run `terraform login` to start the login process with Terraform Cloud. If you're using Terraform Enterprise you'll need to pass the hostname of the server as an additional argument `terraform login my.tfe.com` +The entire process is kicked off directly from the Terraform CLI. Run `terraform login` to start the login process with Terraform Cloud. If you're using Terraform Enterprise or another Terraform Automation and Collaboration Software solution you'll need to pass the hostname of the server as an additional argument `terraform login my.tacos.com` -You'll be sent to your Terraform Cloud instance where you'll be requested to sign-in with your account, and then sent to create an API token. Create the API token with any name you'd like for this example we'll use `terracreds` +You'll be sent to your Terraform Automation and Collaboration Software instance where you'll be requested to sign-in with your account, and then sent to create an API token. Create the API token with any name you'd like for this example we'll use `terracreds` Once completed, copy the generated token, paste it into your terminal, and then hit enter. Terraform will then leverage `terracreds` to store the credentials in the operating system's credential manager. If all went well you should receive the following success message: @@ -190,17 +197,33 @@ Additionally, you can check the `terracreds.log` if logging is enabled for more ## List Credentials > New in version `2.1.0` -You can pass in a comma separated list of secrets via `terracreds list -l mysecret,mysecret2` to print out the secret values to the screen. +You can pass in a comma separated list of secrets to print out the secret values to the screen: +```bash +terracreds list -l mysecret,mysecret2 +``` -You can also setup a list of secrets in the configuration file by using `terracreds config secrets -l mysecret,mysecret2` and then calling `terracreds list --from-config` to print out the secrets. +You can also setup a list of secrets in the configuration file by using: + ```bash + terracreds config secrets -l mysecret,mysecret2 + ``` -There's a helper flag `terracreds list --as-tfvars` which will return the secret values formatted for use with `terraform`. Depending on the shell calling this command will determine how you can readily use these values. + To print out the secrets from the names stored in the configuration file: + ```bash + terracreds list --from-config + ``` -For instance on Linux/macOS you can simply call `eval` to evaluate the output and use those as variables in your current shell. +There's a helper flag `--as-tfvars` which will return the secret values formatted for use with `terraform`. Depending on the shell calling this command will determine how you can readily use these values + +For instance on Linux/macOS you can simply call `eval` to evaluate the output to then convert the returned values into variables in your current shell ## Setting Up a Vault Provider > You can reference example configs in our [repo](https://github.com/tonedefdev/terracreds/blob/main/config.yaml) plus we have example [terraform](https://github.com/tonedefdev/terracreds/tree/main/terraform) code you can reference in order to setup your `AWS` or `Azure` VMs to use `terracreds` for a CI/CD piepline agent or a development workstation +### Configure from Terracreds +> New in version `2.1.0` + +You can create and view the configuration for any vault provider by running `terracreds config` and then using the subcommand for the specific vault provider. The list is extensive so it's best to run `terracreds config --help` to get a better understanding of the values required for each provider + ### AWS Secrets Manager > Currently, we only support using an `EC2 Instance Role` for authentication. This ensures the highest level of security by alleviating the `secret zero` dilemma @@ -256,7 +279,9 @@ secret_permissions = [ > Since `Azure Key Vault` doesn't support the period character in a secret name a helper function will replace any periods with dashes so they can be successfully stored. This means a `terraform` API token name that would usually be `app.terraform.io` will become `app-terraform-io` ### Google Secret Manager -In to leverage `terracreds` to manage secrets in `Google Secret Manager` the following block needs to be provided in the configuration file: +> New in version `2.1.0` + +In order to leverage `terracreds` to manage secrets in `Google Secret Manager` the following block needs to be provided in the configuration file: ```yaml gcp: projectId: my-gcp-project @@ -270,7 +295,6 @@ The `Google IAM` role `secretmanager.admin` is suggested in order to fully manag | `projectId` | The name of the `GCP` project ID where the `Secret Manager` API has been enabled | `yes` | | `secretId` | The name of the secret ID | `no` | - ### HashiCorp Vault In order to leverage `terracreds` to manage secrets in `HashiCorp Vault` the following block needs to be provided in the configuration file: ```yaml From 04a38df886febf52a3cf0320e825665d26f6dd42 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sun, 5 Jun 2022 23:27:32 -0700 Subject: [PATCH 13/22] Updated to go 1.18. Updated to using azidentity sdk for Azure. Removed useMSI since azidentity has a default auth flow that leverages more than just MSI. Fixed bug where terraform login didn't work with external vaults. Removed limitations for external vault usage on Mac --- api/api.go | 4 +-- go.mod | 23 +++++++++--- go.sum | 50 +++++++++++++++++--------- main.go | 32 +++++++++-------- pkg/helpers/helpers.go | 4 +-- pkg/platform/linux.go | 11 +++++- pkg/platform/mac.go | 18 ++++++---- pkg/platform/windows.go | 11 +++++- pkg/vault/azure.go | 77 +++++++++++++++++++++++++---------------- terraform/azure.tf | 6 ++-- terraform/variables.tf | 14 +++++++- 11 files changed, 168 insertions(+), 82 deletions(-) diff --git a/api/api.go b/api/api.go index dcb9b57..99136d8 100644 --- a/api/api.go +++ b/api/api.go @@ -19,8 +19,8 @@ type Azure struct { // if omitted Terracreds will use the hostname value instead SecretName string `yaml:"secretName,omitempty"` - // UseMSI (Required) A flag to indicate if the Managed Identity of the Azure VM should be used for authentication - UseMSI bool `yaml:"useMSI"` + // SubscriptionId (Required) The subscription ID where the target Key Vault has been created + SubscriptionId string `yaml:"subscriptionId,omitempty"` // VaultUri (Required) The FQDN of the Azure Key Vault resource VaultUri string `yaml:"vaultUri,omitempty"` diff --git a/go.mod b/go.mod index d8176f5..01b3ea9 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,10 @@ module github.com/tonedefdev/terracreds -go 1.17 +go 1.18 require ( github.com/Azure/azure-sdk-for-go v58.1.0+incompatible + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/MakeNowJust/heredoc v1.0.0 @@ -13,7 +14,19 @@ require ( github.com/hashicorp/vault/api v1.1.1 github.com/urfave/cli/v2 v2.2.0 github.com/zalando/go-keyring v0.1.0 - gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect + github.com/golang-jwt/jwt v3.2.1+incompatible // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect ) require ( @@ -57,10 +70,10 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect go.opencensus.io v0.23.0 // indirect - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect - golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect - golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect google.golang.org/api v0.74.0 // indirect diff --git a/go.sum b/go.sum index 0900456..8e333eb 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,16 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v58.1.0+incompatible h1:WFsr3Efy7uweykOAEfOHO3ACtuwIv+rrFmSn9K48VnA= github.com/Azure/azure-sdk-for-go v58.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0 h1:sVPhtT2qjO86rTUaWMr4WoES4TkjGnzcioXcnHV9s5k= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 h1:Yoicul8bnVdQrhDMTHxdEckRGX01XvwXDHUT9zYZ3k0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1 h1:X7FHRMKr0u5YiPnD6L/nqG64XBOcK0IYavhAHBQEmms= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1/go.mod h1:WcC2Tk6JyRlqjn2byvinNnZzgdXmZ1tOiIOWNh1u0uA= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 h1:9cn6ICCGiWFNA/slKnrkf+ENyvaCRKHtuoGtnLIAgao= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -81,6 +91,8 @@ github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 h1:WVsrXCnHlDDX8ls+tootqRE87/hL9S/g4ewig9RsD/c= +github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -149,6 +161,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= @@ -195,6 +208,9 @@ github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -227,7 +243,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -244,7 +259,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -268,6 +282,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -347,6 +363,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= @@ -373,6 +391,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -390,6 +409,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -436,13 +457,11 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 h1:MCfT24H3f//U5+UCrZp1/riVO3B50BovxtDiNn0XKkk= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= @@ -473,8 +492,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -552,12 +572,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA= +golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -633,6 +653,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -641,8 +662,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -656,8 +675,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -667,7 +687,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -733,8 +752,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -847,7 +866,6 @@ google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf h1:JTjwKJX9erVpsw17w+OIPP7iAgEkN/r8urhWSunEDTs= google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= @@ -881,7 +899,6 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -920,9 +937,10 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 626687b..8a64710 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,9 @@ import ( "github.com/tonedefdev/terracreds/pkg/vault" ) +const cfgName = "config.yaml" +const version = "2.1.0" + var ( cfg api.Config configFilePath string @@ -69,9 +72,9 @@ func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { if cfg.Azure.VaultUri != "" { vault := &vault.AzureKeyVault{ - SecretName: hostname, - UseMSI: cfg.Azure.UseMSI, - VaultUri: cfg.Azure.VaultUri, + SecretName: hostname, + SubscriptionId: cfg.Azure.SubscriptionId, + VaultUri: cfg.Azure.VaultUri, } if cfg.Azure.SecretName != "" { @@ -114,14 +117,12 @@ func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { } func main() { - version := "2.1.0" - fileEnvVar := os.Getenv("TC_CONFIG_PATH") if fileEnvVar != "" { - configFilePath = fileEnvVar + "config.yaml" + configFilePath = fileEnvVar + cfgName } else { binPath := helpers.GetBinaryPath(os.Args[0], runtime.GOOS) - configFilePath = binPath + "config.yaml" + configFilePath = binPath + cfgName } err := helpers.CreateConfigFile(configFilePath) @@ -185,7 +186,7 @@ func main() { // Set all other config values to empty cfg.Azure.SecretName = "" - cfg.Azure.UseMSI = false + cfg.Azure.SubscriptionId = "" cfg.Azure.VaultUri = "" cfg.GCP.ProjectId = "" @@ -215,9 +216,10 @@ func main() { Value: "", Required: false, }, - &cli.BoolFlag{ - Name: "use-msi", - Usage: "A flag to indicate if the Managed Identity of the Azure VM should be used for authentication", + &cli.StringFlag{ + Name: "subscription-id", + Aliases: []string{"id"}, + Usage: "The subscription ID where the Key Vault instance has been created", Required: true, }, &cli.StringFlag{ @@ -228,7 +230,7 @@ func main() { }, Action: func(c *cli.Context) error { cfg.Azure.SecretName = c.String("secret-name") - cfg.Azure.UseMSI = c.Bool("use-msi") + cfg.Azure.SubscriptionId = c.String("subscription-id") cfg.Azure.VaultUri = c.String("vault-uri") // Set all other config values to empty @@ -275,7 +277,7 @@ func main() { // Set all other config values to empty cfg.Azure.SecretName = "" - cfg.Azure.UseMSI = false + cfg.Azure.SubscriptionId = "" cfg.Azure.VaultUri = "" cfg.Aws.Description = "" @@ -340,7 +342,7 @@ func main() { cfg.Aws.SecretName = "" cfg.Azure.SecretName = "" - cfg.Azure.UseMSI = false + cfg.Azure.SubscriptionId = "" cfg.Azure.VaultUri = "" cfg.GCP.ProjectId = "" @@ -437,7 +439,7 @@ func main() { cfg.Aws.SecretName = "" cfg.Azure.SecretName = "" - cfg.Azure.UseMSI = false + cfg.Azure.SubscriptionId = "" cfg.Azure.VaultUri = "" cfg.GCP.ProjectId = "" diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 0a00107..6a96497 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -202,14 +202,14 @@ func GenerateTerraCreds(c *cli.Context, version string, confirm string) { userProfile := os.Getenv("USERPROFILE") cliConfig = userProfile + "\\AppData\\Roaming\\terraform.rc" tfPlugins = userProfile + "\\AppData\\Roaming\\terraform.d\\plugins" - binary = fmt.Sprintf("%s\\terraform-credentials-terracreds_%s.exe", tfPlugins, version) + binary = fmt.Sprintf("%s\\terraform-credentials-terracreds.exe", tfPlugins) } if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { userProfile := os.Getenv("HOME") cliConfig = userProfile + "/.terraform.d/.terraformrc" tfPlugins = userProfile + "/.terraform.d/plugins" - binary = fmt.Sprintf("%s/terraform-credentials-terracreds_%s", tfPlugins, version) + binary = fmt.Sprintf("%s/terraform-credentials-terracreds", tfPlugins) } NewDirectory(tfPlugins) diff --git a/pkg/platform/linux.go b/pkg/platform/linux.go index 9461e8d..20d6e86 100644 --- a/pkg/platform/linux.go +++ b/pkg/platform/linux.go @@ -29,6 +29,16 @@ func (l *Linux) Create(cfg api.Config, hostname string, token interface{}, user method = "Created" } + if token == nil { + var response api.CredentialResponse + err = json.NewDecoder(os.Stdin).Decode(&response) + if err != nil { + helpers.CheckError(err) + } + + token = response.Token + } + secretValue := fmt.Sprintf("%v", token) err = vault.Create(secretValue, method) if err != nil { @@ -47,7 +57,6 @@ func (l *Linux) Create(cfg api.Config, hostname string, token interface{}, user if token == nil { var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) if err != nil { helpers.CheckError(err) diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index adad2c3..64e6d15 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -2,7 +2,6 @@ package platform import ( "encoding/json" - "errors" "fmt" "os" "os/user" @@ -24,16 +23,22 @@ func (m *Mac) Create(cfg api.Config, hostname string, token interface{}, user *u var method string method = "Updated" - if cfg.Aws.Region != "" || cfg.Azure.VaultUri != "" { - return errors.New("terracreds doesn't currently support using the AWS or Azure provider on macOS") - } - - if vault != nil && cfg.HashiVault.VaultUri != "" { + if vault != nil { _, err := vault.Get() if err != nil { method = "Created" } + if token == nil { + var response api.CredentialResponse + err = json.NewDecoder(os.Stdin).Decode(&response) + if err != nil { + helpers.CheckError(err) + } + + token = response.Token + } + secretValue := fmt.Sprintf("%v", token) err = vault.Create(secretValue, method) if err != nil { @@ -52,7 +57,6 @@ func (m *Mac) Create(cfg api.Config, hostname string, token interface{}, user *u if token == nil { var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) if err != nil { helpers.CheckError(err) diff --git a/pkg/platform/windows.go b/pkg/platform/windows.go index 87d449d..15ccbf1 100644 --- a/pkg/platform/windows.go +++ b/pkg/platform/windows.go @@ -29,6 +29,16 @@ func (w *Windows) Create(cfg api.Config, hostname string, token interface{}, use method = "Created" } + if token == nil { + var response api.CredentialResponse + err = json.NewDecoder(os.Stdin).Decode(&response) + if err != nil { + helpers.CheckError(err) + } + + token = response.Token + } + secretValue := fmt.Sprintf("%v", token) err = vault.Create(secretValue, method) if err != nil { @@ -48,7 +58,6 @@ func (w *Windows) Create(cfg api.Config, hostname string, token interface{}, use cred := wincred.NewGenericCredential(hostname) if token == nil { var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) if err != nil { helpers.CheckError(err) diff --git a/pkg/vault/azure.go b/pkg/vault/azure.go index aa7eddd..7257e60 100644 --- a/pkg/vault/azure.go +++ b/pkg/vault/azure.go @@ -4,33 +4,31 @@ import ( "context" "strings" - "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" - "github.com/Azure/go-autorest/autorest/azure/auth" - "github.com/Azure/go-autorest/autorest/to" - "github.com/tonedefdev/terracreds/pkg/helpers" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets" ) type AzureKeyVault struct { - SecretList []string - SecretName string - UseMSI bool - VaultUri string + SecretList []string + SecretName string + SubscriptionId string + VaultUri string } -// getVaultClientMSI returns a keyvault.BaseClient with an MSI authorizer for an Azure Key Vault resource -func getVaultClientMSI() keyvault.BaseClient { - const vaultUri = "https://vault.azure.net" - vaultClient := keyvault.New() - msiConfig := auth.NewMSIConfig() - msiConfig.Resource = vaultUri +// getDefaultAzureClient returns a pointer to an azsecrets.Client using the default +// authorization scheme for azidentity +func getDefaultAzureClient(akv *AzureKeyVault) (*azsecrets.Client, error) { + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return nil, err + } - authorizer, err := msiConfig.Authorizer() + vaultClient, err := azsecrets.NewClient(akv.VaultUri, cred, nil) if err != nil { - helpers.CheckError(err) + return nil, err } - vaultClient.Authorizer = authorizer - return vaultClient + return vaultClient, err } // formatSecretName replaces the periods from the hostname with dashes @@ -43,46 +41,67 @@ func formatSecretName(secretName string) string { // Create stores a secret in an Azure Key Vault func (akv *AzureKeyVault) Create(secretValue string, method string) error { ctx := context.Background() - client := getVaultClientMSI() + client, err := getDefaultAzureClient(akv) + if err != nil { + return err + } - secretParams := keyvault.SecretSetParameters{ - ContentType: to.StringPtr("password"), - Value: &secretValue, + content := "password" + options := azsecrets.SetSecretOptions{ + ContentType: &content, } secret := formatSecretName(akv.SecretName) - _, err := client.SetSecret(ctx, akv.VaultUri, secret, secretParams) + _, err = client.SetSecret(ctx, secret, secretValue, &options) return err } // Delete removes a secret stored in an Azure Key Vault func (akv *AzureKeyVault) Delete() error { ctx := context.Background() - client := getVaultClientMSI() + client, err := getDefaultAzureClient(akv) + if err != nil { + return err + } + options := azsecrets.BeginDeleteSecretOptions{} secret := formatSecretName(akv.SecretName) - _, err := client.DeleteSecret(ctx, akv.VaultUri, secret) + + _, err = client.BeginDeleteSecret(ctx, secret, &options) return err } // Get retrieves a secrete stored in an Azure Key Vault func (akv *AzureKeyVault) Get() ([]byte, error) { ctx := context.Background() - client := getVaultClientMSI() + client, err := getDefaultAzureClient(akv) + if err != nil { + return nil, err + } + options := azsecrets.GetSecretOptions{} secret := formatSecretName(akv.SecretName) - get, err := client.GetSecret(ctx, akv.VaultUri, secret, "") + + get, err := client.GetSecret(ctx, secret, &options) + if err != nil { + return nil, err + } return []byte(*get.Value), err } func (akv *AzureKeyVault) List(secretNames []string) ([]string, error) { var secretValues []string ctx := context.Background() - client := getVaultClientMSI() + client, err := getDefaultAzureClient(akv) + if err != nil { + return nil, err + } for _, secret := range secretNames { - get, err := client.GetSecret(ctx, akv.VaultUri, secret, "") + options := azsecrets.GetSecretOptions{} + secret := formatSecretName(secret) + get, err := client.GetSecret(ctx, secret, &options) if err != nil { return nil, err } diff --git a/terraform/azure.tf b/terraform/azure.tf index 022e532..22a751e 100644 --- a/terraform/azure.tf +++ b/terraform/azure.tf @@ -1,7 +1,7 @@ data "azurerm_client_config" "current" {} resource "azurerm_resource_group" "rg" { - count = var.test_az ? 1 : 0 + count = var.keyvault_only || var.test_az ? 1 : 0 name = var.rg_name location = var.location } @@ -74,7 +74,7 @@ resource "azurerm_windows_virtual_machine" "vm" { } resource "azurerm_key_vault" "keyvault" { - count = var.test_az ? 1 : 0 + count = var.keyvault_only || var.test_az ? 1 : 0 name = var.keyvault_name location = azurerm_resource_group.rg[0].location resource_group_name = azurerm_resource_group.rg[0].name @@ -87,7 +87,7 @@ resource "azurerm_key_vault" "keyvault" { access_policy { tenant_id = data.azurerm_client_config.current.tenant_id - object_id = azurerm_windows_virtual_machine.vm[count.index].identity[count.index].principal_id + object_id = var.keyvault_only ? var.object_id : azurerm_windows_virtual_machine.vm[count.index].identity[count.index].principal_id secret_permissions = [ "Get", diff --git a/terraform/variables.tf b/terraform/variables.tf index f40e9b0..cd08575 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -10,6 +10,18 @@ variable "location" { description = "Resource location" } +variable "keyvault_only" { + type = bool + default = true + description = "Create only the Azure Key Vault resources and not any VMs" +} + +variable "object_id" { + type = string + default = "5277cb3b-c86d-4bdc-8034-6220bdd5b4d3" + description = "The Object ID of the user to add to the Azure Key Vault Access Policy" +} + variable "rg_name" { type = string default = "terracreds-test-rg" @@ -24,7 +36,7 @@ variable "test_az" { variable "test_aws" { type = bool - default = true + default = false description = "A flag to enable testing of AWS resources" } From 56f7ea07b8b81f92ae1363edb3e85b0c38bab2bc Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sun, 5 Jun 2022 23:32:56 -0700 Subject: [PATCH 14/22] Removing unncessary SecretList slice. Updated struct name for GCP --- config.yaml | 5 ----- main.go | 2 +- pkg/vault/azure.go | 1 - pkg/vault/gcp.go | 18 ++++++++---------- 4 files changed, 9 insertions(+), 17 deletions(-) delete mode 100644 config.yaml diff --git a/config.yaml b/config.yaml deleted file mode 100644 index 6eccaf3..0000000 --- a/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -logging: - enabled: true - path: C:\Temp\ -gcp: - projectId: rekrootu diff --git a/main.go b/main.go index 8a64710..ae4243f 100644 --- a/main.go +++ b/main.go @@ -85,7 +85,7 @@ func NewTerraVault(cfg *api.Config, hostname string) vault.TerraVault { } if cfg.GCP.ProjectId != "" { - vault := &vault.GCPSecretsManager{ + vault := &vault.GCPSecretManager{ ProjectId: cfg.GCP.ProjectId, SecretId: hostname, } diff --git a/pkg/vault/azure.go b/pkg/vault/azure.go index 7257e60..13f1eaf 100644 --- a/pkg/vault/azure.go +++ b/pkg/vault/azure.go @@ -9,7 +9,6 @@ import ( ) type AzureKeyVault struct { - SecretList []string SecretName string SubscriptionId string VaultUri string diff --git a/pkg/vault/gcp.go b/pkg/vault/gcp.go index 6c27cac..9b99066 100644 --- a/pkg/vault/gcp.go +++ b/pkg/vault/gcp.go @@ -13,13 +13,12 @@ import ( var ctx = context.Background() -type GCPSecretsManager struct { - ProjectId string - SecretId string - SecretList []string +type GCPSecretManager struct { + ProjectId string + SecretId string } -func (gcp *GCPSecretsManager) getClient() *secretmanager.Client { +func (gcp *GCPSecretManager) getClient() *secretmanager.Client { client, err := secretmanager.NewClient(ctx) if err != nil { helpers.CheckError(err) @@ -34,7 +33,7 @@ func formatGcpSecretName(secretName string) string { return hostname } -func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { +func (gcp *GCPSecretManager) Create(secretValue string, method string) error { client := gcp.getClient() defer client.Close() @@ -79,7 +78,6 @@ func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { } payload := []byte(secretValue) - addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{ Parent: secret.Name, Payload: &secretmanagerpb.SecretPayload{ @@ -95,7 +93,7 @@ func (gcp *GCPSecretsManager) Create(secretValue string, method string) error { return err } -func (gcp *GCPSecretsManager) Delete() error { +func (gcp *GCPSecretManager) Delete() error { client := gcp.getClient() defer client.Close() @@ -122,7 +120,7 @@ func (gcp *GCPSecretsManager) Delete() error { return err } -func (gcp *GCPSecretsManager) Get() ([]byte, error) { +func (gcp *GCPSecretManager) Get() ([]byte, error) { client := gcp.getClient() defer client.Close() @@ -139,7 +137,7 @@ func (gcp *GCPSecretsManager) Get() ([]byte, error) { return result.Payload.Data, err } -func (gcp *GCPSecretsManager) List(secretNames []string) ([]string, error) { +func (gcp *GCPSecretManager) List(secretNames []string) ([]string, error) { var secretValues []string client := gcp.getClient() defer client.Close() From 299736aa9366ca89336f3e2500887d99571bf505 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 09:20:10 -0700 Subject: [PATCH 15/22] Refactored duplicate code. Added --as-json to return list secrets as JSON string. Updated interface{} to use go 1.18 any aliase. Added logging for list --- main.go | 29 ++++++++++++++++++++---- pkg/platform/linux.go | 42 +++++++++++++++++------------------ pkg/platform/mac.go | 42 +++++++++++++++++------------------ pkg/platform/windows.go | 49 ++++++++++++++++++----------------------- 4 files changed, 87 insertions(+), 75 deletions(-) diff --git a/main.go b/main.go index ae4243f..8a225fe 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "fmt" "os" "os/user" @@ -30,7 +31,7 @@ var ( // TerraCreds interface implements these methods for a credential's lifecycle type TerraCreds interface { // Create or store a secret in a vault - Create(cfg api.Config, hostname string, token interface{}, user *user.User, vault vault.TerraVault) error + Create(cfg api.Config, hostname string, token any, user *user.User, vault vault.TerraVault) error // Delete or forget a secret in a vault Delete(cfg api.Config, command string, hostname string, user *user.User, vault vault.TerraVault) error // Get or retrieve a secret in a vault @@ -144,7 +145,7 @@ func main() { app := &cli.App{ Name: "terracreds", Usage: "a credential helper for Terraform Automation and Collaboration Software (TACOS) that leverages your vault provider of choice for securely storing API tokens or other secrets.\n\n Visit https://github.com/tonedefdev/terracreds for more information", - UsageText: "Store Terraform Automation and Collaboration Software API tokens by running 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -s myAPItoken'", + UsageText: "Store Terraform Automation and Collaboration Software API tokens by running 'terraform login' or manually store them using 'terracreds create -n app.terraform.io -v myAPItoken'", Version: version, Commands: []*cli.Command{ { @@ -627,6 +628,12 @@ func main() { Usage: "Exports the secret keys and values as 'TF_VARS_secret_key=secret_value' for the given operating system", Required: false, }, + &cli.BoolFlag{ + Name: "as-json", + Value: false, + Usage: "Prints the secret keys and values as a JSON string", + Required: false, + }, }, Action: func(c *cli.Context) error { if len(os.Args) == 2 { @@ -661,10 +668,24 @@ func main() { helpers.CheckError(err) } + if c.Bool("as-json") { + body := make(map[string]string, len(secretNames)) + for i, name := range secretNames { + body[name] = list[i] + } + + json, err := json.Marshal(body) + if err != nil { + helpers.CheckError(err) + } + + fmt.Println(string(json)) + return nil + } + if c.Bool("as-tfvars") { for i, name := range secretNames { fmt.Printf("TF_VAR_%s=%s\n", name, list[i]) - continue } return nil @@ -672,7 +693,7 @@ func main() { for _, secret := range list { value := fmt.Sprintf("%s\n", secret) - print(value) + fmt.Println(value) } return nil diff --git a/pkg/platform/linux.go b/pkg/platform/linux.go index 20d6e86..e8e8f46 100644 --- a/pkg/platform/linux.go +++ b/pkg/platform/linux.go @@ -19,24 +19,24 @@ import ( type Linux struct{} // Create stores or updates a Terraform API token in Gnome Keyring or an external vault provider -func (l *Linux) Create(cfg api.Config, hostname string, token interface{}, user *user.User, vault vault.TerraVault) error { +func (l *Linux) Create(cfg api.Config, hostname string, token any, user *user.User, vault vault.TerraVault) error { var method string method = "Updated" - if vault != nil { - _, err := vault.Get() + if token == nil { + var response api.CredentialResponse + err := json.NewDecoder(os.Stdin).Decode(&response) if err != nil { - method = "Created" + helpers.CheckError(err) } - if token == nil { - var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) - if err != nil { - helpers.CheckError(err) - } + token = response.Token + } - token = response.Token + if vault != nil { + _, err := vault.Get() + if err != nil { + method = "Created" } secretValue := fmt.Sprintf("%v", token) @@ -55,17 +55,6 @@ func (l *Linux) Create(cfg api.Config, hostname string, token interface{}, user method = "Created" } - if token == nil { - var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) - if err != nil { - helpers.CheckError(err) - } - - err = keyring.Set(hostname, string(user.Username), response.Token) - return err - } - str := fmt.Sprintf("%v", token) err = keyring.Set(hostname, string(user.Username), str) @@ -178,6 +167,10 @@ func (l *Linux) Get(cfg api.Config, hostname string, user *user.User, vault vaul func (l *Linux) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { var secretValues []string + if cfg.Logging.Enabled == true { + msg := fmt.Sprintf("- user requesting access: %s", string(user.Username)) + helpers.Logging(cfg, msg, "INFO") + } if vault != nil { secrets, err := vault.List(secretNames) @@ -189,6 +182,11 @@ func (l *Linux) List(c *cli.Context, cfg api.Config, secretNames []string, user } for _, secret := range secretNames { + if cfg.Logging.Enabled == true { + msg := fmt.Sprintf("- secret name requested: %s", secret) + helpers.Logging(cfg, msg, "INFO") + } + cred, err := keyring.Get(secret, string(user.Username)) if err == nil { value := string(cred) diff --git a/pkg/platform/mac.go b/pkg/platform/mac.go index 64e6d15..a50bda6 100644 --- a/pkg/platform/mac.go +++ b/pkg/platform/mac.go @@ -19,24 +19,24 @@ import ( type Mac struct{} // Create stores or updates a Terraform API token in MacOS Keyring -func (m *Mac) Create(cfg api.Config, hostname string, token interface{}, user *user.User, vault vault.TerraVault) error { +func (m *Mac) Create(cfg api.Config, hostname string, token any, user *user.User, vault vault.TerraVault) error { var method string method = "Updated" - if vault != nil { - _, err := vault.Get() + if token == nil { + var response api.CredentialResponse + err := json.NewDecoder(os.Stdin).Decode(&response) if err != nil { - method = "Created" + helpers.CheckError(err) } - if token == nil { - var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) - if err != nil { - helpers.CheckError(err) - } + token = response.Token + } - token = response.Token + if vault != nil { + _, err := vault.Get() + if err != nil { + method = "Created" } secretValue := fmt.Sprintf("%v", token) @@ -55,17 +55,6 @@ func (m *Mac) Create(cfg api.Config, hostname string, token interface{}, user *u method = "Created" } - if token == nil { - var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) - if err != nil { - helpers.CheckError(err) - } - - err = keyring.Set(hostname, string(user.Username), response.Token) - return err - } - str := fmt.Sprintf("%v", token) err = keyring.Set(hostname, string(user.Username), str) @@ -177,6 +166,10 @@ func (m *Mac) Get(cfg api.Config, hostname string, user *user.User, vault vault. func (m *Mac) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { var secretValues []string + if cfg.Logging.Enabled == true { + msg := fmt.Sprintf("- user requesting access: %s", string(user.Username)) + helpers.Logging(cfg, msg, "INFO") + } if vault != nil { secrets, err := vault.List(secretNames) @@ -188,6 +181,11 @@ func (m *Mac) List(c *cli.Context, cfg api.Config, secretNames []string, user *u } for _, secret := range secretNames { + if cfg.Logging.Enabled == true { + msg := fmt.Sprintf("- secret name requested: %s", secret) + helpers.Logging(cfg, msg, "INFO") + } + cred, err := keyring.Get(secret, string(user.Username)) if err == nil { value := string(cred) diff --git a/pkg/platform/windows.go b/pkg/platform/windows.go index 15ccbf1..741ab12 100644 --- a/pkg/platform/windows.go +++ b/pkg/platform/windows.go @@ -19,24 +19,24 @@ import ( type Windows struct{} // Create stores or updates a Terafform API token in Windows Credential Manager or a specified Cloud Vault -func (w *Windows) Create(cfg api.Config, hostname string, token interface{}, user *user.User, vault vault.TerraVault) error { +func (w *Windows) Create(cfg api.Config, hostname string, token any, user *user.User, vault vault.TerraVault) error { var method string method = "Updated" - if vault != nil { - _, err := vault.Get() + if token == nil { + var response api.CredentialResponse + err := json.NewDecoder(os.Stdin).Decode(&response) if err != nil { - method = "Created" + helpers.CheckError(err) } - if token == nil { - var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) - if err != nil { - helpers.CheckError(err) - } + token = response.Token + } - token = response.Token + if vault != nil { + _, err := vault.Get() + if err != nil { + method = "Created" } secretValue := fmt.Sprintf("%v", token) @@ -56,19 +56,6 @@ func (w *Windows) Create(cfg api.Config, hostname string, token interface{}, use } cred := wincred.NewGenericCredential(hostname) - if token == nil { - var response api.CredentialResponse - err = json.NewDecoder(os.Stdin).Decode(&response) - if err != nil { - helpers.CheckError(err) - } - - cred.CredentialBlob = []byte(response.Token) - cred.UserName = string(user.Username) - err = cred.Write() - return err - } - str := fmt.Sprintf("%v", token) cred.CredentialBlob = []byte(str) cred.UserName = string(user.Username) @@ -140,7 +127,7 @@ func (w *Windows) Delete(cfg api.Config, command string, hostname string, user * // Get retrieves a Terraform API token in Windows Credential Manager func (w *Windows) Get(cfg api.Config, hostname string, user *user.User, vault vault.TerraVault) ([]byte, error) { if cfg.Logging.Enabled == true { - msg := fmt.Sprintf("- terraform server: %s", hostname) + msg := fmt.Sprintf("- secret name requested: %s", hostname) helpers.Logging(cfg, msg, "INFO") msg = fmt.Sprintf("- user requesting access: %s", string(user.Username)) helpers.Logging(cfg, msg, "INFO") @@ -167,9 +154,8 @@ func (w *Windows) Get(cfg api.Config, hostname string, user *user.User, vault va } token, err := json.Marshal(response) - if cfg.Logging.Enabled == true { - msg := fmt.Sprintf("- token was retrieved for: %s", hostname) + msg := fmt.Sprintf("- secret was retrieved for: %s", hostname) helpers.Logging(cfg, msg, "INFO") } @@ -186,6 +172,10 @@ func (w *Windows) Get(cfg api.Config, hostname string, user *user.User, vault va func (w *Windows) List(c *cli.Context, cfg api.Config, secretNames []string, user *user.User, vault vault.TerraVault) ([]string, error) { var secretValues []string + if cfg.Logging.Enabled == true { + msg := fmt.Sprintf("- user requesting access: %s", string(user.Username)) + helpers.Logging(cfg, msg, "INFO") + } if vault != nil { secrets, err := vault.List(secretNames) @@ -197,6 +187,11 @@ func (w *Windows) List(c *cli.Context, cfg api.Config, secretNames []string, use } for _, secret := range secretNames { + if cfg.Logging.Enabled == true { + msg := fmt.Sprintf("- secret name requested: %s", secret) + helpers.Logging(cfg, msg, "INFO") + } + cred, err := wincred.GetGenericCredential(secret) if err == nil && cred.UserName == user.Username { value := string(cred.CredentialBlob) From c1a95a2957cab6344e80b4c411adf03aff201035 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 09:28:17 -0700 Subject: [PATCH 16/22] Ensuring errors bubble up --- pkg/helpers/helpers.go | 79 ++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 6a96497..262233a 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -27,17 +27,24 @@ func CheckError(e error) { // destination path. func CopyTerraCreds(dest string) error { from, err := os.Open(string(os.Args[0])) - CheckError(err) + if err != nil { + return err + } defer from.Close() to, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0755) - CheckError(err) + if err != nil { + return err + } defer to.Close() _, err = io.Copy(to, from) - CheckError(err) + if err != nil { + return err + } + fmt.Fprintf(color.Output, "%s: Copied binary '%s' to '%s'\n", color.CyanString("INFO"), string(os.Args[0]), dest) - return nil + return err } // GetBinaryPath returns the directory of the binary path @@ -95,7 +102,10 @@ func NewDirectory(path string) error { if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { err := os.Mkdir(path, 0755) - CheckError(err) + if err != nil { + return err + } + fmt.Fprintf(color.Output, "%s: Created directory '%s'\n", color.CyanString("INFO"), path) } } @@ -106,11 +116,17 @@ func NewDirectory(path string) error { // checking for errors and syncing at the end. func WriteToFile(filename string, data string) error { file, err := os.Create(filename) - CheckError(err) + if err != nil { + return err + } + defer file.Close() _, err = io.WriteString(file, data) - CheckError(err) + if err != nil { + return err + } + fmt.Fprintf(color.Output, "%s: Created file '%s'\n", color.CyanString("INFO"), filename) return file.Sync() } @@ -118,9 +134,11 @@ func WriteToFile(filename string, data string) error { // WriteToLog will create a log if it doesn't exist and then append // messages to the log func WriteToLog(path string, data string, level string) error { - f, err := os.OpenFile(path, - os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) - CheckError(err) + f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) + if err != nil { + return err + } + defer f.Close() logger := log.New(f, level, log.LstdFlags) @@ -141,18 +159,25 @@ func CreateConfigFile(path string) error { bytes, err := yaml.Marshal(&cfgFile) err = ioutil.WriteFile(path, bytes, 0755) - CheckError(err) + if err != nil { + return err + } + fmt.Fprintf(color.Output, "%s: Created file '%s'\n", color.CyanString("INFO"), path) return err } } + return nil } // LoadConfig loads the config file if it exists func LoadConfig(path string, cfg *api.Config) error { bytes, err := ioutil.ReadFile(path) - CheckError(err) + if err != nil { + return err + } + err = yaml.Unmarshal(bytes, &cfg) return err } @@ -160,9 +185,15 @@ func LoadConfig(path string, cfg *api.Config) error { // WriteConfig makes requested changes to config file func WriteConfig(path string, cfg *api.Config) error { bytes, err := yaml.Marshal(&cfg) - CheckError(err) + if err != nil { + return err + } + err = ioutil.WriteFile(path, bytes, 0755) - CheckError(err) + if err != nil { + return err + } + fmt.Fprintf(color.Output, "%s: Modified config file '%s'\n", color.GreenString("SUCCESS"), path) return err } @@ -193,7 +224,7 @@ func LogLevel(level string) string { // GenerateTerracreds creates the binary to use this package as a credential helper // and optionally the terraform.rc file -func GenerateTerraCreds(c *cli.Context, version string, confirm string) { +func GenerateTerraCreds(c *cli.Context, version string, confirm string) error { var cliConfig string var tfPlugins string var binary string @@ -212,8 +243,16 @@ func GenerateTerraCreds(c *cli.Context, version string, confirm string) { binary = fmt.Sprintf("%s/terraform-credentials-terracreds", tfPlugins) } - NewDirectory(tfPlugins) - CopyTerraCreds(binary) + err := NewDirectory(tfPlugins) + if err != nil { + return err + } + + err = CopyTerraCreds(binary) + if err != nil { + return err + } + if c.Bool("create-cli-config") == true { const verbiage = "This command will delete any settings in your .terraformrc file\n\n Enter 'yes' to coninue or press 'enter' or 'return' to cancel: " fmt.Fprintf(color.Output, "%s: %s", color.YellowString("WARNING"), verbiage) @@ -225,9 +264,13 @@ func GenerateTerraCreds(c *cli.Context, version string, confirm string) { credentials_helper "terracreds" { args = [] }`) - WriteToFile(cliConfig, doc) + + err := WriteToFile(cliConfig, doc) + return err } } + + return nil } // GetSecretName returns the name of the secret from the config From c0989945c7852a6aba59a9f9affd967a171a1b7f Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 09:30:12 -0700 Subject: [PATCH 17/22] Updated go mod --- go.mod | 15 +-------------- go.sum | 32 -------------------------------- 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/go.mod b/go.mod index 01b3ea9..6725378 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,8 @@ module github.com/tonedefdev/terracreds go 1.18 require ( - github.com/Azure/azure-sdk-for-go v58.1.0+incompatible github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 - github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 - github.com/Azure/go-autorest/autorest/to v0.4.0 + github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1 github.com/MakeNowJust/heredoc v1.0.0 github.com/aws/aws-sdk-go v1.41.0 github.com/danieljoos/wincred v1.1.0 @@ -20,7 +18,6 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect github.com/golang-jwt/jwt v3.2.1+incompatible // indirect @@ -34,18 +31,8 @@ require ( cloud.google.com/go/compute v1.5.0 // indirect cloud.google.com/go/iam v0.3.0 // indirect cloud.google.com/go/secretmanager v1.4.0 - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.17 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.0 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect github.com/godbus/dbus v4.1.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/go.sum b/go.sum index 8e333eb..a7efb62 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v58.1.0+incompatible h1:WFsr3Efy7uweykOAEfOHO3ACtuwIv+rrFmSn9K48VnA= -github.com/Azure/azure-sdk-for-go v58.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0 h1:sVPhtT2qjO86rTUaWMr4WoES4TkjGnzcioXcnHV9s5k= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 h1:Yoicul8bnVdQrhDMTHxdEckRGX01XvwXDHUT9zYZ3k0= @@ -68,29 +66,6 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1/go.mod h1:WcC2Tk github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 h1:9cn6ICCGiWFNA/slKnrkf+ENyvaCRKHtuoGtnLIAgao= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.17 h1:2zCdHwNgRH+St1J+ZMf66xI8aLr/5KMy+wWLH97zwYM= -github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.11 h1:L4/pmq7poLdsy41Bj1FayKvBhayuWRYkx9HU5i4Ybl0= -github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 h1:WVsrXCnHlDDX8ls+tootqRE87/hL9S/g4ewig9RsD/c= github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -158,9 +133,6 @@ github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7h github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -181,8 +153,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -491,8 +461,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= From 9461d9a449bb49304d00baa659be332c77d0ee9f Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 12:10:42 -0700 Subject: [PATCH 18/22] Almost finalized readme. Waiting for homebrew install instructions --- README.md | 135 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 6ba5454..75e60d4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Terracreds A credential helper for Terraform Automation and Collaboration Software, or to store any other secrets, securely in the operating system's credential vault or through a third party vault provider. No longer keep secrets in a plain text configuration file! -We all know storing secrets in plain text can pose major security threats, and Terraform doesn't come pre-packaged with a credential helper, so we decided to create one and to share it with the greater Terraform/DevOps community to help enable stronger security practices +We all know storing secrets in plain text can pose major security threats, and Terraform doesn't come pre-packaged with a credential helper, so we decided to create one and to share it with the greater Terraform/DevOps community to help enable stronger security practices. #### Currently supported Operating Systems: - [x] Windows (Credential Manager) @@ -18,7 +18,7 @@ We all know storing secrets in plain text can pose major security threats, and T - [x] Google Secret Manager - [x] HashiCorp Vault -#### Currently Supported Terraform Automation and Collaboration Software +#### Currently Supported Terraform Automation and Collaboration Software: - [x] env0 - [x] Scalr - [x] Spacelift @@ -43,7 +43,7 @@ choco upgrade terracreds --version "2.1.0" -y ## macOS Install We are currently working on a `homebrew` package, however, to install the package simply download our latest release from this repository, -extract the package, and then place it in a directory available on `$HOME` +extract the package, and then place it in a directory available on `$HOME`. ## Linux Install You'll need to download the latest binary from our release page and place it anywhere on `$PATH` of your system. You can also copy and run the following commands: @@ -56,7 +56,7 @@ rm -f terracreds_2.1.0_linux_amd64.tar.gz README.md ``` The `terracreds` Linux implementation uses `gnome-keyring` in conjunction with `gnome-keyring-daemon` -to utilize the credential storage engine +to utilize the credential storage engine. In order to leverage `terracreds` to have access to the default `Login` collection you'll need to unlock the collection with `gnome-keyring-daemon` using an empty password: @@ -65,7 +65,7 @@ the collection with `gnome-keyring-daemon` using an empty password: echo "" | gnome-keyring-daemon --unlock ``` > You do have the option of setting a password by passing it in with `echo` but every call to `terracreds get` will require the -unlock password +unlock password. The command, if successful, should return the following: ```txt @@ -100,22 +100,22 @@ Once the files have been downloaded navigate to the `terracreds` directory in th go install -v ``` -Navigate to the root of the project directory and you should see the `terracreds.exe` binary for Windows or `terracreds` for macOS and Linux. On Windows, copy the `.exe` to any directory of your choosing. Be sure to add the directory on `$env:PATH` for Windows to make using the application easier. On macOS and Linux we recommend you place the binary in `/usr/bin` as this directory should already be on the `$PATH` environment variable +Navigate to the root of the project directory and you should see the `terracreds.exe` binary for Windows or `terracreds` for macOS and Linux. On Windows, copy the `.exe` to any directory of your choosing. Be sure to add the directory on `$env:PATH` for Windows to make using the application easier. On macOS and Linux we recommend you place the binary in `/usr/bin` as this directory should already be on the `$PATH` environment variable. ## Upgrading -If you're upgrading to the latest version of `terracreds` from a previous version use one of the methods above to install the latest binary. Once successfully installed on your system you just need to run `terracreds generate` to copy the latest version to the correct `plugins` directory for your operating system +If you're upgrading to the latest version of `terracreds` from a previous version use one of the methods above to install the latest binary. Once successfully installed on your system you just need to run `terracreds generate` to copy the latest version to the correct `plugins` directory for your operating system. ## Initial Configuration -In order for `terracreds` to act as your credential provider you'll need to generate the binary and the plugin directory in the default location that Terraform looks for plugins. Specifically, for credential helpers, and for Windows, the directory is `%APPDATA%\terraform.d\plugins` and for macOS and Linux `$HOME/.terraform.d/.terraformrc` +In order for `terracreds` to act as your credential provider you'll need to generate the binary and the plugin directory in the default location that Terraform looks for plugins. Specifically, for credential helpers, and for Windows, the directory is `%APPDATA%\terraform.d\plugins` and for macOS and Linux `$HOME/.terraform.d/.terraformrc`. To make things as simple as possible we created a helper command to generate everthing needed to use the app. All you need to do is run the following command in `terracreds` to generate the plugin directory, and the correctly formatted binary that Terraform will use: ```bash terracreds generate ``` -This command will generate the binary as `terraform-credentials-terracreds.exe` for Windows or `terraform-credentials-terracreds` for macOS and Linux which is the valid naming convention for Terraform to recognize this plugin as a credential helper +This command will generate the binary as `terraform-credentials-terracreds.exe` for Windows or `terraform-credentials-terracreds` for macOS and Linux which is the valid naming convention for Terraform to recognize this plugin as a credential helper. -In addition to the binary and plugin a `terraform.rc` file is required for Windows or `.terraformrc` for macOS and Linux with a `credentials_helper` block which instructs Terraform to use the specified credential helper. If you don't already have a `terraform.rc` or a `.terraformrc` file you can pass in `--create-cli-config` to create the file with the credentials helper block already generated for use with the `terracreds` binary for your OS +In addition to the binary and plugin a `terraform.rc` file is required for Windows or `.terraformrc` for macOS and Linux with a `credentials_helper` block which instructs Terraform to use the specified credential helper. If you don't already have a `terraform.rc` or a `.terraformrc` file you can pass in `--create-cli-config` to create the file with the credentials helper block already generated for use with the `terracreds` binary for your OS. However, if you already have a `terraform.rc` or `.terraformrc` file you will need to add the following block to your file instead: @@ -125,16 +125,16 @@ credentials_helper "terracreds" { } ``` -Once you have moved all of your tokens from this file to the `Windows Credential Manager` or `KeyChain` via `terracreds` you can remove the tokens from the file. If you don't remove the tokens, and you add the `credentials_helper` block to this file, Terraform will still use the tokens instead of `terracreds` to retreive the tokens, so be sure to remove your tokens from this file once you have used the `create` or `terraform login` command to create the credentials in `terracreds` so you can actually leverage the credential helper +Once you have moved all of your tokens from this file to the `Windows Credential Manager` or `KeyChain` via `terracreds` you can remove the tokens from the file. If you don't remove the tokens, and you add the `credentials_helper` block to this file, Terraform will still use the tokens instead of `terracreds` to retreive the tokens, so be sure to remove your tokens from this file once you have used the `create` or `terraform login` command to create the credentials in `terracreds` so you can actually leverage the credential helper. ## Storing Credentials -For Terraform to properly use the credentials stored in your credential manager they need to be stored a specific way. The name of the credential object must be the domain name of the Terraform Automation and Collaboration server. For instance `app.terraform.io` which is the default name `terraform login` will use +For Terraform to properly use the credentials stored in your credential manager they need to be stored a specific way. The name of the credential object must be the domain name of the Terraform Automation and Collaboration server. For instance `app.terraform.io` which is the default name `terraform login` will leverage. -The value for the password will correspond to the API token associated for that specific Terraform Automation and Collaboration server +The value for the password will correspond to the API token associated for that specific Terraform Automation and Collaboration server. -The entire process is kicked off directly from the Terraform CLI. Run `terraform login` to start the login process with Terraform Cloud. If you're using Terraform Enterprise or another Terraform Automation and Collaboration Software solution you'll need to pass the hostname of the server as an additional argument `terraform login my.tacos.com` +The entire process is kicked off directly from the Terraform CLI. Run `terraform login` to start the login process with Terraform Cloud. If you're using Terraform Enterprise or another Terraform Automation and Collaboration Software solution you'll need to pass the hostname of the server as an additional argument `terraform login my.tacos.com`. -You'll be sent to your Terraform Automation and Collaboration Software instance where you'll be requested to sign-in with your account, and then sent to create an API token. Create the API token with any name you'd like for this example we'll use `terracreds` +You'll be sent to your Terraform Automation and Collaboration Software instance where you'll be requested to sign-in with your account, and then sent to create an API token. Create the API token with any name you'd like for this example we'll use `terracreds`. Once completed, copy the generated token, paste it into your terminal, and then hit enter. Terraform will then leverage `terracreds` to store the credentials in the operating system's credential manager. If all went well you should receive the following success message: @@ -148,6 +148,11 @@ In the background `terraform` calls `terracreds` as its credential helper, `terr terraform-credentials-terracreds store app.terraform.io ``` +If you prefer, you can also perform creating credentials manually by running: +```bash +terracreds create -n app.terraform.io -v +``` + ## Verifying Credentials When Terraform leverages `terracreds` as the credential provider it will run the following command to get the credentials value: ```bash @@ -174,7 +179,7 @@ Success! Terraform has obtained and saved an API token. You can also run `terracreds update -n my-secret -v my-secret-value` to update a secret value. -Additionally, you can check the `terracreds.log` if logging is enabled for more information +Additionally, you can check the `terracreds.log` if logging is enabled for more information. ## Forgetting Credentials You can delete the credential object at any time by running: @@ -192,7 +197,9 @@ If the credential was successfully deleted `terraform` will return: Success! Terraform has removed the stored API token for app.terraform.io. ``` -Additionally, you can check the `terracreds.log` if logging is enabled for more information +You can also run `terracreds delete -n app.terraform.io` if you want to manually remove the credential. + +Additionally, you can check the `terracreds.log` if logging is enabled for more information. ## List Credentials > New in version `2.1.0` @@ -212,21 +219,25 @@ You can also setup a list of secrets in the configuration file by using: terracreds list --from-config ``` -There's a helper flag `--as-tfvars` which will return the secret values formatted for use with `terraform`. Depending on the shell calling this command will determine how you can readily use these values +There's a helper flag `--as-tfvars` which will return the secret values formatted for use with `terraform`. Depending on the shell calling this command will determine how you can readily use these values. + +For instance on Linux/macOS you can simply call `eval` to evaluate the output to then convert the returned values into variables in your current shell. -For instance on Linux/macOS you can simply call `eval` to evaluate the output to then convert the returned values into variables in your current shell +Additionally, you can use `--as-json` to return the secret names and values as a JSON string. This is printed to standard output so you can make use of shell pipes and other commands to ingest the data. ## Setting Up a Vault Provider -> You can reference example configs in our [repo](https://github.com/tonedefdev/terracreds/blob/main/config.yaml) plus we have example [terraform](https://github.com/tonedefdev/terracreds/tree/main/terraform) code you can reference in order to setup your `AWS` or `Azure` VMs to use `terracreds` for a CI/CD piepline agent or a development workstation +> You can reference example configs in our [repo](https://github.com/tonedefdev/terracreds/blob/main/config.yaml) plus we have example [terraform](https://github.com/tonedefdev/terracreds/tree/main/terraform) code you can reference in order to setup your `AWS` or `Azure` VMs to use `terracreds` for a CI/CD piepline agent or a development workstation. + +> New in version `2.1.0` + +All of the external vault providers now make use of the provider's default credential authentication schemes. Please, refer to the documentation for each provider's default authentication mechanisms for more information on what options are availabile, and what is required to set up authentication for each method. ### Configure from Terracreds > New in version `2.1.0` -You can create and view the configuration for any vault provider by running `terracreds config` and then using the subcommand for the specific vault provider. The list is extensive so it's best to run `terracreds config --help` to get a better understanding of the values required for each provider +You can create and view the configuration for any vault provider by running `terracreds config` and then using the subcommand for the specific vault provider. The commands to generate the config from `terracreds` will be shown for each provider listed below. ### AWS Secrets Manager -> Currently, we only support using an `EC2 Instance Role` for authentication. This ensures the highest level of security by alleviating the `secret zero` dilemma - In order to leverage `terracreds` to manage secrets in `AWS Secrets Manager` the following block needs to be provided in the configuration file: ```yaml aws: @@ -235,11 +246,16 @@ aws: secretName: my-secret-name ``` +This can be generated via `terracreds` by running: +```bash +terracreds config aws --description 'my super secret' --region 'us-west-2' --secret-name 'my-secret-name' +``` + | Value | Description | Required | | ----- | ----------- | -------- | | `description` | A brief description to provide for the secret object viewable in `Secrets Manager` | `yes` | | `region` | The `Secrets Manager` instance's region where the secret will be stored | `yes` | -| `secretName` | A name for the secret. If omitted and using `terraform login` the hostname of the TFC\TFE server will be used for the name instead | `no` | +| `secretName` | A name for the secret. If omitted and using `terraform login` the hostname of the TACOS server will be used for the name instead | `no` | The following role permissions are required in order for the `EC2 Instance Role` to levearge `terracreds` with `AWS Secrets Manager`: ```hcl @@ -251,20 +267,23 @@ Action = [ ] ``` ### Azure Key Vault -> Currently, we only support using a `Managed Service Identity` for authentication. This ensures the highest level of security by alleviating the `secret zero` dilemma - In order to leverage `terracreds` to manage secrets in `Azure Key Vault` the following block needs to be provided in the configuration file: ```yaml azure: secretName: my-secret-name - useMSI: true - vaultUri: https://keyvault.azure.net + subscriptionId: 5df41dfe-4310-46e5-800a-c5bc71ac7ac0 + vaultUri: https://mykeyvault.vault.azure.net +``` + +The configuration can be generated via `terracreds` by running: +```bash +terracreds config azure --subscription-id '5df41dfe-4310-46e5-800a-c5bc71ac7ac0' --vault-uri 'https://mykeyvault.vault.azure.net' --secret-name 'my-secret-name' ``` | Value | Description | Required | | ----- | ----------- | -------- | -| `secretName` | A name for the secret. If omitted and using `terraform login` the hostname of the TFC\TFE server will be used for the name instead | `no` | -| `useMSI` | A flag to choose whether or not to use `Manged Service Identity`. Currently, `true` is required | `yes` | +| `secretName` | A name for the secret. If omitted and using `terraform login` the hostname of the TACOS server will be used for the name instead | `no` | +| `subscriptionId` | The Azure subscription ID where the `Azure Key Vault` has been created | `yes` | | `vaultUri` | The URI for the `Azure Key Vault` where you want to store or retrieve your credentials | `yes` | The following `Azure Key Vault Access Policies` are required to be given to the `Managed Service Identity` for it to leverage `terracreds`: @@ -285,16 +304,21 @@ In order to leverage `terracreds` to manage secrets in `Google Secret Manager` t ```yaml gcp: projectId: my-gcp-project - secretId: my-secret-id + secretId: my-secret-name ``` -The `Google IAM` role `secretmanager.admin` is suggested in order to fully manage the secrets with `terracreds` +The configuration can be generated via `terracreds` by running: +```bash +terracreds config gcp --project-id 'my-gcp-project' --secret-id 'my-secret-name' +``` | Value | Description | Required | | ----- | ----------- | -------- | | `projectId` | The name of the `GCP` project ID where the `Secret Manager` API has been enabled | `yes` | | `secretId` | The name of the secret ID | `no` | +The `Google IAM` role `secretmanager.admin` is suggested in order to fully manage the secrets with `terracreds` + ### HashiCorp Vault In order to leverage `terracreds` to manage secrets in `HashiCorp Vault` the following block needs to be provided in the configuration file: ```yaml @@ -306,20 +330,45 @@ hcvault: vaultUri: http://localhost:8200 ``` +The configuration can be generated via `terracreds` by running: +```bash +terracreds config hashicorp \ + --environment-token-name 'HASHI_TOKEN' \ + --key-vault-path 'kv' \ + --secret-name 'my-secret-name' \ + --secret-path 'tfe' \ + --vault-uri 'http://localhost:8200" +``` + | Value | Description | Required | | ----- | ----------- | -------- | | `environmentTokenName` | The name of the environment variable that contains the token value to authenticate with `HashiCorp Vault` | `yes` | | `keyVaultPath` | The path to the `Key Vault` object within the vault | `yes` | -| `secretName` | A name for the secret. If omitted and using `terraform login` the hostname of the TFC\TFE server will be used for the name instead | `no` | +| `secretName` | A name for the secret. If omitted and using `terraform login` the hostname of the TACOS server will be used for the name instead | `no` | | `secretPath` | The path of the secret within `HashiCorp Vault` | `yes` | | `vaultUri` | The URI for the `HashiCorp Vault` instance | `yes` | ## Protection -In order to add some protection `terracreds` adds a username to the credential object to secrets stored in the local operating system, and checks to ensure that the user requesting access to the token is the same user as the token's creator. This means that only the user account used to create the token can view the token from `terracreds` which ensures that the token can only be read by the account used to create it. Any attempt to access or modify this token from `terracreds` outside of the user that created the credentail will lead to denial messages. Additionally, if the credential name is not found, the same access denied message will be provided in lieu of a generic not found message to help prevent brute force attempts +In order to add some protection `terracreds` adds a username to the credential object stored in the local operating system, and checks to ensure that the user requesting access to the secret is the same user as the secret's creator. + +Any attempt to access or modify this secret from `terracreds` outside of the user that created the credentail will lead to denial messages. Additionally, if the credential name is not found, the same access denied message will be provided in lieu of a generic not found message to help prevent brute force attempts ## Logging -Wherever either binary is stored `terracreds` or `terraform-credential-terracreds` a `config.yaml` file is generated on first launch of the binary. Currently, this configuration file only enables/disables logging and sets the log path. If logging is enabled you'll find the log named `terracreds.log` at the provided path ->It's important to note that you'll have two configuration files due to Terraform requiring that the credential helper have a very specific binary name, so when troubleshooting credential issues with Terraform remember to setup the configuration file in the `%APPDATA%\terraform.d\plugins` directory for Windows and `$HOME/.terraform.d/plugins` directory for macOS and Linux +> New in version `2.1.0` +By default `terracreds` will generate a configuration file in the same location where the `terracreds` binary was first run. This can now be overridden by setting an environment variable that sets the path to the desired location of the configuration file: + +For Linux/macOS: +```bash +export TC_CONFIG_PATH=/home/username/ +``` + +For Windows: +```powershell +$env:TC_CONFIG_PATH="C:\Temp\" +``` +> Please, take note of the trailing slash! + +To persist this change you can set this variables either in `.bashrc` for Linux/macOS or setup a PowerShell profile for Windows. To enable logging for Windows setup the `config.yaml` as follows: ```yaml @@ -335,9 +384,14 @@ logging: path: /home/username/ ``` -The log is helpful in understanding if an object was found, deleted, updated or added, and will be found at the path defined in the configuration file as `terracreds.log` +You can also use `terracreds` to configure logging: +```bash +terracreds config logging --path '/home/username/' --enabled +``` + +The log is helpful in understanding if an object was found, deleted, updated or added, and will be found at the path defined in the configuration file as `terracreds.log`. -In addition all error messages returned by the underlying libraries will be logged when logging is enabled and an error is encountered +In addition all error messages returned by the underlying libraries will be logged when logging is enabled and an error is encountered. ## Troubleshooting Linux If you are having trouble viewing, deleting, or saving credentials on Linux systems using `gnome-keyring` you must ensure that you have unlocked the collection using `gnome-keyring-daemon --unlock` otherwise you will see the following error message in the logs: @@ -346,12 +400,11 @@ If you are having trouble viewing, deleting, or saving credentials on Linux syst ERROR: - failed to unlock correct collection '/org/freedesktop/secrets/collection/login' ``` -If the daemon has unlocked the collection but you're still getting prompted for credentials -- -check to make sure that only a single instance of the daemon is running: +If the daemon has unlocked the collection but you're still getting prompted for credentials check to make sure that only a single instance of the daemon is running: ```bash ps -ef | grep gnome-keyring ``` If more than one daemon is running, take note of the pid, and use `kill` to terminate the additional daemon. Try your previous command again -and it should now be working +and it should now be working. From 2dafd13b7060cc14d7247eceb3879b48fb93e876 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 12:13:11 -0700 Subject: [PATCH 19/22] Fixed readme formatting --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 75e60d4..e78bd71 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,7 @@ Any attempt to access or modify this secret from `terracreds` outside of the use ## Logging > New in version `2.1.0` + By default `terracreds` will generate a configuration file in the same location where the `terracreds` binary was first run. This can now be overridden by setting an environment variable that sets the path to the desired location of the configuration file: For Linux/macOS: From 0d714a90e4c1511834ace148848e14e24b7adf37 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 14:27:07 -0700 Subject: [PATCH 20/22] Allowing override of replace string via flag. Fixing unit tests. Updating goreleaser to allow arm builds and github actions to use 1.18 --- .github/workflows/release.yml | 2 +- .goreleaser.yml | 1 + main.go | 30 ++++++++++++++++++++++-------- main_test.go | 28 +++++++++++++++++++--------- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 36a4f76..a41a45d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.15 + go-version: ^1.18 id: go - name: Checkout diff --git a/.goreleaser.yml b/.goreleaser.yml index acc1e57..7cac202 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -5,6 +5,7 @@ builds: - linux goarch: - amd64 + - arm64 binary: terracreds archives: - name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" diff --git a/main.go b/main.go index 8a225fe..a3987f1 100644 --- a/main.go +++ b/main.go @@ -18,14 +18,17 @@ import ( "github.com/tonedefdev/terracreds/pkg/vault" ) -const cfgName = "config.yaml" -const version = "2.1.0" +const ( + cfgName = "config.yaml" + version = "2.1.0" +) var ( - cfg api.Config - configFilePath string - confirm string - secretNames []string + cfg api.Config + configFilePath string + confirm string + defaultReplaceString = "_" + secretNames []string ) // TerraCreds interface implements these methods for a credential's lifecycle @@ -634,6 +637,12 @@ func main() { Usage: "Prints the secret keys and values as a JSON string", Required: false, }, + &cli.StringFlag{ + Name: "override-replace-string", + Value: "", + Usage: "When running '--as-tfvars' the default is to replace any dashes [-] in the secret name with underscores [_]. This flag overrides that behavior and will instead replace dashes with this value", + Required: false, + }, }, Action: func(c *cli.Context) error { if len(os.Args) == 2 { @@ -685,7 +694,12 @@ func main() { if c.Bool("as-tfvars") { for i, name := range secretNames { - fmt.Printf("TF_VAR_%s=%s\n", name, list[i]) + if c.String("override-replace-string") != "" { + defaultReplaceString = c.String("override-replace-string") + } + + formatSecretName := strings.Replace(name, "-", defaultReplaceString, -1) + fmt.Printf("TF_VAR_%s=%s\n", formatSecretName, list[i]) } return nil @@ -693,7 +707,7 @@ func main() { for _, secret := range list { value := fmt.Sprintf("%s\n", secret) - fmt.Println(value) + fmt.Print(value) } return nil diff --git a/main_test.go b/main_test.go index 67282eb..15de98a 100644 --- a/main_test.go +++ b/main_test.go @@ -11,7 +11,6 @@ import ( "github.com/urfave/cli/v2" - api "github.com/tonedefdev/terracreds/api" helpers "github.com/tonedefdev/terracreds/pkg/helpers" ) @@ -65,9 +64,13 @@ func TestGetBinaryPath(t *testing.T) { } func TestCreateConfigFile(t *testing.T) { - var cfg api.Config - helpers.CreateConfigFile() - helpers.LoadConfig(&cfg) + cfgPath := fmt.Sprintf("%s\\config.yaml", t.TempDir()) + + err := helpers.CreateConfigFile(cfgPath) + if err != nil { + helpers.CheckError(err) + } + if cfg.Logging.Enabled != false { t.Errorf("Expected logging enabled 'false' got 'true'") } else { @@ -76,9 +79,17 @@ func TestCreateConfigFile(t *testing.T) { } func TestLoadConfig(t *testing.T) { - var cfg api.Config - helpers.CreateConfigFile() - helpers.LoadConfig(&cfg) + cfgPath := fmt.Sprintf("%s\\config.yaml", t.TempDir()) + err := helpers.CreateConfigFile(cfgPath) + if err != nil { + helpers.CheckError(err) + } + + err = helpers.LoadConfig(cfgPath, &cfg) + if err != nil { + helpers.CheckError(err) + } + if cfg.Logging.Enabled != false { t.Errorf("Expected logging enabled 'false' got 'true'") } else { @@ -91,11 +102,10 @@ func TestGenerateTerracreds(t *testing.T) { path := t.TempDir() tfUser := path + "\\terraform.d" helpers.NewDirectory(tfUser) - helpers.GenerateTerraCreds(c) + helpers.GenerateTerraCreds(c, version, "yes") } func TestTerracreds(t *testing.T) { - var cfg api.Config const hostname = "terracreds.test.io" const apiToken = "9ZWRa0Ge0iQCtA.atlasv1.HpZAd8426rHFskeEFo3AzimnkfR1ldYy69zz0op0NJZ79et8nrgjw3lQfi0FyJ1o8iw" const command = "delete" From c099b627d5a58daa685fcfd6cb6990ac3ad4085e Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Tue, 7 Jun 2022 21:29:24 -0700 Subject: [PATCH 21/22] Enabling bash completion --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index a3987f1..b21ec11 100644 --- a/main.go +++ b/main.go @@ -742,6 +742,6 @@ func main() { }, } - err = app.Run(os.Args) - helpers.CheckError(err) + app.EnableBashCompletion = true + app.Run(os.Args) } From 2d1cf37fa836218d924b59dbb4a57bd111c354e5 Mon Sep 17 00:00:00 2001 From: Tony <33327990+tonedefdev@users.noreply.github.com> Date: Wed, 8 Jun 2022 09:52:07 -0700 Subject: [PATCH 22/22] Update README.md --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e78bd71..5bff467 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,13 @@ There's a helper flag `--as-tfvars` which will return the secret values formatte For instance on Linux/macOS you can simply call `eval` to evaluate the output to then convert the returned values into variables in your current shell. +Also, by default, `terracreds` will convert any dashes `[-]` in a secret name with underscores `[_]` since this is the typical variable naming style convention in Terraform. However, you can override that behavior by passing in an override flag with any string value you'd prefer to use: +```bash +terracreds list --as-tfvars --override-replace-string - +``` + +The above example would maintain the dash `[-]` in the outuput of the formatted TF_VARS instead of replacing it by the default underscore `[_]` + Additionally, you can use `--as-json` to return the secret names and values as a JSON string. This is printed to standard output so you can make use of shell pipes and other commands to ingest the data. ## Setting Up a Vault Provider @@ -394,7 +401,17 @@ The log is helpful in understanding if an object was found, deleted, updated or In addition all error messages returned by the underlying libraries will be logged when logging is enabled and an error is encountered. -## Troubleshooting Linux +## Troubleshooting + +### Known Issues +When you enable `terracreds` as a credential helper Terraform will begin using it for all authentication regardless of the destination server. This means that when you try to install/download providers or modules from the public Terraform registry `https://registry.terraform.io/`, or any other public registry, Terraform will try to authenticate against the server using `terracreds`. If there's no credential in the vault found for that server it will error out. + +To work around this issue you'll need to set a dummy value for any public registries. Run this command for each public repo that Terraform will need to access. In this example we're using `registry.terraform.io` so be sure to replace it with the correct server value if the one you require is different: +```bash +terracreds create -n registry.terraform.io -v dummy_token +``` + +### Linux If you are having trouble viewing, deleting, or saving credentials on Linux systems using `gnome-keyring` you must ensure that you have unlocked the collection using `gnome-keyring-daemon --unlock` otherwise you will see the following error message in the logs: ```txt