Vault Associate Certification (Part 0): Hello World
Tags: hashicorp, vault, security
Published: 2023-08-07 17:40:29

As promised, before I dig into the exam objectives for becoming Vault Associate certified I will present a _Hello World_ of HashiCorp Vault in this post. Going through the exam objectives part by part in order would be a somewhat difficult way of learning Vault unless you have first seen the whole picture at least once. This is also clear if you follow the official study guide provided by HashiCorp, they mix objectives left and right in each part of the guide. I believe it will be helpful to first go through this _Hello World_ type of post where we touch on many of the concepts that we will later dive deeper into.

---

**Note:** In the posts that follow in this series I am running Vault on a MacBook, and some of the example commands you will see will not work if you run Windows and use PowerShell or the regular command prompt. Keep this in mind, I will not mention this again!

---

## Installing Vault

Before we can do anything with Vault we need to install it. I prefer to install stuff with Homebrew, and conveniently enough HashiCorp allows me to do just that:

```console
$ brew tap hashicorp/tap
$ brew install hashicorp/tap/vault
```

More detailed instructions for how to install Vault can be found in the official [documentation](https://developer.hashicorp.com/vault/downloads).

After installing Vault I can verify that the installation was successful by running my first Vault CLI command:

```console
$ vault -version

Vault v1.14.0 (13a649f860186dffe3f3a4459814d87191efc321), built 2023-06-19T11:40:23Z
```

I will be using Vault version 1.14.X for this series, unless I am taking too long to write this series so that 1.15.X arrives.

## Starting the Vault server

In this post, and in most of the following posts in this series, I will be using the **Vault development server**. This is, as the name suggests, a server specifically for development and testing. It is not for production use. The development server simplifies the startup significantly compared to a production grade Vault cluster. In part nine of this series I will go through how to start Vault _for real_. We need to run a Vault server to be able to work with Vault, this is true no matter where you run Vault. So even if we use a hosted version of Vault in the HashiCorp Cloud Platform (HCP) we still have a server (but managed by HashiCorp).

Enough chit-chat, to start the development server I run a single command:

```console
$ vault server -dev

(... output truncated ...)

==> Vault server started! Log data will stream in below:

(... output truncated ...)

WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key. The root token is already
authenticated to the CLI, so you can immediately begin using Vault.

You may need to set the following environment variables:

    $ export VAULT_ADDR='http://127.0.0.1:8200'

The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.

Unseal Key: I+HW1DY/NHB8HS5Nc3uaK1phDLt0+zFTP1/W48JsF0I=
Root Token: hvs.4pr1W6DoLP7vjs6EIuxZHrjP

Development mode should NOT be used in production installations!
```

There are a few pieces to note in the output:

- We are recommended to set an environment variable by running `export VAULT_ADDR='http://127.0.0.1:8200'`, this variable sets the address to Vault server. It runs on localhost (127.0.0.1) on port 8200. Note that the address uses HTTP and not HTTPS. You can run the development server with HTTPS by changing the start-up command to `vault server -dev-tls`, but I will not do that here.
- We are given an unseal key with the value `I+HW1DY/NHB8HS5Nc3uaK1phDLt0+zFTP1/W48JsF0I=`. We will not need to bother with unseal keys until part nine of this series, so more on those then.
- We are given a root token with a value of `hvs.4pr1W6DoLP7vjs6EIuxZHrjP`. This token will be used in all interactions with Vault.

Take a look at the contents of a file called `.vault-token` in your home directory: 

```console
$ cat ~/.vault-token

hvs.4pr1W6DoLP7vjs6EIuxZHrjP
```

This is the root token from the output above. This token will be used automatically when you run Vault CLI commands unless you explicitly set another token. Also, if you set an environment variable named `VAULT_TOKEN` with the value of a Vault token, then that environment variable will take precedence and be used first.

Apart from this we can see from the output clear warnings that the development server should not be used in production. We are ready to move on in our _Hello World_ journey.

## Working with secrets through secrets engines

What is a _secrets engine_?

> Secrets engines are components which store, generate, or encrypt data. Secrets engines are incredibly flexible, so it is easiest to think about them in terms of their function. Secrets engines are provided some set of data, they take some action on that data, and they return a result.[^secrets]

The easiest secret engine to start working with is the Key/Value secrets engine, or kv secrets engine for short. The kv secrets engine actually exists in two versions, version 1 and 2. The main difference between them is that version 2 allows to have a history of versions for a given secret. When you start a development server Vault comes with the kv secrets engine version 2 enabled by default. However, I will enable the kv v1 secret engine:

```console
$ vault secrets enable kv

Success! Enabled the kv secrets engine at: kv/
```

The output says that the kv secrets engine is enabled at `kv/`. This is a **path** in the Vault system. **Everything in Vault exists at a path.** If you remember only one thing from this post, let it be the previous sentence! We will see many examples of this throughout this blog series. I can verify what secrets engines I have with the following command:

```console
$ vault secrets list

Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_29554004    per-token private secret storage
identity/     identity     identity_07cbe533     identity store
kv/           kv           kv_7b18d0e0           n/a
secret/       kv           kv_f93f6e98           key/value secret storage
sys/          system       system_9cca3491       system endpoints used for control, policy and debugging
```

This list contains five secrets engines. The third one in the list is the one we just enabled. For now we will ignore the other secret engines listed, just remember that the Vault development server (and a regular Vault server) comes with a few secrets engines pre-enabled.

I would like to store a new secret using my kv secrets engine. I can do this with the following command:

```console
$ vault kv put -mount=kv my-secret hello=world

Success! Data written to: kv/my-secret
```

At first the syntax is not at all intuitive. What the command is saying is that we want to `put` (or create) a new secret named `my-secret` using the `kv` secrets engine. The specific instance of the `kv` secrets engine is _mounted_ at the path `kv`. Yes, this suggests that we can enable multiple instances of the same secrets engine at different paths. More on that in a future post. The content of the secret named `my-secret` is `hello=world`. Once I have a secret I can read that secret back from Vault:

```console
$ vault kv get kv/my-secret

==== Data ====
Key      Value
---      -----
hello    world
```

This command says that we want to `get` (or read) a secret from the `kv` secrets engine, specifically the one available at the path `kv/my-secret`. Our data with `Key` equal to `hello` and `Value` equal to `world` is returned to us.

## Authenticating using authentication methods

In the previous section we enabled a secrets engine, we wrote a secret, and finally read that secret back again. We did all this using the root token that was provided for us when we started the Vault development server. In essence we authorized our actions using this token. Is this how Vault is supposed to be used? Not at all!

The root token can do anything in Vault, so it should not be shared with more people than absolutely necessary. In fact, a best-practice is to only use the root token during initial server setup, and then create a new admin token with only the necessary permissions to administer the Vault server during its lifetime. The root token should be kept in a secure storage location of some sort.

So how are we supposed to use Vault? Users of Vault should authenticate to Vault using an _authentication method_. What is an authentication method, or _auth method_ for short?

> Auth methods are the components in Vault that perform authentication and are responsible for assigning identity and a set of policies to a user. In all cases, Vault will enforce authentication as part of the request processing. In most cases, Vault will delegate the authentication administration and decision to the relevant configured external auth method (e.g., Amazon Web Services, GitHub, Google Cloud Platform, Kubernetes, Microsoft Azure, Okta ...).[^auth]

To put it another way, auth methods allow us to use a third-party (e.g. GitHub) to authenticate to Vault in order to get a token that allows us to perform actions in Vault. The quote above mentions identities and policies. A user of Vault can use any of the auth methods we enable on our server to prove that they are a specific identity on our server. This identity has one or more associated policies which are documents that specify what actions we can perform at specific paths in Vault. We will soon write a policy, but first let us enable an auth method.

I start by first listing what auth methods I currently have enabled:

```console
$ vault auth list

Path      Type     Accessor               Description                Version
----      ----     --------               -----------                -------
token/    token    auth_token_c932f51c    token based credentials    n/a
```

It seems like I only have the `token` auth method enabled at the `token/` path. This is a required auth method, because no matter what other auth method we use we end up with a token, which we then use to run Vault commands.

For this _Hello World_ tutorial I will enable an auth method named `userpass`:

```console
$ vault auth enable userpass

Success! Enabled userpass auth method at: userpass/
```

The `userpass` auth method is a basic username and password type of authentication mechanism. It is in fact not using a third-party system for authentication. In a production environment I would not recommend using it, but it is illustrative for the purpose of _Hello World_.

I will create a first username and password combination:

```console
$ vault write auth/userpass/users/jane.doe password=S3cr3t policies=default

Success! Data written to: auth/userpass/users/jane.doe
```

I wrote data to `auth/userpass/jane.doe`, which basically means I created a user with username `jane.doe`. At this path I entered `password=S3cr3t` and `policies=default`. The data is self-explanatory but I will still explain it! I specified that the user with username `jane.doe` should have a password with the value of `S3cr3t` and that the user should have the policy named `default` attached to the token the user gets when she signs in. What is the `default` policy? It is a required policy that every user in Vault has, by default. You can edit the contents of the default policy if you wish but you can't delete it. More on policies soon!

Now it is time for this `jane.doe` to sign in to Vault:

```console
$ vault login -method=userpass username=jane.doe password=S3cr3t

Success! You are now authenticated.

Key                    Value
---                    -----
token                  hvs.CAESICC<truncated>bU9ZYnJxbVk
token_accessor         sGkEujtl9ZU9i4Cqk2iPcn57
token_duration         768h
token_renewable        true
token_policies         ["default"]
identity_policies      []
policies               ["default"]
token_meta_username    jane.doe
```

`jane.doe` successfully signed in and received a token in the response. This token can now be used to run other Vault commands as allowed by the attached policies - only the `default` policy in this case.

## Restrictions using policies

In the previous section we authenticated using an auth method and we connected this with the default policy. As mentioned, the default policy is always provided to everyone who authenticates to the Vault server. We will now write our own policy and provide this policy to someone who authenticates to Vault. The purpose of a policy is to restrict what someone can do in Vault. In essence a policy states what actions can be performed on a given path in Vault - remember that everything in Vault is path based.

You write policies in Vault using the HashiCorp Configuration Language (HCL). A basic policy might look like this:

```hcl
# jane.hcl

path "kv/jane.doe" {
    capabilities = ["read", "list", "create"]
}
```

This policy defines one `path` block for the path `kv/jane.doe`. This path corresponds to a secret in the kv secrets engine mounted at the `kv/` path. The policy further says that at this path it allows the _capabilities_ of _read_, _list_, and _create_. This is basically what policies are, one or more paths with associated capabilities.

I can create an actual policy object from the policy document:

```console
$ vault policy write jane jane.hcl

Success! Uploaded policy: jane
```

With the policy created I update my user `jane.doe` to get this policy, apart from the default policy:

```console
$ vault write auth/userpass/users/jane.doe password=S3cr3t policies=default,jane

Success! Data written to: auth/userpass/users/jane.doe
```

If I now sign in with the user I can see that the token I get is associated with the `jane` policy:

```console
$ vault login -method=userpass username=jane.doe password=S3cr3t

Success! You are now authenticated.

Key                    Value
---                    -----
token                  hvs.CAESINa6<truncated>1U3ZZbjhwSlE
token_accessor         fhXC5AnQy2MiqI2hM32qbQub
token_duration         768h
token_renewable        true
token_policies         ["default" "jane"]
identity_policies      []
policies               ["default" "jane"]
token_meta_username    jane.doe
```

Using my new token, or `jane.doe`'s new token, to create the original secret we created earlier I am met by a 403 error:

```console
$ vault kv put -mount=kv my-secret hello=world

Error making API request.

URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/kv
Code: 403. Errors:

* preflight capability check returned 403, please ensure client's policies grant access to path "kv/"
```

This error happens because the policy (neither `jane` or `default`) allows me to create a secret at `kv/my-secret`. However, the `jane` policy does allow me to create a secret under `kv/jane.doe`:

```console
$ vault kv put -mount=kv jane.doe hello=world

Success! Data written to: kv/jane.doe
```

## Summary

There we have it! We have configured vault with secret engines and auth methods. We have created a custom policy and assigned it to a user. We have signed in as a user and created a secret. This has been my version of _Hello World_ for Vault! In the next post in this series we will begin taking on the exam objectives and we start with the objective named: _Compare authentication methods_.

[^secrets]: From the official documentation https://developer.hashicorp.com/vault/docs/secrets
[^auth]: From the official documentation https://developer.hashicorp.com/vault/docs/auth