How to decrypt Terraform states stored in GitLab backend
Assuming you are using the GitLab Terraform state feature in your self managed instance and you are using the embded backup utility provided by GitLab.
The Terraform state files are encrypted before they are stored. This means that you cannot retreiv the content at rest. For this purpose, GitLab use application secrets (and derive new secrets from thoses keys when needed) to encrypt sensitive content.
You want to retreiv the content of a state file from a GitLab backup. Like explained in this issue, it's not possible to easily retreiv a decrypted content is the instance is offline.
Recover procedure
Retreiving GitLab instance secrets
Using the GitLab documentation about application secrets you need to retreiv the value of the db_key_base
.
Retreiving project informations
Terraform state storage is linked to a GitLab project. If the project still exists in your instance, save the project_id
for later.
If you have deleted the project we are going to need the database backup (also stored in the GitLab backup).
First search in the public.routes
table for your project. You can filter by the source_type=Project
and path=path/of/your/project
. Write down the value in the namespace_id
column.
Then look at the public.projects
table filtering the namespace_id
retreived just before. Write down the id
of the project.
Calculating the state storage path
States are stored with hashed path.
The first part is the hash256 sum of the project id
. You can use the following command to calculate it :
The second part can be retreived from the database in the table public.terraform_states
. Filter by the project_id
and state name
save the uuid
.
The full folder path of the state file is now :
If you have enabled versionning state are stored with the filename <serial_number>.tfstate
Retreiving the content
This is the hardest part and you need to be a little familiar with Ruby.
GitLab use a tool named Lockbox to encrypt Terraform state content before storing them. We are going to use the same tool to decrypt our files.
Fill and use the following script :
#################
# Configuration #
#################
# Retreived from GitLab rails secrets
# https://docs.gitlab.com/ee/development/application_secrets.html
# This is a dummy key base. Don't bother using it
db_key_base = "<FILL_DB_KEY_BASE>"
# The project ID in GitLab
project_id = "<FILL_PROJECT_ID>"
# The file to decrypt
input_file = "<FILL_ENCRYPTED_STATE_FILE_PATH>"
# The file where to write the terraform state content
output_file = "<FILL_DECRYPTED_STATE_FILE_PATH>"
#############
# ALGORITHM #
#############
# Compute encryption key
key = OpenSSL::HMAC.digest('SHA256', db_key_base, project_id)
# Generate LockBox tool
# https://github.com/ankane/lockbox
lockbox = Lockbox.new(key: key)
encrypted_state_content = File.binread(input_file)
state_content = lockbox.decrypt_str(encrypted_state_content)
File.write(output_file, state_content)
I'm not an expert in Ruby but here is a simple bash script to setup the correct environment :
#!/bin/bash
rails new myapp
cd myapp/
echo 'gem "lockbox"' >> Gemfile
bundle install
bundle exec rails runner decode.rb