├── .github ├── lock.yml └── reaction.yml ├── .gitignore ├── LICENSE ├── README.md ├── k8s ├── db-sidecar.yaml ├── kv-sidecar.yaml └── vault.yaml └── scripts ├── 00-install-vault.sh ├── 01-enable-services.sh ├── 02-setup-storage.sh ├── 03-setup-kms.sh ├── 04-create-iam-service-account.sh ├── 05-create-k8s-cluster.sh ├── 06-create-public-ip.sh ├── 07-create-certs.sh ├── 08-setup-config.sh ├── 09-deploy-vault.sh ├── 10-deploy-lb.sh ├── 11-setup-comms.sh ├── 12-setup-static-kv.sh ├── 13-create-another-cluster.sh ├── 14-create-service-account.sh ├── 15-setup-vault-comms-k8s.sh ├── 16-create-kv-role.sh ├── 17-run-kv-sidecar.sh ├── 18-setup-dynamic-creds.sh ├── 19-run-db-sidecar.sh ├── 20-cleanup.sh ├── __helpers.sh └── kubectl-logs.sh /.github/lock.yml: -------------------------------------------------------------------------------- 1 | daysUntilLock: 90 2 | lockLabel: false 3 | lockComment: false 4 | -------------------------------------------------------------------------------- /.github/reaction.yml: -------------------------------------------------------------------------------- 1 | reactionComment: false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tls/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vault Kubernetes Workshop on Google Cloud Platform 2 | 3 | ## Prerequisites & Caveats 4 | 5 | - This workshop is designed to be run on [Google Cloud Shell][cs]. It may work 6 | in other environments without modification, but the materials are only tested 7 | and guaranteed against Cloud Shell. 8 | 9 | - You must have a Google Cloud Platform account and be authenticated as a 10 | project owner. Again, if you are using Cloud Shell, this happens 11 | automatically. If you are running locally, you will need to download and 12 | install the [Google Cloud SDK][sdk], and then authenticate to Google Cloud 13 | appropriately. 14 | 15 | - There are places where this workshop sacrifices "best practices" for "things 16 | feasible to complete in the duration of a workshop". In particular, this 17 | workshop generates self-signed SSL certificates and does not encrypt the 18 | resulting keys. For more details on a production-hardened setup, please see 19 | the [Vault production hardening docs][vault-prod-hardening]. 20 | 21 | - You must clone this repo: 22 | 23 | ```text 24 | git clone https://github.com/sethvargo/vault-kubernetes-workshop 25 | cd vault-kubernetes-workshop 26 | ``` 27 | 28 | ## 00 Install Vault 29 | 30 | The first step is to install Vault. To install the Vault binary in the current 31 | working directory, run the install script. 32 | 33 | ```text 34 | ./scripts/00-install-vault.sh 35 | ``` 36 | 37 | This will install Vault using the `sethvargo/hashicorp-installer` Docker 38 | container which verifies GPG signatures and checksums from the download. 39 | 40 | This represents a "best practices" installation for installing secure software 41 | like Vault. By verifying the signature of the SHASUMs and then verifying the 42 | SHASUMs themselves, we guarantee both the integrity of the download and ensure 43 | the binary has not been tampered with beyond it's original publishing. For added 44 | security, you can download and compile Vault yourself from source, but that is 45 | out of scope for this workshop. 46 | 47 | Cloud Shell does not persist things in `/usr/local/bin` or `/usr/bin` between 48 | sessions. As such, Vault will be installed in `~/bin`. We recommend installing 49 | other binaries or software you need between sessions in this folder. 50 | 51 | ## 01 Enable Services 52 | 53 | By default, a new Google Cloud project does not have many services enabled. 54 | Enable the required services (this only needs to be done once per project): 55 | 56 | ```text 57 | ./scripts/01-enable-services.sh 58 | ``` 59 | 60 | This will make the necessary calls to enable the enable the right APIs on your 61 | project. This process can take some time, but it is idempotent (you can run it 62 | multiple times to achieve the same result). 63 | 64 | ## 02 Setup Storage 65 | 66 | Vault requires a storage backend to persist its data. This workshop leverages 67 | [Google Cloud Storage][gcs]. Vault does not automatically create the storage 68 | bucket, so we need to create: 69 | 70 | ```text 71 | ./scripts/02-setup-storage.sh 72 | ``` 73 | 74 | Cloud Storage bucket names must be globally unique across all of Google Cloud. 75 | The bucket will be named "${PROJECT}-vault-storage". 76 | 77 | For security purposes, it is not recommended that other applications or services 78 | have access to this bucket. Even though the data is encrypted at rest, it is 79 | best to limit the scope of access as much as possible. 80 | 81 | ## 03 Setup KMS 82 | 83 | Vault will leverage [Google Cloud KMS][kms] to encrypt its unseal keys for 84 | auto-unsealing and auto-scaling purposes. We must create the KMS key in advance: 85 | 86 | ```text 87 | ./scripts/03-setup-kms.sh 88 | ``` 89 | 90 | ## 04 Create IAM Service Account 91 | 92 | It is a best practice to create a limited, dedicated service account that has 93 | only the required permissions. Create a dedicated service account in the project 94 | and grant it the most minimal set of permissions, in particular: 95 | 96 | - The ability to read/write to the Cloud Storage bucket created above 97 | - The ability to encrypt/decrypt data with the KMS key created above 98 | - The ability to generate new service accounts (not required to use Vault, but 99 | helpful if you plan to use the Vault GCP secrets engine) 100 | 101 | ```text 102 | ./scripts/04-create-iam-service-account.sh 103 | ``` 104 | 105 | ## 05 Create Kubernetes Cluster 106 | 107 | Next we need to create the [Google Kubernetes Engine][gke] (GKE) cluster which 108 | will run Vault. It is recommended that you run Vault in a dedicated namespace or 109 | (even better) a dedicated cluster and a dedicated project. Vault will then act 110 | as a service with an IP/DNS entry that other projects and services query. 111 | 112 | ```text 113 | ./scripts/05-create-k8s-cluster.sh 114 | ``` 115 | 116 | This will create the cluster and attach the service account created in the 117 | previous step to the cluster. It also ensures the cluster has the correct oauth 118 | scopes. 119 | 120 | ## 06 Create Public IP 121 | 122 | This step creates and reserves a regional public IP address. In a future step, 123 | we will attach this reserved IP address to a Kubernetes load balancer. For now, 124 | we will just reserve the dedicated IP address. 125 | 126 | ```text 127 | ./scripts/06-create-public-ip.sh 128 | ``` 129 | 130 | We use a regional IP address instead of a global IP address because global IPs 131 | perform load balancing at L7 whereas regional IP addresses perform load 132 | balancing at L4. Ideally we do not want the load balancer to perform TLS 133 | termination, and let Vault manage TLS, etc. While recent versions of Vault do 134 | support advanced routing like `X-Forwarded-For` headers, it is still a better 135 | practice to let Vault fully manage the TLS and thus use an L4 load balancer. 136 | 137 | This step is not required if you are comfortable assigning your Vault Kubernetes 138 | service an ephemeral IP or will manage it via an external DNS service. 139 | 140 | ## 07 Create Certificates 141 | 142 | This is arguably the most complex and nuanced piece of the workshop - generating 143 | Vault's certificate authority and server certificates for TLS. Vault can run 144 | without TLS, but this is _highly_ discouraged. This step could be replaced with 145 | a trusted CA like Let's Encrypt, but that is out of scope for this workshop. 146 | 147 | ```text 148 | ./scripts/07-create-certs.sh 149 | ``` 150 | 151 | This will create the Certificate Authority (`ca.key`, `ca.crt`) and Vault 152 | certificate (`vault.key`, `vault.crt`). In a future step, we will put these 153 | values in a Kubernetes secret so our pods can access them. 154 | 155 | ## 08 Create Config 156 | 157 | Next we create the config map and secrets to store our data for our pods. The 158 | insecure data such as the storage bucket name and IP address are placed in a 159 | configmap. The secure data like the TLS certificates are put in a Kubernetes 160 | secret. 161 | 162 | ```text 163 | ./scripts/08-setup-config.sh 164 | ``` 165 | 166 | ## 09 Deploy Vault 167 | 168 | The next step is to actually deploy Vault as a StatefulSet on Kubernetes. The 169 | reason we use a StatefulSet is two-fold: 170 | 171 | 1. It guarantees exactly one service starts at a time. This is required by the 172 | [vault-init][vault-init] sidecar service. 173 | 174 | 1. It gives us consistent naming for referencing the Vault servers (which is 175 | nice for a workshop). 176 | 177 | ```text 178 | ./scripts/09-deploy-vault.sh 179 | ``` 180 | 181 | Vault will automatically be initialized and unsealed via the vault-init service. 182 | 183 | ## 10 Deploy Load Balancer 184 | 185 | Even though Vault is deployed, it is not publicly accessible. We need to create 186 | a Kubernetes Service Load Balancer to forward from the IP address reserved in 187 | the previous steps to the pods we just created. 188 | 189 | ```text 190 | ./scripts/10-deploy-lb.sh 191 | ``` 192 | 193 | The load balancer listens on port 443 and forwards to port 8200 on the 194 | containers. 195 | 196 | For production-hardened scenarios, you may want to include firewall rules to 197 | limit access to Vault at the network layer. 198 | 199 | ## 11 Setup Comms 200 | 201 | Lastly, we need to configure our local Vault CLI to communicate with these 202 | newly-created Vault servers through the Load Balancer. Since we used custom TLS 203 | certificates, we'll need to trust the appropriate CA, etc. This will: 204 | 205 | - Set `VAULT_ADDR` to the Load Balancer address 206 | 207 | - Set `VAULT_CAPATH` to the path of the CA cert created in previous steps for 208 | properly verifying the TLS connection 209 | 210 | - Set `VAULT_TOKEN` to the decrypted root token by decrypting it from KMS 211 | 212 | ```text 213 | ./scripts/11-setup-comms.sh 214 | ``` 215 | 216 | At this point, the local Vault CLI is configured to communicate with our Vault 217 | cluster. Verify by running `vault status`: 218 | 219 | ```text 220 | vault status 221 | ``` 222 | 223 | ## 12 Setup Static KV 224 | 225 | Next we will explore techniques for retrieving static (i.e. non-expiring) 226 | credentials from Vault. The kv secrets engine is commonly used with legacy 227 | applications which cannot handle graceful restarts or when secrets cannot be 228 | dynamically generated by Vault. 229 | 230 | ```text 231 | ./scripts/12-setup-static-kv.sh 232 | ``` 233 | 234 | This will: 235 | 236 | 1. Enable the KV secrets engine 237 | 1. Create a policy to read data from a subpath 238 | 1. Store some static username/password data in the secrets engine 239 | 240 | Try reading back the secret by running: 241 | 242 | ```text 243 | vault kv get kv/myapp/config 244 | ``` 245 | 246 | You can also read the data via a request tool like curl. 247 | 248 | ```text 249 | curl -k -H "x-vault-token:${VAULT_TOKEN}" "${VAULT_ADDR}/v1/kv/myapp/config" 250 | ``` 251 | 252 | ## 13 Another Cluster 253 | 254 | Next we are going to create another Kubernetes cluster. There is no requirement 255 | that our Vault servers run under Kubernetes (they could be running on dedicated 256 | VMs or as a managed service). It is a best practice to treat the Vault server 257 | cluster as a "service" through which other applications and services request 258 | credentials. As such, moving forward, the Vault cluster will be treated simply 259 | as an IP address. We will not leverage K8S for "discovering" the Vault cluster, 260 | etc. 261 | 262 | **To put it another way, completely forget that Vault is running in Kubernetes. 263 | If it helps, think that Vault is running in a PaaS like Heroku instead.** 264 | 265 | Next create the Kubernetes cluster where our services will actually run. This is 266 | completely separate from the Vault K8S cluster. In fact, on GCP, is it 267 | recommended that you run these in completely separate projects. For the purpose 268 | of this workshop, we will run them in the same project. 269 | 270 | ```text 271 | ./scripts/13-create-another-cluster.sh 272 | ``` 273 | 274 | This will provision a new Kubernetes cluster named "my-apps". We will deploy 275 | all future apps and services in this cluster. 276 | 277 | Unlike the previous cluster, this cluster does not attach a service account. 278 | 279 | ## 14 Service Account 280 | 281 | In our cluster, services will authenticate to Vault using the [Kubernetes auth 282 | method][vault-k8s-auth-method]. In this model, services present their JWT token 283 | to Vault as part of an authentication request. Vault takes that signed JWT token 284 | and, using the token reviewer API, verifies the token is authenticated. If the 285 | authentication is successful, Vault generates a token and maps a series of 286 | configured policies onto the token which is returned to the caller. 287 | 288 | ```text 289 | ./scripts/14-create-service-account.sh 290 | ``` 291 | 292 | This will create a dedicated service account named "vault-auth" and grant that 293 | service account the ability to communicate with the token reviewer API. 294 | 295 | ## 15 Configure Vault to talk to Kubernetes 296 | 297 | Next we need to configure the Vault cluster to talk to our new Kubernetes 298 | cluster ("my-apps"). We will need to give Vault the IP address of the 299 | cluster, the CA information, and the service account to use for accessing the 300 | token reviewer API. 301 | 302 | ```text 303 | ./scripts/15-setup-vault-comms-k8s.sh 304 | ``` 305 | 306 | This process will: 307 | 308 | 1. Look up the service account JWT token (this is how Vault will talk to the 309 | Kubernetes API) 310 | 311 | 1. Extract the Kubernetes host from the local kube configuration (this where 312 | Vault will make API requests) 313 | 314 | 1. Extract the Kubernetes CA from the local kube configuration (this is how 315 | Vault will authenticate requests) 316 | 317 | 1. Enable the Kubernetes auth method in Vault 318 | 319 | 1. Give Vault the service account JWT, host, and CA so that Vault can 320 | communicate with Kubernetes 321 | 322 | There are other techniques for retrieving some of these values, but leveraging 323 | `kubectl` makes it easy to script. 324 | 325 | ## 16 Create KV Role 326 | 327 | Typically this process is done by a security team or operations team. We need to 328 | configure Vault to map an application or service in Kubernetes to a series of 329 | policies in Vault. That way, when an application successfully authenticates to 330 | Vault via its JWT token, Vault knows which policies to assign to the response. 331 | 332 | ```text 333 | ./scripts/16-create-kv-role.sh 334 | ``` 335 | 336 | This will create a role named "myapp-role" that permits pods in the "default" 337 | namespace with the "default" service account to receive a Vault token that has 338 | the "myapp-kv-rw" policy attached. 339 | 340 | ## 17 Sidecar Static App 341 | 342 | This is one of the most common techniques for injecting Vault secrets into an 343 | application. 344 | 345 | 1. An init container pulls the service account JWT token and performs the auth 346 | mechanism for that service account. If successful, it stores the resulting 347 | _Vault_ token in somewhere on a shared volume mount. 348 | 349 | 1. A tool like [Consul Template][consul-template] runs as the first container. 350 | This tool uses the Vault token acquired by the init container and makes the 351 | appropriate API calls to Vault based off of a template file. The template file 352 | can reference one or more Vault credentials. Consul Template writes the rendered 353 | file with the secrets from Vault to a shared volume mount which the app reads. 354 | 355 | 1. The app reads credentials. In this example, our application is a dummy 356 | application that just reads the contents of `/etc/secrets/config` repeatedly. 357 | 358 | ```text 359 | ./scripts/17-run-kv-sidecar.sh 360 | ``` 361 | 362 | Verify the app is authenticating and retrieving secrets from Vault: 363 | 364 | ```text 365 | ./scripts/kubectl-logs.sh kv-sidecar 366 | ``` 367 | 368 | ## 18 Setup Dynamic Credentials 369 | 370 | Next we configure Vault to generate dynamic credentials. Vault can generate many 371 | types of dynamic credentials like database credentials, certificates, etc. For 372 | this example, we will leverage the GCP secrets engine to dynamically generate 373 | Google Cloud Platform CloudSQL MySQL users. 374 | 375 | ```text 376 | ./scripts/18-setup-dynamic-creds.sh 377 | ``` 378 | 379 | This will: 380 | 381 | 1. Create a CloudSQL database 382 | 383 | 1. Enable the `database` secrets engine 384 | 385 | 1. Configure the `database` secrets engine 386 | 387 | 1. Create a "role" which configures the permissions the SQL user has 388 | 389 | 1. Create a new policy which allows generating these dynamic credentials 390 | 391 | 1. Update the Vault Kubernetes auth mapping to include this new policy when 392 | authenticating 393 | 394 | This process can take up to 10 minutes to complete. 395 | 396 | ## 19 Sidecar Dynamic App 397 | 398 | In this example, we follow the same pattern as the static KV secrets, but our 399 | sidecar application will pull dynamic credentials from Vault. In this case, we 400 | will be creating a database password. 401 | 402 | ```text 403 | ./scripts/19-run-db-sidecar.sh 404 | ``` 405 | 406 | This also configures a command to run which will signal the application when the 407 | underlying service account changes. This is important as we need to notify the 408 | application (which is not aware of Vault's existence) that it should reload its 409 | configuration. 410 | 411 | Verify the app is authenticating and retrieving secrets from Vault: 412 | 413 | ```text 414 | ./scripts/kubectl-logs.sh db-sidecar 415 | ``` 416 | 417 | [cs]: https://cloud.google.com/shell 418 | [gcs]: https://cloud.google.com/storage 419 | [gke]: https://cloud.google.com/gke 420 | [kms]: https://cloud.google.com/kms 421 | [sdk]: https://cloud.google.com/sdk 422 | [consul-template]: https://github.com/hashicorp/consul-template 423 | [vault-init]: https://github.com/sethvargo/vault-init 424 | [vault-k8s-auth-method]: https://www.vaultproject.io/docs/auth/kubernetes.html 425 | [vault-prod-hardening]: https://www.vaultproject.io/guides/operations/production.html 426 | -------------------------------------------------------------------------------- /k8s/db-sidecar.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: db-sidecar 6 | labels: 7 | app: db-sidecar 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: db-sidecar 13 | template: 14 | metadata: 15 | labels: 16 | app: db-sidecar 17 | spec: 18 | shareProcessNamespace: true 19 | volumes: 20 | - name: secrets 21 | emptyDir: {} 22 | - name: vault-tls 23 | secret: 24 | secretName: vault-tls 25 | - name: vault-token 26 | emptyDir: 27 | medium: Memory 28 | 29 | securityContext: 30 | runAsUser: 1000 31 | runAsGroup: 1000 32 | 33 | initContainers: 34 | # The vault-authenticator container authenticates the container using the 35 | # kubernetes auth method and puts the resulting token on the filesystem. 36 | - name: vault-authenticator 37 | image: sethvargo/vault-kubernetes-authenticator:0.2.0 38 | imagePullPolicy: IfNotPresent 39 | volumeMounts: 40 | - name: vault-token 41 | mountPath: /var/run/secrets/vaultproject.io 42 | - name: vault-tls 43 | mountPath: /etc/vault/tls 44 | env: 45 | - name: VAULT_ADDR 46 | valueFrom: 47 | configMapKeyRef: 48 | name: vault 49 | key: vault_addr 50 | - name: VAULT_CACERT 51 | value: /etc/vault/tls/ca.crt 52 | - name: VAULT_ROLE 53 | value: myapp-role 54 | 55 | containers: 56 | # The consul-template container will pull secrets from Vault and expose 57 | # them as files on disk. 58 | - name: consul-template 59 | image: hashicorp/consul-template:0.20.0-light 60 | imagePullPolicy: IfNotPresent 61 | securityContext: 62 | capabilities: 63 | add: ['SYS_PTRACE'] 64 | volumeMounts: 65 | - name: secrets 66 | mountPath: /etc/secrets 67 | - name: vault-tls 68 | mountPath: /etc/vault/tls 69 | - name: vault-token 70 | mountPath: /var/run/secrets/vaultproject.io 71 | env: 72 | - name: VAULT_ADDR 73 | valueFrom: 74 | configMapKeyRef: 75 | name: vault 76 | key: vault_addr 77 | - name: VAULT_CACERT 78 | value: /etc/vault/tls/ca.crt 79 | - name: CT_LOCAL_CONFIG 80 | value: | 81 | vault { 82 | vault_agent_token_file = "/var/run/secrets/vaultproject.io/.vault-token" 83 | 84 | ssl { 85 | ca_cert = "/etc/vault/tls/ca.crt" 86 | } 87 | 88 | retry { 89 | backoff = "1s" 90 | } 91 | } 92 | 93 | template { 94 | contents = </dev/null && pwd)/__helpers.sh" 5 | 6 | echo "--> Unpacking and installing" 7 | docker run -v $HOME/bin:/software sethvargo/hashicorp-installer vault 1.1.2 8 | sudo chown $(whoami):$(whoami) $HOME/bin/vault 9 | sudo chmod +x $HOME/bin/vault 10 | 11 | echo "--> Setting PATH" 12 | export PATH="${PATH}:${HOME}/bin" 13 | 14 | echo "--> Installing completions" 15 | vault -autocomplete-install || true 16 | 17 | echo "--> Done!" 18 | exec $SHELL 19 | -------------------------------------------------------------------------------- /scripts/01-enable-services.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | gcloud services enable \ 7 | --async \ 8 | --project="$(google-project)" \ 9 | cloudapis.googleapis.com \ 10 | cloudkms.googleapis.com \ 11 | cloudresourcemanager.googleapis.com \ 12 | cloudshell.googleapis.com \ 13 | container.googleapis.com \ 14 | containerregistry.googleapis.com \ 15 | iam.googleapis.com 16 | -------------------------------------------------------------------------------- /scripts/02-setup-storage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | gsutil mb -p "$(google-project)" "gs://$(google-project)-vault-storage" 7 | -------------------------------------------------------------------------------- /scripts/03-setup-kms.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | gcloud kms keyrings create vault \ 7 | --project="$(google-project)" \ 8 | --location="$(google-region)" 9 | 10 | gcloud kms keys create vault-init \ 11 | --project="$(google-project)" \ 12 | --location="$(google-region)" \ 13 | --keyring="vault" \ 14 | --purpose="encryption" 15 | -------------------------------------------------------------------------------- /scripts/04-create-iam-service-account.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | SERVICE_ACCOUNT="vault-server@$(google-project).iam.gserviceaccount.com" 7 | 8 | # Create the service account 9 | gcloud iam service-accounts create vault-server \ 10 | --project="$(google-project)" \ 11 | --display-name="vault server" 12 | 13 | # (Optional) grant the service account the ability to generate new service 14 | # accounts. This is required to use the Vault GCP secrets engine, otherwise it 15 | # can be omitted. 16 | ROLES=( 17 | "roles/resourcemanager.projectIamAdmin" 18 | "roles/iam.serviceAccountAdmin" 19 | "roles/iam.serviceAccountKeyAdmin" 20 | "roles/iam.serviceAccountTokenCreator" 21 | "roles/iam.serviceAccountUser" 22 | "roles/viewer" 23 | ) 24 | for role in "${ROLES[@]}"; do 25 | gcloud projects add-iam-policy-binding "$(google-project)" \ 26 | --member "serviceAccount:${SERVICE_ACCOUNT}" \ 27 | --role "${role}" 28 | done 29 | 30 | # Grant the service account the ability to read and write objects in our storage 31 | # bucket 32 | gsutil iam ch \ 33 | "serviceAccount:${SERVICE_ACCOUNT}:objectAdmin" \ 34 | "serviceAccount:${SERVICE_ACCOUNT}:legacyBucketReader" \ 35 | "gs://$(google-project)-vault-storage" 36 | 37 | # Grant the service account the ability to access the Cloud KMS crypto key 38 | gcloud kms keys add-iam-policy-binding vault-init \ 39 | --project="$(google-project)" \ 40 | --location="$(google-region)" \ 41 | --keyring="vault" \ 42 | --member="serviceAccount:${SERVICE_ACCOUNT}" \ 43 | --role="roles/cloudkms.cryptoKeyEncrypterDecrypter" 44 | -------------------------------------------------------------------------------- /scripts/05-create-k8s-cluster.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | SERVICE_ACCOUNT="vault-server@$(google-project).iam.gserviceaccount.com" 7 | 8 | gcloud container clusters create vault \ 9 | --project="$(google-project)" \ 10 | --cluster-version="$(gke-latest-master-version)" \ 11 | --enable-autorepair \ 12 | --enable-autoupgrade \ 13 | --enable-ip-alias \ 14 | --machine-type="n1-standard-2" \ 15 | --node-version="$(gke-latest-node-version)" \ 16 | --num-nodes="1" \ 17 | --region="$(google-region)" \ 18 | --scopes="cloud-platform" \ 19 | --service-account="${SERVICE_ACCOUNT}" 20 | -------------------------------------------------------------------------------- /scripts/06-create-public-ip.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | gcloud compute addresses create vault \ 7 | --project="$(google-project)" \ 8 | --region="$(google-region)" 9 | -------------------------------------------------------------------------------- /scripts/07-create-certs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | LB_IP="$(vault-lb-ip)" 7 | 8 | DIR="$(pwd)/tls" 9 | 10 | rm -rf "${DIR}" 11 | mkdir -p "${DIR}" 12 | 13 | # Create the conf file 14 | cat > "${DIR}/openssl.cnf" << EOF 15 | [req] 16 | default_bits = 2048 17 | encrypt_key = no 18 | default_md = sha256 19 | prompt = no 20 | utf8 = yes 21 | 22 | distinguished_name = req_distinguished_name 23 | req_extensions = v3_req 24 | 25 | [req_distinguished_name] 26 | C = US 27 | ST = California 28 | L = The Cloud 29 | O = Demo 30 | CN = vault 31 | 32 | [v3_req] 33 | basicConstraints = CA:FALSE 34 | subjectKeyIdentifier = hash 35 | keyUsage = digitalSignature, keyEncipherment 36 | extendedKeyUsage = clientAuth, serverAuth 37 | subjectAltName = @alt_names 38 | 39 | [alt_names] 40 | IP.1 = ${LB_IP} 41 | DNS.1 = vault.default.svc.cluster.local 42 | EOF 43 | 44 | # Generate Vault's certificates and a CSR 45 | openssl genrsa -out "${DIR}/vault.key" 2048 46 | 47 | openssl req \ 48 | -new -key "${DIR}/vault.key" \ 49 | -out "${DIR}/vault.csr" \ 50 | -config "${DIR}/openssl.cnf" 51 | 52 | # Create our CA 53 | openssl req \ 54 | -new \ 55 | -newkey rsa:2048 \ 56 | -days 120 \ 57 | -nodes \ 58 | -x509 \ 59 | -subj "/C=US/ST=California/L=The Cloud/O=Vault CA" \ 60 | -keyout "${DIR}/ca.key" \ 61 | -out "${DIR}/ca.crt" 62 | 63 | # Sign CSR with our CA 64 | openssl x509 \ 65 | -req \ 66 | -days 120 \ 67 | -in "${DIR}/vault.csr" \ 68 | -CA "${DIR}/ca.crt" \ 69 | -CAkey "${DIR}/ca.key" \ 70 | -CAcreateserial \ 71 | -extensions v3_req \ 72 | -extfile "${DIR}/openssl.cnf" \ 73 | -out "${DIR}/vault.crt" 74 | 75 | # Export combined certs for vault 76 | cat "${DIR}/vault.crt" "${DIR}/ca.crt" > "${DIR}/vault-combined.crt" 77 | -------------------------------------------------------------------------------- /scripts/08-setup-config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | DIR="$(pwd)/tls" 7 | 8 | kubectl create configmap vault \ 9 | --cluster="$(gke-cluster-name "vault")" \ 10 | --from-literal="load_balancer_address=$(vault-lb-ip)" \ 11 | --from-literal="gcs_bucket_name=$(google-project)-vault-storage" \ 12 | --from-literal="kms_project=$(google-project)" \ 13 | --from-literal="kms_region=$(google-region)" \ 14 | --from-literal="kms_key_ring=vault" \ 15 | --from-literal="kms_crypto_key=vault-init" \ 16 | --from-literal="kms_key_id=projects/$(google-project)/locations/$(google-region)/keyRings/vault/cryptoKeys/vault-init" 17 | 18 | kubectl create secret generic vault-tls \ 19 | --cluster="$(gke-cluster-name "vault")" \ 20 | --from-file="${DIR}/ca.crt" \ 21 | --from-file="vault.crt=${DIR}/vault-combined.crt" \ 22 | --from-file="vault.key=${DIR}/vault.key" 23 | -------------------------------------------------------------------------------- /scripts/09-deploy-vault.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | kubectl apply \ 7 | --cluster="$(gke-cluster-name "vault")" \ 8 | --filename="k8s/vault.yaml" 9 | -------------------------------------------------------------------------------- /scripts/10-deploy-lb.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | kubectl apply \ 7 | --cluster="$(gke-cluster-name "vault")" \ 8 | --filename=-</dev/null && pwd)/__helpers.sh" 5 | 6 | export VAULT_CACERT="$(pwd)/tls/ca.crt" 7 | export VAULT_ADDR="https://$(vault-lb-ip):443" 8 | export VAULT_TOKEN="$(gsutil cat "gs://$(google-project)-vault-storage/root-token.enc" | \ 9 | base64 --decode | \ 10 | gcloud kms decrypt \ 11 | --project="$(google-project)" \ 12 | --location="$(google-region)" \ 13 | --keyring="vault" \ 14 | --key="vault-init" \ 15 | --ciphertext-file - \ 16 | --plaintext-file -)" 17 | 18 | export PATH="${PATH}:${HOME}/bin" 19 | 20 | alias vualt=vault 21 | 22 | exec $SHELL 23 | -------------------------------------------------------------------------------- /scripts/12-setup-static-kv.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | # Enable kv secrets engine - this used to be enabled by default at secret/, but 7 | # that's not the case anymore. 8 | vault secrets enable kv 9 | 10 | # This creates a policy that allows reading, writing, and deleting from anything 11 | # under "myapp" in the kv secrets engine just created. This policy still must 12 | # be attached to tokens in order to receive the permission(s). 13 | vault policy write myapp-kv-rw - </dev/null && pwd)/__helpers.sh" 5 | 6 | # Create a cluster to do process namespace sharing 7 | gcloud container clusters create my-apps \ 8 | --project="$(google-project)" \ 9 | --cluster-version="$(gke-latest-master-version)" \ 10 | --enable-autorepair \ 11 | --enable-autoupgrade \ 12 | --enable-ip-alias \ 13 | --machine-type=n1-standard-2 \ 14 | --node-version="$(gke-latest-node-version)" \ 15 | --num-nodes=1 \ 16 | --region="$(google-region)" \ 17 | --scopes="cloud-platform" 18 | -------------------------------------------------------------------------------- /scripts/14-create-service-account.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | kubectl create serviceaccount vault-auth \ 7 | --cluster="$(gke-cluster-name "my-apps")" 8 | 9 | kubectl apply \ 10 | --cluster="$(gke-cluster-name "my-apps")" \ 11 | --filename=-</dev/null && pwd)/__helpers.sh" 5 | 6 | DIR="$(pwd)/tls" 7 | 8 | # Get the name of the secret corresponding to the service account 9 | SECRET_NAME="$(kubectl get serviceaccount vault-auth \ 10 | --cluster="$(gke-cluster-name "my-apps")" \ 11 | -o go-template='{{ (index .secrets 0).name }}')" 12 | 13 | # Get the actual token reviewer account 14 | TR_ACCOUNT_TOKEN="$(kubectl get secret ${SECRET_NAME} \ 15 | --cluster="$(gke-cluster-name "my-apps")" \ 16 | -o go-template='{{ .data.token }}' | base64 --decode)" 17 | 18 | # Get the host for the cluster (IP address) 19 | K8S_HOST="$(kubectl config view --raw \ 20 | -o go-template="{{ range .clusters }}{{ if eq .name \"$(gke-cluster-name "my-apps")\" }}{{ index .cluster \"server\" }}{{ end }}{{ end }}")" 21 | 22 | # Get the CA for the cluster 23 | K8S_CACERT="$(kubectl config view --raw \ 24 | -o go-template="{{ range .clusters }}{{ if eq .name \"$(gke-cluster-name "my-apps")\" }}{{ index .cluster \"certificate-authority-data\" }}{{ end }}{{ end }}" | base64 --decode)" 25 | 26 | # Enable the Kubernetes auth method 27 | vault auth enable kubernetes 28 | 29 | # Configure Vault to talk to our Kubernetes host with the cluster's CA and the 30 | # correct token reviewer JWT token 31 | vault write auth/kubernetes/config \ 32 | kubernetes_host="${K8S_HOST}" \ 33 | kubernetes_ca_cert="${K8S_CACERT}" \ 34 | token_reviewer_jwt="${TR_ACCOUNT_TOKEN}" 35 | 36 | # Create a config map to store the vault address 37 | kubectl create configmap vault \ 38 | --cluster="$(gke-cluster-name "my-apps")" \ 39 | --from-literal "vault_addr=https://$(vault-lb-ip)" 40 | 41 | # Create a secret for our CA 42 | kubectl create secret generic vault-tls \ 43 | --cluster="$(gke-cluster-name "my-apps")" \ 44 | --from-file "${DIR}/ca.crt" 45 | -------------------------------------------------------------------------------- /scripts/16-create-kv-role.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | vault write auth/kubernetes/role/myapp-role \ 7 | bound_service_account_names="default" \ 8 | bound_service_account_namespaces="default" \ 9 | policies="default,myapp-kv-rw" \ 10 | ttl="15m" 11 | -------------------------------------------------------------------------------- /scripts/17-run-kv-sidecar.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | kubectl apply \ 7 | --cluster="$(gke-cluster-name "my-apps")" \ 8 | --filename="k8s/kv-sidecar.yaml" 9 | -------------------------------------------------------------------------------- /scripts/18-setup-dynamic-creds.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | # Create CloudSQL instance 7 | gcloud sql instances create my-instance \ 8 | --project="$(google-project)" \ 9 | --activation-policy="always" \ 10 | --authorized-networks="0.0.0.0/0" \ 11 | --database-version="MYSQL_5_7" \ 12 | --no-backup \ 13 | --region="$(google-region)" \ 14 | --tier="db-n1-standard-1" 15 | 16 | INSTANCE_IP="$(gcloud sql instances describe my-instance --project="$(google-project)" --format='value(ipAddresses[0].ipAddress)')" 17 | 18 | # Change password 19 | gcloud sql users set-password root \ 20 | --project="$(google-project)" \ 21 | --host="%" \ 22 | --instance="my-instance" \ 23 | --password="my-password" 24 | 25 | # Enable the gcp secrets engine 26 | vault secrets enable database 27 | 28 | # Configure the database secrets engine TTLs 29 | vault write database/config/my-cloudsql-db \ 30 | plugin_name="mysql-database-plugin" \ 31 | connection_url="{{username}}:{{password}}@tcp(${INSTANCE_IP}:3306)/" \ 32 | allowed_roles="readonly" \ 33 | username="root" \ 34 | password="my-password" 35 | 36 | # Rotate the root cred 37 | vault write -f database/rotate-root/my-cloudsql-db 38 | 39 | # Create a role which will create a readonly user 40 | vault write database/roles/readonly \ 41 | db_name="my-cloudsql-db" \ 42 | creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT SELECT ON *.* TO '{{name}}'@'%';" \ 43 | default_ttl="1h" \ 44 | max_ttl="24h" 45 | 46 | # Create a new policy which allows generating these dynamic credentials 47 | vault policy write myapp-db-r -</dev/null && pwd)/__helpers.sh" 5 | 6 | kubectl apply \ 7 | --cluster="$(gke-cluster-name "my-apps")" \ 8 | --filename="k8s/db-sidecar.yaml" 9 | -------------------------------------------------------------------------------- /scripts/20-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | SERVICE_ACCOUNT="vault-server@$(google-project).iam.gserviceaccount.com" 7 | 8 | kubectl delete deployment kv-sidecar \ 9 | --cluster="$(gke-cluster-name "my-apps")" \ 10 | --force \ 11 | --grace-period=0 12 | 13 | kubectl delete deployment sa-sidecar \ 14 | --cluster="$(gke-cluster-name "my-apps")" \ 15 | --force \ 16 | --grace-period=0 17 | 18 | gcloud container clusters delete my-apps \ 19 | --async \ 20 | --quiet \ 21 | --project="$(google-project)" \ 22 | --region="$(google-region)" 23 | 24 | vault lease revoke -prefix gcp/ 25 | 26 | kubectl delete service vault \ 27 | --cluster="$(gke-cluster-name "vault")" 28 | 29 | kubectl delete statefulsets vault \ 30 | --cluster="$(gke-cluster-name "vault")" \ 31 | --grace-period=0 \ 32 | --force 33 | 34 | gcloud container clusters delete vault \ 35 | --async \ 36 | --quiet \ 37 | --project="$(google-project)" \ 38 | --region="$(google-region)" 39 | 40 | gcloud compute addresses delete vault \ 41 | --quiet \ 42 | --project="$(google-project)" \ 43 | --region="$(google-region)" 44 | 45 | gcloud iam service-accounts delete "${SERVICE_ACCOUNT}" \ 46 | --quiet \ 47 | --project="$(google-project)" 48 | 49 | gsutil -m rm -rf "gs://$(google-project)-vault-storage" 50 | gsutil rb -f "gs://$(google-project)-vault-storage" 51 | -------------------------------------------------------------------------------- /scripts/__helpers.sh: -------------------------------------------------------------------------------- 1 | # google-project returns the name of the current project, accounting for a 2 | # variety of common environments. If no project is found in any of the common 3 | # places, an error is returned. 4 | google-project() { 5 | ( 6 | set -Eeuo pipefail 7 | 8 | local project="${PROJECT:-${GOOGLE_PROJECT:-${GOOGLE_CLOUD_PROJECT:-${DEVSHELL_PROJECT_ID:-}}}}" 9 | if [ -z "${project:-}" ]; then 10 | echo "Missing project ID. Please set PROJECT, GOOGLE_PROJECT, or" 11 | echo "GOOGLE_CLOUD_PROJECT to the ID of your project to continue:" 12 | echo "" 13 | echo " export GOOGLE_CLOUD_PROJECT=$(whoami)-foobar123" 14 | echo "" 15 | return 127 16 | fi 17 | echo "${project}" 18 | ) 19 | } 20 | 21 | # google-region returns the region in which resources should be created. This 22 | # variable must be changed before running any commands. 23 | google-region() { 24 | ( 25 | echo "us-east4" 26 | ) 27 | } 28 | 29 | # gke-cluster-name is the name of the cluster for the given suffix. 30 | gke-cluster-name() { 31 | ( 32 | set -Eeuo pipefail 33 | 34 | echo "gke_$(google-project)_$(google-region)_${1}" 35 | ) 36 | } 37 | 38 | # gke-latest-master-version returns the latest GKE master version. 39 | gke-latest-master-version() { 40 | ( 41 | set -Eeuo pipefail 42 | 43 | gcloud container get-server-config \ 44 | --project="$(google-project)" \ 45 | --region="$(google-region)" \ 46 | --format='value(validMasterVersions[0])' \ 47 | 2>/dev/null 48 | ) 49 | } 50 | 51 | # gke-latest-node-version returns the latest GKE node version. 52 | gke-latest-node-version() { 53 | ( 54 | set -Eeuo pipefail 55 | 56 | gcloud container get-server-config \ 57 | --project="$(google-project)" \ 58 | --region="$(google-region)" \ 59 | --format='value(validNodeVersions[0])' \ 60 | 2>/dev/null 61 | ) 62 | } 63 | 64 | vault-lb-ip() { 65 | ( 66 | set -Eeuo pipefail 67 | 68 | gcloud compute addresses describe vault \ 69 | --project="$(google-project)" \ 70 | --region="$(google-region)" \ 71 | --format='value(address)' 72 | ) 73 | } 74 | -------------------------------------------------------------------------------- /scripts/kubectl-logs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(cd "$(dirname "${0}")" &>/dev/null && pwd)/__helpers.sh" 5 | 6 | if [ -z "${1:-}" ]; then 7 | echo "Missing pod name!" 8 | exit 1 9 | fi 10 | 11 | POD="$(kubectl get pods \ 12 | --cluster="$(gke-cluster-name "my-apps")" \ 13 | --selector="app=${1}" \ 14 | -o=jsonpath='{.items[0].metadata.name}')" 15 | 16 | kubectl logs "${POD}" -c "app" \ 17 | --context="$(gke-cluster-name "my-apps")" \ 18 | --------------------------------------------------------------------------------