Skip to content
This repository was archived by the owner on Dec 16, 2020. It is now read-only.

Commit 31a1e30

Browse files
authored
Merge pull request #25 from gruntwork-io/yori-tiller-module
Tiller module
2 parents 91fc779 + 067a608 commit 31a1e30

File tree

16 files changed

+656
-86
lines changed

16 files changed

+656
-86
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defaults: &defaults
55
environment:
66
GRUNTWORK_INSTALLER_VERSION: v0.0.21
77
TERRATEST_LOG_PARSER_VERSION: v0.13.13
8-
KUBERGRUNT_VERSION: v0.3.6
8+
KUBERGRUNT_VERSION: v0.3.8
99
HELM_VERSION: v2.12.2
1010
MODULE_CI_VERSION: v0.13.12
1111
TERRAFORM_VERSION: 0.11.11

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ build/
2121
*/build/
2222
out/
2323

24+
# Module artifacts
25+
os.txt
26+
2427
# Go best practices dictate that libraries should not include the vendor directory
2528
vendor
2629

README.md

+16-6
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,26 @@ This repo provides a Gruntwork IaC Package and has the following folder structur
3939
all the security best practices.
4040
* [modules](/modules): This folder contains the main implementation code for this Module, broken down into multiple
4141
standalone Submodules.
42+
43+
The primary module is:
44+
45+
* [k8s-tiller](/modules/k8s-tiller): Deploy Tiller with all the security features turned on. This includes using
46+
`Secrets` for storing state and enabling TLS verification.
47+
48+
The deployed Tiller requires TLS certificate key pairs to operate. Additionally, clients will each need to their
49+
own TLS certificate key pairs to authenticate to the deployed Tiller instance. This is based on [kubergrunt model of
50+
deploying helm](https://github.com/gruntwork-io/kubergrunt/blob/master/HELM_GUIDE.md).
51+
52+
There are also several supporting modules that help with setting up the deployment:
53+
54+
* [k8s-namespace](/modules/k8s-namespace): Provision a Kubernetes `Namespace` with a default set of RBAC roles.
55+
* [k8s-namespace-roles](/modules/k8s-namespace-roles): Provision a default set of RBAC roles to use in a `Namespace`.
56+
* [k8s-service-account](/modules/k8s-service-account): Provision a Kubernetes `ServiceAccount`.
57+
4258
* [examples](/examples): This folder contains examples of how to use the Submodules. The [example root
4359
README](/examples/README.md) provides a quickstart guide on how to use the Submodules in this Module.
4460
* [test](/test): Automated tests for the Submodules and examples.
4561

46-
The following submodules are available in this module:
47-
48-
- [k8s-namespace](/modules/k8s-namespace): Provision a Kubernetes `Namespace` with a default set of RBAC roles.
49-
- [k8s-namespace-roles](/modules/k8s-namespace-roles): Provision a default set of RBAC roles to use in a `Namespace`.
50-
- [k8s-service-account](/modules/k8s-service-account): Provision a Kubernetes `ServiceAccount`.
51-
5262

5363
## What is Kubernetes?
5464

examples/k8s-tiller-minikube/README.md

+29-43
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ respective repositories for how to deploy Tiller on those platforms. <!-- TODO:
4444

4545
## Installing necessary tools
4646

47-
In addition to `terraform`, this guide uses `kubergrunt` to manage the deployment of Tiller. You can read more about the
48-
decision behind this approach in [the Appendix](#appendix-a-why-kubergrunt) of this guide.
47+
In addition to `terraform`, this guide uses `kubergrunt` to manage TLS certificates for the deployment of Tiller. You
48+
can read more about the decision behind this approach in [the Appendix](#appendix-a-why-kubergrunt) of this guide.
4949

5050
This means that your system needs to be configured to be able to find `terraform`, `kubergrunt`, and `helm` client
5151
utilities on the system `PATH`. Here are the installation guide for each:
@@ -76,7 +76,7 @@ Tiller! To deploy Tiller, we will use the example Terraform code at the root of
7676
- `terraform apply`
7777
- Fill in the required variables based on your needs. <!-- TODO: show example inputs here -->
7878

79-
The Terraform code creates a few resources before deploying Tiller using `kubergrunt`:
79+
The Terraform code creates a few resources before deploying Tiller:
8080

8181
- A Kubernetes `Namespace` (the `tiller-namespace`) to house the Tiller instance. This namespace is where all the
8282
Kubernetes resources that Tiller needs to function will live. In production, you will want to lock down access to this
@@ -89,32 +89,27 @@ The Terraform code creates a few resources before deploying Tiller using `kuberg
8989
`tiller-namespace` and the `resource-namespace`, so that it can:
9090
- Manage its own resources in the `tiller-namespace`, where the Tiller metadata (e.g release tracking information) will live.
9191
- Manage the resources deployed by helm charts in the `resource-namespace`.
92+
- Using `kubergrunt`, generate a TLS CA certificate key pair and a set of signed certificate key pairs for the server
93+
and the client. These will then be uploaded as `Secrets` on the Kubernetes cluster.
9294

93-
Then it will feed the names of the created resources into the `kubergrunt helm deploy` command. As part of the
94-
deployment, `kubergrunt` will:
95+
These resources are then passed into the `k8s-tiller` module where the Tiller `Deployment` resources will be created.
96+
Once the resources are applied to the cluster, this will wait for the Tiller `Deployment` to roll out the `Pods` using
97+
`kubergrunt helm wait-for-tiller`.
9598

96-
- Create a new TLS certificate key pair to use as the CA and upload it to Kubernetes as a `Secret` in the `kube-system`
97-
namespace.
98-
- Using the generated CA TLS certificate key pair, create a signed TLS certificate key pair to use to identify the
99-
Tiller server and upload it to Kubernetes as a `Secret` in the `tiller-namespace`.
100-
- Deploy Tiller with the following configurations turned on:
101-
- TLS verification
102-
- `Secrets` as the storage engine
103-
- Provisioned in the `tiller-namespace` with the service account as the `tiller-service-account`
99+
Finally, to allow you to use `helm` right away, this code also sets up the local `helm` client. This involves:
104100

105-
- Grant access to the provided RBAC entity and configure the local helm client to use those credentials:
106-
- Using the CA TLS certificate key pair, create a signed TLS certificate key pair to use to identify the client.
107-
- Upload the certificate key pair to the `tiller-namespace`.
108-
- Grant the RBAC entity access to:
109-
- Get the client certificate `Secret` (`kubergrunt helm configure` uses this to install the client certificate
110-
key pair locally)
111-
- Get and List pods in `tiller-namespace` (the `helm` client uses this to find the Tiller pod)
112-
- Create a port forward to the Tiller pod (the `helm` client uses this to make requests to the Tiller pod)
101+
- Using the CA TLS certificate key pair, create a signed TLS certificate key pair to use to identify the client.
102+
- Upload the certificate key pair to the `tiller-namespace`.
103+
- Grant the RBAC entity access to:
104+
- Get the client certificate `Secret` (`kubergrunt helm configure` uses this to install the client certificate
105+
key pair locally)
106+
- Get and List pods in `tiller-namespace` (the `helm` client uses this to find the Tiller pod)
107+
- Create a port forward to the Tiller pod (the `helm` client uses this to make requests to the Tiller pod)
113108

114-
- Install the client certificate key pair to the helm home directory so the client can use it.
109+
- Install the client certificate key pair to the helm home directory so the client can use it.
115110

116-
You should now have a working Tiller deployment with your helm client configured to access it.
117-
So let's verify that in the next step!
111+
At the end of the `apply`, you should now have a working Tiller deployment with your `helm` client configured to access
112+
it. So let's verify that in the next step!
118113

119114

120115
## Verify Tiller Deployment
@@ -177,20 +172,6 @@ kubergrunt helm configure --tiller-namespace NAMESPACE_OF_TILLER --rbac-group de
177172
At the end of this, your users should have the same helm client setup as above.
178173

179174

180-
## Upgrading Deployed Tiller
181-
182-
At some point in the lifetime of the Tiller deployment, you will want to upgrade it. You can upgrade the deployed Tiller
183-
instance using the helm client with the following command:
184-
185-
```
186-
helm init --upgrade --tiller-namespace TILLER_NAMESPACE
187-
```
188-
189-
**Note**: You need to be an administrator to run this command. Specifically, this should be done with the same `kubectl`
190-
context as the one used to deploy Tiller. You can use the `--kube-context` option to use a different context from the
191-
default.
192-
193-
194175
## Appendix A: Why kubergrunt?
195176

196177
This Terraform example is not idiomatic Terraform code in that it relies on an external binary, `kubergrunt` as opposed
@@ -204,16 +185,21 @@ to implementing the functionalities using pure Terraform providers. This approac
204185
`destroy`.
205186

206187
That said, we decided to use this approach because of limitations in the existing providers to implement the
207-
functionalities here in pure Terraform code:
188+
functionalities here in pure Terraform code.
189+
190+
`kubergrunt` fulfills the role of generating and managing TLS certificate key pairs using Kubernetes `Secrets` as a
191+
database. This allows us to deploy Tiller with TLS verification enabled. We could instead use the `tls` and `kubernetes`
192+
providers in Terraform, but this has a few drawbacks:
208193

209-
- The Helm provider does not have [a resource that manages
210-
Tiller](https://github.com/terraform-providers/terraform-provider-helm/issues/134).
211194
- The [TLS provider](https://www.terraform.io/docs/providers/tls/index.html) stores the certificate key pairs in plain
212195
text into the Terraform state.
213196
- The Kubernetes Secret resource in the provider [also stores the value in plain text in the Terraform
214197
state](https://www.terraform.io/docs/providers/kubernetes/r/secret.html).
215198
- The grant and configure workflows are better suited as CLI tools than in Terraform.
216199

217-
Note that [we intend to implement a pure Terraform version of this when the Helm provider is
218-
updated](https://github.com/gruntwork-io/terraform-kubernetes-helm/issues/13), but we plan to continue to maintain the
200+
`kubergrunt` works around this by generating the TLS certs and storing them in Kubernetes `Secrets` directly. In this
201+
way, the generated TLS certs never leak into the Terraform state as they are referenced by name when deploying Tiller as
202+
opposed to by value.
203+
204+
Note that we intend to implement a pure Terraform version of this functionality, but we plan to continue to maintain the
219205
`kubergrunt` approach for folks who are wary of leaking secrets into Terraform state.

main.tf

+76-16
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ provider "kubernetes" {
2222
module "tiller_namespace" {
2323
# When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you
2424
# to a specific version of the modules, such as the following example:
25-
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-namespace?ref=v0.1.0"
25+
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-namespace?ref=v0.3.0"
2626
source = "./modules/k8s-namespace"
2727

2828
name = "${var.tiller_namespace}"
@@ -31,7 +31,7 @@ module "tiller_namespace" {
3131
module "resource_namespace" {
3232
# When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you
3333
# to a specific version of the modules, such as the following example:
34-
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-namespace?ref=v0.1.0"
34+
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-namespace?ref=v0.3.0"
3535
source = "./modules/k8s-namespace"
3636

3737
name = "${var.resource_namespace}"
@@ -40,7 +40,7 @@ module "resource_namespace" {
4040
module "tiller_service_account" {
4141
# When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you
4242
# to a specific version of the modules, such as the following example:
43-
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-service-account?ref=v0.1.0"
43+
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-service-account?ref=v0.3.0"
4444
source = "./modules/k8s-service-account"
4545

4646
name = "${var.service_account_name}"
@@ -63,17 +63,88 @@ module "tiller_service_account" {
6363
}
6464
}
6565

66+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67+
# GENERATE TLS CERTIFICATES FOR USE WITH TILLER
68+
# This will use kubergrunt to generate TLS certificates, and upload them as Kubernetes Secrets that can then be used by
69+
# Tiller.
70+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
71+
72+
resource "null_resource" "tiller_tls_certs" {
73+
provisioner "local-exec" {
74+
command = <<-EOF
75+
# Generate CA TLS certs
76+
kubergrunt tls gen --ca --namespace kube-system --secret-name ${local.tls_ca_secret_name} --secret-label gruntwork.io/tiller-namespace=${var.tiller_namespace} --secret-label gruntwork.io/tiller-credentials=true --secret-label gruntwork.io/tiller-credentials-type=ca --tls-subject-json '${jsonencode(var.tls_subject)}' --tls-private-key-algorithm ${var.private_key_algorithm} ${local.tls_algorithm_config} ${local.kubectl_config_options}
77+
78+
# Then use that CA to generate server TLS certs
79+
kubergrunt tls gen --namespace ${module.tiller_namespace.name} --ca-secret-name ${local.tls_ca_secret_name} --ca-namespace kube-system --secret-name ${local.tls_secret_name} --secret-label gruntwork.io/tiller-namespace=${var.tiller_namespace} --secret-label gruntwork.io/tiller-credentials=true --secret-label gruntwork.io/tiller-credentials-type=server --tls-subject-json '${jsonencode(var.tls_subject)}' --tls-private-key-algorithm ${var.private_key_algorithm} ${local.tls_algorithm_config} ${local.kubectl_config_options}
80+
EOF
81+
}
82+
}
83+
6684
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6785
# DEPLOY TILLER
6886
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6987

88+
module "tiller" {
89+
# When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you
90+
# to a specific version of the modules, such as the following example:
91+
# source = "git::[email protected]:gruntwork-io/terraform-kubernetes-helm.git//modules/k8s-tiller?ref=v0.3.0"
92+
source = "./modules/k8s-tiller"
93+
94+
tiller_service_account_name = "${module.tiller_service_account.name}"
95+
tiller_service_account_token_secret_name = "${module.tiller_service_account.token_secret_name}"
96+
tiller_tls_secret_name = "${local.tls_secret_name}"
97+
namespace = "${module.tiller_namespace.name}"
98+
tiller_image_version = "${var.tiller_version}"
99+
100+
# Kubergrunt will store the private key under the key "tls.pem" in the corresponding Secret resource, which will be
101+
# accessed as a file when mounted into the container.
102+
tiller_tls_key_file_name = "tls.pem"
103+
104+
dependencies = ["${null_resource.tiller_tls_certs.id}"]
105+
}
106+
107+
# The Deployment resources created in the module call to `k8s-tiller` will be complete creation before the rollout is
108+
# complete. We use kubergrunt here to wait for the deployment to complete, so that when this resource is done creating,
109+
# any resources that depend on this can assume Tiller is successfully deployed and up at that point.
110+
resource "null_resource" "wait_for_tiller" {
111+
provisioner "local-exec" {
112+
command = "kubergrunt helm wait-for-tiller --tiller-namespace ${module.tiller_namespace.name} --tiller-deployment-name ${module.tiller.deployment_name} --expected-tiller-version ${var.tiller_version}"
113+
}
114+
}
115+
116+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
117+
# CONFIGURE OPERATOR HELM CLIENT
118+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119+
120+
resource "null_resource" "grant_and_configure_helm" {
121+
count = "${var.configure_helm}"
122+
123+
provisioner "local-exec" {
124+
command = <<-EOF
125+
kubergrunt helm grant --tiller-namespace ${module.tiller_namespace.name} ${local.kubectl_config_options} --tls-subject-json '${jsonencode(var.client_tls_subject)}' ${local.configure_args}
126+
127+
kubergrunt helm configure --helm-home ${local.helm_home_with_default} --tiller-namespace ${module.tiller_namespace.name} --resource-namespace ${module.resource_namespace.name} ${local.kubectl_config_options} ${local.configure_args}
128+
EOF
129+
}
130+
131+
depends_on = ["null_resource.wait_for_tiller"]
132+
}
133+
134+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135+
# COMPUTATIONS
136+
# These locals compute various useful information used throughout this Terraform module.
137+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
138+
70139
locals {
71-
helm_home_with_default = "${var.helm_home == "" ? pathexpand("~/.helm") : var.helm_home}"
72140
kubectl_config_options = "${var.kubectl_config_context_name != "" ? "--kubectl-context-name ${var.kubectl_config_context_name}" : ""} ${var.kubectl_config_path != "" ? "--kubeconfig ${var.kubectl_config_path}" : ""}"
73141

142+
tls_ca_secret_name = "${var.tiller_namespace}-namespace-tiller-ca-certs"
143+
tls_secret_name = "tiller-certs"
144+
74145
tls_algorithm_config = "${var.private_key_algorithm == "ECDSA" ? "--tls-private-key-ecdsa-curve ${var.private_key_ecdsa_curve}" : "--tls-private-key-rsa-bits ${var.private_key_rsa_bits}"}"
75146

76-
undeploy_args = "${var.force_undeploy ? "--force" : ""} ${var.undeploy_releases ? "--undeploy-releases" : ""}"
147+
helm_home_with_default = "${var.helm_home == "" ? pathexpand("~/.helm") : var.helm_home}"
77148

78149
configure_args = "${
79150
var.helm_client_rbac_user != "" ? "--rbac-user ${var.helm_client_rbac_user}"
@@ -82,14 +153,3 @@ locals {
82153
: ""
83154
}"
84155
}
85-
86-
resource "null_resource" "tiller" {
87-
provisioner "local-exec" {
88-
command = "kubergrunt helm deploy ${local.kubectl_config_options} --service-account ${module.tiller_service_account.name} --resource-namespace ${module.resource_namespace.name} --tiller-namespace ${module.tiller_namespace.name} --tls-private-key-algorithm ${var.private_key_algorithm} ${local.tls_algorithm_config} --tls-subject-json '${jsonencode(var.tls_subject)}' --client-tls-subject-json '${jsonencode(var.client_tls_subject)}' --helm-home ${local.helm_home_with_default} ${local.configure_args} --tiller-version ${var.tiller_version}"
89-
}
90-
91-
provisioner "local-exec" {
92-
command = "kubergrunt helm undeploy ${local.kubectl_config_options} --helm-home ${local.helm_home_with_default} --tiller-namespace ${module.tiller_namespace.name} ${local.undeploy_args}"
93-
when = "destroy"
94-
}
95-
}

modules/k8s-namespace-roles/main.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ terraform {
2020
# ---------------------------------------------------------------------------------------------------------------------
2121

2222
resource "null_resource" "dependency_getter" {
23-
provisioner "local-exec" {
24-
command = "echo ${length(var.dependencies)}"
23+
triggers = {
24+
instance = "${join(",", var.dependencies)}"
2525
}
2626
}
2727

modules/k8s-namespace/main.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ terraform {
2121
# ---------------------------------------------------------------------------------------------------------------------
2222

2323
resource "null_resource" "dependency_getter" {
24-
provisioner "local-exec" {
25-
command = "echo ${length(var.dependencies)}"
24+
triggers = {
25+
instance = "${join(",", var.dependencies)}"
2626
}
2727
}
2828

modules/k8s-service-account/main.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ terraform {
2121
# ---------------------------------------------------------------------------------------------------------------------
2222

2323
resource "null_resource" "dependency_getter" {
24-
provisioner "local-exec" {
25-
command = "echo ${length(var.dependencies)}"
24+
triggers = {
25+
instance = "${join(",", var.dependencies)}"
2626
}
2727
}
2828

modules/k8s-service-account/outputs.tf

+7
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@ output "name" {
44

55
depends_on = ["kubernetes_role_binding.service_account_role_binding"]
66
}
7+
8+
output "token_secret_name" {
9+
description = "The name of the secret that holds the default ServiceAccount token that can be used to authenticate to the Kubernetes API."
10+
value = "${kubernetes_service_account.service_account.default_secret_name}"
11+
12+
depends_on = ["kubernetes_role_binding.service_account_role_binding"]
13+
}

0 commit comments

Comments
 (0)