Skip to content
This repository has been archived by the owner on Mar 29, 2023. It is now read-only.

Commit

Permalink
Merge pull request #17 from gruntwork-io/byo-service-account
Browse files Browse the repository at this point in the history
BYO Service Accounts
  • Loading branch information
robmorgan authored Apr 5, 2019
2 parents 2ef8a86 + 7499c92 commit f5566e3
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 12 deletions.
20 changes: 17 additions & 3 deletions examples/gke-basic-tiller/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ module "gke_cluster" {

name = "${var.cluster_name}"

// TODO(rileykarson): Update this when a new version comes out
kubernetes_version = "1.12.5-gke.5"

project = "${var.project}"
location = "${var.location}"
network = "${google_compute_network.main.name}"
Expand Down Expand Up @@ -112,6 +109,8 @@ resource "google_container_node_pool" "node_pool" {
disk_type = "pd-standard"
preemptible = false

service_account = "${module.gke_service_account.email}"

oauth_scopes = [
"https://www.googleapis.com/auth/cloud-platform",
]
Expand All @@ -128,6 +127,21 @@ resource "google_container_node_pool" "node_pool" {
}
}

# ---------------------------------------------------------------------------------------------------------------------
# CREATE A CUSTOM SERVICE ACCOUNT TO USE WITH THE GKE CLUSTER
# ---------------------------------------------------------------------------------------------------------------------

module "gke_service_account" {
# When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you
# to a specific version of the modules, such as the following example:
# source = "git::[email protected]:gruntwork-io/gke-cluster.git//modules/gke-service-account?ref=v0.0.1"
source = "../../modules/gke-service-account"

name = "${var.cluster_service_account_name}"
project = "${var.project}"
description = "${var.cluster_service_account_description}"
}

# TODO(rileykarson): Add proper VPC network config once we've made a VPC module
resource "random_string" "suffix" {
length = 4
Expand Down
12 changes: 11 additions & 1 deletion examples/gke-basic-tiller/variables.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# These variables are expected to be passed in by the operator
# These variables are expected to be passed in by the operator.
# ---------------------------------------------------------------------------------------------------------------------

variable "project" {
Expand Down Expand Up @@ -55,6 +55,16 @@ variable "cluster_name" {
default = "example-cluster"
}

variable "cluster_service_account_name" {
description = "The name of the custom service account used for the GKE cluster. This parameter is limited to a maximum of 28 characters."
default = "example-cluster-sa"
}

variable "cluster_service_account_description" {
description = "A description of the custom service account used for the GKE cluster."
default = "Example GKE Cluster Service Account managed by Terraform"
}

# TLS algorithm configuration

variable "private_key_algorithm" {
Expand Down
28 changes: 25 additions & 3 deletions examples/gke-public-cluster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,33 @@ planes which do not.
By default, regional clusters will create nodes across 3 zones in a region. If you're interested in how nodes are
distributed in regional clusters, read the GCP docs about [balancing across zones](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler#balancing_across_zones).

Nodes in a public cluster are accessible from the public internet; try using a private cluster such as in
The example follows best-practices and runs nodes using a custom service account to follow the principle of
least privilege. However you will need to ensure that the Identity and Access Management (IAM) API has been
enabled for the given project. This can be enabled in the Google API Console:
https://console.developers.google.com/apis/api/iam.googleapis.com/overview. See "Why use Custom Service
Accounts?" for more information.

**Important:** Nodes in a public cluster are accessible from the public internet; try using a private cluster such as in
[`gke-private-cluster`](../gke-private-cluster) to limit access to/from your nodes. Private clusters are recommended for
running most apps and services.

## Why use Custom Service Accounts?

Each node in a GKE cluster is a Compute Engine instance. Therefore, applications running on a GKE cluster
inherit the scopes of the Compute Engine instances to which they are deployed.

The recommended way to authenticate to GCP services from applications running on GKE is to create
your own service accounts. Ideally you must create a new service account for each application/service that makes requests to
Cloud Platform APIs.

GCP automatically creates a default service account, the "Compute Engine default service account" that GKE
associates it with the nodes it creates by default. Depending on how your project is configured, the default service account comes
pre-configured with project-wide permissions meaning that any given node will have access to every service every other
node has. Updating the default service account's permissions or assigning more access scopes to compute instances is
not the recommended way to authenticate to other Cloud Platform services from Pods running on GKE. In general, we
recommend using a per-node pool or per-cluster custom service account to allow you to more granularly restrict those
permissions.

## Limitations

When using a regional cluster, no region shares GPU types across all of their zones; you will need to explicitly specify
Expand All @@ -29,8 +52,7 @@ your new zones are within the region your cluster is present in.
## How do you run these examples?

1. Install [Terraform](https://learn.hashicorp.com/terraform/getting-started/install.html) v0.10.3 or later.
1. Open `variables.tf`, and fill in any required variables that don't have a
default.
1. Open `variables.tf`, and fill in any required variables that don't have a default.
1. Run `terraform get`.
1. Run `terraform plan`.
1. If the plan looks good, run `terraform apply`.
20 changes: 17 additions & 3 deletions examples/gke-public-cluster/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ module "gke_cluster" {

name = "${var.cluster_name}"

// TODO(rileykarson): Update this when a new version comes out
kubernetes_version = "1.12.5-gke.5"

project = "${var.project}"
location = "${var.location}"
network = "${google_compute_network.main.name}"
Expand Down Expand Up @@ -77,6 +74,8 @@ resource "google_container_node_pool" "node_pool" {
disk_type = "pd-standard"
preemptible = false

service_account = "${module.gke_service_account.email}"

oauth_scopes = [
"https://www.googleapis.com/auth/cloud-platform",
]
Expand All @@ -93,6 +92,21 @@ resource "google_container_node_pool" "node_pool" {
}
}

# ---------------------------------------------------------------------------------------------------------------------
# CREATE A CUSTOM SERVICE ACCOUNT TO USE WITH THE GKE CLUSTER
# ---------------------------------------------------------------------------------------------------------------------

module "gke_service_account" {
# When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you
# to a specific version of the modules, such as the following example:
# source = "git::[email protected]:gruntwork-io/gke-cluster.git//modules/gke-service-account?ref=v0.0.1"
source = "../../modules/gke-service-account"

name = "${var.cluster_service_account_name}"
project = "${var.project}"
description = "${var.cluster_service_account_description}"
}

# TODO(rileykarson): Add proper VPC network config once we've made a VPC module
resource "random_string" "suffix" {
length = 4
Expand Down
12 changes: 11 additions & 1 deletion examples/gke-public-cluster/variables.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# These variables are expected to be passed in by the operator
# These variables are expected to be passed in by the operator.
# ---------------------------------------------------------------------------------------------------------------------

variable "project" {
Expand All @@ -24,3 +24,13 @@ variable "cluster_name" {
description = "The name of the Kubernetes cluster."
default = "example-cluster"
}

variable "cluster_service_account_name" {
description = "The name of the custom service account used for the GKE cluster. This parameter is limited to a maximum of 28 characters."
default = "example-cluster-sa"
}

variable "cluster_service_account_description" {
description = "A description of the custom service account used for the GKE cluster."
default = "Example GKE Cluster Service Account managed by Terraform"
}
5 changes: 5 additions & 0 deletions modules/gke-service-account/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# GKE Service Account Module

The GKE Service Account module is used to create a GCP service account for use with a GKE cluster. It is based on
the best practices referenced in this article:
https://cloud.google.com/kubernetes-engine/docs/tutorials/authenticating-to-cloud-platform.
24 changes: 24 additions & 0 deletions modules/gke-service-account/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
resource "google_service_account" "service_account" {
project = "${var.project}"
account_id = "${var.name}"
display_name = "${var.description}"
}

# Grant the service account the minimum necessary roles and permissions in order to run the GKE cluster
resource "google_project_iam_member" "service_account-log_writer" {
project = "${google_service_account.service_account.project}"
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "service_account-metric_writer" {
project = "${google_project_iam_member.service_account-log_writer.project}"
role = "roles/monitoring.metricWriter"
member = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "service_account-monitoring_viewer" {
project = "${google_project_iam_member.service_account-metric_writer.project}"
role = "roles/monitoring.viewer"
member = "serviceAccount:${google_service_account.service_account.email}"
}
7 changes: 7 additions & 0 deletions modules/gke-service-account/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "email" {
# This may seem redundant with the `name` input, but it serves an important
# purpose. Terraform won't establish a dependency graph without this to interpolate on.
description = "The email address of the custom service account."

value = "${google_service_account.service_account.email}"
}
22 changes: 22 additions & 0 deletions modules/gke-service-account/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED MODULE PARAMETERS
# These parameters must be supplied when consuming this module.
# ---------------------------------------------------------------------------------------------------------------------

variable "project" {
description = "The name of the GCP Project where all resources will be launched."
}

variable "name" {
description = "The name of the custom service account. This parameter is limited to a maximum of 28 characters."
}

# ---------------------------------------------------------------------------------------------------------------------
# OPTIONAL MODULE PARAMETERS
# These parameters have reasonable defaults.
# ---------------------------------------------------------------------------------------------------------------------

variable "description" {
description = "The description of the custom service account."
default = ""
}
1 change: 0 additions & 1 deletion test/gke_basic_tiller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ func TestGKEBasicTiller(t *testing.T) {
// os.Setenv("SKIP_create_test_copy_of_examples", "true")
// os.Setenv("SKIP_create_terratest_options", "true")
// os.Setenv("SKIP_terraform_apply", "true")
// os.Setenv("SKIP_configure_kubectl", "true")
// os.Setenv("SKIP_wait_for_workers", "true")
// os.Setenv("SKIP_helm_install", "true")
// os.Setenv("SKIP_cleanup", "true")
Expand Down

0 comments on commit f5566e3

Please sign in to comment.