Skip to content

Commit

Permalink
Merge pull request #132 from nosportugal/network-policy
Browse files Browse the repository at this point in the history
feat: add option to attach a security policy to the default backend
  • Loading branch information
bschaatsbergen committed Dec 4, 2023
2 parents 263a41a + 430ce8d commit cfa90c8
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -40,6 +40,8 @@ This Terraform module deploys various resources to run Atlantis on Google Comput

- **Shielded VM** - A Shielded VM is a VM that's hardened by a set of security controls that help defend against rootkits and bootkits. Using a Shielded VM helps protect enterprise workloads from threats like remote attacks, privilege escalation, and malicious insiders.

- **Cloud Armor** - Use Google Cloud Armor security policies to protect the default backend service from distributed denial-of-service (DDoS) and other web-based attacks. Security policies can be configured manually, with configurable match conditions and actions in a security policy. Google Cloud Armor also features preconfigured security policies, which cover a variety of use cases.

## Prerequisites

This module expects that you already own or create the below resources yourself.
Expand All @@ -57,7 +59,7 @@ Here are some examples to choose from. Look at the prerequisites above to find o
- [Basic](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/basic)
- [Complete](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/complete)
- [Secure Environment Variables](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/secure-env-vars)

- [Cloud Armor](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/cloud-armor)

```hcl
module "atlantis" {
Expand Down
32 changes: 32 additions & 0 deletions examples/cloud-armor/README.md
@@ -0,0 +1,32 @@
# Example usage

This example deploys Cloud Armor to ensure requests to the default backend are coming from GitHub Webhooks.

Since IAP is enabled, two backend services will be created:

- atlantis: the backend to receive GitHub events, protected with Cloud Armor
- atlantis-iap: the backend to serve the Atlantis UI, protected with IAP

Read through the below before you deploy this module.

- [Prerequisites](#prerequisites)
- [How to deploy](#how-to-deploy)
- [After it's successfully deployed](#after-its-successfully-deployed)

## Prerequisites

This module expects that you already own or create the below resources yourself.

- Google network, subnetwork and a Cloud NAT
- Service account, [specifics can be found here](../../README.md#service-account)
- Domain, [specifics can be found here](../../README.md#dns-record)

If you prefer an example that includes the above resources, see [`complete example`](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/complete).

## How to deploy

See [`main.tf`](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/basic/main.tf) and the [`server-atlantis.yaml`](https://github.com/bschaatsbergen/atlantis-on-gcp-vm/tree/master/examples/basic/server-atlantis.yaml).

## After it's successfully deployed

Once you're done, see [Configuring Webhooks for Atlantis](https://www.runatlantis.io/docs/configuring-webhooks.html#configuring-webhooks)
130 changes: 130 additions & 0 deletions examples/cloud-armor/main.tf
@@ -0,0 +1,130 @@
locals {
project_id = "<your-project-id>"
network = "<your-network>"
subnetwork = "<your-subnetwork>"
region = "<your-region>"
zone = "<your-zone>"
domain = "<example.com>"
managed_zone = "<your-managed-zone>"

github_repo_allow_list = "github.com/example/*"
github_user = "<your-github-handle>"
github_token = "<your-github-user>"
github_webhook_secret = "<your-github-webhook-secret>"

google_iap_brand_name = "<brand-name>"
}

# Create a service account and attach the required Cloud Logging permissions to it.
resource "google_service_account" "atlantis" {
account_id = "atlantis"
display_name = "Service Account for Atlantis"
project = local.project_id
}

resource "google_project_iam_member" "atlantis_log_writer" {
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.atlantis.email}"
project = local.project_id
}

resource "google_project_iam_member" "atlantis_metric_writer" {
role = "roles/monitoring.metricWriter"
member = "serviceAccount:${google_service_account.atlantis.email}"
project = local.project_id
}

module "atlantis" {
source = "bschaatsbergen/atlantis/gce"
name = "atlantis"
network = local.network
subnetwork = local.subnetwork
region = local.region
zone = local.zone
service_account = {
email = google_service_account.atlantis.email
scopes = ["cloud-platform"]
}
# Note: environment variables are shown in the Google Cloud UI
# See the `examples/secure-env-vars` if you want to protect sensitive information
env_vars = {
ATLANTIS_GH_USER = local.github_user
ATLANTIS_GH_TOKEN = local.github_token
ATLANTIS_GH_WEBHOOK_SECRET = local.github_webhook_secret
ATLANTIS_REPO_ALLOWLIST = local.github_repo_allow_list
ATLANTIS_ATLANTIS_URL = "https://${local.domain}"
ATLANTIS_REPO_CONFIG_JSON = jsonencode(yamldecode(file("${path.module}/server-atlantis.yaml")))
}
domain = local.domain
project = local.project_id

default_backend_security_policy = google_compute_security_policy.atlantis.name

iap = {
oauth2_client_id = google_iap_client.atlantis.client_id
oauth2_client_secret = google_iap_client.atlantis.secret
}
}

# As your DNS records might be managed at another registrar's site, we create the DNS record outside of the module.
# This record is mandatory in order to provision the managed SSL certificate successfully.
resource "google_dns_record_set" "default" {
name = "${local.domain}."
type = "A"
ttl = 60
managed_zone = local.managed_zone
rrdatas = [
module.atlantis.ip_address
]
project = local.project_id
}

resource "google_compute_security_policy" "atlantis" {
name = "atlantis-security-policy"
description = "Policy blocking all traffic except from Github Webhooks"
project = local.project_id

rule {
# Allow from GitHub Webhooks
# https://api.github.com/meta
action = "allow"
priority = "2"
description = "Rule: Allow github hooks"
match {
expr {
expression = "(inIpRange(origin.ip, '140.82.112.0/20') || inIpRange(origin.ip, '185.199.108.0/22') || inIpRange(origin.ip, '143.55.64.0/20') || inIpRange(origin.ip, '192.30.252.0/22'))"
}
}
}

rule {
# Deny all by default
action = "deny(403)"
priority = "2147483647"
description = "Default rule: deny all"

match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["*"]
}
}
}

rule {
# Log4j vulnerability
action = "deny(403)"
priority = "1"
description = "CVE-2021-44228 (https://nvd.nist.gov/vuln/detail/CVE-2021-44228)"
match {
expr {
expression = "evaluatePreconfiguredExpr('cve-canary')"
}
}
}
}

resource "google_iap_client" "atlantis" {
display_name = "iap-client"
brand = local.google_iap_brand_name
}
6 changes: 6 additions & 0 deletions examples/cloud-armor/server-atlantis.yaml
@@ -0,0 +1,6 @@
repos:
- id: /.*/
apply_requirements: [mergeable]
allowed_overrides: [apply_requirements, workflow]
allow_custom_workflows: true
delete_source_branch_on_merge: true
1 change: 1 addition & 0 deletions main.tf
Expand Up @@ -308,6 +308,7 @@ resource "google_compute_backend_service" "default" {
connection_draining_timeout_sec = 5
load_balancing_scheme = "EXTERNAL_MANAGED"
health_checks = [google_compute_health_check.default.id]
security_policy = var.default_backend_security_policy

log_config {
enable = true
Expand Down
6 changes: 6 additions & 0 deletions variables.tf
Expand Up @@ -169,3 +169,9 @@ variable "labels" {
description = "Key-value pairs representing labels attaching to instance & instance template"
default = {}
}

variable "default_backend_security_policy" {
type = string
description = "Name of the security policy to apply to the default backend service"
default = null
}

0 comments on commit cfa90c8

Please sign in to comment.