Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable logging with private s3 bucket #210

Merged
merged 22 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from 21 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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
aws-aurora,
aws-aurora-mysql,
aws-aurora-postgres,
aws-cloudfront-logs-bucket,
aws-cloudwatch-log-group,
aws-default-vpc-security,
aws-ecs-job,
Expand Down
64 changes: 64 additions & 0 deletions aws-cloudfront-logs-bucket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# aws-cloudfront-logs-bucket

This module uses the `aws-s3-private-bucket` module as its source and enables logging for Cloudfront to the specified S3 bucket. We include the grant to `aws-logs-delivery` whose canonical id is `c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0`, documentation for this can be found [here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#AccessLogsBucketAndFileOwnership). The suggestion is found here:

```
Restoring the ACL for the bucket
If you remove permissions for the awslogsdelivery account, CloudFront won't be able to save logs to the S3 bucket. To enable CloudFront to start saving logs for your distribution again, restore the ACL permission by doing one of the following:

...

Add the ACL permission for awslogsdelivery manually by navigating to the S3 bucket in the Amazon S3 console and adding permission. To add the ACL for awslogsdelivery, you must provide the canonical ID for the account, which is the following:

c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0
```

## Example

```hcl
module "s3-bucket" {
source = "github.com/chanzuckerberg/cztack/aws-cloudfront-logs-bucket?ref=v0.33.1"
bucket_name = "..."
env = var.env
owner = var.owner
project = var.project
service = var.component
}
```

<!-- START -->
## Requirements

No requirements.

## Providers

| Name | Version |
|------|---------|
| aws | n/a |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| abort\_incomplete\_multipart\_upload\_days | Number of days after which an incomplete multipart upload is canceled. | `number` | `14` | no |
| bucket\_name | n/a | `string` | n/a | yes |
| bucket\_policy | n/a | `string` | `""` | no |
| enable\_versioning | Keep old versions of overwritten S3 objects. | `bool` | `true` | no |
| env | n/a | `string` | n/a | yes |
| lifecycle\_rules | List of maps containing configuration of object lifecycle management. | `any` | <pre>[<br> {<br> "enabled": true,<br> "expiration": {<br> "expired_object_delete_marker": true<br> },<br> "noncurrent_version_expiration": {<br> "days": 365<br> },<br> "noncurrent_version_transition": {<br> "days": 30,<br> "storage_class": "STANDARD_IA"<br> }<br> }<br>]</pre> | no |
| owner | n/a | `string` | n/a | yes |
| project | n/a | `string` | n/a | yes |
| public\_access\_block | n/a | `bool` | `true` | no |
| service | n/a | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| arn | n/a |
| domain\_name | n/a |
| id | n/a |
| name | HACK(el): we do this to hint TF dependency graph since modules can't depend\_on |

<!-- END -->
36 changes: 36 additions & 0 deletions aws-cloudfront-logs-bucket/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
locals {
# Define the grant ACL for the Cloudfront logging S3 bucket,
# In order for the awslogsdelivery account to write log files to the bucket,
# we need to grant the AWS log delivery group the FULL_CONTROL access to the logging bucket
# LP's AWS account also has the FULL_CONTROL access to the bucket, this is specified by the canonical user id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is confusing -- please remove the reference to LP

# More details in https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#ChangeSettings
grants = [
{
canonical_user_id : data.aws_canonical_user_id.current_user.id
permissions : ["FULL_CONTROL"]

},
{
canonical_user_id : "c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0" # AWS log delivery group's canonical user id
permissions : ["FULL_CONTROL"]

}
]
}

data "aws_canonical_user_id" "current_user" {}

module "aws-cloudfront-logs-bucket" {
source = "../aws-s3-private-bucket"
grants = local.grants
env = var.env
owner = var.owner
project = var.project
service = var.service
bucket_name = var.bucket_name
bucket_policy = var.bucket_policy
enable_versioning = var.enable_versioning
abort_incomplete_multipart_upload_days = var.abort_incomplete_multipart_upload_days
public_access_block = var.public_access_block
lifecycle_rules = var.lifecycle_rules
}
59 changes: 59 additions & 0 deletions aws-cloudfront-logs-bucket/module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package test

import (
"testing"

"github.com/aws/aws-sdk-go/service/s3"
"github.com/chanzuckerberg/cztack/testutil"
"github.com/gruntwork-io/terratest/modules/aws"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/require"
)

func TestPrivateBucketDefaults(t *testing.T) {

test := &testutil.Test{
Options: func(t *testing.T) *terraform.Options {
project := testutil.UniqueId()
env := testutil.UniqueId()
service := testutil.UniqueId()
owner := testutil.UniqueId()

bucketName := testutil.UniqueId()

return testutil.Options(
testutil.DefaultRegion,
map[string]interface{}{
"project": project,
"env": env,
"service": service,
"owner": owner,

"bucket_name": bucketName,
},
)
},

Validate: func(t *testing.T, options *terraform.Options) {
r := require.New(t)
region := options.EnvVars["AWS_DEFAULT_REGION"]
bucket := options.Vars["bucket_name"].(string)

// get a client to query for other assertions
s3Client := aws.NewS3Client(t, region)

acl, err := s3Client.GetBucketAcl(&s3.GetBucketAclInput{
Bucket: &bucket,
})

r.NoError(err)
r.Len(acl.Grants, 2)

r.Equal("CanonicalUser", *acl.Grants[0].Grantee.Type)
r.Equal("FULL_CONTROL", *acl.Grants[0].Permission)
r.Equal("c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0", *acl.Grants[1].Grantee.ID)
r.Equal("FULL_CONTROL", *acl.Grants[1].Permission)
},
}
test.Run(t)
}
16 changes: 16 additions & 0 deletions aws-cloudfront-logs-bucket/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// HACK(el): we do this to hint TF dependency graph since modules can't depend_on
output "name" {
value = module.aws-cloudfront-logs-bucket.name
}

output "domain_name" {
value = module.aws-cloudfront-logs-bucket.domain_name
}

output "arn" {
value = module.aws-cloudfront-logs-bucket.arn
}

output "id" {
value = module.aws-cloudfront-logs-bucket.id
}
64 changes: 64 additions & 0 deletions aws-cloudfront-logs-bucket/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
variable "bucket_name" {
type = string
}

variable "bucket_policy" {
type = string
default = ""
}

variable "project" {
type = string
}

variable "env" {
type = string
}

variable "service" {
type = string
}

variable "owner" {
type = string
}

variable "enable_versioning" {
type = bool
description = "Keep old versions of overwritten S3 objects."
default = true
}

variable "abort_incomplete_multipart_upload_days" {
type = number
description = "Number of days after which an incomplete multipart upload is canceled."
default = 14
}

variable "lifecycle_rules" {
description = "List of maps containing configuration of object lifecycle management."
type = any
default = [
{
enabled = true

expiration = {
expired_object_delete_marker = true
}

noncurrent_version_transition = {
days = 30
storage_class = "STANDARD_IA"
}

noncurrent_version_expiration = {
days = 365
}
}
]
}

variable public_access_block {
type = bool
default = true
}