Skip to content

Commit 3c9b64f

Browse files
SweetOpsosterman
authored andcommitted
feat: add policy for preventing unencrypted uploads, add new output, add possibility to create tfstate-backend by terraform (#17)
* feat: add policy for preventing unencrypted uploads, add new output, add possibility to create tfstate-backend by terraform * docs: regenerate README.md
1 parent 647f942 commit 3c9b64f

File tree

7 files changed

+166
-4
lines changed

7 files changed

+166
-4
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ We literally have [*hundreds of terraform modules*][terraform_modules] that are
6464
}
6565
6666
module "terraform_state_backend" {
67-
source = "git::https://github.com/cloudposse/terraform- aws-tfstate-backend.git?ref=master"
67+
source = "git::https://github.com/cloudposse/terraform-aws-tfstate-backend.git?ref=master"
6868
namespace = "cp"
6969
stage = "prod"
7070
name = "terraform"
@@ -136,12 +136,19 @@ Available targets:
136136
| mfa_delete | A boolean that indicates that versions of S3 objects can only be deleted with MFA. ( Terraform cannot apply changes of this value; https://github.com/terraform-providers/terraform-provider-aws/issues/629 ) | string | `false` | no |
137137
| name | Solution name, e.g. 'app' or 'jenkins' | string | `terraform` | no |
138138
| namespace | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | string | `` | no |
139+
| prevent_unencrypted_uploads | Prevent uploads of unencrypted objects to S3 | string | `true` | no |
140+
| profile | AWS profile name as set in the shared credentials file | string | `` | no |
139141
| read_capacity | DynamoDB read capacity units | string | `5` | no |
140142
| regex_replace_chars | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. By default only hyphens, letters and digits are allowed, all other chars are removed | string | `/[^a-zA-Z0-9-]/` | no |
141143
| region | AWS Region the S3 bucket should reside in | string | - | yes |
142144
| restrict_public_buckets | Whether Amazon S3 should restrict public bucket policies for this bucket. | string | `false` | no |
145+
| role_arn | The role to be assumed | string | `` | no |
143146
| stage | Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' | string | `` | no |
144147
| tags | Additional tags (e.g. `map('BusinessUnit','XYZ')` | map | `<map>` | no |
148+
| terraform_backend_config_file_name | Name of terraform backend config file | string | `terraform.tf` | no |
149+
| terraform_backend_config_file_path | The path to terrafrom project directory | string | `` | no |
150+
| terraform_state_file | The path to the state file inside the bucket | string | `terraform.tfstate` | no |
151+
| terraform_version | The minimum required terraform version | string | `0.11.3` | no |
145152
| write_capacity | DynamoDB write capacity units | string | `5` | no |
146153

147154
## Outputs
@@ -154,6 +161,7 @@ Available targets:
154161
| s3_bucket_arn | S3 bucket ARN |
155162
| s3_bucket_domain_name | S3 bucket domain name |
156163
| s3_bucket_id | S3 bucket ID |
164+
| terraform_backend_config | Rendered Terraform backend config file |
157165

158166

159167

@@ -291,15 +299,17 @@ Check out [our other projects][github], [follow us on twitter][twitter], [apply
291299

292300
### Contributors
293301

294-
| [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]<br/>[Andriy Knysh][aknysh_homepage] | [![Erik Osterman][osterman_avatar]][osterman_homepage]<br/>[Erik Osterman][osterman_homepage] | [![Maarten van der Hoef][maartenvanderhoef_avatar]][maartenvanderhoef_homepage]<br/>[Maarten van der Hoef][maartenvanderhoef_homepage] |
295-
|---|---|---|
302+
| [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]<br/>[Andriy Knysh][aknysh_homepage] | [![Erik Osterman][osterman_avatar]][osterman_homepage]<br/>[Erik Osterman][osterman_homepage] | [![Maarten van der Hoef][maartenvanderhoef_avatar]][maartenvanderhoef_homepage]<br/>[Maarten van der Hoef][maartenvanderhoef_homepage] | [![Vladimir][SweetOps_avatar]][SweetOps_homepage]<br/>[Vladimir][SweetOps_homepage] |
303+
|---|---|---|---|
296304

297305
[aknysh_homepage]: https://github.com/aknysh
298306
[aknysh_avatar]: https://github.com/aknysh.png?size=150
299307
[osterman_homepage]: https://github.com/osterman
300308
[osterman_avatar]: https://github.com/osterman.png?size=150
301309
[maartenvanderhoef_homepage]: https://github.com/maartenvanderhoef
302310
[maartenvanderhoef_avatar]: https://github.com/maartenvanderhoef.png?size=150
311+
[SweetOps_homepage]: https://github.com/SweetOps
312+
[SweetOps_avatar]: https://github.com/SweetOps.png?size=150
303313

304314

305315

README.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ usage: |-
6464
}
6565
6666
module "terraform_state_backend" {
67-
source = "git::https://github.com/cloudposse/terraform- aws-tfstate-backend.git?ref=master"
67+
source = "git::https://github.com/cloudposse/terraform-aws-tfstate-backend.git?ref=master"
6868
namespace = "cp"
6969
stage = "prod"
7070
name = "terraform"
@@ -115,3 +115,5 @@ contributors:
115115
github: "osterman"
116116
- name: "Maarten van der Hoef"
117117
github: "maartenvanderhoef"
118+
- name: "Vladimir"
119+
github: "SweetOps"

docs/terraform.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@
1717
| mfa_delete | A boolean that indicates that versions of S3 objects can only be deleted with MFA. ( Terraform cannot apply changes of this value; https://github.com/terraform-providers/terraform-provider-aws/issues/629 ) | string | `false` | no |
1818
| name | Solution name, e.g. 'app' or 'jenkins' | string | `terraform` | no |
1919
| namespace | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | string | `` | no |
20+
| prevent_unencrypted_uploads | Prevent uploads of unencrypted objects to S3 | string | `true` | no |
21+
| profile | AWS profile name as set in the shared credentials file | string | `` | no |
2022
| read_capacity | DynamoDB read capacity units | string | `5` | no |
2123
| regex_replace_chars | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. By default only hyphens, letters and digits are allowed, all other chars are removed | string | `/[^a-zA-Z0-9-]/` | no |
2224
| region | AWS Region the S3 bucket should reside in | string | - | yes |
2325
| restrict_public_buckets | Whether Amazon S3 should restrict public bucket policies for this bucket. | string | `false` | no |
26+
| role_arn | The role to be assumed | string | `` | no |
2427
| stage | Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' | string | `` | no |
2528
| tags | Additional tags (e.g. `map('BusinessUnit','XYZ')` | map | `<map>` | no |
29+
| terraform_backend_config_file_name | Name of terraform backend config file | string | `terraform.tf` | no |
30+
| terraform_backend_config_file_path | The path to terrafrom project directory | string | `` | no |
31+
| terraform_state_file | The path to the state file inside the bucket | string | `terraform.tfstate` | no |
32+
| terraform_version | The minimum required terraform version | string | `0.11.3` | no |
2633
| write_capacity | DynamoDB write capacity units | string | `5` | no |
2734

2835
## Outputs
@@ -35,4 +42,5 @@
3542
| s3_bucket_arn | S3 bucket ARN |
3643
| s3_bucket_domain_name | S3 bucket domain name |
3744
| s3_bucket_id | S3 bucket ID |
45+
| terraform_backend_config | Rendered Terraform backend config file |
3846

main.tf

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
locals {
2+
prevent_unencrypted_uploads = "${var.prevent_unencrypted_uploads == "true" && var.enable_server_side_encryption == "true" ? 1 : 0}"
3+
policy = "${local.prevent_unencrypted_uploads ? join("", data.aws_iam_policy_document.prevent_unencrypted_uploads.*.json) : ""}"
4+
terraform_backend_config_file = "${format("%s/%s", var.terraform_backend_config_file_path, var.terraform_backend_config_file_name)}"
5+
}
6+
17
module "base_label" {
28
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.6.3"
39
namespace = "${var.namespace}"
@@ -18,11 +24,72 @@ module "s3_bucket_label" {
1824
context = "${module.base_label.context}"
1925
}
2026

27+
data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
28+
count = "${local.prevent_unencrypted_uploads}"
29+
30+
statement = {
31+
sid = "DenyIncorrectEncryptionHeader"
32+
33+
effect = "Deny"
34+
35+
principals {
36+
identifiers = ["*"]
37+
type = "AWS"
38+
}
39+
40+
actions = [
41+
"s3:PutObject",
42+
]
43+
44+
resources = [
45+
"arn:aws:s3:::${module.s3_bucket_label.id}/*",
46+
]
47+
48+
condition {
49+
test = "StringNotEquals"
50+
variable = "s3:x-amz-server-side-encryption"
51+
52+
values = [
53+
"AES256",
54+
]
55+
}
56+
}
57+
58+
statement = {
59+
sid = "DenyUnEncryptedObjectUploads"
60+
61+
effect = "Deny"
62+
63+
principals {
64+
identifiers = ["*"]
65+
type = "AWS"
66+
}
67+
68+
actions = [
69+
"s3:PutObject",
70+
]
71+
72+
resources = [
73+
"arn:aws:s3:::${module.s3_bucket_label.id}/*",
74+
]
75+
76+
condition {
77+
test = "Null"
78+
variable = "s3:x-amz-server-side-encryption"
79+
80+
values = [
81+
"true",
82+
]
83+
}
84+
}
85+
}
86+
2187
resource "aws_s3_bucket" "default" {
2288
bucket = "${module.s3_bucket_label.id}"
2389
acl = "${var.acl}"
2490
region = "${var.region}"
2591
force_destroy = "${var.force_destroy}"
92+
policy = "${local.policy}"
2693

2794
versioning {
2895
enabled = true
@@ -95,3 +162,24 @@ resource "aws_dynamodb_table" "without_server_side_encryption" {
95162

96163
tags = "${module.dynamodb_table_label.tags}"
97164
}
165+
166+
data "template_file" "terraform_backend_config" {
167+
template = "${file("${path.module}/templates/terraform.tf.tpl")}"
168+
169+
vars {
170+
region = "${var.region}"
171+
bucket = "${aws_s3_bucket.default.id}"
172+
dynamodb_table = "${element(coalescelist(aws_dynamodb_table.with_server_side_encryption.*.name, aws_dynamodb_table.without_server_side_encryption.*.name), 0)}"
173+
encrypt = "${var.enable_server_side_encryption == "true" ? "true" : "false"}"
174+
role_arn = "${var.role_arn}"
175+
profile = "${var.profile}"
176+
terraform_version = "${var.terraform_version}"
177+
terraform_state_file = "${var.terraform_state_file}"
178+
}
179+
}
180+
181+
resource "local_file" "terraform_backend_config" {
182+
count = "${length(var.terraform_backend_config_file_path) > 0 ? 1 : 0}"
183+
content = "${data.template_file.terraform_backend_config.rendered}"
184+
filename = "${local.terraform_backend_config_file}"
185+
}

output.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ output "dynamodb_table_arn" {
2727
value = "${element(coalescelist(aws_dynamodb_table.with_server_side_encryption.*.arn, aws_dynamodb_table.without_server_side_encryption.*.arn), 0)}"
2828
description = "DynamoDB table ARN"
2929
}
30+
31+
output "terraform_backend_config" {
32+
value = "${data.template_file.terraform_backend_config.rendered}"
33+
description = "Rendered Terraform backend config file"
34+
}

templates/terraform.tf.tpl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
terraform {
2+
required_version = ">= ${terraform_version}"
3+
4+
backend "s3" {
5+
region = "${region}"
6+
bucket = "${bucket}"
7+
key = "${terraform_state_file}"
8+
dynamodb_table = "${dynamodb_table}"
9+
profile = "${profile}"
10+
role_arn = "${role_arn}"
11+
encrypt = "${encrypt}"
12+
}
13+
}

variables.tf

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,39 @@ variable "regex_replace_chars" {
119119
default = "/[^a-zA-Z0-9-]/"
120120
description = "Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. By default only hyphens, letters and digits are allowed, all other chars are removed"
121121
}
122+
123+
variable "prevent_unencrypted_uploads" {
124+
type = "string"
125+
default = "true"
126+
description = "Prevent uploads of unencrypted objects to S3"
127+
}
128+
129+
variable "profile" {
130+
default = ""
131+
description = "AWS profile name as set in the shared credentials file"
132+
}
133+
134+
variable "role_arn" {
135+
default = ""
136+
description = "The role to be assumed"
137+
}
138+
139+
variable "terraform_backend_config_file_name" {
140+
default = "terraform.tf"
141+
description = "Name of terraform backend config file"
142+
}
143+
144+
variable "terraform_backend_config_file_path" {
145+
default = ""
146+
description = "The path to terrafrom project directory"
147+
}
148+
149+
variable "terraform_version" {
150+
default = "0.11.3"
151+
description = "The minimum required terraform version"
152+
}
153+
154+
variable "terraform_state_file" {
155+
default = "terraform.tfstate"
156+
description = "The path to the state file inside the bucket"
157+
}

0 commit comments

Comments
 (0)