forked from GoogleCloudPlatform/professional-services
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
VPC Flow Logs enforcer (GoogleCloudPlatform#619)
* Added VPC Flow Logs enforcer Cloud Function * Fixed pylint errors * Fixed last pylint errros * Fix last pylint error * Fix for empty CAI messages + update from legacy to new audit logs * Applying yapf format * Restructuring cloud function code
- Loading branch information
Showing
19 changed files
with
827 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# VPC Flow logs enforcement via Cloud Function | ||
|
||
## Description | ||
|
||
This sample code shows how a Cloud Function can be used to enforce VPC Flow logs in all the networks under a particular folder. The Cloud Function will listen on a Pub/Sub topic for notifications about chenges in subnets. The notifications can be configured in two ways: | ||
|
||
1. By creating a log sink that will filter all the network change events under the chosen folders and send them to a Pub Sub topic. This can be enabled using the `configure_log_sinks` variable. | ||
2. By creating Cloud Asset Inventry feeds that will send ntifications to a Pub Sub topic when a subnet is modified under a particular folder. This can be enabled using the `configure_asset_feeds` variable. Currently, the Cloud Asset Inventory feeds cannot be configured via Terraform. The terraform configuration will create shell scripts to create and delete the feeds. | ||
|
||
You should only enable one of the two options to avoid duplicate operations. If you enable both, you will see hat the asset inventory notifications are received a few seconds before the stackdriver based ones. You will probably want to use these in production, but I have included both options for demonstration purposes. | ||
|
||
The Terraform code included in this example creates the following resources: | ||
|
||
* A GCP project where all the resources will be located. | ||
* A pub Sub topic where the subnet change events will be published by the log sink or the inventory feed. | ||
* A cloud function that listens to subnet change notifications and makes sure VPC flow logs are activated. | ||
* If enabled, it will create the log sinks that will send notifications each time a subnet is modified. | ||
* If enabled, it will create two shell scripts for creating and deleting the asset inventory feeds (not yet supported by terraform). | ||
* Creates all the necessary permissions: | ||
* For the Cloud Function to be able to modify the subnets under the folders being monitored. | ||
* For the log sin service accounts to publish notifications in the Pub Sub topic. | ||
* For the Cloud Asset Inventory service account to publish notifications in the Pub Sub topic. | ||
|
||
## Setup instructions | ||
|
||
This terraform code uses service account impersionation to authenticate in the GCP APIs. The reasons for this are: | ||
|
||
1. It is recommended to only grant high level permissions to one service account, and allow impersonating this SA to specific users who will need to run the terraform code. This way individual users will not need to have excessive permissions. | ||
2. By always using a service account it is simpler to configure fine-grained permissions to run terraform code. | ||
3. By using impersonation instead of just a service account key we eliminate the private key exfiltration risk. | ||
4. By using impersonation instead of just a service account key we can see in the logs who executed the terraform code, even if the authentication was made via a SA key (the `serviceAccountDelegationInfo` attribute in the logs contain the email of the user who impersonated the SA). | ||
|
||
In order to run this terraform code, you will need to: | ||
|
||
1. Create a service account in the main project and grant it the necessary permissions. You will need: | ||
* If using CAI feeds, you will need the Cloud Asset Viewer role at the organization level, or above the folders you want to monitor. | ||
* If using log sinks, you will need the Logs Configuration Writer role at the organization level, or above the folders you want to monitor. | ||
* Project Creator and Billing account User roles, if you want to creat ethe project using terraform. If you are using an existing project, you will need to grant the service account the project Editor or Owner role. | ||
2. Identify the team members who will need to run teh terraform code, and grant them the `roles/iam.serviceAccountTokenCreator` role. | ||
3. Edit the `terraform.tfvars` files and replace the value of the `terraform_service_account` variable with the email of your service account. | ||
|
||
The setup is quite straightforward: | ||
|
||
1. Decide on which which folders in your organization you want to enfoce VPC flow logs and take note of those folder IDs. | ||
2. Choose a name for your demo project and a folder where you want to place it. | ||
3. Decide the VPC flow logs configuration you want to apply to your networks. See [here](https://cloud.google.com/compute/docs/reference/rest/v1/subnetworks) for the options. | ||
4. Decide if you want to use asset inventory feeds or log sinks for the subnet change notifications. | ||
5. Edit the `terraform.tf` file with your configuration options. | ||
6. Apply the terraform configuration. | ||
7. If you chose to use asset inventory feeds, make sure you run terraform using a service account. The Cloud Asset Inventory API requires being invoked using a service account. | ||
|
||
## Testing locally | ||
|
||
You can test the cloud function locally using the sample logs provided. To do this, you will first need to configure you rpython environment. While in this filder (vpc_log_enforcer), use [virtualenv](https://virtualenv.pypa.io/en/latest/) to set up a python3 environment: | ||
|
||
``` | ||
virtualenv --python python3 env | ||
source env/bin/activate | ||
pip3 install -r terraform/templates/cloud_function/requirements.txt | ||
``` | ||
|
||
Run the cloud funtion using any of the sample logs provided (or create your own): | ||
|
||
``` | ||
python3 terraform/templates/cloud_function/main.py sample_logs/insert_subnet_call_last.json | ||
``` | ||
|
||
sample output: | ||
|
||
``` | ||
reading sample message from: sample_logs/insert_subnet_call_last.json | ||
/Users/alpalacios/Workspaces/RNLT/PSO_Repo/vpc_log_enforcer/env/lib/python3.6/site-packages/google/auth/_default.py:69: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/ | ||
warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING) | ||
got subnet change notification from stackdriver | ||
enabling flow logs in subnetwork /projects/apszaz-crf-minutis-prod/regions/europe-west4/subnetworks/dummy. | ||
flow logs successfully enabled in subnetwork /projects/apszaz-crf-minutis-prod/regions/europe-west4/subnetworks/dummy. | ||
``` |
45 changes: 45 additions & 0 deletions
45
tools/vpc-flowlogs-enforcer/sample_logs/asset_inventory.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"asset": { | ||
"ancestors": [ | ||
"projects/717196263256", | ||
"folders/1094373516899", | ||
"folders/273647969471", | ||
"organizations/116143322321" | ||
], | ||
"assetType": "compute.googleapis.com/Subnetwork", | ||
"name": "//compute.googleapis.com/projects/apszaz-shsvc-services-5af0da/regions/asia-east1/subnetworks/dummy", | ||
"resource": { | ||
"data": { | ||
"creationTimestamp": "2020-05-11T01:02:09.269-07:00", | ||
"description": "", | ||
"enableFlowLogs": false, | ||
"fingerprint": "0/V6UwWr3n4=", | ||
"gatewayAddress": "10.0.7.1", | ||
"id": "8070357952081737838", | ||
"ipCidrRange": "10.0.7.0/24", | ||
"logConfig": { | ||
"aggregationInterval": "INTERVAL_5_SEC", | ||
"enable": true, | ||
"flowSampling": 0.75, | ||
"metadata": "INCLUDE_ALL_METADATA" | ||
}, | ||
"name": "dummy", | ||
"network": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/global/networks/dummy", | ||
"privateIpGoogleAccess": true, | ||
"privateIpv6GoogleAccess": "DISABLE_GOOGLE_ACCESS", | ||
"purpose": "PRIVATE", | ||
"region": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/regions/asia-east1", | ||
"selfLink": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/regions/asia-east1/subnetworks/dummy" | ||
}, | ||
"discoveryDocumentUri": "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest", | ||
"discoveryName": "Subnetwork", | ||
"location": "asia-east1", | ||
"parent": "//cloudresourcemanager.googleapis.com/projects/717196263256", | ||
"version": "v1" | ||
}, | ||
"updateTime": "2020-05-11T08:44:29.328882Z" | ||
}, | ||
"window": { | ||
"startTime": "2020-05-11T08:44:29.328882Z" | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
tools/vpc-flowlogs-enforcer/sample_logs/insert_subnet_call_first.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
{ | ||
"protoPayload": { | ||
"@type": "type.googleapis.com/google.cloud.audit.AuditLog", | ||
"authenticationInfo": { | ||
"principalEmail": "alfonso@apszaz.com" | ||
}, | ||
"requestMetadata": { | ||
"callerIp": "82.64.56.220", | ||
"callerSuppliedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.58 Safari/537.36,gzip(gfe),gzip(gfe)", | ||
"requestAttributes": { | ||
"time": "2021-02-26T06:06:38.713021Z", | ||
"reason": "8uSywAYQGg5Db2xpc2V1bSBGbG93cw", | ||
"auth": {} | ||
}, | ||
"destinationAttributes": {} | ||
}, | ||
"serviceName": "compute.googleapis.com", | ||
"methodName": "v1.compute.subnetworks.insert", | ||
"authorizationInfo": [ | ||
{ | ||
"permission": "compute.subnetworks.create", | ||
"granted": true, | ||
"resourceAttributes": { | ||
"service": "compute", | ||
"name": "projects/apszaz-shsvc-services-5af0da/regions/asia-east1/subnetworks/dummy", | ||
"type": "compute.subnetworks" | ||
} | ||
}, | ||
{ | ||
"permission": "compute.networks.updatePolicy", | ||
"granted": true, | ||
"resourceAttributes": { | ||
"service": "compute", | ||
"name": "projects/apszaz-shsvc-services-5af0da/global/networks/dummy", | ||
"type": "compute.networks" | ||
} | ||
} | ||
], | ||
"resourceName": "projects/apszaz-shsvc-services-5af0da/regions/asia-east1/subnetworks/dummy", | ||
"request": { | ||
"ipCidrRange": "10.0.1.0/24", | ||
"privateIpGoogleAccess": false, | ||
"@type": "type.googleapis.com/compute.subnetworks.insert", | ||
"name": "dummy", | ||
"enableFlowLogs": false, | ||
"description": "", | ||
"network": "projects/apszaz-shsvc-services-5af0da/global/networks/dummy", | ||
"region": "projects/apszaz-shsvc-services-5af0da/regions/asia-east1" | ||
}, | ||
"response": { | ||
"user": "alfonso@apszaz.com", | ||
"operationType": "insert", | ||
"name": "operation-1614319596411-5bc3712191b9f-b52b323b-a8235fcc", | ||
"@type": "type.googleapis.com/operation", | ||
"insertTime": "2021-02-25T22:06:38.487-08:00", | ||
"selfLink": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/regions/asia-east1/operations/operation-1614319596411-5bc3712191b9f-b52b323b-a8235fcc", | ||
"selfLinkWithId": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/regions/asia-east1/operations/8394696394217435393", | ||
"status": "RUNNING", | ||
"region": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/regions/asia-east1", | ||
"targetLink": "https://www.googleapis.com/compute/v1/projects/apszaz-shsvc-services-5af0da/regions/asia-east1/subnetworks/dummy", | ||
"targetId": "4811090641529680129", | ||
"id": "8394696394217435393", | ||
"startTime": "2021-02-25T22:06:38.495-08:00", | ||
"progress": "0" | ||
}, | ||
"resourceLocation": { | ||
"currentLocations": [ | ||
"asia-east1" | ||
] | ||
} | ||
}, | ||
"insertId": "-4fkj6ed1h10", | ||
"resource": { | ||
"type": "gce_subnetwork", | ||
"labels": { | ||
"subnetwork_name": "dummy", | ||
"location": "asia-east1", | ||
"project_id": "apszaz-shsvc-services-5af0da", | ||
"subnetwork_id": "4811090641529680129" | ||
} | ||
}, | ||
"timestamp": "2021-02-26T06:06:36.446815Z", | ||
"severity": "NOTICE", | ||
"logName": "projects/apszaz-shsvc-services-5af0da/logs/cloudaudit.googleapis.com%2Factivity", | ||
"operation": { | ||
"id": "operation-1614319596411-5bc3712191b9f-b52b323b-a8235fcc", | ||
"producer": "compute.googleapis.com", | ||
"first": true | ||
}, | ||
"receiveTimestamp": "2021-02-26T06:06:39.239420333Z" | ||
} |
37 changes: 37 additions & 0 deletions
37
tools/vpc-flowlogs-enforcer/sample_logs/insert_subnet_call_last.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"protoPayload": { | ||
"@type": "type.googleapis.com/google.cloud.audit.AuditLog", | ||
"authenticationInfo": { | ||
"principalEmail": "alfonso@apszaz.com" | ||
}, | ||
"requestMetadata": { | ||
"callerIp": "82.64.56.220", | ||
"callerSuppliedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.58 Safari/537.36,gzip(gfe),gzip(gfe)" | ||
}, | ||
"serviceName": "compute.googleapis.com", | ||
"methodName": "v1.compute.subnetworks.insert", | ||
"resourceName": "projects/apszaz-shsvc-services-5af0da/regions/asia-east1/subnetworks/dummy", | ||
"request": { | ||
"@type": "type.googleapis.com/compute.subnetworks.insert" | ||
} | ||
}, | ||
"insertId": "-num1c4cfzy", | ||
"resource": { | ||
"type": "gce_subnetwork", | ||
"labels": { | ||
"subnetwork_id": "4811090641529680129", | ||
"subnetwork_name": "dummy", | ||
"location": "asia-east1", | ||
"project_id": "apszaz-shsvc-services-5af0da" | ||
} | ||
}, | ||
"timestamp": "2021-02-26T06:06:52.654074Z", | ||
"severity": "NOTICE", | ||
"logName": "projects/apszaz-shsvc-services-5af0da/logs/cloudaudit.googleapis.com%2Factivity", | ||
"operation": { | ||
"id": "operation-1614319596411-5bc3712191b9f-b52b323b-a8235fcc", | ||
"producer": "compute.googleapis.com", | ||
"last": true | ||
}, | ||
"receiveTimestamp": "2021-02-26T06:06:53.190459199Z" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright 2021 Google LLC. This software is provided as-is, without warranty | ||
* or representation for any use or purpose. Your use of it is subject to your | ||
* agreement with Google. | ||
*/ | ||
|
||
# Create a feed that sends notifications about subnetwork resource updates under the | ||
# chosen folders. | ||
resource "google_cloud_asset_folder_feed" "subnet_change" { | ||
count = length(var.enforcement_folders) * (var.configure_asset_feeds ? 1 : 0) | ||
billing_project = google_project.demo_project.project_id | ||
folder = var.enforcement_folders[count.index] | ||
feed_id = "subnet_change" | ||
content_type = "RESOURCE" | ||
|
||
asset_types = [ | ||
"compute.googleapis.com/Subnetwork", | ||
] | ||
|
||
feed_output_config { | ||
pubsub_destination { | ||
topic = google_pubsub_topic.subnet_change.id | ||
} | ||
} | ||
|
||
depends_on = [ | ||
google_project_service.demo_project["cloudasset.googleapis.com"], | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright 2021 Google LLC. This software is provided as-is, without warranty | ||
* or representation for any use or purpose. Your use of it is subject to your | ||
* agreement with Google. | ||
*/ | ||
resource "google_cloudfunctions_function" "net_logs" { | ||
project = google_project.demo_project.project_id | ||
name = "net_logs" | ||
entry_point = "main" | ||
description = "Enables network logs whenever a subnet is created or modified." | ||
runtime = "python37" | ||
region = "europe-west1" | ||
|
||
service_account_email = google_service_account.net_logs_cf.email | ||
source_archive_bucket = google_storage_bucket.cloud_function.name | ||
source_archive_object = google_storage_bucket_object.cloud_function.name | ||
event_trigger { | ||
event_type = "providers/cloud.pubsub/eventTypes/topic.publish" | ||
resource = google_pubsub_topic.subnet_change.id | ||
failure_policy { | ||
# enable retry, since the function may be called before the subnet is ready | ||
# to be updated. | ||
retry = true | ||
} | ||
} | ||
|
||
environment_variables = { | ||
LOG_CONFIG = jsonencode(var.log_config) | ||
} | ||
|
||
depends_on = [google_project_service.demo_project] | ||
} | ||
|
||
# Push the zip file containing the cloud function to the bucket | ||
resource "google_storage_bucket_object" "cloud_function" { | ||
name = "net_logs.zip" | ||
source = data.archive_file.cloud_function.output_path | ||
bucket = google_storage_bucket.cloud_function.name | ||
} | ||
|
||
# GCS bucket to store the clouf function code | ||
resource "google_storage_bucket" "cloud_function" { | ||
name = "${google_project.demo_project.project_id}-cf${local.suffix_dash}" | ||
project = google_project.demo_project.project_id | ||
location = var.region | ||
uniform_bucket_level_access = true | ||
force_destroy = true | ||
storage_class = "REGIONAL" | ||
depends_on = [google_project_service.demo_project] | ||
} | ||
|
||
# Create a ZIP file with the cloud function. This file will be uploaded to a | ||
# GCS bucket, so it can be published as a Cloud function. | ||
data "archive_file" "cloud_function" { | ||
type = "zip" | ||
output_path = "templates/cloud_function.zip" | ||
source_dir = "templates/cloud_function" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* Copyright 2021 Google LLC. This software is provided as-is, without warranty | ||
* or representation for any use or purpose. Your use of it is subject to your | ||
* agreement with Google. | ||
*/ | ||
|
||
locals { | ||
suffix = var.random_suffix == "false" ? "" : "_${random_id.this.hex}" | ||
suffix_dash = var.random_suffix == "false" ? "" : "-${random_id.this.hex}" | ||
} | ||
|
||
# If enabled in the config parameters, this random suffix will be added to | ||
# resources that cannot be deleted and recreated with the same name right away. | ||
# Some resources, like projects, GCS buckets and KMS keys are deleted only | ||
# after a grece period. For test purposes, this random suffix can be useful. | ||
resource "random_id" "this" { | ||
byte_length = 2 | ||
} |
Oops, something went wrong.