From d087e6ce83d9fc1fe18aa2dd2714db3609b93d2e Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 18 Sep 2016 01:16:31 +0200 Subject: [PATCH] Add support for AppRole authentication --- README.md | 13 +++++++----- main.go | 60 +++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index bab9c10..c17ad19 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # Luzifer / vault2env -`vault2env` is a really small utility to transfer fields of a key in [Vault](https://www.vaultproject.io/) into the environment. It uses the [`app-id` authentication mechanism](https://www.vaultproject.io/docs/auth/app-id.html) or simple [token authentication](https://www.vaultproject.io/docs/auth/token.html) to identify itself with the Vault server, fetches all fields in the specified key and returns export directives for bash / zsh. That way you can do `eval` stuff and pull those fields into your ENV. +`vault2env` is a really small utility to transfer fields of a key in [Vault](https://www.vaultproject.io/) into the environment. It uses the [`app-role`](https://www.vaultproject.io/docs/auth/approle.html), [`app-id` authentication mechanism](https://www.vaultproject.io/docs/auth/app-id.html) or simple [token authentication](https://www.vaultproject.io/docs/auth/token.html) to identify itself with the Vault server, fetches all fields in the specified key and returns export directives for bash / zsh. That way you can do `eval` stuff and pull those fields into your ENV. ## Usage @@ -32,6 +32,13 @@ firstvalue ``` ### Using CLI parameters + +The command does differ only with its parameters specified for the different authentication mechanisms: + +- When using AppRole you need to specify `--vault-role-id` and optionally `--vault-secret-id` if you're using the `bind_secret_id` flag for your AppRole +- When using AppID specify `--vault-app-id` and `--vault-user-id` +- When using Token auth only specify `--vault-token` + ```bash # vault2env --vault-addr="..." --vault-app-id="..." --vault-user-id="..." secret/my/path/with/keys export FIRST_KEY="firstvalue" @@ -39,7 +46,3 @@ export SECOND_KEY="secondvalue" ``` Though it's possible to use CLI parameters I strongly recommend to stick to the ENV variant as it's possible under certain conditions to read CLI parameters on a shared system using for example `ps aux`. - -### Using a token instead of app-id authentication - -This is quite simple: Omit parameters `--vault-app-id` and `--vault-user-id` and their respective ENV variables but set `VAULT_TOKEN` or `--vault-token`. diff --git a/main.go b/main.go index 47ee948..f2e3af1 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/exec" + "strings" "github.com/Luzifer/go_helpers/env" "github.com/Luzifer/rconfig" @@ -15,12 +16,20 @@ import ( var ( cfg = struct { - VaultAddress string `flag:"vault-addr" env:"VAULT_ADDR" default:"https://127.0.0.1:8200" description:"Vault API address"` - VaultAppID string `flag:"vault-app-id" env:"VAULT_APP_ID" default:"" description:"The app-id to use for authentication"` - VaultUserID string `flag:"vault-user-id" env:"VAULT_USER_ID" default:"" description:"The user-id to use for authentication"` - VaultToken string `flag:"vault-token" env:"VAULT_TOKEN" vardefault:"vault-token" description:"Specify a token to use instead of app-id auth"` - Export bool `flag:"export,e" default:"false" description:"Show export statements instead of running the command specified"` - VersionAndExit bool `flag:"version" default:"false" description:"Print program version and exit"` + VaultAddress string `flag:"vault-addr" env:"VAULT_ADDR" default:"https://127.0.0.1:8200" description:"Vault API address"` + AppIDAuth struct { + AppID string `flag:"vault-app-id" env:"VAULT_APP_ID" default:"" description:"[DEPRECATED] The app-id to use for authentication"` + UserID string `flag:"vault-user-id" env:"VAULT_USER_ID" default:"" description:"[DEPRECATED] The user-id to use for authentication"` + } + AppRoleAuth struct { + RoleID string `flag:"vault-role-id" env:"VAULT_ROLE_ID" default:"" description:"ID of the role to use"` + SecretID string `flag:"vault-secret-id" env:"VAULT_SECRET_ID" default:"" description:"Corresponding secret ID to the role"` + } + TokenAuth struct { + Token string `flag:"vault-token" env:"VAULT_TOKEN" vardefault:"vault-token" description:"Specify a token to use instead of app-id auth"` + } + Export bool `flag:"export,e" default:"false" description:"Show export statements instead of running the command specified"` + VersionAndExit bool `flag:"version" default:"false" description:"Print program version and exit"` }{} version = "dev" ) @@ -50,10 +59,6 @@ func init() { os.Exit(0) } - if (cfg.VaultAppID == "" || cfg.VaultUserID == "") && cfg.VaultToken == "" { - log.Fatalf("[ERR] You need to either set vault-app-id and vault-user-id or set vault-token") - } - if cfg.Export { if len(rconfig.Args()) != 2 { log.Fatalf("[ERR] Usage: vault2env --export [secret path]") @@ -73,9 +78,28 @@ func main() { log.Fatalf("Unable to create client: %s", err) } - if cfg.VaultToken == "" { - loginSecret, lserr := client.Logical().Write("auth/app-id/login/"+cfg.VaultAppID, map[string]interface{}{ - "user_id": cfg.VaultUserID, + switch { + case cfg.TokenAuth.Token != "": + client.SetToken(cfg.TokenAuth.Token) + + case cfg.AppRoleAuth.RoleID != "": + data := map[string]interface{}{ + "role_id": cfg.AppRoleAuth.RoleID, + } + if cfg.AppRoleAuth.SecretID != "" { + data["secret_id"] = cfg.AppRoleAuth.SecretID + } + loginSecret, lserr := client.Logical().Write("auth/approle/login", data) + if lserr != nil || loginSecret.Auth == nil { + log.Fatalf("Unable to fetch authentication token: %s", lserr) + } + + client.SetToken(loginSecret.Auth.ClientToken) + defer client.Auth().Token().RevokeSelf(client.Token()) + + case cfg.AppIDAuth.AppID != "" && cfg.AppIDAuth.UserID != "": + loginSecret, lserr := client.Logical().Write("auth/app-id/login/"+cfg.AppIDAuth.AppID, map[string]interface{}{ + "user_id": cfg.AppIDAuth.UserID, }) if lserr != nil || loginSecret.Auth == nil { log.Fatalf("Unable to fetch authentication token: %s", lserr) @@ -83,8 +107,14 @@ func main() { client.SetToken(loginSecret.Auth.ClientToken) defer client.Auth().Token().RevokeSelf(client.Token()) - } else { - client.SetToken(cfg.VaultToken) + + default: + log.Fatalf(strings.Join([]string{ + "[ERR] Did not find any authentication method. Try one of these:", + "- Specify `--vault-token` for token based authentication", + "- Specify `--vault-role-id` and optionally `--vault-secret-id` for AppRole authentication", + "- Specify `--vault-app-id` and `--vault-user-id` for deprecated AppID authentication", + }, "\n")) } data, err := client.Logical().Read(rconfig.Args()[1])