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
1brew install onefootprint/tap/footprint-dr

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 Output
1$ 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:

terraform
1resource "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:

terraform
1resource "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 Output
1$ 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:

terraform
1locals {
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:

bash
1# 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:

bash
1ykman 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:

bash
1age-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:

bash
1age-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 Output
1$ 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.