From 8e3180452305aa5cffc266cb2111f4e556692e09 Mon Sep 17 00:00:00 2001 From: Mobeen Tariq Date: Mon, 2 Mar 2020 12:06:28 -0800 Subject: [PATCH] Initial commit for gcp terraform template Added output file and modified variables initial azure template commit updated azure template with comments from PR Created one main.tf for both config and audit log Signed-off-by: Mobeen Tariq --- .gitignore | 9 ++ azure/main.tf | 201 +++++++++++++++++++++++++++++++++++++++++++++ azure/output.tf | 15 ++++ azure/variables.tf | 109 ++++++++++++++++++++++++ gcp/main.tf | 164 ++++++++++++++++++++++++++++++++++++ gcp/output.tf | 12 +++ gcp/variables.tf | 48 +++++++++++ 7 files changed, 558 insertions(+) create mode 100644 azure/main.tf create mode 100644 azure/output.tf create mode 100644 azure/variables.tf create mode 100644 gcp/main.tf create mode 100644 gcp/output.tf create mode 100644 gcp/variables.tf diff --git a/.gitignore b/.gitignore index 7a3e2fd..014b595 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,12 @@ override.tf.json # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan # example: *tfplan* + +# Credentials Files +**/credentials.json + +# Local testing variables +*.tfvars + +/.idea/ +.DS_Store diff --git a/azure/main.tf b/azure/main.tf new file mode 100644 index 0000000..3762c32 --- /dev/null +++ b/azure/main.tf @@ -0,0 +1,201 @@ +provider "azuread" { + version = "=0.7.0" +} + +provider "azurerm" { + version = "=1.44.0" +} + +provider "random" { + version = "=2.2" +} + +locals { + resource_group = var.resource_group == null ? "${var.prefix}lwresourcegroup" : var.resource_group + storage = var.storage == null ? "${var.prefix}lwstorage" : var.storage + storage_queue = var.storage_queue == null ? "${var.prefix}lwstoragequeue" : var.storage_queue + event_subscription = var.event_subscription == null ? "${var.prefix}lweventsub" : var.event_subscription + log_profile = var.log_profile == null ? "${var.prefix}lwlogprofile" : var.log_profile +} + +resource "azuread_application" "default" { + name = var.app_name + identifier_uris = var.identifier_uris + + // Microsoft Graph + required_resource_access { + resource_app_id = "00000003-0000-0000-c000-000000000000" + + resource_access { + id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" + type = "Scope" + } + + resource_access { + id = "df021288-bdef-4463-88db-98f22de89214" + type = "Role" + } + } + + // AAD Graph API + required_resource_access { + resource_app_id = "00000002-0000-0000-c000-000000000000" + + resource_access { + id = "5778995a-e1bf-45b8-affa-663a9f3f4d04" + type = "Role" + } + } + + // Azure Storage + required_resource_access { + resource_app_id = "e406a681-f3d4-42a8-90b6-c2b029497af1" + + resource_access { + id = "03e0da56-190b-40ad-a80c-ea378c433f7f" + type = "Scope" + } + } + + // Azure Key Vault + required_resource_access { + resource_app_id = "cfa8b339-82a2-471a-a3c9-0fc0be7a4093" + + resource_access { + id = "f53da476-18e3-4152-8e01-aec403e6edc0" + type = "Scope" + } + } +} + +resource "random_uuid" "generator" {} + +resource "azuread_application_password" "client_secret" { + application_object_id = azuread_application.default.id + value = random_uuid.generator.result + end_date = "2299-12-31T01:02:03Z" +} + +resource "azuread_service_principal" "default" { + application_id = azuread_application.default.application_id +} + +data "azurerm_subscriptions" "available" {} + +resource "azurerm_role_assignment" "ex" { + count = length(data.azurerm_subscriptions.available.subscriptions) + + scope = "/subscriptions/${data.azurerm_subscriptions.available.subscriptions[count.index].subscription_id}" + role_definition_name = "Reader" + principal_id = azuread_service_principal.default.id +} + +data "azurerm_subscription" "primary" {} + +resource "azurerm_key_vault_access_policy" "default" { + count = length(var.key_vault_ids) + + key_vault_id = var.key_vault_ids[count.index] + object_id = azuread_service_principal.default.id + tenant_id = data.azurerm_subscription.primary.tenant_id + + key_permissions = [ + "List" + ] + secret_permissions = [ + "List" + ] +} + +// ------------------------------------------------------------------------------ + +resource "azurerm_resource_group" "default" { + name = local.resource_group + location = var.location +} + +resource "azurerm_storage_account" "default" { + name = local.storage + account_kind = "StorageV2" + account_replication_type = "LRS" + account_tier = "Standard" + enable_blob_encryption = true + enable_https_traffic_only = true + location = var.location + resource_group_name = azurerm_resource_group.default.name +} + +resource "azurerm_storage_queue" "default" { + name = local.storage_queue + storage_account_name = azurerm_storage_account.default.name +} + +resource "azurerm_eventgrid_event_subscription" "default" { + name = local.event_subscription + scope = azurerm_storage_account.default.id + + storage_queue_endpoint { + storage_account_id = azurerm_storage_account.default.id + queue_name = azurerm_storage_queue.default.name + } + + subject_filter { + subject_begins_with = "/blobServices/default/containers/insights-operational-logs/" + } + + included_event_types = [ + "Microsoft.Storage.BlobCreated" + ] +} + +resource "azurerm_monitor_log_profile" "default" { + name = local.log_profile + + categories = [ + "Action", + "Delete", + "Write", + ] + + locations = var.log_profile_locations + + storage_account_id = azurerm_storage_account.default.id + + retention_policy { + enabled = true + days = 7 + } +} + +resource "azurerm_role_definition" "default" { + name = "${var.prefix}lwrole" + description = "Monitors Activity Log" + + scope = data.azurerm_subscription.primary.id + assignable_scopes = [ + data.azurerm_subscription.primary.id + ] + + permissions { + actions = [ + "Microsoft.Resources/subscriptions/resourceGroups/read", + "Microsoft.Storage/storageAccounts/read", + "Microsoft.Storage/storageAccounts/blobServices/containers/read", + "Microsoft.Storage/storageAccounts/queueServices/queues/read", + "Microsoft.EventGrid/eventSubscriptions/read", + "Microsoft.Storage/storageAccounts/listkeys/action" + ] + + data_actions = [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read", + "Microsoft.Storage/storageAccounts/queueServices/queues/messages/read", + "Microsoft.Storage/storageAccounts/queueServices/queues/messages/delete" + ] + } +} + +resource "azurerm_role_assignment" "default" { + role_definition_id = azurerm_role_definition.default.id + principal_id = azuread_service_principal.default.id + scope = data.azurerm_subscription.primary.id +} diff --git a/azure/output.tf b/azure/output.tf new file mode 100644 index 0000000..ea6ee5d --- /dev/null +++ b/azure/output.tf @@ -0,0 +1,15 @@ +output "client_id" { + value = azuread_application.default.application_id +} + +output "client_secret" { + value = azuread_application_password.client_secret.value +} + +output "tenant_id" { + value = data.azurerm_subscription.primary.tenant_id +} + +output "queue_url" { + value = "https://${azurerm_storage_account.default.name}.queue.core.windows.net/${azurerm_storage_queue.default.name}" +} diff --git a/azure/variables.tf b/azure/variables.tf new file mode 100644 index 0000000..f5b2f2b --- /dev/null +++ b/azure/variables.tf @@ -0,0 +1,109 @@ +variable "app_name" { + type = string + description = "Name of the Azure Active Directory App." + default = "LaceworkSAAudit" +} + +variable "identifier_uris" { + type = list(string) + description = "URI's for app." + default = [ + "https://securityaudit.lacework.net" + ] +} + +variable "key_vault_ids" { + type = list(string) + description = "Key Vault Ids." + default = [] +} + +variable "prefix" { + type = string + description = "The Prefix used for all resources in this example." + default = "dev" +} + +variable "location" { + type = string + description = "The Azure Region in which all resources in this example should be created." + default = "West US 2" +} + +variable "resource_group" { + type = string + description = "Resource group name to use." + default = null +} + +variable "storage" { + type = string + description = "Storage name to use for Activity Log." + default = null +} + +variable "storage_queue" { + type = string + description = "Storage queue name to use for Activity Log." + default = null +} + +variable "event_subscription" { + type = string + description = "Event subscription name to use for Activity Log." + default = null +} + +variable "log_profile" { + type = string + description = "Log profile name for Activity Log." + default = null +} + +variable "log_profile_locations" { + type = list(string) + description = "Locations for log profile." + default = [ + "eastasia", + "southeastasia", + "centralus", + "eastus", + "eastus2", + "westus", + "northcentralus", + "southcentralus", + "northeurope", + "westeurope", + "japanwest", + "japaneast", + "brazilsouth", + "australiaeast", + "australiasoutheast", + "southindia", + "centralindia", + "westindia", + "canadacentral", + "canadaeast", + "uksouth", + "ukwest", + "westcentralus", + "westus2", + "koreacentral", + "koreasouth", + "francecentral", + "francesouth", + "australiacentral", + "australiacentral2", + "uaecentral", + "uaenorth", + "southafricanorth", + "southafricawest", + "switzerlandnorth", + "switzerlandwest", + "germanynorth", + "germanywestcentral", + "norwaywest", + "norwayeast", + "global" + ] +} diff --git a/gcp/main.tf b/gcp/main.tf new file mode 100644 index 0000000..1d8af31 --- /dev/null +++ b/gcp/main.tf @@ -0,0 +1,164 @@ +provider "google" { + credentials = file(var.credentials_file) + + project = var.project_id + region = var.region + zone = var.zone +} + +data "google_project" "project" { +} + +resource "google_service_account" "service_account" { + account_id = "${var.prefix}-lacework-cfg-sa" + display_name = "${var.prefix}-lacework-cfg-sa" +} + +resource "google_project_iam_member" "project_viewer_binding" { + count = var.org_integration ? 0 : 1 + + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/viewer" + project = var.project_id +} + +resource "google_project_iam_member" "project_security_reviewer_binding" { + count = var.org_integration ? 0 : 1 + + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/iam.securityReviewer" + project = var.project_id +} + +resource "google_organization_iam_member" "organization_viewer_binding" { + count = var.org_integration ? 1 : 0 + + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/viewer" + org_id = var.organization_id +} + +resource "google_organization_iam_member" "organization_security_reviewer_binding" { + count = var.org_integration ? 1 : 0 + + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/iam.securityReviewer" + org_id = var.organization_id +} + +resource "google_organization_iam_member" "organization_resource_viewer_binding" { + count = var.org_integration ? 1 : 0 + + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/resourcemanager.organizationViewer" + org_id = var.organization_id +} + +resource "google_service_account_key" "service-account-key-lacework" { + service_account_id = google_service_account.service_account.name +} + +resource "google_storage_bucket" "lacework_bucket" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + name = "${var.prefix}-${var.project_id}-lacework-bucket" +} + +resource "google_storage_bucket_iam_member" "bucket_object_viewer" { + bucket = var.existing_bucket_name != "" && var.audit_log ? var.existing_bucket_name : google_storage_bucket.lacework_bucket[0].name + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/storage.objectViewer" +} + +resource "google_storage_bucket_iam_binding" "legacy_bucket_owner" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + bucket = google_storage_bucket.lacework_bucket[count.index].name + role = "roles/storage.legacyBucketOwner" + members = ["projectEditor:${var.project_id}", "projectOwner:${var.project_id}"] +} + +resource "google_storage_bucket_iam_binding" "legacy_bucket_reader" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + bucket = google_storage_bucket.lacework_bucket[count.index].name + role = "roles/storage.legacyBucketReader" + members = ["projectViewer:${var.project_id}"] +} + +resource "google_pubsub_topic" "lacework_topic" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + name = "${var.prefix}-${var.project_id}-lacework-topic" +} + +resource "google_pubsub_topic_iam_member" "topic_publisher" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + member = "serviceAccount:service-${data.google_project.project.number}@gs-project-accounts.iam.gserviceaccount.com" + role = "roles/pubsub.publisher" + topic = google_pubsub_topic.lacework_topic[count.index].name +} + +resource "google_pubsub_subscription" "lacework_subscription" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + name = "${var.prefix}-${var.project_id}-lacework-subscription" + topic = google_pubsub_topic.lacework_topic[count.index].name + ack_deadline_seconds = 300 + message_retention_duration = "432000s" +} + +resource "google_pubsub_subscription_iam_member" "pubsub_subscriber" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + member = "serviceAccount:${google_service_account.service_account.email}" + role = "roles/pubsub.subscriber" + subscription = google_pubsub_subscription.lacework_subscription[count.index].name +} + +resource "google_storage_notification" "lacework_notification" { + count = var.existing_bucket_name != "" && var.audit_log ? 0 : 1 + + bucket = google_storage_bucket.lacework_bucket[count.index].name + payload_format = "JSON_API_V1" + topic = google_pubsub_topic.lacework_topic[count.index].name + event_types = ["OBJECT_FINALIZE"] + + depends_on = [google_pubsub_topic_iam_member.topic_publisher] +} + +resource "google_logging_project_sink" "lacework_project_sink" { + count = var.org_integration && var.audit_log ? 0 : 1 + + destination = "storage.googleapis.com/${var.existing_bucket_name != "" ? var.existing_bucket_name : google_storage_bucket.lacework_bucket[0].name}" + name = "${var.prefix}-${var.project_id}-lacework-sink" + unique_writer_identity = true + filter = "protoPayload.@type=type.googleapis.com/google.cloud.audit.AuditLog AND NOT protoPayload.methodName:'storage.objects'" +} + +resource "google_storage_bucket_iam_member" "project_sink_writer" { + count = var.org_integration && var.audit_log ? 0 : 1 + + bucket = var.existing_bucket_name != "" ? var.existing_bucket_name : google_storage_bucket.lacework_bucket[0].name + role = "roles/storage.objectCreator" + member = google_logging_project_sink.lacework_project_sink[count.index].writer_identity +} + +resource "google_logging_organization_sink" "lacework_organization_sink" { + count = var.org_integration && var.audit_log ? 1 : 0 + + destination = "storage.googleapis.com/${var.existing_bucket_name != "" ? var.existing_bucket_name : google_storage_bucket.lacework_bucket[0].name}" + name = "${var.prefix}-${var.organization_id}-lacework-sink" + org_id = var.organization_id + include_children = true + filter = "protoPayload.@type=type.googleapis.com/google.cloud.audit.AuditLog AND NOT protoPayload.methodName:'storage.objects'" +} + +resource "google_storage_bucket_iam_member" "organization_sink_writer" { + count = var.org_integration && var.audit_log ? 1 : 0 + + bucket = var.existing_bucket_name != "" ? var.existing_bucket_name : google_storage_bucket.lacework_bucket[0].name + role = "roles/storage.objectCreator" + member = google_logging_organization_sink.lacework_organization_sink[count.index].writer_identity +} diff --git a/gcp/output.tf b/gcp/output.tf new file mode 100644 index 0000000..41b46c6 --- /dev/null +++ b/gcp/output.tf @@ -0,0 +1,12 @@ +output "privateKey" { + value = base64decode(google_service_account_key.service-account-key-lacework.private_key) +} + +output "subscription" { + value = "projects/test-project-mobeen/subscriptions/${google_pubsub_subscription.lacework_subscription[0].name}" + depends_on = [google_pubsub_subscription.lacework_subscription] +} + +output "existing_subscription" { + value = var.existing_bucket_name != "" ? "Use existing subscription associated with the existing bucket" : "Use subscription printed with output" +} diff --git a/gcp/variables.tf b/gcp/variables.tf new file mode 100644 index 0000000..6e4f34d --- /dev/null +++ b/gcp/variables.tf @@ -0,0 +1,48 @@ +variable "prefix" { + type = string + description = "The Prefix used for all resources in this example" +} + +variable "credentials_file" { + type = string + description = "Location of the credentials file for gcp" +} + +variable "org_integration" { + type = bool + description = "If set to true, configure an organization level integration" + default = false +} + +variable "project_id" { + type = string + description = "Id of the project" +} + +variable "organization_id" { + type = string + description = "Id of the organization" +} + +variable "region" { + type = string + description = "Region you want to create resources in" + default = "us-central1" +} + +variable "zone" { + type = string + description = "Zone you want to create resources in" + default = "us-central1-c" +} + +variable "audit_log" { + type = bool + description = "If set to true, create resources for both Config and Audit Logs" +} + +variable "existing_bucket_name" { + type = string + description = "Name of an existing bucket you want to send the logs to" + default = "" +}