Workflow
💽 Overview¶
When working with a cloud provider it is common to require some kind of authentication when accessing their api. I have setup a similar system for this project. If you use BitWarden you can control the login process allowing for a seamless experience when sourcing all of you credentials. In this document I will not go over setting up BitWarden or the cli as that is out of scope for this project. If you need to install it follow the documentation here.
🖥️ Required Secrets¶
you need a few environment variables in order to authenticate and control all of the infrastructure in this project. I have tried to minimize the number of distinct keys you need to set. There are a few that are technically optional that I will go into more detail later.
# OpenTofu Secrets #
export TF_HTTP_PASSWORD=
export TF_TOKEN_gitlab_com=
export TF_HTTP_USERNAME=""
# Vault #
export VAULT_ADDR='https://localhost:8200'
export VAULT_CAPATH=/etc/ssl/certs/{domain-name}.pem
export VAULT_DEV_ROOT_TOKEN_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export UNSEAL_KEY_1=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export UNSEAL_KEY_2=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export UNSEAL_KEY_3=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
These variables are part of 2 groups: OpenTofu and Vault. These are the 2 different services that you need to authenticate with in order to deploy this code.
📦 OpenTofu Variables:¶
TF_HTTP_PASSWORD and TF_HTTP_USERNAME¶
These variables permit opentofu to read the remote state-file hosted on gitlab. Tooling
TF_TOKEN_gitlab_com¶
This variable permits opentofu to upload a module to the module repository hosted on gitlab. (this authentication also uses the TF_HTTP_USERNAME variable) Module Machine
🔐 Vault Variables:¶
VAULT_ADDR (may be optional)¶
This variable defines the url for the hosted docker application. In most cases it defaults to localhost, however if running on a different host machine or if you have a different setup then this variable will need to be set. I always set it just to make things easier.
VAULT_CAPATH¶
This is the cert used to validate the local https request to the vault. Since the vault workflow generates the certificates it is signing the vault with, the ca cert is not implicitly trusted. In order to get the trust to work the Vault workflow will attempt to install the certificate in your local trusted authority. If this fails or does not work setting this will ensure that the vault is able to find the cert.
VAULT_DEV_ROOT_TOKEN_ID¶
This is the user that the vault will assume when pulling secrets from it. The vault is deployed locally and you probably do not have other users on your home lab, in this case we authenticate with the root token. This is just easier than setting up a bunch of users and groups. Which the automation does deploy, however I dont use it. It is best practice and I would encourage you to always follow least privilege.
UNSEAL_KEY_1,2,3 (only require 2/3)¶
These are used to authenticate with the vault to unseal it. You only need 2 of the 3 keys in order to authenticate with this vault, you can modify the initialization of the vault and do more. This is the minimum number of keys you can have. I used to have 6 but this was just simpler and could easily be changed.
📂 BitWarden Entry¶
Open the bitwarden vault and create a new login entry, with the following information:
- name: Vault this correlates with the script so if you change the name make sure to modify the script as well
- username: empty
- password: the root token value (VAULT_DEV_ROOT_TOKEN_ID)
- website: https://localhost:8200/ui
Create a custom field where the name is the environment variable and the value is the secret for that variable
- TF_HTTP_PASSWORD: the http password should be gplat-XXXXX
- TF_TOKEN_gitlab_com: the api token should be gplat-XXXXX
- TF_HTTP_USERNAME: gitlab username
- VAULT_ADDR: 'https://localhost:8200'
- VAULT_CAPATH: /etc/ssl/certs/{domain-name}.pem
- VAULT_DEV_ROOT_TOKEN_ID: should be of the format XXX.XXXXXXXXXXXXXX
- UNSEAL_KEY_1: XXXXXXXXXXXXXX
- UNSEAL_KEY_2: XXXXXXXXXXXXXX
- UNSEAL_KEY_3: XXXXXXXXXXXXXX
📃 Script¶
I store this function in my bash alias but you can also alias this to a script somewhere on your system.
function tfauth() {
# Check if BW_SESSION is set in the current environment
if [ -z "$BW_SESSION" ]; then
# echo "BW_SESSION is not set."
# Check if the /tmp/bw.env file exists and source it
if [ -f /tmp/bw.env ]; then
# echo "Sourcing BW_SESSION from /tmp/bw.env"
source /tmp/bw.env
fi
# If BW_SESSION is still not set, log in to Bitwarden
if [ -z "$BW_SESSION" ]; then
echo "Logging in to Bitwarden..."
local login_output=$(bw unlock --raw) # Use --raw to get the session key directly
if [ $? -ne 0 ]; then
echo "Failed to log in to Bitwarden."
return 1
fi
eval "BW_SESSION=$login_output"
echo "export BW_SESSION=\"$login_output\"" > /tmp/bw.env
fi
fi
# Retrieve the JSON from Bitwarden
echo "Retrieving Vault Secrets"
local vault_json=$(bw get item "Vault" --session "$BW_SESSION")
if [ $? -ne 0 ]; then
echo "Failed to retrieve item from Bitwarden."
return 1
fi
# Use jq to extract the name and value fields and prefix them with 'export'
export_commands=$(echo "$vault_json" | jq -r '.fields[] | "export \(.name)=\(.value)"')
if [ $? -ne 0 ]; then
echo "Failed to parse JSON from Bitwarden."
return 1
fi
# Evaluate the export commands to set the environment variables
echo "Setting Vault Secrets"
eval "$export_commands"
}
# Vault Alias
alias tfunseal="/Path-To-Project/vault/scripts/unseal.sh"
With this script and the unseal alias in your terminal you can simply type tfauth
and it will prompt you for your master password from bitwarden and save the session while you are logged in. Then when you want to deploy just tfunseal
to unseal the vault for programming and deployment. All secrets are now stored in 2 vaults and are encrypted at rest.