From 61d337b764b692b1cfc18f99aee4c80be064e1ce Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Wed, 9 Jun 2021 21:59:56 -0700 Subject: [PATCH 01/10] Adding Linux support, updated README with details for Linux, and added a step for goreleaser to create a Linux binary --- .goreleaser.yml | 1 + README.md | 20 ++++++++++++++++++-- main.go | 16 ++++++++++------ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index efacddc..acc1e57 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,6 +2,7 @@ builds: - goos: - darwin - windows + - linux goarch: - amd64 binary: terracreds diff --git a/README.md b/README.md index 6867890..0e5b837 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ We all know storing secrets in plain text can pose major security threats, and T #### Currently supported Operating Systems: - [x] Windows (Credential Manager) - [x] MacOS (Keychain) -- [ ] Linux (ksecretservice or gnome-keyring) +- [x] Linux (ksecretservice or gnome-keyring) *The Linux version is currently in development. If you'd like to support the project please feel free to submit a PR* @@ -25,7 +25,23 @@ Once installed run the following command to verify `terracreds` was installed pr terracreds -v ``` ## 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` +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` + +## Linux Install +You'll need to download the latest binary from our release page and place it on `$PATH` of your system. + +The `terracreds` Linux implementation depends on the [Secret Service][SecretService] dbus +interface, which is provided by [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring). + +It's expected that the default collection `login` exists in the keyring, because +it's the default in most distros. If it doesn't exist, you can create it through the +keyring frontend program [Seahorse](https://wiki.gnome.org/Apps/Seahorse): + + * Open `seahorse` + * Go to **File > New > Password Keyring** + * Click **Continue** + * When asked for a name, use: **login** ## Manual Install Download the source files by entering the following command: diff --git a/main.go b/main.go index 7d32c69..eeaa12d 100644 --- a/main.go +++ b/main.go @@ -55,7 +55,7 @@ func GetBinaryPath(binary string) string { } } - if runtime.GOOS == "darwin" { + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { if strings.Contains(binary, "terraform-credentials-terracreds") { path = strings.Replace(binary, "terraform-credentials-terracreds", "", -1) } else if strings.Contains(binary, "terracreds.test") { @@ -190,7 +190,7 @@ func CreateCredential(c *cli.Context, hostname string, token interface{}, cfg Co } } - if runtime.GOOS == "darwin" { + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { var method string _, err := keyring.Get(hostname, string(user.Username)) if err != nil { @@ -200,6 +200,10 @@ func CreateCredential(c *cli.Context, hostname string, token interface{}, cfg Co } if token == nil { + err = json.NewDecoder(os.Stdin).Decode(&apiToken) + if err != nil { + fmt.Print(err.Error()) + } err = keyring.Set(hostname, string(user.Username), apiToken.Token) } else { str := fmt.Sprintf("%v", token) @@ -274,7 +278,7 @@ func DeleteCredential(c *cli.Context, cfg Config, hostname string, command strin } } - if runtime.GOOS == "darwin" { + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { err := keyring.Delete(hostname, string(user.Username)) if err == nil { msg := "- the credential object '" + hostname + "' has been removed" @@ -309,7 +313,7 @@ func GenerateTerracreds(c *cli.Context) { binary = tfPlugins + "\\terraform-credentials-terracreds.exe" } - if runtime.GOOS == "darwin" { + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { userProfile := os.Getenv("HOME") cliConfig = userProfile + "/.terraformrc" tfPlugins = userProfile + "/plugins" @@ -360,7 +364,7 @@ func GetCredential(c *cli.Context, cfg Config, hostname string) { } } - if runtime.GOOS == "darwin" { + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { if cfg.Logging.Enabled == true { logPath = cfg.Logging.Path + "terracreds.log" WriteToLog(logPath, "- terraform server: "+hostname, "INFO: ") @@ -402,7 +406,7 @@ type CredentialResponse struct { func main() { var cfg Config - version := "1.1.0" + version := "1.1.1" LoadConfig(&cfg) app := &cli.App{ Name: "terracreds", From 7e818d084272b86ad2980c3d134b17ce30c52993 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Wed, 9 Jun 2021 23:21:33 -0700 Subject: [PATCH 02/10] Using new Logging helper and making further readme updates --- README.md | 16 +++++++--------- main.go | 36 +++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 0e5b837..22d10d0 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ We all know storing secrets in plain text can pose major security threats, and T - [x] MacOS (Keychain) - [x] Linux (ksecretservice or gnome-keyring) -*The Linux version is currently in development. If you'd like to support the project please feel free to submit a PR* - ## Windows Install via Chocolatey The fastest way to install `terracreds` on Windows is via our Chocolatey package: ```bash @@ -39,7 +37,7 @@ it's the default in most distros. If it doesn't exist, you can create it through keyring frontend program [Seahorse](https://wiki.gnome.org/Apps/Seahorse): * Open `seahorse` - * Go to **File > New > Password Keyring** + * Go to **+ > Password > Password Keyring** * Click **Continue** * When asked for a name, use: **login** @@ -69,16 +67,16 @@ 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. 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 we recommend you place the binary in `/usr/bin` as this directory should already be on the `$PATH` environment variable. ## 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 `$HOME/.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/.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 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 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: @@ -88,7 +86,7 @@ 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` 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 Cloud or Enterprise server. For instance `app.terraform.io` which is the default name `terraform login` will use. @@ -160,7 +158,7 @@ In order to add some protection `terracreds` adds a username to the credential o ## 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/.terraformrc` directory for macOS. +>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/.terraformrc` directory for macOS and Linux. To enable logging for Windows setup the `config.yaml` as follows: ```yaml @@ -169,7 +167,7 @@ logging: path: C:\Temp\ ``` -To enable logging for macOS: +To enable logging for macOS and Linux: ```yaml logging: enabled: true diff --git a/main.go b/main.go index eeaa12d..e8477fc 100644 --- a/main.go +++ b/main.go @@ -245,6 +245,8 @@ func LogLevel(level string) string { return "ERROR: " case "SUCCESS": return "SUCCESS: " + case "WARNING": + return "WARNING: " default: return "" } @@ -335,14 +337,14 @@ func GenerateTerracreds(c *cli.Context) { // object as required to be consumed by Terraform Cloud/Enterprise func GetCredential(c *cli.Context, cfg Config, hostname string) { user, err := user.Current() - var logPath string CheckError(err) if runtime.GOOS == "windows" { if cfg.Logging.Enabled == true { - logPath = cfg.Logging.Path + "terracreds.log" - WriteToLog(logPath, "- terraform server: "+hostname, "INFO: ") - WriteToLog(logPath, "- user requesting access: "+string(user.Username), "INFO: ") + msg := fmt.Sprintf("- terraform server: %s", hostname) + Logging(cfg, msg, "INFO") + msg = fmt.Sprintf("- user requesting access: %s", string(user.Username)) + Logging(cfg, msg, "INFO") } cred, err := wincred.GetGenericCredential(hostname) @@ -354,11 +356,13 @@ func GetCredential(c *cli.Context, cfg Config, hostname string) { fmt.Println(string(responseA)) if cfg.Logging.Enabled == true { - WriteToLog(logPath, "- token was retrieved for: "+hostname, "INFO: ") + msg := fmt.Sprintf("- token was retrieved for: %s", hostname) + Logging(cfg, msg, "INFO") } } else { if cfg.Logging.Enabled == true { - WriteToLog(logPath, "- access was denied for user: "+string(user.Username), "ERROR: ") + msg := fmt.Sprintf("- access was denied for user: %s", string(user.Username)) + Logging(cfg, msg, "ERROR") } fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) } @@ -366,15 +370,17 @@ func GetCredential(c *cli.Context, cfg Config, hostname string) { if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { if cfg.Logging.Enabled == true { - logPath = cfg.Logging.Path + "terracreds.log" - WriteToLog(logPath, "- terraform server: "+hostname, "INFO: ") - WriteToLog(logPath, "- user requesting access: "+string(user.Username), "INFO: ") + msg := fmt.Sprintf("- terraform server: %s", hostname) + Logging(cfg, msg, "INFO") + msg = fmt.Sprintf("- user requesting access: %s", string(user.Username)) + Logging(cfg, msg, "INFO") } secret, err := keyring.Get(hostname, string(user.Username)) if err != nil { if cfg.Logging.Enabled == true { - WriteToLog(logPath, "- access was denied for user: "+string(user.Username), "ERROR: ") + msg := fmt.Sprintf("- access was denied for user: %s", string(user.Username)) + Logging(cfg, msg, "ERROR") } fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) } else { @@ -385,7 +391,8 @@ func GetCredential(c *cli.Context, cfg Config, hostname string) { fmt.Println(string(responseA)) if cfg.Logging.Enabled == true { - WriteToLog(logPath, "- token was retrieved for: "+hostname, "INFO: ") + msg := fmt.Sprintf("- token was retrieved for: %s", hostname) + Logging(cfg, msg, "INFO") } } } @@ -455,12 +462,11 @@ func main() { 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")) } else if !strings.Contains(os.Args[2], "-n") && !strings.Contains(os.Args[2], "--hostname") { - msg := "A hostname was not expected here. Did you mean" + msg := fmt.Sprintf("A hostname was not expected here: %s", os.Args[2]) if cfg.Logging.Enabled == true { - logPath := cfg.Logging.Path + "terracreds.log" - WriteToLog(logPath, msg, "WARNING: ") + Logging(cfg, msg, "WARNING") } - fmt.Fprintf(color.Output, "%s: %s 'terracreds delete --hostname/-n %s'?\n", color.YellowString("WARNING"), msg, os.Args[2]) + fmt.Fprintf(color.Output, "%s: %s Did you mean `terracreds delete --hostname/-n %s'?\n", color.YellowString("WARNING"), msg, os.Args[2]) } else { hostname := c.String("hostname") command := os.Args[1] From c5b6e4625fdb63055ddbaa7770f1a5bafaffcc75 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Wed, 9 Jun 2021 23:23:29 -0700 Subject: [PATCH 03/10] Final readme draft --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22d10d0..f65a6a1 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ 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. 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 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. ## 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/.terraformrc` From 1c4e9eb9e5f2b0129744eb130f37f6d3702a3e0f Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 12 Jun 2021 14:14:23 -0700 Subject: [PATCH 04/10] Fixing the path to the plugins dir for Linux and macOS. Added better error messages to the log when errors are encountered --- .goreleaser.yml | 4 ++- README.md | 89 ++++++++++++++++++++++++++++++++++--------------- main.go | 34 ++++++++----------- 3 files changed, 79 insertions(+), 48 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index acc1e57..133a70a 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -10,4 +10,6 @@ archives: - name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" format_overrides: - goos: windows - format: zip \ No newline at end of file + format: zip +- files: + - none* \ No newline at end of file diff --git a/README.md b/README.md index f65a6a1..477ad6b 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,14 @@ # Terracreds -A credential helper for Terraform Cloud/Enterprise that allows secure storage of your API token within the operating system's vault instead of in a plain text configuration file. +A credential helper for Terraform Cloud/Enterprise that allows secure storage of your API token within the operating system's vault instead of 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) - [x] MacOS (Keychain) -- [x] Linux (ksecretservice or gnome-keyring) +- [x] Linux (gnome-keyring) *Tested on Ubuntu 20.04* ## Windows Install via Chocolatey The fastest way to install `terracreds` on Windows is via our Chocolatey package: @@ -27,19 +27,36 @@ We are currently working on a `homebrew` package, however, to install the packag 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 on `$PATH` of your system. +You'll need to download the latest binary from our release page and place anywhere on `$PATH` of your system. You can also copy and run the following commands: -The `terracreds` Linux implementation depends on the [Secret Service][SecretService] dbus -interface, which is provided by [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring). +```bash +wget https://github.com/tonedefdev/terracreds/releases/download/untagged-89883151b71ff5a64c29/terracreds_1.1.1_linux_amd64.tar.gz +tar -xvf terracreds_1.1.1_linux_amd64.tar.gz +sudo mv -f terracreds /usr/bin/terracreds +rm -f terracreds_1.1.1_linux_amd64.tar.gz +``` + +The `terracreds` Linux implementation uses `gnome-keyring` in conjunction with `gnome-keyring-daemon` +to utilize the credential storage engine -It's expected that the default collection `login` exists in the keyring, because -it's the default in most distros. If it doesn't exist, you can create it through the -keyring frontend program [Seahorse](https://wiki.gnome.org/Apps/Seahorse): +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: - * Open `seahorse` - * Go to **+ > Password > Password Keyring** - * Click **Continue** - * When asked for a name, use: **login** +```bash +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 + +The command, if successful, should return the following: +```txt +SSH_AUTH_SOCK=/run/user/1000/keyring/ssh +``` + +You can verify that it's running properly with: +```bash +ps -ef | grep 'gnome-keyring-daemon' +``` ## Manual Install Download the source files by entering the following command: @@ -54,7 +71,7 @@ For Windows: $env:GO111MODULE='on' ``` -For macOS: +For macOS and Linux: ```bash export GO111MODULE='on' ``` @@ -64,19 +81,19 @@ 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 ## 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/.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: @@ -86,12 +103,12 @@ 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 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 Cloud or Enterprise 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 Cloud or Enterprise 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` @@ -133,7 +150,7 @@ If the token was updated successfully the following message is returned: Success! Terraform has obtained and saved an API token. ``` -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: @@ -151,14 +168,14 @@ 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. +Additionally, you can check the `terracreds.log` if logging is enabled for more information ## Protection -In order to add some protection `terracreds` adds a username to the credential object, 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, 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 ## 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/.terraformrc` directory for macOS and Linux. +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/.terraformrc` directory for macOS and Linux To enable logging for Windows setup the `config.yaml` as follows: ```yaml @@ -171,7 +188,25 @@ To enable logging for macOS and Linux: ```yaml logging: enabled: true - path: /usr/ + 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` + +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: + +```txt +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 +the only a single instance of the daemon is runnong: + +```bash +ps -ef | grep gnome-keyring +``` + +If more than one daemon is running then take note of the pid and use `kill` to terminate the additional daemon and try again \ No newline at end of file diff --git a/main.go b/main.go index e8477fc..ed8a141 100644 --- a/main.go +++ b/main.go @@ -174,15 +174,14 @@ func CreateCredential(c *cli.Context, hostname string, token interface{}, cfg Co err = cred.Write() if err == nil { - msg := "- " + strings.ToLower(method) + " the credential object " + hostname + msg := fmt.Sprintf("- %s the credential object %s", strings.ToLower(method), hostname) Logging(cfg, msg, "SUCCESS") if token != nil { fmt.Fprintf(color.Output, "%s: %s the credential object '%s'\n", color.GreenString("SUCCESS"), method, hostname) } } else { - msg := "- you do not have permission to modify this credential" - Logging(cfg, msg, "ERROR") + Logging(cfg, fmt.Sprintf("- %s", err), "ERROR") if token != nil { fmt.Fprintf(color.Output, "%s: You do not have permission to modify this credential\n", color.RedString("ERROR")) @@ -211,15 +210,14 @@ func CreateCredential(c *cli.Context, hostname string, token interface{}, cfg Co } if err == nil { - msg := "- " + strings.ToLower(method) + " the credential object " + hostname + msg := fmt.Sprintf("- %s the credential object %s", strings.ToLower(method), hostname) Logging(cfg, msg, "SUCCESS") if token != nil { fmt.Fprintf(color.Output, "%s: %s the credential object '%s'\n", color.GreenString("SUCCESS"), method, hostname) } } else { - msg := "- you do not have permission to modify this credential" - Logging(cfg, msg, "ERROR") + Logging(cfg, fmt.Sprintf("- %s", err), "ERROR") if token != nil { fmt.Fprintf(color.Output, "%s: You do not have permission to modify this credential\n", color.RedString("ERROR")) @@ -263,16 +261,15 @@ func DeleteCredential(c *cli.Context, cfg Config, hostname string, command strin if err == nil && cred.UserName == user.Username { cred.Delete() - msg := "- the credential object '" + hostname + "' has been removed" + msg := fmt.Sprintf("- the credential object '%s' has been removed", hostname) Logging(cfg, msg, "INFO") if command == "delete" { - msg := "The credential object '" + hostname + "' has been removed" + msg := fmt.Sprintf("- the credential object '%s' has been removed", hostname) fmt.Fprintf(color.Output, "%s: %s\n", color.GreenString("SUCCESS"), msg) } } else { - msg := "- you do not have permission to modify this credential" - Logging(cfg, msg, "ERROR") + Logging(cfg, fmt.Sprintf("- %s", err), "ERROR") if command == "delete" { fmt.Fprintf(color.Output, "%s: You do not have permission to modify this credential\n", color.RedString("ERROR")) @@ -283,16 +280,15 @@ func DeleteCredential(c *cli.Context, cfg Config, hostname string, command strin if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { err := keyring.Delete(hostname, string(user.Username)) if err == nil { - msg := "- the credential object '" + hostname + "' has been removed" + msg := fmt.Sprintf("- the credential object '%s' has been removed", hostname) Logging(cfg, msg, "INFO") if command == "delete" { - msg := "The credential object '" + hostname + "' has been removed" + msg := fmt.Sprintf("- the credential object '%s' has been removed", hostname) fmt.Fprintf(color.Output, "%s: %s\n", color.GreenString("SUCCESS"), msg) } } else { - msg := "- you do not have permission to modify this credential" - Logging(cfg, msg, "ERROR") + Logging(cfg, fmt.Sprintf("- %s", err), "ERROR") if command == "delete" { fmt.Fprintf(color.Output, "%s: You do not have permission to modify this credential\n", color.RedString("ERROR")) @@ -317,8 +313,8 @@ func GenerateTerracreds(c *cli.Context) { if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { userProfile := os.Getenv("HOME") - cliConfig = userProfile + "/.terraformrc" - tfPlugins = userProfile + "/plugins" + cliConfig = userProfile + "/.terraform.d/.terraformrc" + tfPlugins = userProfile + "/.terraform.d/plugins" binary = tfPlugins + "/terraform-credentials-terracreds" } @@ -361,8 +357,7 @@ func GetCredential(c *cli.Context, cfg Config, hostname string) { } } else { if cfg.Logging.Enabled == true { - msg := fmt.Sprintf("- access was denied for user: %s", string(user.Username)) - Logging(cfg, msg, "ERROR") + Logging(cfg, fmt.Sprintf("- %s", err), "ERROR") } fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) } @@ -379,8 +374,7 @@ func GetCredential(c *cli.Context, cfg Config, hostname string) { secret, err := keyring.Get(hostname, string(user.Username)) if err != nil { if cfg.Logging.Enabled == true { - msg := fmt.Sprintf("- access was denied for user: %s", string(user.Username)) - Logging(cfg, msg, "ERROR") + Logging(cfg, fmt.Sprintf("- %s", err), "ERROR") } fmt.Fprintf(color.Output, "%s: You do not have permission to view this credential\n", color.RedString("ERROR")) } else { From b67fb1e895470b58335f8e8ed2fba9707006e178 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 12 Jun 2021 14:17:21 -0700 Subject: [PATCH 05/10] Fixed the formatting of the printed success msg --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index ed8a141..4651070 100644 --- a/main.go +++ b/main.go @@ -265,7 +265,7 @@ func DeleteCredential(c *cli.Context, cfg Config, hostname string, command strin Logging(cfg, msg, "INFO") if command == "delete" { - msg := fmt.Sprintf("- the credential object '%s' has been removed", hostname) + msg := fmt.Sprintf("The credential object '%s' has been removed", hostname) fmt.Fprintf(color.Output, "%s: %s\n", color.GreenString("SUCCESS"), msg) } } else { @@ -284,7 +284,7 @@ func DeleteCredential(c *cli.Context, cfg Config, hostname string, command strin Logging(cfg, msg, "INFO") if command == "delete" { - msg := fmt.Sprintf("- the credential object '%s' has been removed", hostname) + msg := fmt.Sprintf("The credential object '%s' has been removed", hostname) fmt.Fprintf(color.Output, "%s: %s\n", color.GreenString("SUCCESS"), msg) } } else { From 6885aab6eb1cb234ae297c1d1a1560d614059cc9 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 12 Jun 2021 14:23:10 -0700 Subject: [PATCH 06/10] Addning new line entry for Linux install cmd --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 477ad6b..d042618 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ 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 anywhere on `$PATH` of your system. You can also copy and run the following commands: ```bash -wget https://github.com/tonedefdev/terracreds/releases/download/untagged-89883151b71ff5a64c29/terracreds_1.1.1_linux_amd64.tar.gz -tar -xvf terracreds_1.1.1_linux_amd64.tar.gz -sudo mv -f terracreds /usr/bin/terracreds +wget https://github.com/tonedefdev/terracreds/releases/download/untagged-89883151b71ff5a64c29/terracreds_1.1.1_linux_amd64.tar.gz \ +tar -xvf terracreds_1.1.1_linux_amd64.tar.gz \ +sudo mv -f terracreds /usr/bin/terracreds \ rm -f terracreds_1.1.1_linux_amd64.tar.gz ``` From 1dd36bf0c78275bf6cec288142f67378b808169c Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 12 Jun 2021 14:25:27 -0700 Subject: [PATCH 07/10] Fixing typos in readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d042618..19aa5c9 100644 --- a/README.md +++ b/README.md @@ -202,11 +202,12 @@ 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 -the only a single instance of the daemon is runnong: +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 then take note of the pid and use `kill` to terminate the additional daemon and try again \ No newline at end of file +If more than one daemon is running, take note of the pid, and use `kill` to terminate the additional daemon. Try you previous command again +and it should now be working \ No newline at end of file From a9b32eaff82643a82f2ea99c76b3c7a0c1fef742 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 12 Jun 2021 14:26:15 -0700 Subject: [PATCH 08/10] Fixing typos in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19aa5c9..307e713 100644 --- a/README.md +++ b/README.md @@ -209,5 +209,5 @@ check to make sure that only a single instance of the daemon is running: 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 you previous command again +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 \ No newline at end of file From e56c7afc271f8a6219789e91570638085f403a82 Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sat, 12 Jun 2021 14:31:03 -0700 Subject: [PATCH 09/10] Updating correct path on readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 307e713..c4ce004 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ In order to add some protection `terracreds` adds a username to the credential o ## 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/.terraformrc` directory for macOS and Linux +>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 To enable logging for Windows setup the `config.yaml` as follows: ```yaml From c711998b7ca657c007d47b5f4ff5aa6f5c80529f Mon Sep 17 00:00:00 2001 From: Tony Owens Date: Sun, 13 Jun 2021 09:37:28 -0700 Subject: [PATCH 10/10] Adding upgrade information to the readme --- README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c4ce004..571609a 100644 --- a/README.md +++ b/README.md @@ -14,20 +14,26 @@ We all know storing secrets in plain text can pose major security threats, and T ## Windows Install via Chocolatey The fastest way to install `terracreds` on Windows is via our Chocolatey package: -```bash +```powershell choco install terracreds -y ``` Once installed run the following command to verify `terracreds` was installed properly: -```bash +```powershell terracreds -v ``` + +To upgrade `terracreds` to the latest version with Chocolatey run the the following command: +```powershell +choco upgrade terracreds -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` ## Linux Install -You'll need to download the latest binary from our release page and place anywhere on `$PATH` of your system. You can also copy and run the following commands: +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/untagged-89883151b71ff5a64c29/terracreds_1.1.1_linux_amd64.tar.gz \ @@ -58,7 +64,7 @@ You can verify that it's running properly with: ps -ef | grep 'gnome-keyring-daemon' ``` -## Manual Install +## Install From Source Download the source files by entering the following command: ```go go get github.com/tonedefev/terracreds @@ -83,6 +89,9 @@ 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 +## 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 + ## 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`