• Beta
Secrets Management
  • Updated on 14 Jul 2020
  • 9 minutes to read
  • Share
  • Dark
    Light

Secrets Management

  • Share
  • Dark
    Light

The Ops Platform secrets feature is a key component in building a scalable, maintainable and secure workflow automation system for your team. For example, running Ops from within Slack is easy, but entering passwords or other types of secrets into the Slack UI is not secure (anything entered into the UI is available on the Slack servers). To solve this problem, the Ops Platform enables you to connect your secrets provider to your Ops. We currently support Hashicorp Vault, but we expect to add more providers in the future. By integrating a third-party secrets provider, you can rest assured that your shared secrets are safe.

The Ops Platform secrets store complements our configuration store (for storing non-sensitive information).

Using the Default Provider

When you create a team for The Ops Platform, you will get a default encryped secrets provider out of the box. You can optionally register your own provider.

Registering a new Secrets Provider

As soon as you create a new Ops team, you can use the default secret store which we provide for your team. However, in production, we recommend you consider HashiCorp Vault. Please see the section below on Using Hashicorp Vault for detailed instructions on how to set up a Hashicorp Vault secrets provider for your Ops team.

Note: Commands to register and unregister a secrets provider are currently only supported in the CLI.

Accessing the Secrets Store

You can access the secrets store through both the CLI and Slack. Each item in your secrets store represents a key-value pair, where the key represents a unique identifier for some item of data and the value represents the data that is identified.

Using CLI

List Secret Keys
ops secrets:list Lists all the secret keys that are stored for the active team. Secret values are hidden for security.

Set Secret
Adds a new secret key & value.

ops secrets:set Prompts for the key, and then opens an editor for defining the value (recommended for complex value content).
ops secrets:set -k <KEY> -v <VALUE> Sets the key and value pair in one go (recommended for simple value content).
ops secrets:set -k <KEY> --from-file <PATH_TO_FILE> Sets the key and value pair in one go, using the contents of a file as the value content.

Delete Secret
Deletes a secret stored for the active team.

ops secrets:delete Prompts for the key to be deleted.
ops secrets:delete -k <KEY> Deletes the indicated key and associated value in one go.

Register Secret Provider
ops secrets:register Sets up an existing HashiCorp Vault provider as the secret store for your team. By default, each team will have a default secret store associated, so you will have to first unregister the default store before you can register a new one.

Unregister Secret Provider
ops secrets:unregister Unregister the secret provider associated to your team. Required before you can register a new one.

Using Slack

List Secret Keys
/ops secrets:list Displays a list of all keys. Secret values are hidden for security.

Set Secret
/ops secrets:set Opens browser to add a new key-value pair in the browser (requires authentication).

Passing the key and/or value as arguments in one go, as well as setting the value from a file are currently only supported in the CLI.

Delete Secret
/ops secrets:delete Displays a list of all secret keys, with the option to confirm deletion in the browser (requires authentication). Secret values are hidden for security.

Using SDKs

In addition to listing and setting and deleting secrets manually by running the above commands, your Ops can interact with the active team's secrets store automatically at runtime.

Our SDKs support the following operations:

  • Retrieving a single secret by key
  • Adding a new key-value pair to the secrets store
  • Using a value set as a secret as part of a prompt (e.g. NodeJS secret prompt)

Note: If trying to retrieve the value saved under a key that does not exist, the Op will prompt the user to select an alternate key that holds the desired value (from the list of all keys available in the team's secrets store), or enter a one-time use value instead.

Node.JS Example

// Node.js SDK

const KEY = 'API_TOKEN'
const VALUE = 'sensitive-token-value'
const NEW_VALUE = 'new-sensitive-token-value'

await sdk.setSecret(KEY, VALUE)
await ux.print(`${KEY} set!`)

// Secret values get masked when printed the conventional way
// We print the lengths to check if it has changed
const { [KEY]: value } = await sdk.getSecret(KEY)
await ux.print(`${KEY} currently set to a value of length: ${value.length}`)

await sdk.setSecret(KEY, NEW_VALUE)
await ux.print(`${KEY} updated!`)

const { [KEY]: newValue } = await sdk.getSecret(KEY)
await ux.print(`${KEY} now set to a value of length: ${newValue.length}`)
// Node.js secret prompt

const { [KEY]: value } = await ux.prompt({
    type: "secret",
    name: KEY,
    message: "What API TOKEN do you want to use?"
})
await ux.print(`${KEY} selected; value has length: ${value.length}`)

Python Example

# Python SDK

KEY = 'API_TOKEN'
VALUE = 'sensitive-token-value'
NEW_VALUE = 'new-sensitive-token-value'

sdk.set_secret(KEY, VALUE)
ux.print(f"{KEY} set!")

# Secret values get masked when printed the conventional way
# We print the lengths to check if it has changed
value = sdk.get_secret(KEY)
ux.print(f"{KEY} currently set to a value of length: {len(value)}")

sdk.set_secret(KEY, NEW_VALUE)
ux.print(f"{KEY} updated!")

new_value = sdk.get_secret(KEY)
ux.print(f"{KEY} is now set to a value of length: {len(new_value)}")
# Python secret prompt

value = prompt.secret(
    name=KEY,
    message="What API TOKEN do you want to use?"
)
ux.print("API TOKEN selected; value has length: {}".format(len(value)))

GO Example

// GO SDK

s := ctoai.NewSdk()
u := ctoai.NewUx()

const KEY = "API_TOKEN"
const VALUE = "sensitive-token-value"
const NEW_VALUE = "new-sensitive-token-value"

key, err := s.SetSecret(KEY, VALUE)
if err != nil {
  panic(err)
}
err = u.Print(fmt.Sprintf("%s set!", key))
if err != nil {
  panic(err)
}

// Secret values get masked when printed the conventional way
// We print the lengths to check if it has changed
value, err := s.GetSecret(KEY)
if err != nil {
  panic(err)
}
err = u.Print(fmt.Sprintf("%s currently set to a value of length: %d", KEY, len(value)))
if err != nil {
  panic(err)
}

updatedKey, err := s.SetSecret(KEY, NEW_VALUE)
if err != nil {
  panic(err)
}
err = u.Print(fmt.Sprintf("%s updated!", updatedKey))
if err != nil {
  panic(err)
}

newValue, err := s.GetSecret(KEY)
if err != nil {
  panic(err)
}
err = u.Print(fmt.Sprintf("%s now set to a value of length: %d", KEY, len(newValue)))
if err != nil {
  panic(err)
}
// GO secret prompt

p := ctoai.NewPrompt()
value, err := p.Secret(KEY, "What API TOKEN do you want to use?", ctoai.OptSecretFlag("s"))
if err != nil {
    panic(err)
}

fmt.Println(fmt.Sprintf("API TOKEN selected; value has length: %d", len(value)))

Bash Example

# Bash SDK

sdk secret set --key API_TOKEN --value sensitive-token-value
ux print "API_TOKEN set!"

# Secret values get masked when printed the conventional way
# We print the lengths to check if it has changed
value=$(sdk secret get API_TOKEN)
ux print "API_TOKEN currently set to a value of length: ${#value}"

sdk secret set --key API_TOKEN --value new-sensitive-token-value
ux print "API_TOKEN updated!"

newValue=$(sdk secret get API_TOKEN)
ux print "API_TOKEN currently set to a value of length: ${#newValue}"
# Bash secret prompt

value=$(ux prompt secret --message='What API TOKEN do you want to use?' --name='API_TOKEN')
ux print "API_TOKEN selected; value has length: ${#value}"

Using the Hashicorp Vault Provider

We provide a secrets store that is setup by default for your account. However, if you would prefer to use Hashicorp Vault, you can optionally use that as your secret provider. We recommend you consider HashiCorp Vault especially when interacting with production environments.

If you are not familiar with Vault, these instructions will help you set up a testing instance so that you can try The Ops Platform with Vault. If you wish to use Vault in your production environment, you will need to use a production deployment of Vault--not a local test instance as you'll set up in this tutorial. We also highly encourage you to consider best practices recommended by Hashicorp for a production hardened deployment of Vault available in this guide: Hashicorp - Production Hardening.

Install Vault locally

Download the Hashicorp Vault binary and run it on your computer. From the directory where you downloaded the binary, run:

./vault server -dev

Note: If you’re using Vault on MacOS, you may see an error when you first try to run Vault. If you see this error, you’ll have to allow the executable to run. To do this, open System Preferences > Security and Privacy and choose “Open Anyway” for the error that Vault was blocked.

Retrieve ROOT_TOKEN from the logs

You can use the returned token to register your vault with the Ops CLI:

ops run @vahid/vault --url "[http://host.docker.internal:8200](http://host.docker.internal:8200/)" --token [ROOT_TOKEN] --team [YOUR_TEAM_NAME] configure

This will setup the team structure in the vault and will return a new team token associated with your team.

Note: You can retrieve your current team name by running: ops whoami

You can also use this Op to work with the created team in the vault. For example, you can view the existing secrets:

ops run @vahid/vault --url "[http://host.docker.internal:8200](http://host.docker.internal:8200/)" --token [TEAM_TOKEN] --team [YOUR_TEAM_NAME] secret list

You can also check the contents of your Vault by viewing the Vault UI in a web browser: http://127.0.0.1:8200/ui/vault/secrets.

Note: Once you have Vault installed, you will need to create a static URL, so that it’s accessible externally. You would not do this for production, it’s only for this exercise and you should turn off the connection after you are done.

Exposing your local vault to the public Internet requires a service such as https://ngrok.com.

Install ngrok

To set up ngrok, follow these steps: https://dashboard.ngrok.com/get-started. Essentially, you need to install the application and then add your authentication token. After that, you can add your internal Vault as a public URL by running:

./ngrok http 8200

When you run this command, ngrok will open a terminal UI that shows the URL in use for your Vault.

Register your new provider with The Ops Platform

Note: Commands to register and unregister a secrets provider are currently only supported in the CLI.

Once you have your Vault set up and accessible publicly via the Internet, you can register your new provider using:

ops secrets:register

Enter the URL (your public Vault URL from ngrok plus the team name, e.g., http://xxxx.ngrok.io/teamName) and token you received when you set up the provider for your team.

After you have registered your new provider, you can set and use secrets with the same commands as the default provider.

Other Information

Please consult the dedicated SDK pages for more details and examples:

Note: When adding a new key-value pair to the secrets store, you have the freedom to set any content as the value, including collections of values (e.g. JSON, YAML, etc.). However, it will be your responsibility to handle the parsing of the content accordingly in your Ops, using parsing features and/or libraries available for your language of choice.

🚀 What's next?

Was this article helpful?