Skip to content

Commit 40e05c8

Browse files
authored
Merge pull request hashicorp#267 from hashicorp/add-tf-code-used-to-generate-Sentinel-mocks
add Terraform code for two Sentinel policies
2 parents 9c72881 + 94cec4d commit 40e05c8

File tree

4 files changed

+332
-0
lines changed

4 files changed

+332
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# AWS S3 Buckets, Bucket Policies, and IAM Policy Documents
2+
3+
The Terraform code in this directory was used to generate some S3 buckets,
4+
S3 bucket policies, and an IAM policy document in order to run a plan and
5+
generate Sentinel mocks for use with the [restrict-s3-bucket-policies.sentinel](../governance/third-generation/restrict-s3-bucket-policies.sentinel)
6+
Sentinel policy.
7+
8+
That policy requires all S3 buckets created with the `aws_s3_bucket` resource and
9+
all S3 bucket policies created with the `aws_s3_bucket_policy` resource to set
10+
their `policy` argument to an instance of the `aws_iam_policy_document` data
11+
source if they set it at all. The policy also restricts policy documents within
12+
`aws_iam_policy_document` data sources that certain S3 actions like "s3:ListBucket",
13+
"s3:GetObject", and "s3:PutObject" to include statements with conditions that
14+
mandate access over HTTPS and from specific VPC endpoints.
15+
16+
Mandating the use of the `aws_iam_policy_document` data source in IAM policies set
17+
in S3 buckets and other AWS resources is useful when using Sentinel since each
18+
element of the policy documents created with that data source are given in their
19+
own distinct attributes. This allows Sentinel to see many elements of the policy
20+
document even if some of them are marked as computed because they refer to
21+
attributes of other resources that won't be known until the apply is run. In
22+
contrast, when you embed a policy directly inside the `policy` attribute of an S3
23+
bucket or S3 bucket policy resource, if any element of the policy refers to an
24+
attribute of some other resource that will not be known until the apply is run,
25+
then the entire policy ends up being marked as computed ("known after apply") and
26+
it is then completely opaque to Sentinel.
27+
28+
The Terraform code in main.tf intentionally creates some buckets and bucket
29+
policies that will fail the restrict-s3-bucket-policies.sentinel policy. When I
30+
created the test cases for that policy, I copied the generated Sentinel mocks
31+
multiple times and then edited them to leave in only the instances of S3 buckets,
32+
S3 bucket policies, and IAM policy documents needed by each test case.
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 3.0"
6+
}
7+
}
8+
}
9+
10+
provider "aws" {
11+
region = "us-east-1"
12+
}
13+
14+
variable "bucket_name" {
15+
description = "Name of the bucket to create"
16+
default = "roger-bucket-0"
17+
}
18+
19+
variable "bucket_acl" {
20+
description = "ACL for S3 bucket: private, public-read, public-read-write, etc"
21+
default = "private"
22+
}
23+
24+
variable "ip_addresses" {
25+
description = "list of prohibited IP address"
26+
default = ["1.1.1.1"]
27+
}
28+
29+
variable "s3_vpce_id" {
30+
description = "S3 VPC endpoint"
31+
default = ""
32+
}
33+
34+
variable "shared_s3_vpce_id" {
35+
description = "Shared S3 VPC endpoint"
36+
default = ""
37+
}
38+
39+
resource "aws_kms_key" "my_key" {
40+
description = "This key is used to encrypt bucket objects"
41+
deletion_window_in_days = 10
42+
}
43+
44+
resource "aws_s3_bucket" "bucket_0" {
45+
bucket = var.bucket_name
46+
acl = var.bucket_acl
47+
48+
policy = <<POLICY
49+
{
50+
"Version":"2012-10-17",
51+
"Statement":[
52+
{
53+
"Sid":"PublicRead",
54+
"Effect":"Allow",
55+
"Principal": "*",
56+
"Action":["s3:GetObject","s3:GetObjectVersion"],
57+
"Resource":["arn:aws:s3:::*"]
58+
}
59+
]
60+
}
61+
POLICY
62+
63+
server_side_encryption_configuration {
64+
rule {
65+
apply_server_side_encryption_by_default {
66+
kms_master_key_id = aws_kms_key.my_key.arn
67+
sse_algorithm = "aws:kms"
68+
}
69+
}
70+
}
71+
72+
}
73+
74+
resource "aws_s3_bucket" "bucket_1" {
75+
bucket = "roger-bucket-1"
76+
acl = var.bucket_acl
77+
78+
server_side_encryption_configuration {
79+
rule {
80+
apply_server_side_encryption_by_default {
81+
kms_master_key_id = aws_kms_key.my_key.arn
82+
sse_algorithm = "aws:kms"
83+
}
84+
}
85+
}
86+
87+
}
88+
89+
resource "aws_s3_bucket" "bucket_2" {
90+
bucket = "roger-bucket-2"
91+
acl = var.bucket_acl
92+
93+
server_side_encryption_configuration {
94+
rule {
95+
apply_server_side_encryption_by_default {
96+
kms_master_key_id = aws_kms_key.my_key.arn
97+
sse_algorithm = "aws:kms"
98+
}
99+
}
100+
}
101+
102+
}
103+
104+
resource "aws_s3_bucket_policy" "bucket_policy_1" {
105+
bucket = aws_s3_bucket.bucket_1.id
106+
policy = data.aws_iam_policy_document.example.json
107+
}
108+
109+
resource "aws_s3_bucket_policy" "bucket_policy_2" {
110+
bucket = aws_s3_bucket.bucket_2.id
111+
policy = <<POLICY
112+
{
113+
"Version":"2012-10-17",
114+
"Statement":[
115+
{
116+
"Sid":"PublicRead",
117+
"Effect":"Allow",
118+
"Principal": "*",
119+
"Action":["s3:GetObject","s3:GetObjectVersion"],
120+
"Resource":["arn:aws:s3:::*"]
121+
}
122+
]
123+
}
124+
POLICY
125+
}
126+
127+
data "aws_iam_policy_document" "example" {
128+
statement {
129+
sid = "Deny HTTP for bucket level operations"
130+
effect = "Deny"
131+
principals {
132+
type = "*"
133+
identifiers = ["*"]
134+
}
135+
actions = [
136+
"s3:ListBucket",
137+
]
138+
resources = [
139+
"arn:aws:s3:::${aws_s3_bucket.bucket_1.id}",
140+
]
141+
condition {
142+
test = "Bool"
143+
variable = "aws:SecureTransport"
144+
values = [
145+
"false",
146+
]
147+
}
148+
}
149+
150+
statement {
151+
sid = "Deny HTTP for object operations"
152+
effect = "Deny"
153+
principals {
154+
type = "*"
155+
identifiers = ["*"]
156+
}
157+
actions = [
158+
"s3:GetObject",
159+
"s3:PutObject",
160+
]
161+
resources = [
162+
"arn:aws:s3:::${aws_s3_bucket.bucket_1.id}",
163+
]
164+
condition {
165+
test = "Bool"
166+
variable = "aws:SecureTransport"
167+
values = [
168+
"false",
169+
]
170+
}
171+
}
172+
173+
statement {
174+
sid = "Deny bucket access not through vpce"
175+
effect = "Deny"
176+
principals {
177+
type = "AWS"
178+
identifiers = ["*"]
179+
}
180+
actions = [
181+
"s3:ListBucket",
182+
]
183+
resources = [
184+
"arn:aws:s3:::${aws_s3_bucket.bucket_1.id}",
185+
]
186+
condition {
187+
test = "NotIpAddress"
188+
variable = "aws:SourceIp"
189+
values = var.ip_addresses
190+
}
191+
condition {
192+
test = "StringNotEquals"
193+
variable = "aws:SourceVpce"
194+
values = [
195+
"vpce-111111",
196+
var.s3_vpce_id,
197+
var.shared_s3_vpce_id
198+
]
199+
}
200+
}
201+
202+
statement {
203+
sid = "Deny object access not through vpce"
204+
effect = "Deny"
205+
principals {
206+
type = "AWS"
207+
identifiers = ["*"]
208+
}
209+
actions = [
210+
"s3:GetObject",
211+
"s3:PutObject"
212+
]
213+
resources = [
214+
"arn:aws:s3:::${aws_s3_bucket.bucket_1.id}",
215+
]
216+
condition {
217+
test = "StringNotEquals"
218+
variable = "aws:SourceVpce"
219+
values = [
220+
"vpce-111111111111111111",
221+
var.s3_vpce_id,
222+
var.shared_s3_vpce_id
223+
]
224+
}
225+
}
226+
}
227+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Sagemaker Notebooks
2+
3+
The Terraform code in this directory was used to generate a Sagemaker notebook
4+
instance and various supporting AWS resources in order to run a plan and generate
5+
Sentinel mocks for use with the [restrict-sagemaker-notebooks.sentinel](../governance/third-generation/restrict-sagemaker-notebooks.sentinel)
6+
Sentinel policy.
7+
8+
That policy requires all Sagemaker Notebook instances created with the
9+
`aws_sagemaker_notebook_instance` resource to set their `root_access` and `direct_internet_access` arguments to `false`.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 3.0"
6+
}
7+
}
8+
}
9+
10+
provider "aws" {
11+
region = "us-east-1"
12+
}
13+
14+
resource "aws_vpc" "sagemaker" {
15+
cidr_block = "10.0.0.0/16"
16+
}
17+
18+
resource "aws_subnet" "sagemaker" {
19+
vpc_id = aws_vpc.sagemaker.id
20+
cidr_block = "10.0.1.0/24"
21+
}
22+
23+
resource "aws_security_group" "allow_tls" {
24+
name = "allow_tls"
25+
description = "Allow TLS inbound traffic"
26+
vpc_id = aws_vpc.sagemaker.id
27+
28+
ingress {
29+
description = "TLS from VPC"
30+
from_port = 443
31+
to_port = 443
32+
protocol = "tcp"
33+
cidr_blocks = [aws_vpc.sagemaker.cidr_block]
34+
}
35+
}
36+
37+
data "aws_iam_policy_document" "instance-assume-role-policy" {
38+
statement {
39+
actions = ["sts:AssumeRole"]
40+
41+
principals {
42+
type = "Service"
43+
identifiers = ["ec2.amazonaws.com"]
44+
}
45+
}
46+
}
47+
48+
resource "aws_iam_role" "sagemaker" {
49+
name = "instance_role"
50+
path = "/system/"
51+
assume_role_policy = data.aws_iam_policy_document.instance-assume-role-policy.json
52+
}
53+
54+
resource "aws_sagemaker_notebook_instance" "ni" {
55+
name = "roger-notebook-instance"
56+
role_arn = aws_iam_role.sagemaker.arn
57+
instance_type = "ml.t2.medium"
58+
59+
root_access = "Enabled"
60+
direct_internet_access = "Enabled"
61+
62+
subnet_id = aws_subnet.sagemaker.id
63+
security_groups = [aws_security_group.allow_tls.id]
64+
}

0 commit comments

Comments
 (0)