Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

Vault and IAM integration #208

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

### Modules

* `iam-user-policy`: Module for IAM user and corresponding policy

### Examples

* `vault-iam`: New example which creates a admin user for vault with policy suitable for it.
* `vault-s3-private`: New example showing vault and IAM integration with restricted access.

# v0.8.2

Expand Down
43 changes: 43 additions & 0 deletions examples/vault-iam/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.PHONY: init plan apply destroy clean

.DEFAULT_GOAL = help

# Hardcoding value of 3 minutes when we check if the plan file is stale
STALE_PLAN_FILE := `find "tf.out" -mmin -3 | grep -q tf.out`

## Check if tf.out is stale (Older than 2 minutes)
check-plan-file:
@if ! ${STALE_PLAN_FILE} ; then \
echo "ERROR: Stale tf.out plan file (older than 3 minutes)!"; \
exit 1; \
fi

## Runs terraform get and terraform init for env
init:
@terraform get
@terraform init

## use 'terraform plan' to map out updates to apply
plan:
@terraform plan -out=tf.out

## use 'terraform apply' to apply updates in a 'tf.out' plan file
apply: check-plan-file
@terraform apply tf.out

## use 'terraform destroy' to remove all resources from AWS
destroy:
@terraform destroy

## Show help screen.
help:
@echo "Please use \`make <target>' where <target> is one of\n\n"
@awk '/^[a-zA-Z\-\_0-9]+:/ { \
helpMessage = match(lastLine, /^## (.*)/); \
if (helpMessage) { \
helpCommand = substr($$1, 0, index($$1, ":")-1); \
helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
printf "%-30s %s\n", helpCommand, helpMessage; \
} \
} \
{ lastLine = $$0 }' $(MAKEFILE_LIST)
10 changes: 10 additions & 0 deletions examples/vault-iam/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Vault IAM user

When setting up vault with AWS as it's secret engine, you need to have
AWS secret and access keys for an IAM user with relevant
permission. This example creates IAM user named "vault_user" and
appropriate policy for it.

In order for this example to create keys, you need to modify
`variables.tf` appropriately.

43 changes: 43 additions & 0 deletions examples/vault-iam/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
resource "aws_iam_access_key" "vaultkey" {
user = module.vault_iam_user_policy.user_name
count = var.key_count
}

data "aws_caller_identity" "current" {}

module "vault_iam_user_policy" {
source = "../../modules/iam-user-policy/"
user_name = "vault_user"
environment = "dev"
iam_policy_name = "vault_user_policy"
iam_user_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:AttachUserPolicy",
"iam:CreateAccessKey",
"iam:CreateUser",
"iam:DeleteAccessKey",
"iam:DeleteUser",
"iam:DeleteUserPolicy",
"iam:DetachUserPolicy",
"iam:ListAccessKeys",
"iam:ListAttachedUserPolicies",
"iam:ListGroupsForUser",
"iam:ListUserPolicies",
"iam:PutUserPolicy",
"iam:RemoveUserFromGroup"
],
"Resource": [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/vtest-*"
]
}
]
}
EOF

}

8 changes: 8 additions & 0 deletions examples/vault-iam/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
output "secret_key" {
value = [aws_iam_access_key.vaultkey.*.secret]
}

output "access_id" {
value = [aws_iam_access_key.vaultkey.*.id]
}

5 changes: 5 additions & 0 deletions examples/vault-iam/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
variable "key_count" {
description = "Set to 1 to optionally create access keys for the created IAM user"
default = "0"
}

49 changes: 49 additions & 0 deletions examples/vault-s3-private/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.PHONY: init plan apply destroy clean

.DEFAULT_GOAL = help

# Hardcoding value of 3 minutes when we check if the plan file is stale
STALE_PLAN_FILE := `find "tf.out" -mmin -3 | grep -q tf.out`

## Check if tf.out is stale (Older than 2 minutes)
check-plan-file:
@if ! ${STALE_PLAN_FILE} ; then \
echo "ERROR: Stale tf.out plan file (older than 3 minutes)!"; \
exit 1; \
fi

## Runs terraform get and terraform init for env
init:
@terraform get
@terraform init

## use 'terraform plan' to map out updates to apply
plan:
@terraform plan -out=tf.out

## use 'terraform apply' to apply updates in a 'tf.out' plan file
apply: check-plan-file
@terraform apply tf.out

## use 'terraform destroy' to remove all resources from AWS
destroy:
@terraform destroy

## rm -rf all files and state
clean:
@rm -f tf.out
@rm -f terraform.*.backup
@rm -f terraform.tfstate

## Show help screen.
help:
@echo "Please use \`make <target>' where <target> is one of\n\n"
@awk '/^[a-zA-Z\-\_0-9]+:/ { \
helpMessage = match(lastLine, /^## (.*)/); \
if (helpMessage) { \
helpCommand = substr($$1, 0, index($$1, ":")-1); \
helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
printf "%-30s %s\n", helpCommand, helpMessage; \
} \
} \
{ lastLine = $$0 }' $(MAKEFILE_LIST)
128 changes: 128 additions & 0 deletions examples/vault-s3-private/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Example showing Vault and IAM Integration

This example creates a private s3 bucket resources. It then uses vault
to create keys which only has access to those s3 buckets. The example
code will create an IAM role with access to that bucket and will also
configure vault so that we can dynamically generate credentials for
accessing that bucket.

## Requirements

These are the required things for this example:

* A running vault server. If you just want to experiment with this,
run a development server using:

``` shellsession
vault server -dev
```

* The AWS access and secret keys for an IAM user which the AWS Secret
Backend for Vault will use for issuing new credentials. If you don't
have any, you can create one using [vault-iam
example](../vault-iam). You need to put the access keys in
[variables.tf](./variables.tf)


## Environment creation and deployment

``` shellsession
$ make init
$ make plan
$ make apply
module.vault_aws_backend.vault_aws_secret_backend.aws: Creating...
module.vault_aws_backend.vault_aws_secret_backend.aws: Creation complete after 0s [id=fpco/aws/dev/vault]
aws_iam_role.vault_bucket_role: Creating...
aws_s3_bucket.vault-test-bucket: Creating...
aws_iam_role.vault_bucket_role: Still creating... [10s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [10s elapsed]
aws_iam_role.vault_bucket_role: Still creating... [20s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [20s elapsed]
aws_iam_role.vault_bucket_role: Creation complete after 22s [id=bucket_access_role]
module.vault_aws_backend.vault_aws_secret_backend_role.aws_role: Creating...
module.vault_aws_backend.vault_aws_secret_backend_role.aws_role: Creation complete after 0s [id=fpco/aws/dev/vault/roles/s3_app_user]
aws_s3_bucket.vault-test-bucket: Still creating... [30s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [40s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [50s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [1m0s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [1m10s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [1m20s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [1m30s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [1m40s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [1m50s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [2m0s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [2m10s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [2m20s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [2m30s elapsed]
aws_s3_bucket.vault-test-bucket: Still creating... [2m40s elapsed]
aws_s3_bucket.vault-test-bucket: Creation complete after 2m48s [id=vault-fpco-test-bucket]
aws_iam_role_policy.vault_bucket_policy: Creating...
aws_iam_role_policy.vault_bucket_policy: Still creating... [10s elapsed]
aws_iam_role_policy.vault_bucket_policy: Still creating... [20s elapsed]
aws_iam_role_policy.vault_bucket_policy: Creation complete after 24s [id=bucket_access_role:bucket-policy]

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate
```

## Testing

Make sure you are already authorized with the vault server. If not,
use `vault login` to do it. And then, you can dynamically create AWS
credentials for accessing the s3 bucket you created:

``` shellsession
$ vault read fpco/aws/dev/vault/creds/s3_app_user
Key Value
--- -----
lease_id fpco/aws/prod/vault/creds/s3_app_user/eJcLUNbpTNRFpLoTL9mEW76p
lease_duration 14m59s
lease_renewable false
access_key xxx
secret_key xxx
security_token xxx
```

Now let's try to see all the files in our bucket:

``` shellsession
$ env AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx AWS_SESSION_TOKEN=xxx aws s3 ls s3://s3-vault-demo-dev-bucket
```

It gives you no output since there are no files. But the command
works, which confirms us that the generated credentials are working as
expected.

Now let's try to do something for which you don't have access with the
same credentials:

``` shellsession
$ env AWS_ACCESS_KEY_ID=xxxx AWS_SECRET_ACCESS_KEY=xxxx AWS_SESSION_TOKEN=xxx aws ec2 describe-instances --region="us-east-2"
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
```

That doesn't work, which is expected. Let's try to see if we can
access files of some other buckets which is present:

``` shellsession
$ env AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx AWS_SESSION_TOKEN=xxx aws s3 ls s3://some-other-existing-bucket
An error occurred (AccessDenied) when calling the ListObjects operation: Access Denied
```

## Destruction

``` shellsession
$ make destroy
$ make clean
```

## Notes

- This example was last tested with `Terraform v0.12.3`
- This example assumes AWS credentials setup with access to the **us-east-2** region.
77 changes: 77 additions & 0 deletions examples/vault-s3-private/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
data "aws_region" "current" {}

locals {
name_prefix = "${var.application}-${var.environment}"
region = data.aws_region.current.name
}

resource "aws_s3_bucket" "vault-test-bucket" {
bucket = "${local.name_prefix}-bucket"
acl = "private"
region = local.region

tags = {
Environment = var.environment
}
}

# Here we allow everyone to assume this role. In production systems
# it's best to restrict it's scope so that only some IAM users are
# able to assume this role.
resource "aws_iam_role" "vault_bucket_role" {
name = "${local.name_prefix}-bucket-role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "*"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF


tags = {
Environment = var.environment
}
}

resource "aws_iam_role_policy" "vault_bucket_policy" {
name = "${local.name_prefix}-bucket-policy"
role = aws_iam_role.vault_bucket_role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*", "iam:CreateAccessKey"],
"Resource": ["${aws_s3_bucket.vault-test-bucket.arn}"]
}
]
}
EOF

}

module "vault_aws_backend" {
source = "../../modules/vault-aws-backend/"
vault_address = var.vault_address
vault_token = var.vault_token
secret_backend_path = var.secret_backend_path
default_lease_ttl_seconds = var.default_lease_ttl_seconds
max_lease_ttl_seconds = var.max_lease_ttl_seconds
credential_type = var.credential_type
role_name = var.role_name
role_arn = aws_iam_role.vault_bucket_role.arn
access_key = var.access_key
secret_key = var.secret_key
}

Loading