Onboarding
6 min read
By the end, Footprint continuously backs up your vaulted data, encrypted, to an S3 bucket you own, and the org keys that unlock a recovery live on YubiKeys you control.
Before you start
- An AWS account and permission to configure AWS resources.
- The Admin role in the Footprint dashboard.
- A YubiKey that supports the PIV card interface over USB, such as a YubiKey 5C. Amazon often has the YubiKey 5C NFC available for fast delivery.
1. Install Vault Disaster Recovery tools
The footprint-dr CLI handles enrollment and decryption. On a Mac, install it with Homebrew:
bash
For other platforms, download a release from footprint-dr-releases.
2. Create an API key and log in with the CLI
Open the API keys page in the Footprint dashboard and use the toggle in the top right corner to select Sandbox or Production, whichever data set you are enrolling.
Create an API key with an admin scope.
Run footprint-dr login [--sandbox/--live] and paste the API key at the prompt:
Example Output1$ footprint-dr login --live 2Enter Footprint Live API key: <hidden>
3. Create a bucket for encrypted data storage
Footprint stores the encrypted data in a dedicated S3 bucket you own. Create one like the Terraform example below, in the us-east-1 region for the best performance:
terraform1resource "aws_s3_bucket" "fp_vault_data" { 2 bucket = "acme-inc-footprint-vault-data" 3 4} 5 6resource "aws_s3_bucket_public_access_block" "fp_vault_data_public_access_block" { 7 bucket = aws_s3_bucket.fp_vault_data.id 8 9 block_public_acls = true 10 block_public_policy = true 11 ignore_public_acls = true 12 restrict_public_buckets = true 13}
We recommend a dedicated AWS account with tight access controls for this bucket. The fewer principals that can read it, the less an unintentional leak of an org private key exposes.
We recommend CloudTrail audit logs for data events on the bucket as well. This Terraform creates the trail and its log bucket:
terraform1resource "aws_s3_bucket" "fp_vault_data_cloudtrail" { 2 bucket = "acme-inc-footprint-vault-data-cloudtrail" 3} 4 5resource "aws_s3_bucket_public_access_block" "fp_vault_data_cloudtrail_pab" { 6 bucket = aws_s3_bucket.fp_vault_data_cloudtrail.id 7 8 block_public_acls = true 9 block_public_policy = true 10 ignore_public_acls = true 11 restrict_public_buckets = true 12} 13 14locals { 15 fp_vault_data_cloudtrail_name = "footprint-vault-data-cloudtrail" 16} 17 18data "aws_iam_policy_document" "fp_vault_data_cloudtrail" { 19 statement { 20 sid = "AWSCloudTrailAclCheck" 21 effect = "Allow" 22 23 principals { 24 type = "Service" 25 identifiers = ["cloudtrail.amazonaws.com"] 26 } 27 28 actions = ["s3:GetBucketAcl"] 29 resources = [aws_s3_bucket.fp_vault_data_cloudtrail.arn] 30 condition { 31 test = "StringEquals" 32 variable = "aws:SourceArn" 33 values = ["arn:${data.aws_partition.current.partition}:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/${local.fp_vault_data_cloudtrail_name}"] 34 } 35 } 36 37 statement { 38 sid = "AWSCloudTrailWrite" 39 effect = "Allow" 40 41 principals { 42 type = "Service" 43 identifiers = ["cloudtrail.amazonaws.com"] 44 } 45 46 actions = ["s3:PutObject"] 47 resources = ["${aws_s3_bucket.fp_vault_data_cloudtrail.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"] 48 49 condition { 50 test = "StringEquals" 51 variable = "s3:x-amz-acl" 52 values = ["bucket-owner-full-control"] 53 } 54 condition { 55 test = "StringEquals" 56 variable = "aws:SourceArn" 57 values = ["arn:${data.aws_partition.current.partition}:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/${local.fp_vault_data_cloudtrail_name}"] 58 } 59 } 60} 61 62resource "aws_s3_bucket_policy" "fp_vault_data_cloudtrail" { 63 bucket = aws_s3_bucket.fp_vault_data_cloudtrail.id 64 policy = data.aws_iam_policy_document.fp_vault_data_cloudtrail.json 65} 66 67resource "aws_cloudtrail" "fp_vault_data_cloudtrail" { 68 name = local.fp_vault_data_cloudtrail_name 69 s3_bucket_name = aws_s3_bucket.fp_vault_data_cloudtrail.id 70 71 include_global_service_events = false 72 73 depends_on = [aws_s3_bucket_policy.fp_vault_data_cloudtrail] 74 75 event_selector { 76 read_write_type = "All" 77 include_management_events = true 78 79 data_resource { 80 type = "AWS::S3::Object" 81 82 values = ["${aws_s3_bucket.fp_vault_data.arn}/"] 83 } 84 } 85}
4. Fetch your external ID
The external ID secures the cross-account IAM access. Fetch it with footprint-dr get-external-id [--sandbox/--live]:
Example Output1$ footprint-dr get-external-id --live 242ee4f928973996f8f855aebaebf70cd
5. Create an IAM role for bucket management
Footprint needs read and write access to the bucket to manage the encrypted data. Create an IAM role that delegates that access, like the one below, substituting your external ID:
terraform1locals { 2 external_id = "42ee4f928973996f8f855aebaebf70cd" 3} 4 5resource "aws_iam_role" "fp_vault_data_management" { 6 name = "fp-vault-data-management" 7 8 assume_role_policy = data.aws_iam_policy_document.fp_vault_data_management_assume_role_policy.json 9} 10 11data "aws_iam_policy_document" "fp_vault_data_management_assume_role_policy" { 12 statement { 13 actions = ["sts:AssumeRole"] 14 15 principals { 16 type = "AWS" 17 identifiers = ["725896863556"] 18 } 19 20 condition { 21 test = "StringEquals" 22 variable = "sts:ExternalId" 23 values = [local.external_id] 24 } 25 } 26} 27 28data "aws_iam_policy_document" "fp_vault_data_management_policy" { 29 statement { 30 sid = "AllowPutObject" 31 32 actions = [ 33 "s3:PutObject", 34 ] 35 36 resources = [ 37 "${aws_s3_bucket.fp_vault_data.arn}/*", 38 ] 39 } 40 41 statement { 42 sid = "AllowListBucket" 43 44 actions = [ 45 "s3:ListBucket", 46 ] 47 48 resources = [ 49 aws_s3_bucket.fp_vault_data.arn, 50 ] 51 } 52 53 statement { 54 sid = "AllowGetBucketLocation" 55 56 actions = [ 57 "s3:GetBucketLocation", 58 ] 59 60 resources = [ 61 aws_s3_bucket.fp_vault_data.arn, 62 ] 63 } 64} 65 66resource "aws_iam_policy" "fp_vault_data_management" { 67 name = "fp-vault-data-management" 68 policy = data.aws_iam_policy_document.fp_vault_data_management_policy.json 69} 70 71resource "aws_iam_role_policy_attachment" "fp_vault_data_management_assume_role" { 72 role = aws_iam_role.fp_vault_data_management.name 73 policy_arn = aws_iam_policy.fp_vault_data_management.arn 74}
6. Generate your disaster recovery org key pair
Org identities (private keys) live on one or more YubiKeys. A hardware security token all but removes the risk of accidentally leaking a private key, a sensitive component of the recovery flow. Depending on your data durability requirements, you can register redundant YubiKeys to guard against lost or damaged hardware.
Enrollment uses the age YubiKey plugin. Install age and age-plugin-yubikey on a workstation with a USB port for the YubiKey:
bash1# On a Mac: 2brew install age age-plugin-yubikey ykman
Plug in the YubiKey and change its management key. Press Enter to use the default management key, and enter the default YubiKey PIN 123456 at the prompt:
bash1ykman piv access change-management-key -a TDES --protect
Generate an age identity for your org with a command like the one below. Adjust the name and the mode (sandbox or live) for your own reference. The command prompts you to change your PIN and PUK; choose a PIN and keep it in your password manager, because your YubiKey needs it to decrypt your Vault Disaster Recovery backups:
bash1age-plugin-yubikey \ 2 --generate \ 3 --name "Footprint Vault DR org age identity: Acme Inc. Live" \ 4 --pin-policy once \ 5 --touch-policy cached \ 6 --slot 1
Pick a different slot if slot 1 is in use, though we recommend dedicated YubiKeys for live Vault Disaster Recovery. We recommend the PIN policy once and the touch policy cached, which make the test recovery flow less tedious.
You do not need to save the output, but note the recipient (it starts with age1yubikey); you paste it in the next step. To print it again:
bash1age-plugin-yubikey --list
Repeat the key generation once for each YubiKey you want to register, collecting your org's age1yubikey age recipients.
7. Complete enrollment
Run footprint-dr enroll [--sandbox/--live] and follow the prompts:
Example Output1$ footprint-dr enroll --live 2Enrolling Acme Inc. (Live) in Vault Disaster Recovery. 3 4Enter org public key (age recipient): age1yubikey1qgceu0h4fzsv46jg32gnfz0hf5lnaaqm8wn8skxf33qm0t4v4rz427fh79x 5Add another org public key? [y/n] y 6Enter org public key (age recipient): age1yubikey1q2eggw2hftplqfr27s9h8nwuez39m45ms6qv78m9m6kfhmsyf6gacj2km7u 7Add another org public key? [y/n] n 8 9Enter AWS Account ID: 123456789012 10Enter AWS role name: acme-inc-footprint-disaster-recovery 11Enter S3 bucket name: acme-inc-footprint-encrypted-data 12 13Verifying configuration... OK 14 15Enrollment complete. 16 17Store the following information to locate your encrypted data: 18 S3 Bucket Name: acme-inc-footprint-disaster-recovery 19 Bucket Namespace: a39evoii5rgqhdz4jansho3tten4z0oz
Unplug the YubiKey, label it, and store it somewhere physically secure, such as an office safe. You need it to decrypt disaster recovery data.
Record the printed S3 Bucket Name and Bucket Namespace so you can locate your disaster recovery data later.
Next steps
From here, Operations is the day-to-day: checking backup status, inspecting what is backed up, and testing recovery. If you want to revisit what the keys you generated protect and who can decrypt what, that is the Introduction and security model.