Building a vault-integrated CLI tool, recommendations for auth handling?

I’m working on a CLI tool that integrates with vault to retrieve application passwords.

For the most part it will be run on workstations, though we’ll probably also have executions triggered from rundeck.

The tool may have to connect to different vault servers depending on the secret involved (staging, production, etc).

Are there any best practices around vault authentication handling in this case? It seems like I could:

  1. Have the tool expect VAULT_ADDR and VAULT_TOKEN to be exported as env vars, have some script or Makefile ensure those values get set up and point to the right vault server.
  2. Have the tool take the vault address & login method (should be either oidc or approle) as configuration and handle the login & token retrieval on its own.

Are there any best practices in this space we should track toward?

It seems like a lot of tools (vault cli itself) assume one vault server and rely on an exported addr/token, but I do like the simplicity of having all the vault info in some sort of production.conf then letting the tool get its own token.

Thanks in advance for any advice,
Mat

Hey Mat,
If your primary concern is switching between servers or tokens… a token helper (https://www.vaultproject.io/docs/commands/token-helper/) might be all you need if you want to keep separate sessions (Maybe you have the helper check the VAULT_ADDR and store it separately so you can keep parallel sessions). In my home setup I’m trying to wrap as much stuff as possible in the vault cli with minimal wrappers, and I use token helpers to store the tokens directly in configuration files.

If you are planning on writing a CLI on your own, YMMV on auth providers. It is, for example, quite simple to implement userpass or LDAP as it is a single API call, but things like OIDC are considerably more complex, with callbacks and stuff. I haven’t used them myself, but I know the API includes some CLI helpers that you might be able to leverage to avoid some heavy lifting (https://godoc.org/github.com/hashicorp/vault/builtin/credential/ldap#CLIHandler.Auth for example).

Now my personal opinion and going straight to your question, I always try to follow the application pattern when I write something. My ruby or golang stuff generally expects a VAULT_TOKEN and VAULT_ADDR that it is assumed to be provided by something else (in your case, human authentication).

Hope this helps set you in the right direction.

2 Likes

Thanks! I did a PoC with https://github.com/hashicorp/vault-plugin-auth-jwt that worked. Just copied in come code from the vault CLI and it popped a browser as expected.

	vaultAddr := "<vault addr from app config file>"
	vaultClient, err := vault.NewClient(&vault.Config{Address: vaultAddr})
	if err != nil {
		fmt.Println(fmt.Errorf("unable to initialize Vault client: %v", err))
		os.Exit(1)
	}

	oidcHandler := &credOIDC.CLIHandler{}
	if secret, err := oidcHandler.Auth(vaultClient, make(map[string]string)); err != nil {
		fmt.Println(fmt.Errorf("unable to authenticate with vault %v: %v", vaultAddr, err))
		os.Exit(1)
	} else {
		vaultClient.SetToken(secret.Auth.ClientToken)
	}

But it does seem like the general ecosystem assumes auth gets handled outside the app, then the app consumes an appropriate VAULT_ADDR & VAULT_TOKEN.

And the above approach limited to only oidc (or specifically the auth methods coded into the app).

So might go with VAULT_ADDR & VAULT_TOKEN and wrap the app in bash or a Makefile that can pick the right VAULT_ADDR and check for or retrieve a token as needed.