AppRole usage best practices
To consume secrets, an application must first login into Vault and obtain a short lived token. The process is usually dependent on either the platform where the application is deployed or the workflow used to deploy it. Platform examples are AWS, GCE, Azure, Kubernetes, or OIDC. Workflow examples are CI tools such as Jenkins or CircleCI.
In this tutorial, you will learn the platform-agnostic best practices for securely delivering the credentials generated by the Vault's AppRole auth method. You will then apply those best practices to a Jenkins CI/CD pipeline.
Platform credential delivery method
To prevent any one system, other than the target client, from obtaining the complete set of credentials (RoleID and SecretID), we recommend implementing AppRole auth method to deliver those values separately through two different channels. This enables you to provide narrowly-scoped tokens to each trusted orchestrator to access either RoleID or SecretID, but never both.
RoleID delivery best practices
RoleID is an identifier that selects the AppRole against which the other credentials are evaluated. Think of it as a username for an application; therefore, RoleID is not a secret value. It's a static UUID that identifies a specific role configuration. Generally, you create a role per application to ensure that each application will have a unique RoleID.
Because it is not a secret, you can embed the RoleID value into a machine image or container as a text file or environment variable.
For example:
- Build an image with Packer with RoleID stored as an environment variable.
- Use Terraform to provision a machine embedded with RoleID.
There are a number of different patterns through which this value can be delivered.
The application running on the machine or container will read the RoleID from the file or environment variable to authenticate with Vault.
Policy requirement
An appropriate policy is required to read RoleID from Vault. For example, to get the RoleID for a role named, "jenkins", the policy should look as below.
SecretID delivery best practices
SecretID is a credential that is required by default for any login and is intended to always be secret. While RoleID is similar to a username, SecretID is equivalent to a password for its corresponding RoleID.
There are two additional considerations when distributing the SecretID, since it is a secret and should be secured so that only the intended recipient is able to read it.
- Binding CIDRs
- AppRole response wrapping
Binding CIDRs
When defining an AppRole, you can use the secretid_bound_cidrs
parameter to specify blocks of IP addresses which can perform the login operation for this role. You can further limit the IP range per token using token_bound_cidrs
.
Example:
CIDR consideration
While there is no hard limit to how many CIDR blocks you can set using the
token_bound_cidrs
parameter, there are limiting factors. One is the amount of
time it takes for the Vault to compare an IP with the list provided. Another is
the maximum request size of the HTTP when you create the list.
AppRole Response wrapping
To guarantee confidentiality, integrity, and non-repudiation of SecretID, you can use the -wrap-ttl
flag when generating the SecretID. Instead of providing the SecretID in plaintext, it puts it into a new token’s Cubbyhole with a token use count of 1. When the application attempts to read the SecretID, we can guarantee that only this application can read it.
Example: The following CLI command retrieves the SecretID for a role named, "jenkins". The generated SecretID is wrapped in a token which is valid for 60 seconds to unwrap.
Finally, you can monitor your audit logs for attempted read access of your SecretID. If Vault throws a use-limit error when an application tries to read the SecretID, you know that someone else has read the SecretID and alert on that. The audit logs will indicate where the SecretID read attempt originated.
Policy requirement
An appropriate policy is required to read SecretID from Vault. For example, to get the SecretID for a role named, "jenkins", the policy should look as below.
Token lifetime considerations
Tokens must be maintained client side and upon expiration can be renewed. For short lived workflows, traditionally tokens would be created with a lifetime that would match the average deploy time and left to expire, securing new tokens with each deployment.
A long token time-to-live (TTL) can cause out of memory when trying to purge millions of AppRole leases. To avoid this, we recommend that you reduce TTLs for AppRole tokens and implement token renewal where possible. You can increase the memory on the Vault server; however, it won't be a long-term solution.
In general, with any auth method, it's preferable for applications to keep using the same Vault token to fetch secrets repeatedly instead of a new authentication each time. Authentication is an expensive operation and results in a token that Vault must keep track of. If high authentication throughput, 1000s of authentications per second, are expected we recommend using batch tokens which are issued from memory and do not consume storage.
Vault Agent
Consider running Vault Agent on the client host, and let the agent manage the token's lifecycle. Vault Agent reduces the number of tokens used by the client applications. In addition, it eliminates the need to implement the Vault APIs to authenticate with Vault and renew the token TTL if necessary.
To learn more about Vault Agent, read the following tutorials:
Jenkins CI/CD
When you are using Jenkins as a CI tool, Jenkins itself will need an identity; however, you should never have Jenkins log into Vault and pass a client token to the application via workflow. Jenkins needs to give the application its own identity so that the application gets its own secret. The best practice is to use the Vault Agent as much as possible with Jenkins so that Vault token is not managed by Jenkins. You can deliver a SecretID every morning or before every run for x number of uses. Let Vault Agent authenticate with Vault and get the token for Jenkins. Then, Jenkins uses that token for x number of operations against Vault.
A key benefit of AppRole for applications is that it enables you to more easily migrate the application between platforms.
When you use an AppRole for the application, the best practice is to obscure the RoleID from Jenkins but allow Jenkins to deliver a wrapped SecretID to the application. The application bootstrap process should be:
- validate the wrap
- do a lookup
- unwrap
- get SecretID
- get RoleID from the platform
- authenticate to Vault and get its own token
Note, in step 5 that the application should not get the RoleID from Jenkins.
Next steps
Authentication methods are generally configured by an operator at initial configuration time. Most commonly, systems perform an authentication process automatically, though the responsibility of carrying out the process is generally agreed as part of a handover when multiple teams take responsibility for provisioning a system or deploying an application.
Additional AppRole tutorials
In the AppRole Pull Authentication tutorial, get hands-on experience with AppRoles.
Also, read the How (and Why) to Use AppRole Correctly in HashiCorp Vault blog about the motivation behind using the AppRole auth method.