# Service Account

> The DiscrimiNAT Firewall requires different permissions to work effectively across differing setups.

The DiscrimiNAT Firewall requires different permissions to work effectively across differing setups.

The most basic of the setups is a single project with only VM instances to protect within the same project. The permissions for this are included with our Deployment Manager template and Terraform modules as allowed scopes on the DiscrimiNAT instances.

For the rest, and if you'd rather manage the service account with discrete permissions, the following should come in handy.

## TL;DR

For all features and topologies enabled,

1. Create an IAM service account, in a _host_ project, for use by DiscrimiNAT instances.
2. Create an organisation-level IAM Role with the following permissions.
3. Create a folder-level† (or organisation-level) IAM binding granting the above IAM Role to the Principal of the above service account.

:::caution
† The chosen folder should contain the _host_ project and all _service_ projects either directly or through subfolders. Bindings can also be created on multiple folders.
:::

```
logging.logEntries.create
compute.firewalls.list
compute.instances.list
compute.addresses.list
compute.addresses.use
compute.subnetworks.useExternalIp
compute.instances.addAccessConfig
compute.projects.get
vpcaccess.connectors.list
compute.subnetworks.get
container.clusters.list
composer.environments.list
run.jobs.list
run.services.list
run.workerpools.list
roles/monitoring.metricWriter
roles/secretmanager.secretAccessor°
```

:::tip
`roles/monitoring.metricWriter` is the Google [predefined `Monitoring Metric Writer` role](https://gcp.permissions.cloud/predefinedroles/monitoring.metricWriter). It can be associated with the service account as a whole.
:::

:::caution
° The _Secret Manager Secret Accessor_ Role can be conditionally bound to only the Secret(s) that access is required to with the CEL Expression `resource.name == "projects/<project_numeric_id>/secrets/DiscrimiNAT_<deployment_id>/versions/latest"`. Also see Terraform examples below.
:::caution

## LOGGING

The DiscrimiNAT automatically logs config & flow events to StackDriver. The following permission allows its instances to log at `logName="projects/<google-cloud project name>/logs/discriminat-config"` and `logName="projects/<google-cloud project name>/logs/discriminat-flow"`.

```
logging.logEntries.create
```

## ALLOWLIST CONFIG

To form a complete picture of the VM instances in the VPC and their associated Firewall Rules, the DiscrimiNAT needs read access to these resources.

```
compute.firewalls.list
compute.instances.list
```

## DEFAULT PREFERENCES

Global level, behavioural [preferences](/docs/discriminat/gcp/default-prefs/) are stored in a Secret named DiscrimiNAT in Secret Manager. This is best permitted through a built-in Role and a Condition limiting the scope to only that named Secret.

```
roles/secretmanager.secretAccessor
```

Condition Expression: `projects/<gcp_project_numeric_id>/secrets/DiscrimiNAT_<discriminat_deployment_id>/versions/latest`

This is worked out and the interpolation automated for Terraform in the [examples below](#terraform).

## SHARED VPC

In addition to the above, to discover VM instances in service projects, the following permission is required. See the [Shared VPC](../shared-vpc/) page for more information.

```
compute.projects.get
```

## SERVERLESS VPC ACCESS CONNECTORS

In addition to the above, the following will be required to form a complete picture of any Serverless VPC Access Connectors.

If permissions for Shared VPC are included, then any Serverless VPC Access Connectors defined in service projects will also be picked up.

```
vpcaccess.connectors.list
compute.subnetworks.get
```

See the [Serverless VPC Access Connectors](../serverless-vpc-access-connectors/) page for more information.

## CLOUD COMPOSER v2 GKE CLUSTERS

A Composer v2 environment's cluster is an Autopilot mode VPC-native Google Kubernetes Engine cluster. Network Tags to it, however, can be applied at the time of Composer environments creation. Add the following permissions to the service account's role to allow the DiscrimiNAT to apply corresponding Firewall Rules to the clusters' subnets.

```
container.clusters.list
compute.subnetworks.get
```

## CLOUD COMPOSER v3

```
composer.environments.list
```

## CLOUD RUN DIRECT VPC EGRESS

```
run.services.list
```

## EXTERNAL IPs

If a Public IP is not found attached to a DiscrimiNAT instance, it will look for any allocated but unassociated External IPs that have a label-key named `discriminat` – the value which should be set to the value of the variable `custom_deployment_id` of the [Terraform module](https://registry.terraform.io/namespaces/ChaserSystems), if that was set, else anything but blank. One of such External IPs will be attempted to be associated with itself then.

:::tip
This allows you to have a stable set of static IPs to share with your partners, who may wish to allowlist them.
:::

**Private Google Access** enabled on the subnet DiscrimiNAT is deployed in is needed for this mechanism to work though – since making the association needs access to the Compute API. In the [google_network example](https://github.com/ChaserSystems/terraform-google-discriminat-ilb/blob/main/examples/google_network), this is demonstrated by setting `subnet_private_access = true`.

```
compute.addresses.list
compute.addresses.use
compute.subnetworks.useExternalIp
compute.instances.addAccessConfig
```

## OPS AGENT

The DiscrimiNAT comes with Google's [Ops Agent](https://docs.cloud.google.com/stackdriver/docs/solutions/agents/ops-agent) pre-installed. Should you require detailed metrics on the operating system's performance and utilisation, such as memory and load average, the permissions granted by the Monitoring Metric Writer role would be required.

```
roles/monitoring.metricWriter
```

:::tip
`roles/monitoring.metricWriter` is the Google [predefined `Monitoring Metric Writer` role](https://gcp.permissions.cloud/predefinedroles/monitoring.metricWriter). It can be associated with the service account as a whole.
:::

## TERRAFORM

If using Terraform, a new variable from v2.3 onwards has been introduced to let the user override the service account used by DiscrimiNAT instances. This is the `custom_service_account_email` variable and if left unset, defaults to the default compute engine service account with limited scopes of `compute-rw`, `logging-write` and `monitoring-write`. If set to the Principal of any service account, assigns that service account to the instances and changes the scope to `cloud-platform`.

### Sample code to create a Service Account, the IAM Role and an IAM Binding for a simple, single Project only deployment

```terraform
variable "project_id" {
  type        = string
  description = "Project ID for the Service Account to reside in. Think about the actual Project the DiscrimiNAT Firewall will be deployed in (where the VPC Network will be.) You must have privileges to create a new Service Account in this Project."
}

variable "discriminat_deployment_id" {
  type        = string
  description = "As output from DiscrimiNAT's Terraform modules for its deployment. This is the suffix to `DiscrimiNAT_` Secret (in Secret Manager) for building an IAM Conditional Access expression and scope down the access to one the Secret (`DiscrimiNAT_<deployment_id>`) which stores Preferences for this deployment of DiscrimiNAT only."
}

data "google_project" "current" {
  project_id = var.project_id
}

resource "google_project_iam_custom_role" "discriminat" {
  role_id     = "DiscrimiNAT"
  title       = "DiscrimiNAT Firewall Role"
  project     = var.project_id
  description = "Permissions needed for DiscrimiNAT Firewall to read metadata from VMs, Subnetworks, Firewall Rules, etc., write Logs & Metrics, and attach an External IP to itself. See upstream docs for full details: https://chasersystems.com/docs/discriminat/gcp/service-account/"
  permissions = ["logging.logEntries.create", "compute.firewalls.list", "compute.instances.list", "compute.addresses.list", "compute.addresses.use", "compute.subnetworks.useExternalIp", "compute.instances.addAccessConfig", "compute.projects.get", "vpcaccess.connectors.list", "compute.subnetworks.get", "container.clusters.list", "composer.environments.list", "run.services.list"]
}

resource "google_service_account" "discriminat" {
  account_id   = "discriminat"
  project      = var.project_id
  display_name = "DiscrimiNAT Firewall Service Account"
  description  = "For DiscrimiNAT Firewall. See upstream docs for full details: https://chasersystems.com/docs/discriminat/gcp/service-account/"
}

resource "google_project_iam_member" "discriminat_custom_role" {
  project = var.project_id
  role    = google_project_iam_custom_role.discriminat.id
  member  = "serviceAccount:${google_service_account.discriminat.email}"
}

resource "google_project_iam_member" "discriminat_metric_writer" {
  project = var.project_id
  role    = "roles/monitoring.metricWriter"
  member  = "serviceAccount:${google_service_account.discriminat.email}"
}

resource "google_project_iam_member" "discriminat_prefs_access" {
  project = var.project_id
  role    = "roles/secretmanager.secretAccessor"
  member  = "serviceAccount:${google_service_account.discriminat.email}"
  condition {
    title       = "specific_secret"
    description = "Restrict access to the specific Secret for DiscrimiNAT Preferences"
    expression  = <<EOF
resource.name == "projects/${data.google_project.current.number}/secrets/DiscrimiNAT_${var.discriminat_deployment_id}/versions/latest"
EOF
  }
}

output "discriminat_service_account_email" {
  value = google_service_account.discriminat.email
}
```

### Sample code to create a Service Account, the IAM Role and an IAM Binding for a complex, Shared VPC deployment with Projects in a Folder

```
variable "project_id" {
  type        = string
  description = "Project ID for the Service Account to reside in. Think about the actual Project the DiscrimiNAT Firewall will be deployed in (where the VPC Network will be.) You must have privileges to create a new Service Account in this Project."
}

variable "folder_id" {
  type        = string
  description = "The numeric Folder ID that is hierarchically high enough to contain the Host Project and all Service Projects connected to the VPC Network this particular DiscrimiNAT deployment will filter outbound traffic for. You must have privileges to create an IAM Binding at this level."
}

variable "org_id" {
  type        = string
  description = "The numeric Organization ID to create the Role in. You must have privileges to create a Role at this level."
}

variable "discriminat_deployment_id" {
  type        = string
  description = "As output from DiscrimiNAT's Terraform modules for its deployment. This is the suffix to `DiscrimiNAT_` Secret (in Secret Manager) for building an IAM Conditional Access expression and scope down the access to one the Secret (`DiscrimiNAT_<deployment_id>`) which stores Preferences for this deployment of DiscrimiNAT only."
}

data "google_project" "current" {
  project_id = var.project_id
}

resource "google_organization_iam_custom_role" "discriminat" {
  role_id     = "DiscrimiNAT"
  title       = "DiscrimiNAT Firewall Role"
  description = "Permissions needed for DiscrimiNAT Firewall to read metadata from VMs, Subnetworks, Firewall Rules, etc., write Logs & Metrics, and attach an External IP to itself. See upstream docs for full details: https://chasersystems.com/docs/discriminat/gcp/service-account/"
  permissions = ["logging.logEntries.create", "compute.firewalls.list", "compute.instances.list", "compute.addresses.list", "compute.addresses.use", "compute.subnetworks.useExternalIp", "compute.instances.addAccessConfig", "compute.projects.get", "vpcaccess.connectors.list", "compute.subnetworks.get", "container.clusters.list", "composer.environments.list", "run.services.list"]
  org_id      = var.org_id
}

resource "google_service_account" "discriminat" {
  account_id   = "discriminat"
  display_name = "DiscrimiNAT Firewall Service Account"
  description  = "For DiscrimiNAT Firewall. See upstream docs for full details: https://chasersystems.com/docs/discriminat/gcp/service-account/"
  project      = var.project_id
}

resource "google_folder_iam_member" "discriminat_custom_role" {
  folder = var.folder_id
  role   = google_organization_iam_custom_role.discriminat.id
  member = "serviceAccount:${google_service_account.discriminat.email}"
}

resource "google_folder_iam_member" "discriminat_metric_writer" {
  folder = var.folder_id
  role   = "roles/monitoring.metricWriter"
  member = "serviceAccount:${google_service_account.discriminat.email}"
}

resource "google_folder_iam_member" "discriminat_prefs_access" {
  folder = var.folder_id
  role   = "roles/secretmanager.secretAccessor"
  member = "serviceAccount:${google_service_account.discriminat.email}"
  condition {
    title       = "specific_secret"
    description = "Restrict access to the specific Secret for DiscrimiNAT Preferences"
    expression  = <<EOF
resource.name == "projects/${data.google_project.current.number}/secrets/DiscrimiNAT_${var.discriminat_deployment_id}/versions/latest"
EOF
  }
}

output "discriminat_service_account_email" {
  value = google_service_account.discriminat.email
}
```

## FAQ

#### Does DiscrimiNAT work without a custom service account?

Yes. It can work with the default compute engine service account with limited scopes of `compute-rw`, `logging-write` and `monitoring-write`, if Shared VPC and Serverless VPC Access Connectors support is not required.

#### Can the service account be changed on-the-fly?

Our usual deployment patterns encapsulate the instances in a Managed Instance Group (MIG). Any change is applied to the MIG template, which then does need to restart the instances in order to change the service account. If deployed behind an Internal Load Balancer (ILB), there will be near-zero downtime while the instances roll-over. If routed to directly, there will be a minute or two's interruption.
