Skip to content

Latest commit

 

History

History
262 lines (220 loc) · 19.9 KB

README.md

File metadata and controls

262 lines (220 loc) · 19.9 KB

AWS IaC Bootstrapping

Terraform remote state backend on AWS, using discovery-resistant naming patterns and ephemeral local states.

  • Single file ignition.
  • Globally addressable resource names (S3) are fully randomized.
  • State store and lock table reside in home region, and are encrypted with customer managed multi-region KMS keys.
  • State, lock, and keys are replicated to secondary region.
    Keys are additionally replicated to a "keystore region".
  • Resulting identifiers are stored in KMS encrypted SSM parameters.
  • State and replica have distinct log buckets in their respective regions.
    Logs are encrypted and replicated cross-region.
  • State version history and log history are maintained through lifecycle management.

Note

All resources are locked down through resource-based policies, as applicable. For S3 buckets:

  • Require state requests to originate from MFA-authenticated sessions.
  • Require all requests to use TLS v1.3 (or better).
  • Prevent all PUT and DELETE requests on replication targets.
  • Require all objects to be encrypted exclusively with our key.
  • The primary state store additionally requires MFA-authenticated sessions to not be older than 1 hour.

Out of scope (for now):

  • Custom key store
  • Custom key material
  • Multiple state replication regions
  • Cross-account replication
  • History object lock (in planning)

Init

Create an AWS CLI SSO profile for your account, or whatever you have to do to commandeer the account :shipit:

Setup

terraform init
terraform apply
terraform output seed
# Optional: Display a backend configuration.
./display-backend.tf.sh

Important

Take note of the seed output. This is crucial to restore the state later. Keep it safe.

Pre-Seeding

To generate the seed outside of the first infrastructure plan generation, -target the resource.

terraform init
terraform apply -refresh=false -target=random_id.seed
terraform apply

Restore

Restore the state by providing the seed that was used to create it.

cd import
terraform init
# Ensure AWS_PROFILE and AWS_REGION are set appropriately.
# Prefix command with space to prevent history entry.
 terraform import random_id.seed oCxD1aYEn4eSQXIObCAQZd6KpN_5-82G8_7PGYvXvmo
terraform apply

Post-Deployment Validation

State Restore

Expect success

# Note seed.
terraform output -json seed | jq --raw-output '.id'
# Delete state.
rm *.tfstate*
 terraform import random_id.seed oCxD1aYEn4eSQXIObCAQZd6KpN_5-82G8_7PGYvXvmo
terraform apply

S3 State Bucket Initiator Write Access

Expect success

# Write flag to state bucket
echo "$(date) $(whoami)@$(hostname):$PWD" | aws s3 cp - s3://$(terraform output -json s3 | jq --raw-output '.state.id')/flag.txt --sse=aws:kms --sse-kms-key-id=$(terraform output -json kms | jq --raw-output '.state.id')
# Verify
aws s3 cp s3://$(terraform output -json s3 | jq --raw-output '.state.id')/flag.txt -

S3 State Replication

Expect success

# Write flag to state bucket
echo "$(date) $(whoami)@$(hostname):$PWD" | aws s3 cp - s3://$(terraform output -json s3 | jq --raw-output '.state.id')/flag.txt --sse=aws:kms --sse-kms-key-id=$(terraform output -json kms | jq --raw-output '.state.id')
# Verify on replica
aws s3 cp s3://$(terraform output -json s3 | jq --raw-output '.replica.id')/flag.txt -

S3 State Replica Bucket Initiator Write Denial

Expect failure

# Write flag to replica bucket
echo "$(date) $(whoami)@$(hostname):$PWD" | aws s3 cp - s3://$(terraform output -json s3 | jq --raw-output '.replica.id')/flag.txt --sse=aws:kms
# Replace flag with own
echo "$(date) CAPTURE" | aws s3 cp - s3://$(terraform output -json s3 | jq --raw-output '.replica.id')/flag.txt --sse=aws:kms
# Delete flag
aws s3 rm s3://$(terraform output -json s3 | jq --raw-output '.replica.id')/flag.txt --sse=aws:kms

Terraform Implementation Spec

Requirements

Name Version
terraform >=1.5.9
aws ~>5.84.0
random ~>3.6.3

Providers

Name Version
aws 5.84.0
aws.keystore 5.84.0
aws.replica 5.84.0
random 3.6.3

Modules

No modules.

Resources

Name Type
aws_dynamodb_resource_policy.lock resource
aws_dynamodb_resource_policy.lock_replica resource
aws_dynamodb_table.lock resource
aws_dynamodb_table_replica.lock resource
aws_iam_policy.state_logs_replicator resource
aws_iam_policy.state_manager resource
aws_iam_policy.state_observer resource
aws_iam_policy.state_replicator resource
aws_iam_role.state_logs_replicator resource
aws_iam_role.state_manager resource
aws_iam_role.state_observer resource
aws_iam_role.state_replicator resource
aws_iam_role_policy_attachment.state_logs_replicator resource
aws_iam_role_policy_attachment.state_replicator resource
aws_kms_alias.lock resource
aws_kms_alias.lock_replica resource
aws_kms_alias.logs resource
aws_kms_alias.logs_replica resource
aws_kms_alias.ssm resource
aws_kms_alias.ssm_replica resource
aws_kms_alias.state resource
aws_kms_alias.state_replica resource
aws_kms_key.lock resource
aws_kms_key.logs resource
aws_kms_key.ssm resource
aws_kms_key.state resource
aws_kms_replica_key.lock resource
aws_kms_replica_key.lock_keystore resource
aws_kms_replica_key.logs resource
aws_kms_replica_key.logs_keystore resource
aws_kms_replica_key.ssm resource
aws_kms_replica_key.ssm_keystore resource
aws_kms_replica_key.state resource
aws_kms_replica_key.state_keystore resource
aws_s3_bucket.replica resource
aws_s3_bucket.replica_logs resource
aws_s3_bucket.state resource
aws_s3_bucket.state_logs resource
aws_s3_bucket_acl.replica resource
aws_s3_bucket_acl.replica_logs resource
aws_s3_bucket_acl.state resource
aws_s3_bucket_acl.state_logs resource
aws_s3_bucket_lifecycle_configuration.replica resource
aws_s3_bucket_lifecycle_configuration.replica_logs resource
aws_s3_bucket_lifecycle_configuration.state resource
aws_s3_bucket_lifecycle_configuration.state_logs resource
aws_s3_bucket_logging.replica resource
aws_s3_bucket_logging.state resource
aws_s3_bucket_metric.state resource
aws_s3_bucket_metric.state_logs resource
aws_s3_bucket_ownership_controls.replica resource
aws_s3_bucket_ownership_controls.replica_logs resource
aws_s3_bucket_ownership_controls.state resource
aws_s3_bucket_ownership_controls.state_logs resource
aws_s3_bucket_policy.replica resource
aws_s3_bucket_policy.replica_logs resource
aws_s3_bucket_policy.state resource
aws_s3_bucket_policy.state_logs resource
aws_s3_bucket_public_access_block.replica resource
aws_s3_bucket_public_access_block.replica_logs resource
aws_s3_bucket_public_access_block.state resource
aws_s3_bucket_public_access_block.state_logs resource
aws_s3_bucket_replication_configuration.state resource
aws_s3_bucket_replication_configuration.state_logs resource
aws_s3_bucket_server_side_encryption_configuration.replica resource
aws_s3_bucket_server_side_encryption_configuration.replica_logs resource
aws_s3_bucket_server_side_encryption_configuration.state resource
aws_s3_bucket_server_side_encryption_configuration.state_logs resource
aws_s3_bucket_versioning.replica resource
aws_s3_bucket_versioning.replica_logs resource
aws_s3_bucket_versioning.state resource
aws_s3_bucket_versioning.state_logs resource
aws_ssm_parameter.lock_table resource
aws_ssm_parameter.state_bucket resource
aws_ssm_parameter.state_bucket_key resource
random_id.seed resource
aws_caller_identity.current data source
aws_iam_policy_document.assume_role_caller data source
aws_iam_policy_document.assume_role_s3 data source
aws_iam_policy_document.lock_key data source
aws_iam_policy_document.lock_lockdown data source
aws_iam_policy_document.lock_replica_lockdown data source
aws_iam_policy_document.logs_key data source
aws_iam_policy_document.replica_lockdown data source
aws_iam_policy_document.replica_logs_lockdown data source
aws_iam_policy_document.ssm_key data source
aws_iam_policy_document.state_key data source
aws_iam_policy_document.state_lockdown data source
aws_iam_policy_document.state_logs_lockdown data source
aws_iam_policy_document.state_logs_replicator data source
aws_iam_policy_document.state_manager data source
aws_iam_policy_document.state_observer data source
aws_iam_policy_document.state_replicator data source
aws_partition.current data source
aws_region.current data source
aws_region.replica data source

Inputs

Name Description Type Default Required
force_namespace We expect that only a single IaC state is used per AWS account, as anything else just raises potential for conflicts. Thus, account-local resources are created with a fixed name to make them easier to discover.

If you absolutely require multiple IaC states in one account, you can set this variable to true to have all resources namespaced.
bool false no

Outputs

Name Description
dynamodb Details about the created DynamoDB state lock table.
iam ARN of the IAM policy that allows management access to the state resources.
kms Details about Server-Side-Encryption keys for created resources.
s3 Details about all created S3 buckets.
seed Cryptographic seed of this backend deployment.
You need this to recover the state at a later point in time.
ssm Details about SSM parameters in the account, which hold the names of the created resources.