Authenticating Applications with HashiCorp Vault AppRole

Authenticating Applications with HashiCorp Vault AppRole

Mar 13 2018    Anubhav Mishra

HashiCorp Vault is an open source tool for managing secrets. Application identity management with Vault enables applications and machines to automatically create, change, and rotate secrets needed for communications, services, scripts, etc. Additionally, Vault enables administrators to manage applications and machines by providing access control over different secrets. In a previous post we looked at Why We Need Dynamic Secrets. This post explores how machines and applications can use the AppRole auth method to authenticate with Vault and access secrets.

» Auth Methods

Auth methods in Vault are the components that perform authentication and assigning policies to a user, application, or machine. Vault has many auth methods to support different use cases and sources of identity. Organizations using Active Directory to manage users can use the LDAP auth method, some might use Github auth method to manage users, while VMs running in AWS can use AWS auth method.

While auth methods such as LDAP, and Github allow users to login to Vault, there needs to be a similar workflow to allow applications to do the same. AppRole auth method was specifically designed to be used by machines or applications to login to Vault. Let’s take a detailed look on how AppRole auth method works.

» AppRole

The AppRole auth method allows multiple “roles” to be defined corresponding to different applications, each with different levels of access. To authenticate with Vault the application is assigned a static Role ID and a dynamically generated Secret ID which are both required to login and fetch a Vault token. The AppRole auth method was specifically designed to be used by machines and applications but uses similar authentication method that a human might use. You can look at Role ID as a “username” and the Secret ID as a “password” allowing machines to authenticate to Vault. Once authenticated, they are assigned a set of policies which grant access to retrieve secrets from Vault.

» Role ID

As discussed earlier, Role ID can be seen as the “username” for a particular application. This means that multiple instances of the same application can share the same Role ID. For example, a front-end service can have multiple instances running and they all share the same Role ID since they are a single logical service. Role IDs can be created by an operator or a build system and is provided to the application using various methods like configuration management. This can then be used along with a Secret ID to login to Vault.

» Secret ID

Secret ID is always intended to be a secret and can be seen as the “password” that is required to login to Vault. This can be dynamically generated by a trusted entity such as build system or using configuration management. Each Secret ID is meant to be unique for one instance of an application. Unique credentials allow us to better audit access and have fine grained revocation of access. Each Secret ID can have a usage limit, TTL, and expiration to better limit access.

Let’s now take a look at an end to end example to help understand AppRole auth method better.

AppRole End to End Workflow

The above example explores an end to end process of generating a Vault token for an application running on a server or using a container.

In this example, an operator creates a policy to allow an application to fetch its secrets from Vault. Below is an example of a policy that is created for a front-end service named “hello-world”.

$ cat  hello-world.hcl

path "secret/hello-world" {
 capabilities = ["read", "list"]
$ vault policy write hello-world hello-world.hcl

Next, an operator can create a role in Vault and associate the policy with that role.

$ vault write auth/approle/role/hello-world  secret_id_ttl=120m  token_ttl=60m  token_max_tll=120m  policies="hello-world"

The hello-world role is now ready to be used by any instance of the hello-world application. The Role ID for the hello-world role can be fetched using the Vault API by a human or a machine. Below is an example of how Role ID can be fetched using the Vault CLI.

$ vault read auth/approle/role/hello-world/role-id
Key        Value
---        -----
role_id    83b621b3-0aca-1e63-fc05-b7a44318dd85

In this example, a developer triggers a build for the hello-world application by pushing a new version of application to the source code repository. The CI/CD build system can now fetch the Role ID for the hello-world role using the Vault API. This Role ID can be written as part of the metadata for the application.

In this example, Role ID is written to the machine image of the server that is responsible for running the hello-world application. This can be done during the creation of machine image using Packer. The CI/CD system can pass the Role ID as an argument to Packer and it can use a variety of provisioners to write it to the machine image. Once the build for the machine image is complete, this machine image can be used to spin up new instances of the hello-world application. The machine image will contain all application dependencies, application code along with Role ID written file.

Next, the machine image can be sent to the orchestrator used to spin up instances of the application. This can be configuration management system. In this example, Terraform will be used to spin up servers for the hello-world application. The orchestrator will generate a Secret ID for the application and deliver it to the server that is running the application using methods such as SSH. Below is an example of generating a Secret ID using the Vault CLI for the hello-world role.

$ vault write -f auth/approle/role/hello-world/secret-id
Key                   Value
---                   -----
secret_id             238651ce-7962-c9bf-67a4-1ec0573d1058
secret_id_accessor    d5fd9984-d901-cf65-473e-edbb43618d95

Note that the orchestrator(Terraform) can only generate Secret IDs for the hello-world application and does not have access to the secrets for the application. This is essential helps to compartmentalize access in the application delivery pipeline.

The hello-world application can authenticate with Vault using the Role ID, and Secret ID provided by a file written during the build process. Below is an example of using the Role ID, and Secret ID to login to Vault using the Vault CLI.

$ vault write auth/approle/login role_id=${ROLE_ID} secret_id=${SECRET_ID}
Key                     Value
---                     -----
token                   87e9d662-2f9d-5cc5-a7eb-b3e5ed76d887
token_accessor          3e9a58e5-4ea5-fe1c-d3fc-180017b63e98
token_duration          1h
token_renewable         true
token_policies          [default hello-world]
token_meta_role_name    hello-world

The -field=token command line argument can be used to filter the output to only return the Vault token. This is helpful when setting the VAULT_TOKEN environment variable.

Once the authentication successful, Vault will provide a token to the application that can used to request secrets. Tools like envconsul, and consul-template can also be used to populate secret data for the application. Below is an example of fetching secret data using consul-template.

{{ with secret "secret/hello-world" }}
{{ if .Data.password }}
password = "{{ .Data.password }}"
{{ end }}
{{ end }}

Secrets fetched from Vault can be written to a configuration file by consul-template. The application can then be started by consul-template and it can read the configuration file to use the secrets.

» Conclusion

The AppRole auth method provides a workflow for application or machines to authenticate with Vault. It can help provide a multi-part authenticating solution by using the combination of Role ID (sensitive), and Secret ID (secret). AppRole allows applications to be assigned a unique role and securely authenticate with Vault while fitting into existing configuration management or CI/CD workflows. To learn more checkout AppRole auth method.