├── .gitignore ├── CODEOWNERS ├── LICENSE ├── MAIN.md ├── NOTICE ├── README.md ├── _docs ├── architecture-azurelb.png └── architecture.png ├── custom-data-consul.sh ├── custom-data-vault.sh ├── examples ├── vault-consul-image │ ├── README.md │ ├── tls │ │ ├── README.md │ │ ├── ca.crt.pem │ │ ├── vault.crt.pem │ │ └── vault.key.pem │ └── vault-consul.json └── vault-examples-helper │ ├── README.md │ └── vault-examples-helper.sh ├── main.tf ├── modules ├── install-vault │ ├── README.md │ ├── install-vault │ ├── supervisor-initd-script.sh │ └── supervisord.conf ├── private-tls-cert │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── vars.tf ├── run-vault │ ├── README.md │ └── run-vault ├── update-certificate-store │ ├── README.md │ └── update-certificate-store └── vault-cluster │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── vars.tf ├── outputs.tf └── vars.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # Terraform files 2 | .terraform 3 | terraform.tfstate 4 | terraform.tfvars 5 | *.tfstate* 6 | 7 | # OS X files 8 | .history 9 | .DS_Store 10 | 11 | # IntelliJ files 12 | .idea_modules 13 | *.iml 14 | *.iws 15 | *.ipr 16 | .idea/ 17 | build/ 18 | */build/ 19 | out/ 20 | 21 | # Go best practices dictate that libraries should not include the vendor directory 22 | vendor 23 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @bwhaley @mcalhoun @anouarchattouna 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 HashiCorp, Inc. 2 | 3 | 4 | Apache License 5 | Version 2.0, January 2004 6 | http://www.apache.org/licenses/ 7 | 8 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 9 | 10 | 1. Definitions. 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, 13 | and distribution as defined by Sections 1 through 9 of this document. 14 | 15 | "Licensor" shall mean the copyright owner or entity authorized by 16 | the copyright owner that is granting the License. 17 | 18 | "Legal Entity" shall mean the union of the acting entity and all 19 | other entities that control, are controlled by, or are under common 20 | control with that entity. For the purposes of this definition, 21 | "control" means (i) the power, direct or indirect, to cause the 22 | direction or management of such entity, whether by contract or 23 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 24 | outstanding shares, or (iii) beneficial ownership of such entity. 25 | 26 | "You" (or "Your") shall mean an individual or Legal Entity 27 | exercising permissions granted by this License. 28 | 29 | "Source" form shall mean the preferred form for making modifications, 30 | including but not limited to software source code, documentation 31 | source, and configuration files. 32 | 33 | "Object" form shall mean any form resulting from mechanical 34 | transformation or translation of a Source form, including but 35 | not limited to compiled object code, generated documentation, 36 | and conversions to other media types. 37 | 38 | "Work" shall mean the work of authorship, whether in Source or 39 | Object form, made available under the License, as indicated by a 40 | copyright notice that is included in or attached to the work 41 | (an example is provided in the Appendix below). 42 | 43 | "Derivative Works" shall mean any work, whether in Source or Object 44 | form, that is based on (or derived from) the Work and for which the 45 | editorial revisions, annotations, elaborations, or other modifications 46 | represent, as a whole, an original work of authorship. For the purposes 47 | of this License, Derivative Works shall not include works that remain 48 | separable from, or merely link (or bind by name) to the interfaces of, 49 | the Work and Derivative Works thereof. 50 | 51 | "Contribution" shall mean any work of authorship, including 52 | the original version of the Work and any modifications or additions 53 | to that Work or Derivative Works thereof, that is intentionally 54 | submitted to Licensor for inclusion in the Work by the copyright owner 55 | or by an individual or Legal Entity authorized to submit on behalf of 56 | the copyright owner. For the purposes of this definition, "submitted" 57 | means any form of electronic, verbal, or written communication sent 58 | to the Licensor or its representatives, including but not limited to 59 | communication on electronic mailing lists, source code control systems, 60 | and issue tracking systems that are managed by, or on behalf of, the 61 | Licensor for the purpose of discussing and improving the Work, but 62 | excluding communication that is conspicuously marked or otherwise 63 | designated in writing by the copyright owner as "Not a Contribution." 64 | 65 | "Contributor" shall mean Licensor and any individual or Legal Entity 66 | on behalf of whom a Contribution has been received by Licensor and 67 | subsequently incorporated within the Work. 68 | 69 | 2. Grant of Copyright License. Subject to the terms and conditions of 70 | this License, each Contributor hereby grants to You a perpetual, 71 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 72 | copyright license to reproduce, prepare Derivative Works of, 73 | publicly display, publicly perform, sublicense, and distribute the 74 | Work and such Derivative Works in Source or Object form. 75 | 76 | 3. Grant of Patent License. Subject to the terms and conditions of 77 | this License, each Contributor hereby grants to You a perpetual, 78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 79 | (except as stated in this section) patent license to make, have made, 80 | use, offer to sell, sell, import, and otherwise transfer the Work, 81 | where such license applies only to those patent claims licensable 82 | by such Contributor that are necessarily infringed by their 83 | Contribution(s) alone or by combination of their Contribution(s) 84 | with the Work to which such Contribution(s) was submitted. If You 85 | institute patent litigation against any entity (including a 86 | cross-claim or counterclaim in a lawsuit) alleging that the Work 87 | or a Contribution incorporated within the Work constitutes direct 88 | or contributory patent infringement, then any patent licenses 89 | granted to You under this License for that Work shall terminate 90 | as of the date such litigation is filed. 91 | 92 | 4. Redistribution. You may reproduce and distribute copies of the 93 | Work or Derivative Works thereof in any medium, with or without 94 | modifications, and in Source or Object form, provided that You 95 | meet the following conditions: 96 | 97 | (a) You must give any other recipients of the Work or 98 | Derivative Works a copy of this License; and 99 | 100 | (b) You must cause any modified files to carry prominent notices 101 | stating that You changed the files; and 102 | 103 | (c) You must retain, in the Source form of any Derivative Works 104 | that You distribute, all copyright, patent, trademark, and 105 | attribution notices from the Source form of the Work, 106 | excluding those notices that do not pertain to any part of 107 | the Derivative Works; and 108 | 109 | (d) If the Work includes a "NOTICE" text file as part of its 110 | distribution, then any Derivative Works that You distribute must 111 | include a readable copy of the attribution notices contained 112 | within such NOTICE file, excluding those notices that do not 113 | pertain to any part of the Derivative Works, in at least one 114 | of the following places: within a NOTICE text file distributed 115 | as part of the Derivative Works; within the Source form or 116 | documentation, if provided along with the Derivative Works; or, 117 | within a display generated by the Derivative Works, if and 118 | wherever such third-party notices normally appear. The contents 119 | of the NOTICE file are for informational purposes only and 120 | do not modify the License. You may add Your own attribution 121 | notices within Derivative Works that You distribute, alongside 122 | or as an addendum to the NOTICE text from the Work, provided 123 | that such additional attribution notices cannot be construed 124 | as modifying the License. 125 | 126 | You may add Your own copyright statement to Your modifications and 127 | may provide additional or different license terms and conditions 128 | for use, reproduction, or distribution of Your modifications, or 129 | for any such Derivative Works as a whole, provided Your use, 130 | reproduction, and distribution of the Work otherwise complies with 131 | the conditions stated in this License. 132 | 133 | 5. Submission of Contributions. Unless You explicitly state otherwise, 134 | any Contribution intentionally submitted for inclusion in the Work 135 | by You to the Licensor shall be under the terms and conditions of 136 | this License, without any additional terms or conditions. 137 | Notwithstanding the above, nothing herein shall supersede or modify 138 | the terms of any separate license agreement you may have executed 139 | with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade 142 | names, trademarks, service marks, or product names of the Licensor, 143 | except as required for reasonable and customary use in describing the 144 | origin of the Work and reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or 147 | agreed to in writing, Licensor provides the Work (and each 148 | Contributor provides its Contributions) on an "AS IS" BASIS, 149 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 150 | implied, including, without limitation, any warranties or conditions 151 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 152 | PARTICULAR PURPOSE. You are solely responsible for determining the 153 | appropriateness of using or redistributing the Work and assume any 154 | risks associated with Your exercise of permissions under this License. 155 | 156 | 8. Limitation of Liability. In no event and under no legal theory, 157 | whether in tort (including negligence), contract, or otherwise, 158 | unless required by applicable law (such as deliberate and grossly 159 | negligent acts) or agreed to in writing, shall any Contributor be 160 | liable to You for damages, including any direct, indirect, special, 161 | incidental, or consequential damages of any character arising as a 162 | result of this License or out of the use or inability to use the 163 | Work (including but not limited to damages for loss of goodwill, 164 | work stoppage, computer failure or malfunction, or any and all 165 | other commercial damages or losses), even if such Contributor 166 | has been advised of the possibility of such damages. 167 | 168 | 9. Accepting Warranty or Additional Liability. While redistributing 169 | the Work or Derivative Works thereof, You may choose to offer, 170 | and charge a fee for, acceptance of support, warranty, indemnity, 171 | or other liability obligations and/or rights consistent with this 172 | License. However, in accepting such obligations, You may act only 173 | on Your own behalf and on Your sole responsibility, not on behalf 174 | of any other Contributor, and only if You agree to indemnify, 175 | defend, and hold each Contributor harmless for any liability 176 | incurred by, or claims asserted against, such Contributor by reason 177 | of your accepting any such warranty or additional liability. 178 | 179 | END OF TERMS AND CONDITIONS 180 | 181 | APPENDIX: How to apply the Apache License to your work. 182 | 183 | To apply the Apache License to your work, attach the following 184 | boilerplate notice, with the fields enclosed by brackets "[]" 185 | replaced with your own identifying information. (Don't include 186 | the brackets!) The text should be enclosed in the appropriate 187 | comment syntax for the file format. We also recommend that a 188 | file or class name and description of purpose be included on the 189 | same "printed page" as the copyright notice for easier 190 | identification within third-party archives. 191 | 192 | Copyright [yyyy] [name of copyright owner] 193 | 194 | Licensed under the Apache License, Version 2.0 (the "License"); 195 | you may not use this file except in compliance with the License. 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | distributed under the License is distributed on an "AS IS" BASIS, 202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 203 | See the License for the specific language governing permissions and 204 | limitations under the License. -------------------------------------------------------------------------------- /MAIN.md: -------------------------------------------------------------------------------- 1 | # Vault Cluster Example 2 | 3 | This is an example of Terraform code to deploy a [Vault](https://www.vaultproject.io/) cluster in 4 | [Azure](https://azure.microsoft.com/) using the [vault-cluster](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/vault-cluster) module. The Vault cluster uses 5 | [Consul](https://www.consul.io/) as a storage backend, so this example also deploys a separate Consul server cluster 6 | using the [consul-cluster module](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/consul-cluster) 7 | from the Consul Azure Module. 8 | 9 | This example creates a public Vault cluster that is accessible from the public Internet via an 10 | [Azure Load Balancer](https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-overview). 11 | 12 | WARNING: For production use, you should deploy the cluster without a load balancer so that it is only accessible from within 13 | your Azure account. 14 | 15 | ![Vault architecture](https://raw.githubusercontent.com/hashicorp/terraform-azurerm-vault/master/_docs/architecture-azurelb.png) 16 | 17 | You will need to create an [Azure Managed Image](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer) 18 | that has Vault and Consul installed, which you can do using the [vault-consul-image example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-consul-image). 19 | 20 | For more info on how the Vault cluster works, check out the [vault-cluster](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/vault-cluster) documentation. 21 | 22 | 23 | ## Quick start 24 | 25 | To deploy a Vault Cluster: 26 | 27 | 1. `git clone` this repo to your computer. 28 | 1. Build a Vault and Consul Azure Image. See the [vault-consul-image example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-consul-image) documentation for 29 | instructions. 30 | 1. Install [Terraform](https://www.terraform.io/). 31 | 1. Open `vars.tf`, set the environment variables specified at the top of the file, and fill in any other variables that 32 | don't have a default, including putting your AMI ID into the `ami_id` variable. 33 | 1. Run `terraform init`. 34 | 1. Run `terraform plan`. 35 | 1. If the plan looks good, run `terraform apply`. 36 | 1. Run the [vault-examples-helper.sh script](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-examples-helper/vault-examples-helper.sh) to 37 | print out the IP addresses of the Vault servers and some example commands you can run to interact with the cluster: 38 | `../vault-examples-helper/vault-examples-helper.sh`. 39 | 40 | To see how to connect to the Vault cluster, initialize it, and start reading and writing secrets, head over to the 41 | [How do you use the Vault cluster?](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/vault-cluster#how-do-you-use-the-vault-cluster) docs. 42 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | terraform-vault-azure 2 | Copyright 2017 Gruntwork, LLC 3 | 4 | This product includes software developed at 5 | Gruntwork (http://www.gruntwork.io/). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DISCLAIMER 2 | **This repository is no longer supported, please consider using [this repository](https://registry.terraform.io/modules/hashicorp/vault-starter/azure/latest) for the latest and most supported version for Vault.** 3 | 4 | Moving forward in the future this repository will be no longer supported and eventually lead to 5 | deprecation. Please use our latest versions of our products moving forward or alternatively you 6 | may fork the repository to continue use and development for your personal/business use. 7 | 8 | --- 9 | # Vault Azure Module 10 | 11 | This repo contains a Module to deploy a [Vault](https://www.vaultproject.io/) cluster on 12 | [Azure](https://azure.microsoft.com/) using [Terraform](https://www.terraform.io/). Vault is an open source tool for 13 | managing secrets. This Module uses [Azure Storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-dotnet-how-to-use-blobs) as a [storage 14 | backend](https://www.vaultproject.io/docs/configuration/storage/index.html) and a [Consul](https://www.consul.io) 15 | server cluster as a [high availability backend](https://www.vaultproject.io/docs/concepts/ha.html): 16 | 17 | ![Vault architecture](https://raw.githubusercontent.com/hashicorp/terraform-azurerm-vault/master/_docs/architecture.png) 18 | 19 | This Module includes: 20 | 21 | * [install-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault): This module can be used to install Vault. It can be used in a 22 | [Packer](https://www.packer.io/) template to create a Vault 23 | [Azure Manager Image](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer). 24 | 25 | * [run-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault): This module can be used to configure and run Vault. It can be used in a 26 | [Custom Data](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/classic/inject-custom-data) 27 | script to fire up Vault while the server is booting. 28 | 29 | * [vault-cluster](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/vault-cluster): Terraform code to deploy a cluster of Vault servers using an [Scale Set] 30 | (https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-create). 31 | 32 | * [private-tls-cert](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/private-tls-cert): Generate a private TLS certificate for use with a private Vault 33 | cluster. 34 | 35 | * [update-certificate-store](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/update-certificate-store): Add a trusted, CA public key to an OS's 36 | certificate store. This allows you to establish TLS connections to services that use this TLS certs signed by this 37 | CA without getting x509 certificate errors. 38 | 39 | 40 | 41 | 42 | ## What's a Module? 43 | 44 | A Module is a canonical, reusable, best-practices definition for how to run a single piece of infrastructure, such 45 | as a database or server cluster. Each Module is created primarily using [Terraform](https://www.terraform.io/), 46 | includes automated tests, examples, and documentation, and is maintained both by the open source community and 47 | companies that provide commercial support. 48 | 49 | Instead of having to figure out the details of how to run a piece of infrastructure from scratch, you can reuse 50 | existing code that has been proven in production. And instead of maintaining all that infrastructure code yourself, 51 | you can leverage the work of the Module community and maintainers, and pick up infrastructure improvements through 52 | a version number bump. 53 | 54 | 55 | 56 | ## Who created this Module? 57 | 58 | These modules were created by [Gruntwork](http://www.gruntwork.io/?ref=repo_azure_vault), in partnership with HashiCorp, in 2017 and maintained through 2021. They were deprecated in 2022 in favor of newer alternatives (see the top of the README for details). 59 | 60 | 61 | 62 | ## How do you use this Module? 63 | 64 | Each Module has the following folder structure: 65 | 66 | * [root](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/): The root folder contains an example of running a public Vault cluster on Azure 67 | * [modules](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules): This folder contains the reusable code for this Module, broken down into one or more modules. 68 | * [examples](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples): This folder contains examples of how to use the modules. 69 | * [test](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/test): Automated tests for the modules and examples. 70 | 71 | Click on each of the modules above for more details. 72 | 73 | To deploy Vault with this Blueprint, you will need to deploy two separate clusters: one to run 74 | [Consul](https://www.consul.io/) servers (which Vault uses as a [high availability 75 | backend](https://www.vaultproject.io/docs/concepts/ha.html)) and one to run Vault servers. 76 | 77 | To deploy the Consul server cluster, use the [Consul Azure Module](https://github.com/hashicorp/terraform-azurerm-consul). 78 | 79 | To deploy the Vault cluster: 80 | 81 | 1. Create an Azure Image that has Vault installed (using the [install-vault module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault)) and the Consul 82 | agent installed (using the [install-consul 83 | module](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/install-consul)). Here is an 84 | [example Packer template](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/examples/consul-image). 85 | 86 | 1. Deploy that Azure Image across a Scale Set in a private subnet using the Terraform [vault-cluster 87 | module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/vault-cluster). 88 | 89 | 1. Execute the [run-consul script](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/run-consul) 90 | with the `--client` flag during boot on each Instance to have the Consul agent connect to the Consul server cluster. 91 | 92 | 1. Execute the [run-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault) script during boot on each Instance to create the Vault cluster. 93 | 94 | 1. If you only need to access Vault from inside your Azure account (recommended), run the [install-dnsmasq 95 | module](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/install-dnsmasq) on each server, and 96 | that server will be able to reach Vault using the Consul Server cluster as the DNS resolver (e.g. using an address 97 | like `vault.service.consul`). See the [main example](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/MAIN.md) for working 98 | sample code. 99 | 100 | 1. Head over to the [How do you use the Vault cluster?](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/vault-cluster#how-do-you-use-the-vault-cluster) guide 101 | to learn how to initialize, unseal, and use Vault. 102 | 103 | 104 | ## How is this Module versioned? 105 | 106 | This Module follows the principles of [Semantic Versioning](http://semver.org/). You can find each new release, 107 | along with the changelog, in the [Releases Page](../../releases). 108 | 109 | During initial development, the major version will be 0 (e.g., `0.x.y`), which indicates the code does not yet have a 110 | stable API. Once we hit `1.0.0`, we will make every effort to maintain a backwards compatible API and use the MAJOR, 111 | MINOR, and PATCH versions on each release to indicate any incompatibilities. 112 | 113 | 114 | 115 | ## License 116 | 117 | This code is released under the Apache 2.0 License. Please see [LICENSE](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/LICENSE) and [NOTICE](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/NOTICE) for more 118 | details. 119 | 120 | -------------------------------------------------------------------------------- /_docs/architecture-azurelb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashicorp/terraform-azurerm-vault/8551b4666528aa98e614d979b4fa19c323444ca1/_docs/architecture-azurelb.png -------------------------------------------------------------------------------- /_docs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashicorp/terraform-azurerm-vault/8551b4666528aa98e614d979b4fa19c323444ca1/_docs/architecture.png -------------------------------------------------------------------------------- /custom-data-consul.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is meant to be run in the Custom Data of each Azure Instance while it's booting. The script uses the 3 | # run-consul script to configure and start Consul in server mode. Note that this script assumes it's running in an Azure Image 4 | # built from the Packer template in examples/consul-ami/consul.json. 5 | 6 | set -e 7 | 8 | # Send the log output from this script to custom-data.log, syslog, and the console 9 | exec > >(tee /var/log/custom-data.log|logger -t custom-data -s 2>/dev/console) 2>&1 10 | 11 | # These variables are passed in via Terraform template interplation 12 | /opt/consul/bin/run-consul --server --scale-set-name "${scale_set_name}" --subscription-id "${subscription_id}" --tenant-id "${tenant_id}" --client-id "${client_id}" --secret-access-key "${secret_access_key}" 13 | -------------------------------------------------------------------------------- /custom-data-vault.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is meant to be run in the Custom Data of each Azure Instance while it's booting. The script uses the 3 | # run-consul script to configure and start Consul in client mode and then the run-vault script to configure and start 4 | # Vault in server mode. Note that this script assumes it's running in an Azure Image built from the Packer template in 5 | # examples/vault-consul-ami/vault-consul.json. 6 | 7 | set -e 8 | 9 | # Send the log output from this script to custom-data.log, syslog, and the console 10 | exec > >(tee /var/log/custom-data.log|logger -t custom-data -s 2>/dev/console) 2>&1 11 | 12 | # The Packer template puts the TLS certs in these file paths 13 | readonly VAULT_TLS_CERT_FILE="/opt/vault/tls/vault.crt.pem" 14 | readonly VAULT_TLS_KEY_FILE="/opt/vault/tls/vault.key.pem" 15 | 16 | # The cluster_tag variables below are filled in via Terraform interpolation 17 | /opt/consul/bin/run-consul --client --scale-set-name "${scale_set_name}" --subscription-id "${subscription_id}" --tenant-id "${tenant_id}" --client-id "${client_id}" --secret-access-key "${secret_access_key}" 18 | /opt/vault/bin/run-vault --azure-account-name "${azure_account_name}" --azure-account-key "${azure_account_key}" --azure-container "${azure_container}" --tls-cert-file "$VAULT_TLS_CERT_FILE" --tls-key-file "$VAULT_TLS_KEY_FILE" 19 | -------------------------------------------------------------------------------- /examples/vault-consul-image/README.md: -------------------------------------------------------------------------------- 1 | # Vault and Consul AMI 2 | 3 | This folder shows an example of how to use the [install-vault sub-module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault) from this Module and 4 | the [install-consul](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/install-consul) 5 | and [install-dnsmasq](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/install-dnsmasq) modules 6 | from the Consul Azure Module with [Packer](https://www.packer.io/) to create an 7 | [Azure Managed Image](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer) that has 8 | Vault and Consul installed on top of Ubuntu 16.04. 9 | 10 | You can use this Image to deploy a [Vault cluster](https://www.vaultproject.io/) by using the [vault-cluster 11 | module](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/vault-cluster). This Vault cluster will use Consul as its HA backend, so you can also use the 12 | same Image to deploy a separate [Consul server cluster](https://www.consul.io/) by using the [consul-cluster 13 | module](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/consul-cluster). 14 | 15 | Check out the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) for working sample code. For more info on Vault 16 | installation and configuration, check out the [install-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault) documentation. 17 | 18 | ## Quick start 19 | 20 | To build the Vault and Consul Azure Image: 21 | 22 | 1. `git clone` this repo to your computer. 23 | 1. Install [Packer](https://www.packer.io/). 24 | 1. Configure your Azure credentials by setting the `ARM_SUBSCRIPTION_ID`, `ARM_CLIENT_ID`, `ARM_CLIENT_SECRET` and 25 | `ARM_TENANT_ID` environment variables. 26 | 27 | 1. Use the [private-tls-cert module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/private-tls-cert) to generate a CA cert and public and private keys for a 28 | TLS cert: 29 | 30 | 1. Set the `dns_names` parameter to `vault.service.consul`. If you're using the [vault-cluster-public 31 | example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-cluster-public) and want a public domain name (e.g. `vault.example.com`), add that 32 | domain name here too. 33 | 1. Set the `ip_addresses` to `127.0.0.1`. 34 | 1. For production usage, you should take care to protect the private key by encrypting it (see [Using TLS 35 | certs](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/private-tls-cert#using-tls-certs) for more info). 36 | 37 | 1. Update the `variables` section of the `vault-consul.json` Packer template to specify the Azure region, Vault 38 | version, Consul version, and the paths to the TLS cert files you just generated. 39 | 40 | 1. Run `packer build vault-consul.json`. 41 | 42 | To see how to deploy this image, check out the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md). 43 | 44 | 45 | ## Creating your own Packer template for production usage 46 | 47 | When creating your own Packer template for production usage, you can copy the example in this folder more or less 48 | exactly, except for one change: we recommend replacing the `file` provisioner with a call to `git clone` in the `shell` 49 | provisioner. Instead of: 50 | 51 | ```json 52 | { 53 | "provisioners": [{ 54 | "type": "file", 55 | "source": "{{template_dir}}/../../../terraform-vault-azure", 56 | "destination": "/tmp" 57 | },{ 58 | "type": "shell", 59 | "inline": [ 60 | "/tmp/terraform-vault-azure/tree/master/modules/install-vault/install-vault --version {{user `vault_version`}}" 61 | ], 62 | "pause_before": "30s" 63 | }] 64 | } 65 | ``` 66 | 67 | Your code should look more like this: 68 | 69 | ```json 70 | { 71 | "provisioners": [{ 72 | "type": "shell", 73 | "inline": [ 74 | "git clone --branch https://github.com/hashicorp/terraform-azurerm-vault.git /tmp/terraform-vault-azure", 75 | "/tmp/terraform-vault-azure/tree/master/modules/install-vault/install-vault --version {{user `vault_version`}}" 76 | ], 77 | "pause_before": "30s" 78 | }] 79 | } 80 | ``` 81 | 82 | You should replace `` in the code above with the version of this module that you want to use (see 83 | the [Releases Page](../../releases) for all available versions). That's because for production usage, you should always 84 | use a fixed, known version of this Module, downloaded from the official Git repo. On the other hand, when you're 85 | just experimenting with the module, it's OK to use a local checkout of the Module, uploaded from your own 86 | computer. -------------------------------------------------------------------------------- /examples/vault-consul-image/tls/README.md: -------------------------------------------------------------------------------- 1 | # Example TLS Certificate Files 2 | 3 | ### Do NOT use these files in production! 4 | 5 | In a production setting, your TLS private key represents a critical secret. If it were stolen, its possessor could 6 | impersonate your Vault server! For that reason, do NOT use these TLS certificate files in a public setting. They are 7 | here only for convenience when building examples. 8 | 9 | ### Files 10 | 11 | The files in this folder are needed by Vault to accept HTTPS requests. They are: 12 | 13 | - **ca.crt.pem**: The public certificate of the Certificate Authority used to create these files. 14 | - **vault.crt.pem:** The TLS public certificate issued by the Certificate Authority of the Vault server. 15 | - **vault.key.pem:** The TLS private key that corresponds to the TLS public certificate. 16 | 17 | The TLS files are configured as follows: 18 | 19 | - The Vault Server may be reached via TLS at `vault.service.consul`, `vault.example.com`, or `127.0.0.1`. 20 | - The TLS certificate is valid until May 26, 2042. -------------------------------------------------------------------------------- /examples/vault-consul-image/tls/ca.crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDnTCCAoWgAwIBAgIRAMGFenE97vbqkhrN7fhHDxUwDQYJKoZIhvcNAQELBQAw 3 | aDEJMAcGA1UEBhMAMQkwBwYDVQQIEwAxCTAHBgNVBAcTADEJMAcGA1UEERMAMRIw 4 | EAYDVQQKEwlIYXNoaUNvcnAxCTAHBgNVBAsTADEbMBkGA1UEAxMSSGFzaGlDb3Jw 5 | IFZhdWx0IENBMB4XDTE3MDUyNzA0MjQzNVoXDTQyMDUyMTA0MjQzNVowaDEJMAcG 6 | A1UEBhMAMQkwBwYDVQQIEwAxCTAHBgNVBAcTADEJMAcGA1UEERMAMRIwEAYDVQQK 7 | EwlIYXNoaUNvcnAxCTAHBgNVBAsTADEbMBkGA1UEAxMSSGFzaGlDb3JwIFZhdWx0 8 | IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4+Ur+WKS8zypyF5l 9 | IbvuzIzrk4V77F6LaLbpcOvLZ16LGIwCnVRIQybCTRjSg75Cb3FDQofL8mbaHDwS 10 | G7JUnCXtHxlVUllIEDbJchva1RgGSBHJF2k4JEXI1O11amajoU1N9nmo8EMVu/tr 11 | cBNKp746i1DOSx23xmVVNSkNEFd91YcAyR51pwWOdv0zchCZrP39V6mFM2JF7/hU 12 | 7s0Y9hn5LCzU7jlsgxg8FI0O2wWvLZt2SjuW+g2oJnlYQjQHic7qyoEGmJGaBPTW 13 | KPcsH8IcUe1N33J2D5ejTQXwv3dRclLD5slzQbAEWVarWdZ69O5C3RIGu9gm0v7Q 14 | tMkJfQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAd 15 | BgNVHQ4EFgQUKr006sVeVWdjTWDzNV096v03sdswDQYJKoZIhvcNAQELBQADggEB 16 | AF9Il2A1Qoc6r1IQFBs2gE5bSHXVHiuU/ykFaBURU9WCPl9a7oIiKtqnHhhCHokm 17 | qAW4v5VoH7JvhgVxJx6DhgKw6GV1KLWmUIL12asBLfubjaCUeedNNFrO1VSkuA7l 18 | RDSnhzdPk6O4MFa71/Uwo3E91c3igHKfKdxQZ1CcR/iyx2duGTnQDTMkyO8iHzJI 19 | Cs9as+PZzibqlsnpgyUyviCkGYjqH2o/LTsTGKtSru+erPAN3smzfG/wZXRrHyyz 20 | wjid2nbi7gASZVXUocclU9zl253+8ALpGKlDkuvQUJSCEraC7jtMQx16W8ad2aXL 21 | LnnabgKz01Lzggpvfdal+a4= 22 | -----END CERTIFICATE----- 23 | 24 | -------------------------------------------------------------------------------- /examples/vault-consul-image/tls/vault.crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID1zCCAr+gAwIBAgIQdQvxfTEZADMpi5RQmp1FCTANBgkqhkiG9w0BAQsFADBo 3 | MQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkwBwYDVQQREwAxEjAQ 4 | BgNVBAoTCUhhc2hpQ29ycDEJMAcGA1UECxMAMRswGQYDVQQDExJIYXNoaUNvcnAg 5 | VmF1bHQgQ0EwHhcNMTcwNTI3MDQyNDM1WhcNNDIwNTIxMDQyNDM1WjBqMQkwBwYD 6 | VQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkwBwYDVQQREwAxEjAQBgNVBAoT 7 | CUhhc2hpQ29ycDEJMAcGA1UECxMAMR0wGwYDVQQDExRIYXNoaUNvcnAgVmF1bHQg 8 | Q2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQT46pH/1Ljy+6V 9 | h6EKHsJoSBiC7DolQY4tPpPlZpNEEIgftYgq2KA9DJTYDzpLy3sHkUNQmc0eIfUy 10 | xNklHE5N6WcK9oozo4LNIPd4//Tn90maUGG3z4SIan8A+wVuAhz/t8BfuAdmOeax 11 | zjs9mqbqSPiKcBx5LFjWvt30Hq5idup4c/69v8ByimqHuS2PJtv/C6/b2NU6Bq/9 12 | Y5fC1Oxb2iONrhgTYHF44qHpxL6VeSwVxHzUhxe+EaCSbGvNuAe2i/yqffhy/LFu 13 | T15jsiaHfDhj4H3I16hLz/BN49NhNzhJeFG+S3S65JL4P35UJxaRLHu925myOntr 14 | 5QPJujsCAwEAAaN7MHkwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHwYD 15 | VR0jBBgwFoAUKr006sVeVWdjTWDzNV096v03sdswOAYDVR0RBDEwL4IUdmF1bHQu 16 | c2VydmljZS5jb25zdWyCEXZhdWx0LmV4YW1wbGUuY29thwR/AAABMA0GCSqGSIb3 17 | DQEBCwUAA4IBAQCY3I2sbebudms8J9gSxbcTE0GoOpJJ7bBdYEKp7TmshrL8ni9L 18 | S9skHbKcpnn4ZHv9ofjtDJ1LfN1TWTHBTZP92GbAZPAl/TdNMOXdGRpsKBo2ElLy 19 | +RoQWm0YBofi9vkjaUSZMHN7LHZqOnaAEtHcBaRj7eTmOvitim+o5Tw6tpjuXEYf 20 | Y9BgwhlXsm5Y4n3ownbU7A5PsKn0G1/0xykR6UJqYlpEypiZWxuaWQNW/B1cJxQv 21 | ghDwg05HzCccmxMAJLgZrPpA6FclQD5mfJN6+0AaWv1iZFz7LaaXR2QV41PtZ83d 22 | EKoEz9uM8uqoRhQR874axQaBoC4CJNc09J4U 23 | -----END CERTIFICATE----- 24 | 25 | -------------------------------------------------------------------------------- /examples/vault-consul-image/tls/vault.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEApBPjqkf/UuPL7pWHoQoewmhIGILsOiVBji0+k+Vmk0QQiB+1 3 | iCrYoD0MlNgPOkvLeweRQ1CZzR4h9TLE2SUcTk3pZwr2ijOjgs0g93j/9Of3SZpQ 4 | YbfPhIhqfwD7BW4CHP+3wF+4B2Y55rHOOz2apupI+IpwHHksWNa+3fQermJ26nhz 5 | /r2/wHKKaoe5LY8m2/8Lr9vY1ToGr/1jl8LU7FvaI42uGBNgcXjioenEvpV5LBXE 6 | fNSHF74RoJJsa824B7aL/Kp9+HL8sW5PXmOyJod8OGPgfcjXqEvP8E3j02E3OEl4 7 | Ub5LdLrkkvg/flQnFpEse73bmbI6e2vlA8m6OwIDAQABAoIBAF0O8sb3QraOgHF2 8 | 2Y/an4t/fbR5POXVj5LF0oIrT7wilIXABkOOmYJ4XZRl3m0f4+6JYjgdlL2jY3sg 9 | KklGJQG8aq6Ipz/G/ewHz7TMKc+LaNOT9BcYG1h9znjt43E27XfpCRzQrR11O02+ 10 | dsteq0IUCwL78Y4Uo7RXR7W26VfZlEDKhIjbuNSxlU4BnAD7e/HsvB5pmPXsbj4O 11 | 7dkiW6/6XJPZdSpSdc5C9v0nToTw31KpFqODmxL2Iy0kpWvOLJZKIW20cKFSCjo8 12 | 4gu5eog4liQn+wpG9/Czhd/XfXxkg8MdOjpzIlkkC4lUM53rxPmlfbBtiA85um/6 13 | KpZ7M3ECgYEAzBRroR2hrKBo9lqx1Ein6PNEmrLbfhVOXHQZvlczcicO1WeLRbZ9 14 | lrHNzLlVRkY4UiS10VfYUhjX4uLU6GTHYotZ+dJ+B6eVr1qf/K8DWJjhX98CQ1hZ 15 | RdnnQEdIdC7SqPhDo9PKcc7WjOTq1LwawTQneNFDDol6PgBMOS8X9MUCgYEAzdIp 16 | DWzraU9EenUyNgkz55MEsxyryHefRSqtQB53d7Eic4kSJSKm2pcKtrKxJbgTSqAe 17 | MeWs+MT7i8QCScWSJJU7xpP6FhowTABEPrUxR8lFbLfxV9EwzCTKXWpBRwI5SkZ2 18 | YuxOClpZgRAdf8qlCjgEvSvCW0C4aKCA8XTr4v8CgYEAif380NKScYF9t6aXu+zs 19 | 7I0hhGEQHW8Wr1kp1xRrivJyC0aaW6cLwIu1lopy6LOufYypDEaT1N9LivTJ9eG/ 20 | GBkV2+DCqzZb9lgW+er1HkExk3vdsd/ZbWvr/AC3Myg99Vb5lZsttkqftGFNkE8o 21 | 4B792anV58x5xda5s6juT+kCgYEAuEIQDHvZGJMlO/gVdniwpf1hNLRTOYmV5Yb6 22 | SS+9RkMnE4W/38zw0TptFfhNTPZJFwLXZVY3jxJSG+LjJYYhemy7ceBiAE17tV57 23 | uiPeNWUDqPvXrQWTCP9ax1xrihV8knkYXSEuEGioPjneHtyc+dQCshQt0CHVtZ0T 24 | Mpa44qMCgYAExVJEJAsz5rL09ku9IcdUc/b72Xsl2S83esDmohKLt8M/HDaiT9nC 25 | j3pXg4UBIXN+emhR5Lbh+R4QBrclnz3kxuiTc3JEHXAkyrL+DOJnED1qggNKz1Dj 26 | dh5t7ysHNq31750ZssKMoaDj1V1mzbW+qZ3NVbpMZrKRkVs+ixe8Hg== 27 | -----END RSA PRIVATE KEY----- 28 | 29 | -------------------------------------------------------------------------------- /examples/vault-consul-image/vault-consul.json: -------------------------------------------------------------------------------- 1 | { 2 | "min_packer_version": "0.12.0", 3 | "variables": { 4 | "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", 5 | "tenant_id": "{{env `ARM_TENANT_ID`}}", 6 | "client_id": "{{env `ARM_CLIENT_ID`}}", 7 | "client_secret": "{{env `ARM_CLIENT_SECRET`}}", 8 | "azure_location": null, 9 | "consul_module_version": "v0.0.1", 10 | "consul_version": "0.9.2", 11 | "vault_version": "0.8.2", 12 | "github_oauth_token": "{{env `GITHUB_OAUTH_TOKEN`}}", 13 | "resource_group_name": null, 14 | "ca_public_key_path": null, 15 | "tls_public_key_path": null, 16 | "tls_private_key_path": null 17 | }, 18 | "builders": [ 19 | { 20 | "type": "azure-arm", 21 | "subscription_id": "{{user `subscription_id`}}", 22 | "tenant_id": "{{user `tenant_id`}}", 23 | "client_id": "{{user `client_id`}}", 24 | "client_secret": "{{user `client_secret`}}", 25 | "managed_image_name": "vault-consul-ubuntu-{{isotime \"2006-01-02-150405\"}}", 26 | "managed_image_resource_group_name": "{{user `resource_group_name`}}", 27 | "os_type": "Linux", 28 | "image_publisher": "Canonical", 29 | "image_offer": "UbuntuServer", 30 | "image_sku": "16.04.0-LTS", 31 | "location": "{{user `azure_location`}}", 32 | "vm_size": "Standard_DS2_v2" 33 | } 34 | ], 35 | "provisioners": [ 36 | { 37 | "type": "file", 38 | "source": "{{template_dir}}/../../../terraform-vault-azure", 39 | "destination": "/tmp" 40 | }, 41 | { 42 | "type": "file", 43 | "source": "{{user `ca_public_key_path`}}", 44 | "destination": "/tmp/ca.crt.pem" 45 | }, 46 | { 47 | "type": "file", 48 | "source": "{{user `tls_public_key_path`}}", 49 | "destination": "/tmp/vault.crt.pem" 50 | }, 51 | { 52 | "type": "file", 53 | "source": "{{user `tls_private_key_path`}}", 54 | "destination": "/tmp/vault.key.pem" 55 | }, 56 | { 57 | "type": "shell", 58 | "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", 59 | "inline_shebang": "/bin/sh -x", 60 | "inline": [ 61 | "DEBIAN_FRONTEND=noninteractive apt-get -y upgrade", 62 | "echo \"deb https://packages.microsoft.com/repos/azure-cli/ wheezy main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list", 63 | "apt-key adv --keyserver packages.microsoft.com --recv-keys 417A0893", 64 | "apt-get update && apt-get install -y git libssl-dev libffi-dev python-dev build-essential apt-transport-https azure-cli", 65 | 66 | "/tmp/terraform-vault-azure/modules/install-vault/install-vault --version {{user `vault_version`}}", 67 | "git clone --branch {{user `consul_module_version`}} https://{{user `github_oauth_token`}}@github.com/hashicorp/terraform-azurerm-consul.git /tmp/terraform-consul-azure", 68 | "/tmp/terraform-consul-azure/modules/install-consul/install-consul --version {{user `consul_version`}}", 69 | "/tmp/terraform-consul-azure/modules/install-dnsmasq/install-dnsmasq" 70 | ] 71 | }, 72 | { 73 | "type": "shell", 74 | "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", 75 | "inline_shebang": "/bin/sh -x", 76 | "inline": [ 77 | "mv /tmp/ca.crt.pem /opt/vault/tls/", 78 | "mv /tmp/vault.crt.pem /opt/vault/tls/", 79 | "mv /tmp/vault.key.pem /opt/vault/tls/", 80 | "chown vault:vault /opt/vault/tls/*", 81 | "chmod 600 /opt/vault/tls/*", 82 | "/tmp/terraform-vault-azure/modules/update-certificate-store/update-certificate-store --cert-file-path /opt/vault/tls/ca.crt.pem" 83 | ] 84 | }, 85 | { 86 | "type": "shell", 87 | "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", 88 | "inline": [ 89 | "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" 90 | ], 91 | "inline_shebang": "/bin/sh -x" 92 | } 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /examples/vault-examples-helper/README.md: -------------------------------------------------------------------------------- 1 | # Vault Examples Helper 2 | 3 | This folder contains a helper script called `vault-examples-helper.sh` for working with the 4 | [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) After running `terraform apply`, if you 5 | run `vault-examples-helper.sh`, it will automatically: 6 | 7 | 1. Wait for the Vault server cluster to come up. 8 | 1. Print out the IP address of the Vault load balancer. 9 | 1. Print out some example commands you can run against your Vault servers. 10 | 11 | Please note that this helper script only works because the examples deploy with a public load balancer. 12 | As a result, Vault is publicly accessible. This is OK for testing and learning, but for production usage, we strongly 13 | recommend running Vault in private subnets. 14 | -------------------------------------------------------------------------------- /examples/vault-examples-helper/vault-examples-helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A script that is meant to be used with the private Valut cluster examples to: 3 | # 4 | # 1. Wait for the Vault server cluster to come up. 5 | # 2. Print out the IP addresses of the Vault servers. 6 | # 3. Print out some example commands you can run against your Vault servers. 7 | 8 | set -e 9 | 10 | readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 11 | readonly SCRIPT_NAME="$(basename "$0")" 12 | 13 | readonly MAX_RETRIES=30 14 | readonly SLEEP_BETWEEN_RETRIES_SEC=10 15 | 16 | function log { 17 | local readonly level="$1" 18 | local readonly message="$2" 19 | local readonly timestamp=$(date +"%Y-%m-%d %H:%M:%S") 20 | >&2 echo -e "${timestamp} [${level}] [$SCRIPT_NAME] ${message}" 21 | } 22 | 23 | function log_info { 24 | local readonly message="$1" 25 | log "INFO" "$message" 26 | } 27 | 28 | function log_warn { 29 | local readonly message="$1" 30 | log "WARN" "$message" 31 | } 32 | 33 | function log_error { 34 | local readonly message="$1" 35 | log "ERROR" "$message" 36 | } 37 | 38 | function assert_is_installed { 39 | local readonly name="$1" 40 | 41 | if [[ ! $(command -v ${name}) ]]; then 42 | log_error "The binary '$name' is required by this script but is not installed or in the system's PATH." 43 | exit 1 44 | fi 45 | } 46 | 47 | function get_optional_terraform_output { 48 | local readonly output_name="$1" 49 | terraform output -no-color "$output_name" 50 | } 51 | 52 | function get_required_terraform_output { 53 | local readonly output_name="$1" 54 | local output_value 55 | 56 | output_value=$(get_optional_terraform_output "$output_name") 57 | 58 | if [[ -z "$output_value" ]]; then 59 | log_error "Unable to find a value for Terraform output $output_name" 60 | exit 1 61 | fi 62 | 63 | echo "$output_value" 64 | } 65 | 66 | # 67 | # Usage: join SEPARATOR ARRAY 68 | # 69 | # Joins the elements of ARRAY with the SEPARATOR character between them. 70 | # 71 | # Examples: 72 | # 73 | # join ", " ("A" "B" "C") 74 | # Returns: "A, B, C" 75 | # 76 | function join { 77 | local readonly separator="$1" 78 | shift 79 | local readonly values=("$@") 80 | 81 | printf "%s$separator" "${values[@]}" | sed "s/$separator$//" 82 | } 83 | 84 | function wait_for_vault_server_to_come_up { 85 | local readonly server_ip="$1" 86 | 87 | for (( i=1; i<="$MAX_RETRIES"; i++ )); do 88 | local readonly vault_health_url="https://$server_ip:8200/v1/sys/health" 89 | log_info "Checking health of Vault server via URL $vault_health_url" 90 | 91 | local response 92 | local status 93 | local body 94 | 95 | response=$(curl --show-error --location --insecure --silent --write-out "HTTPSTATUS:%{http_code}" "$vault_health_url" || true) 96 | status=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') 97 | body=$(echo "$response" | sed -e 's/HTTPSTATUS\:.*//g') 98 | 99 | log_info "Got a $status response from Vault server $server_ip with body:\n$body" 100 | 101 | # Response code for the health check endpoint are defined here: https://www.vaultproject.io/api/system/health.html 102 | 103 | if [[ "$status" -eq 200 ]]; then 104 | log_info "Vault server $server_ip is initialized, unsealed, and active." 105 | return 106 | elif [[ "$status" -eq 429 ]]; then 107 | log_info "Vault server $server_ip is unsealed and in standby mode." 108 | return 109 | elif [[ "$status" -eq 501 ]]; then 110 | log_info "Vault server $server_ip is uninitialized." 111 | return 112 | elif [[ "$status" -eq 503 ]]; then 113 | log_info "Vault server $server_ip is sealed." 114 | return 115 | else 116 | log_info "Vault server $server_ip returned unexpected status code $status. Will sleep for $SLEEP_BETWEEN_RETRIES_SEC seconds and check again." 117 | sleep "$SLEEP_BETWEEN_RETRIES_SEC" 118 | fi 119 | done 120 | 121 | log_error "Did not get a successful response code from Vault server $server_ip after $MAX_RETRIES retries." 122 | exit 1 123 | } 124 | 125 | function print_instructions { 126 | local num_servers 127 | local load_balancer_ip 128 | local admin_user_name 129 | 130 | num_servers=$(get_required_terraform_output "vault_cluster_size") 131 | load_balancer_ip=$(get_required_terraform_output "load_balancer_ip_address") 132 | admin_user_name=$(get_required_terraform_output "vault_admin_user_name") 133 | 134 | local instructions=() 135 | instructions+=("\nYour Vault servers are running behind the load balancer at the following IP address:\n\n${load_balancer_ip/#/ }\n") 136 | 137 | instructions+=("To initialize your Vault cluster, SSH to the first of the servers and run the init command:\n") 138 | instructions+=(" ssh -p 2200 $admin_user_name@$load_balancer_ip") 139 | instructions+=(" vault init") 140 | 141 | instructions+=("\nTo unseal your Vault cluster, SSH to each of the servers and run the unseal command with 3 of the 5 unseal keys:\n") 142 | local counter 143 | counter=0 144 | 145 | while [[ $counter -lt $num_servers ]]; do 146 | instructions+=(" ssh -p 220${counter} $admin_user_name@$load_balancer_ip") 147 | instructions+=(" vault unseal (run this 3 times)\n") 148 | let counter=counter+1 149 | done 150 | 151 | instructions+=("\nOnce your cluster is unsealed, you can read and write secrets via the load balancer:\n") 152 | instructions+=(" vault auth -address=https://$load_balancer_ip") 153 | instructions+=(" vault write -address=https://$load_balancer_ip secret/example value=secret") 154 | instructions+=(" vault read -address=https://$load_balancer_ip secret/example") 155 | 156 | local instructions_str 157 | instructions_str=$(join "\n" "${instructions[@]}") 158 | 159 | echo -e "$instructions_str\n" 160 | } 161 | 162 | function run { 163 | assert_is_installed "jq" 164 | assert_is_installed "terraform" 165 | assert_is_installed "curl" 166 | 167 | local load_balancer_ip=$(get_required_terraform_output "load_balancer_ip_address") 168 | 169 | wait_for_vault_server_to_come_up "$load_balancer_ip" 170 | print_instructions 171 | } 172 | 173 | run -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # DEPLOY A VAULT CLUSTER IN AZURE 3 | # These configurations show an example of how to use the consul-cluster module to deploy Consul in Azure. We deploy two 4 | # Scale Sets: one with Consul server nodes and one with Consul client nodes. Note that these templates assume 5 | # that the Custom Image you provide via the image_id input variable is built from the 6 | # examples/consul-image/consul.json Packer template. 7 | # --------------------------------------------------------------------------------------------------------------------- 8 | 9 | provider "azurerm" { 10 | subscription_id = "${var.subscription_id}" 11 | client_id = "${var.client_id}" 12 | client_secret = "${var.secret_access_key}" 13 | tenant_id = "${var.tenant_id}" 14 | } 15 | 16 | terraform { 17 | required_version = ">= 0.10.0" 18 | } 19 | 20 | # --------------------------------------------------------------------------------------------------------------------- 21 | # CREATE THE NECESSARY NETWORK RESOURCES FOR THE EXAMPLE 22 | # --------------------------------------------------------------------------------------------------------------------- 23 | resource "azurerm_virtual_network" "consul" { 24 | name = "consulvn" 25 | address_space = ["${var.address_space}"] 26 | location = "${var.location}" 27 | resource_group_name = "${var.resource_group_name}" 28 | } 29 | 30 | resource "azurerm_subnet" "consul" { 31 | name = "consulsubnet" 32 | resource_group_name = "${var.resource_group_name}" 33 | virtual_network_name = "${azurerm_virtual_network.consul.name}" 34 | address_prefix = "${var.subnet_address}" 35 | } 36 | 37 | # --------------------------------------------------------------------------------------------------------------------- 38 | # DEPLOY THE CONSUL SERVER NODES 39 | # --------------------------------------------------------------------------------------------------------------------- 40 | 41 | module "consul_servers" { 42 | source = "git::git@github.com:hashicorp/terraform-azurerm-consul.git//modules/consul-cluster?ref=v0.0.1" 43 | 44 | cluster_name = "${var.consul_cluster_name}" 45 | cluster_size = "${var.num_consul_servers}" 46 | key_data = "${var.key_data}" 47 | 48 | resource_group_name = "${var.resource_group_name}" 49 | storage_account_name = "${var.storage_account_name}" 50 | 51 | location = "${var.location}" 52 | custom_data = "${data.template_file.custom_data_consul.rendered}" 53 | instance_size = "${var.instance_size}" 54 | image_id = "${var.image_uri}" 55 | subnet_id = "${azurerm_subnet.consul.id}" 56 | allowed_inbound_cidr_blocks = [] 57 | } 58 | 59 | # --------------------------------------------------------------------------------------------------------------------- 60 | # THE CUSTOM DATA SCRIPT THAT WILL RUN ON EACH CONSUL SERVER AZURE INSTANCE WHEN IT'S BOOTING 61 | # This script will configure and start Consul 62 | # --------------------------------------------------------------------------------------------------------------------- 63 | 64 | data "template_file" "custom_data_consul" { 65 | template = "${file("${path.module}/custom-data-consul.sh")}" 66 | 67 | vars { 68 | scale_set_name = "${var.consul_cluster_name}" 69 | subscription_id = "${var.subscription_id}" 70 | tenant_id = "${var.tenant_id}" 71 | client_id = "${var.client_id}" 72 | secret_access_key = "${var.secret_access_key}" 73 | } 74 | } 75 | 76 | # --------------------------------------------------------------------------------------------------------------------- 77 | # CREATE THE STORAGE 78 | # --------------------------------------------------------------------------------------------------------------------- 79 | resource "azurerm_storage_container" "vault_storage" { 80 | name = "vault" 81 | resource_group_name = "${var.resource_group_name}" 82 | storage_account_name = "${var.storage_account_name}" 83 | container_access_type = "private" 84 | } 85 | 86 | # --------------------------------------------------------------------------------------------------------------------- 87 | # DEPLOY THE VAULT SERVER NODES 88 | # --------------------------------------------------------------------------------------------------------------------- 89 | 90 | module "vault_servers" { 91 | # When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you 92 | # to a specific version of the modules, such as the following example: 93 | # source = "git::git@github.com:hashicorp/terraform-azurerm-vault.git//modules/vault-cluster?ref=v0.0.1" 94 | source = "./modules/vault-cluster" 95 | 96 | cluster_name = "${var.vault_cluster_name}" 97 | cluster_size = "${var.num_vault_servers}" 98 | key_data = "${var.key_data}" 99 | 100 | resource_group_name = "${var.resource_group_name}" 101 | storage_account_name = "${var.storage_account_name}" 102 | 103 | location = "${var.location}" 104 | custom_data = "${data.template_file.custom_data_vault.rendered}" 105 | instance_size = "${var.instance_size}" 106 | image_id = "${var.image_uri}" 107 | subnet_id = "${azurerm_subnet.consul.id}" 108 | storage_container_name = "vault" 109 | associate_public_ip_address_load_balancer = true 110 | } 111 | 112 | # --------------------------------------------------------------------------------------------------------------------- 113 | # THE CUSTOM DATA SCRIPT THAT WILL RUN ON EACH CONSUL SERVER AZURE INSTANCE WHEN IT'S BOOTING 114 | # This script will configure and start Consul 115 | # --------------------------------------------------------------------------------------------------------------------- 116 | data "template_file" "custom_data_vault" { 117 | template = "${file("${path.module}/custom-data-vault.sh")}" 118 | 119 | vars { 120 | scale_set_name = "${var.consul_cluster_name}" 121 | subscription_id = "${var.subscription_id}" 122 | tenant_id = "${var.tenant_id}" 123 | client_id = "${var.client_id}" 124 | secret_access_key = "${var.secret_access_key}" 125 | azure_account_name = "${var.storage_account_name}" 126 | azure_account_key = "${var.storage_account_key}" 127 | azure_container = "${azurerm_storage_container.vault_storage.name}" 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /modules/install-vault/README.md: -------------------------------------------------------------------------------- 1 | # Vault Install Script 2 | 3 | This folder contains a script for installing Vault and its dependencies. You can use this script, along with the 4 | [run-vault script](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault) it installs, to create a Vault [Azure Managed Image 5 | ](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer) that can be deployed in 6 | [Azure](https://azure.microsoft.com/) across an Scale Set using the [vault-cluster module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/vault-cluster). 7 | 8 | This script has been tested on Ubuntu 16.04. There is a good chance it will work on other flavors of Debian as well. 9 | 10 | ## Quick start 11 | 12 | To install Vault, use `git` to clone this repository at a specific tag (see the [releases page](../../../../releases) 13 | for all available tags) and run the `install-vault` script: 14 | 15 | ``` 16 | git clone --branch https://github.com/hashicorp/terraform-azurerm-vault.git 17 | terraform-vault-azure/tree/master/modules/install-vault/install-vault --version 0.5.4 18 | ``` 19 | 20 | The `install-vault` script will install Vault, its dependencies, and the [run-vault script](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault). 21 | You can then run the `run-vault` script when the server is booting to start Vault. 22 | 23 | We recommend running the `install-vault` script as part of a [Packer](https://www.packer.io/) template to create a 24 | Vault [Azure Managed Image](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer) (see the 25 | [vault-consul-image example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-consul-image) 26 | for sample code). You can then deploy the Azure Image across a Scale Set 27 | (see the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) for fully-working sample code). 28 | 29 | ## Command line Arguments 30 | 31 | The `install-vault` script accepts the following arguments: 32 | 33 | * `version VERSION`: Install Vault version VERSION. Required. 34 | * `path DIR`: Install Vault into folder DIR. Optional. 35 | * `user USER`: The install dirs will be owned by user USER. Optional. 36 | 37 | Example: 38 | 39 | ``` 40 | install-vault --version 0.7.0 41 | ``` 42 | 43 | 44 | 45 | ## How it works 46 | 47 | The `install-vault` script does the following: 48 | 49 | 1. [Create a user and folders for Vault](#create-a-user-and-folders-for-vault) 50 | 1. [Install Vault binaries and scripts](#install-vault-binaries-and-scripts) 51 | 1. [Configure mlock](#configure-mlock) 52 | 1. [Install supervisord](#install-supervisord) 53 | 1. [Follow-up tasks](#follow-up-tasks) 54 | 55 | 56 | ### Create a user and folders for Vault 57 | 58 | Create an OS user named `vault`. Create the following folders, all owned by user `vault`: 59 | 60 | * `/opt/vault`: base directory for Vault data (configurable via the `--path` argument). 61 | * `/opt/vault/bin`: directory for Vault binaries. 62 | * `/opt/vault/data`: directory where the Vault agent can store state. 63 | * `/opt/vault/config`: directory where the Vault agent looks up configuration. 64 | * `/opt/vault/log`: directory where the Vault agent will store log files. 65 | * `/opt/vault/tls`: directory where the Vault will look for TLS certs. 66 | 67 | 68 | ### Install Vault binaries and scripts 69 | 70 | Install the following: 71 | 72 | * `vault`: Download the Vault zip file from the [downloads page](https://www.vaultproject.io/downloads.html) (the 73 | version number is configurable via the `--version` argument), and extract the `vault` binary into 74 | `/opt/vault/bin`. Add a symlink to the `vault` binary in `/usr/local/bin`. 75 | * `run-vault`: Copy the [run-vault script](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault) into `/opt/vault/bin`. 76 | 77 | 78 | ### Configure mlock 79 | 80 | Give Vault permissions to make the `mlock` (memory lock) syscall. This syscall is used to prevent the OS from swapping 81 | Vault's memory to disk. For more info, see: https://www.vaultproject.io/docs/configuration/#disable_mlock. 82 | 83 | 84 | ### Install supervisord 85 | 86 | Install [supervisord](http://supervisord.org/). We use it as a cross-platform supervisor to ensure Vault is started 87 | whenever the system boots and restarted if the Vault process crashes. 88 | 89 | 90 | ### Follow-up tasks 91 | 92 | After the `install-vault` script finishes running, you may wish to do the following: 93 | 94 | 1. If you have custom Vault config (`.hcl`) files, you may want to copy them into the config directory (default: 95 | `/opt/vault/config`). 96 | 1. If `/usr/local/bin` isn't already part of `PATH`, you should add it so you can run the `vault` command without 97 | specifying the full path. 98 | 99 | -------------------------------------------------------------------------------- /modules/install-vault/install-vault: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script can be used to install Vault and its dependencies. This script has been tested with the following 3 | # operating systems: 4 | # 5 | # 1. Ubuntu 16.04 6 | 7 | set -e 8 | 9 | readonly DEFAULT_INSTALL_PATH="/opt/vault" 10 | readonly DEFAULT_VAULT_USER="vault" 11 | 12 | readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13 | readonly SYSTEM_BIN_DIR="/usr/local/bin" 14 | 15 | readonly SUPERVISOR_DIR="/etc/supervisor" 16 | readonly SUPERVISOR_CONF_DIR="$SUPERVISOR_DIR/conf.d" 17 | 18 | readonly SCRIPT_NAME="$(basename "$0")" 19 | 20 | function print_usage { 21 | echo 22 | echo "Usage: install-vault [OPTIONS]" 23 | echo 24 | echo "This script can be used to install Vault and its dependencies. This script has been tested with Ubuntu 16.04 and Amazon Linux." 25 | echo 26 | echo "Options:" 27 | echo 28 | echo -e " --version\t\tThe version of Vault to install. Required." 29 | echo -e " --path\t\tThe path where Vault should be installed. Optional. Default: $DEFAULT_INSTALL_PATH." 30 | echo -e " --user\t\tThe user who will own the Vault install directories. Optional. Default: $DEFAULT_VAULT_USER." 31 | echo 32 | echo "Example:" 33 | echo 34 | echo " install-vault --version 0.7.0" 35 | } 36 | 37 | function log { 38 | local readonly level="$1" 39 | local readonly message="$2" 40 | local readonly timestamp=$(date +"%Y-%m-%d %H:%M:%S") 41 | >&2 echo -e "${timestamp} [${level}] [$SCRIPT_NAME] ${message}" 42 | } 43 | 44 | function log_info { 45 | local readonly message="$1" 46 | log "INFO" "$message" 47 | } 48 | 49 | function log_warn { 50 | local readonly message="$1" 51 | log "WARN" "$message" 52 | } 53 | 54 | function log_error { 55 | local readonly message="$1" 56 | log "ERROR" "$message" 57 | } 58 | 59 | function assert_not_empty { 60 | local readonly arg_name="$1" 61 | local readonly arg_value="$2" 62 | 63 | if [[ -z "$arg_value" ]]; then 64 | log_error "The value for '$arg_name' cannot be empty" 65 | print_usage 66 | exit 1 67 | fi 68 | } 69 | 70 | # Install steps are based on: http://unix.stackexchange.com/a/291098/215969 71 | function install_supervisord { 72 | sudo apt-get install -y supervisor 73 | sudo update-rc.d supervisor defaults 74 | 75 | create_supervisor_config 76 | sudo systemctl enable supervisor 77 | } 78 | 79 | function create_supervisor_config { 80 | sudo mkdir -p "$SUPERVISOR_CONF_DIR" 81 | sudo cp "$SCRIPT_DIR/supervisord.conf" "$SUPERVISOR_DIR/supervisord.conf" 82 | } 83 | 84 | function install_dependencies { 85 | log_info "Installing dependencies" 86 | 87 | sudo apt-get update -y 88 | sudo apt-get install -y azure-cli curl unzip jq 89 | install_supervisord 90 | } 91 | 92 | function user_exists { 93 | local readonly username="$1" 94 | id "$username" >/dev/null 2>&1 95 | } 96 | 97 | function create_vault_user { 98 | local readonly username="$1" 99 | 100 | if $(user_exists "$username"); then 101 | echo "User $username already exists. Will not create again." 102 | else 103 | log_info "Creating user named $username" 104 | sudo useradd "$username" 105 | fi 106 | } 107 | 108 | function create_vault_install_paths { 109 | local readonly path="$1" 110 | local readonly username="$2" 111 | 112 | log_info "Creating install dirs for Vault at $path" 113 | sudo mkdir -p "$path" 114 | sudo mkdir -p "$path/bin" 115 | sudo mkdir -p "$path/config" 116 | sudo mkdir -p "$path/data" 117 | sudo mkdir -p "$path/log" 118 | sudo mkdir -p "$path/tls" 119 | 120 | log_info "Changing ownership of $path to $username" 121 | sudo chown -R "$username:$username" "$path" 122 | } 123 | 124 | function install_binaries { 125 | local readonly version="$1" 126 | local readonly path="$2" 127 | local readonly username="$3" 128 | 129 | local readonly url="https://releases.hashicorp.com/vault/${version}/vault_${version}_linux_amd64.zip" 130 | local readonly download_path="/tmp/vault_${version}_linux_amd64.zip" 131 | local readonly bin_dir="$path/bin" 132 | local readonly vault_dest_path="$bin_dir/vault" 133 | local readonly run_vault_dest_path="$bin_dir/run-vault" 134 | 135 | log_info "Downloading Vault $version from $url to $download_path" 136 | curl -o "$download_path" "$url" 137 | unzip -d /tmp "$download_path" 138 | 139 | log_info "Moving Vault binary to $vault_dest_path" 140 | sudo mv "/tmp/vault" "$vault_dest_path" 141 | sudo chown "$username:$username" "$vault_dest_path" 142 | sudo chmod a+x "$vault_dest_path" 143 | 144 | local readonly symlink_path="$SYSTEM_BIN_DIR/vault" 145 | if [[ -f "$symlink_path" ]]; then 146 | log_info "Symlink $symlink_path already exists. Will not add again." 147 | else 148 | log_info "Adding symlink to $vault_dest_path in $symlink_path" 149 | sudo ln -s "$vault_dest_path" "$symlink_path" 150 | fi 151 | 152 | log_info "Copying Vault run script to $run_vault_dest_path" 153 | sudo cp "$SCRIPT_DIR/../run-vault/run-vault" "$run_vault_dest_path" 154 | sudo chown "$username:$username" "$run_vault_dest_path" 155 | sudo chmod a+x "$run_vault_dest_path" 156 | } 157 | 158 | # For more info, see: https://www.vaultproject.io/docs/configuration/#disable_mlock 159 | function configure_mlock { 160 | echo "Giving Vault permission to use the mlock syscall" 161 | sudo setcap cap_ipc_lock=+ep $(readlink -f $(which vault)) 162 | } 163 | 164 | function install { 165 | local version="" 166 | local path="$DEFAULT_INSTALL_PATH" 167 | local user="$DEFAULT_VAULT_USER" 168 | 169 | while [[ $# > 0 ]]; do 170 | local key="$1" 171 | 172 | case "$key" in 173 | --version) 174 | version="$2" 175 | shift 176 | ;; 177 | --path) 178 | path="$2" 179 | shift 180 | ;; 181 | --user) 182 | user="$2" 183 | shift 184 | ;; 185 | --help) 186 | print_usage 187 | exit 188 | ;; 189 | *) 190 | log_error "Unrecognized argument: $key" 191 | print_usage 192 | exit 1 193 | ;; 194 | esac 195 | 196 | shift 197 | done 198 | 199 | assert_not_empty "--version" "$version" 200 | assert_not_empty "--path" "$path" 201 | assert_not_empty "--user" "$user" 202 | 203 | log_info "Starting Vault install" 204 | 205 | install_dependencies 206 | create_vault_user "$user" 207 | create_vault_install_paths "$path" "$user" 208 | install_binaries "$version" "$path" "$user" 209 | configure_mlock 210 | 211 | log_info "Vault install complete!" 212 | } 213 | 214 | install "$@" -------------------------------------------------------------------------------- /modules/install-vault/supervisor-initd-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # supervisord Startup script for the Supervisor process control system 4 | # 5 | # Author: Mike McGrath (based off yumupdatesd) 6 | # Jason Koppe adjusted to read sysconfig, 7 | # use supervisord tools to start/stop, conditionally wait 8 | # for child processes to shutdown, and startup later 9 | # Erwan Queffelec 10 | # make script LSB-compliant 11 | # 12 | # chkconfig: 345 83 04 13 | # description: Supervisor is a client/server system that allows \ 14 | # its users to monitor and control a number of processes on \ 15 | # UNIX-like operating systems. 16 | # processname: supervisord 17 | # config: /etc/supervisord.conf 18 | # config: /etc/sysconfig/supervisord 19 | # pidfile: /var/run/supervisord.pid 20 | # 21 | ### BEGIN INIT INFO 22 | # Provides: supervisord 23 | # Required-Start: $all 24 | # Required-Stop: $all 25 | # Short-Description: start and stop Supervisor process control system 26 | # Description: Supervisor is a client/server system that allows 27 | # its users to monitor and control a number of processes on 28 | # UNIX-like operating systems. 29 | ### END INIT INFO 30 | 31 | # Source function library 32 | . /etc/rc.d/init.d/functions 33 | 34 | # Source system settings 35 | if [ -f /etc/sysconfig/supervisord ]; then 36 | . /etc/sysconfig/supervisord 37 | fi 38 | 39 | # Path to the supervisorctl script, server binary, 40 | # and short-form for messages. 41 | supervisorctl=/usr/local/bin/supervisorctl 42 | supervisord=${SUPERVISORD-/usr/local/bin/supervisord} 43 | prog=supervisord 44 | pidfile=${PIDFILE-/tmp/supervisord.pid} 45 | lockfile=${LOCKFILE-/var/lock/subsys/supervisord} 46 | STOP_TIMEOUT=${STOP_TIMEOUT-60} 47 | OPTIONS="${OPTIONS--c /etc/supervisor/supervisord.conf}" 48 | RETVAL=0 49 | 50 | start() { 51 | echo -n $"Starting $prog: " 52 | daemon --pidfile=${pidfile} $supervisord $OPTIONS 53 | RETVAL=$? 54 | echo 55 | if [ $RETVAL -eq 0 ]; then 56 | touch ${lockfile} 57 | $supervisorctl $OPTIONS status 58 | fi 59 | return $RETVAL 60 | } 61 | 62 | stop() { 63 | echo -n $"Stopping $prog: " 64 | killproc -p ${pidfile} -d ${STOP_TIMEOUT} $supervisord 65 | RETVAL=$? 66 | echo 67 | [ $RETVAL -eq 0 ] && rm -rf ${lockfile} ${pidfile} 68 | } 69 | 70 | reload() { 71 | echo -n $"Reloading $prog: " 72 | LSB=1 killproc -p $pidfile $supervisord -HUP 73 | RETVAL=$? 74 | echo 75 | if [ $RETVAL -eq 7 ]; then 76 | failure $"$prog reload" 77 | else 78 | $supervisorctl $OPTIONS status 79 | fi 80 | } 81 | 82 | restart() { 83 | stop 84 | start 85 | } 86 | 87 | case "$1" in 88 | start) 89 | start 90 | ;; 91 | stop) 92 | stop 93 | ;; 94 | status) 95 | status -p ${pidfile} $supervisord 96 | RETVAL=$? 97 | [ $RETVAL -eq 0 ] && $supervisorctl $OPTIONS status 98 | ;; 99 | restart) 100 | restart 101 | ;; 102 | condrestart|try-restart) 103 | if status -p ${pidfile} $supervisord >&/dev/null; then 104 | stop 105 | start 106 | fi 107 | ;; 108 | force-reload|reload) 109 | reload 110 | ;; 111 | *) 112 | echo $"Usage: $prog {start|stop|restart|condrestart|try-restart|force-reload|reload}" 113 | RETVAL=2 114 | esac 115 | 116 | exit $RETVAL -------------------------------------------------------------------------------- /modules/install-vault/supervisord.conf: -------------------------------------------------------------------------------- 1 | ; supervisor config file 2 | ; 3 | ; For more information on the config file, please see: 4 | ; http://supervisord.org/configuration.html 5 | ; 6 | ; Notes: 7 | ; - Shell expansion ("~" or "$HOME") is not supported. Environment 8 | ; variables can be expanded using this syntax: "%(ENV_HOME)s". 9 | ; - Comments must have a leading space: "a=b ;comment" not "a=b;comment". 10 | 11 | [unix_http_server] 12 | file=/var/run/supervisor.sock ; (the path to the socket file) 13 | chmod=0700 ; sockef file mode (default 0700) 14 | 15 | [supervisord] 16 | logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log) 17 | pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) 18 | childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP) 19 | logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) 20 | logfile_backups=10 ; (num of main logfile rotation backups;default 10) 21 | loglevel=info ; (log level;default info; others: debug,warn,trace) 22 | 23 | ; the below section must remain in the config file for RPC 24 | ; (supervisorctl/web interface) to work, additional interfaces may be 25 | ; added by defining them in separate rpcinterface: sections 26 | [rpcinterface:supervisor] 27 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 28 | 29 | [supervisorctl] 30 | serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket 31 | 32 | ; The [include] section can just contain the "files" setting. This 33 | ; setting can list multiple files (separated by whitespace or 34 | ; newlines). It can also contain wildcards. The filenames are 35 | ; interpreted as relative to this file. Included files *cannot* 36 | ; include files themselves. 37 | 38 | [include] 39 | files = /etc/supervisor/conf.d/*.conf 40 | -------------------------------------------------------------------------------- /modules/private-tls-cert/README.md: -------------------------------------------------------------------------------- 1 | # Private TLS Cert 2 | 3 | This module can be used to generate a Certificate Authority (CA) public key and the public and private keys of a 4 | TLS certificate signed by this CA. This certificate is meant to be used with **private** services, such as a Vault 5 | cluster accessed solely within your Azure account. For publicly-accessible services, especially services you access 6 | through a web browser, you should NOT use this module, and instead get certificates from a commercial Certificate 7 | Authority, such as [Let's Encrypt](https://letsencrypt.org/). 8 | 9 | If you're unfamiliar with how TLS certificates work, check out the [Background section](#background). 10 | 11 | 12 | 13 | 14 | ## Quick start 15 | 16 | 1. Copy this module to your computer. 17 | 18 | 1. Open `vars.tf` and fill in the variables that do not have a default. 19 | 20 | 1. DO NOT configure Terraform remote state storage for this code. You do NOT want to store the state files as they 21 | will contain the private keys for the certificates. 22 | 23 | 1. Run `terraform apply`. The output will show you the paths to the generated files: 24 | 25 | ``` 26 | Outputs: 27 | 28 | ca_public_key_file_path = ca.key.pem 29 | private_key_file_path = vault.key.pem 30 | public_key_file_path = vault.crt.pem 31 | ``` 32 | 33 | 1. Delete your local Terraform state: 34 | 35 | ``` 36 | rm -rf terraform.tfstate* 37 | ``` 38 | 39 | The Terraform state will contain the private keys for the certificates, so it's important to clean it up! 40 | 41 | 1. To inspect a certificate, you can use OpenSSL: 42 | 43 | ``` 44 | openssl x509 -inform pem -noout -text -in vault.crt.pem 45 | ``` 46 | 47 | Now that you have your TLS certs, check out the next section for how to use them. 48 | 49 | 50 | 51 | 52 | ## Using TLS certs 53 | 54 | 55 | ### Distributing TLS certs to your servers 56 | 57 | Distribute the private and public keys (the files at `private_key_file_path` and `public_key_file_path`) to the 58 | servers that will use them to handle TLS connections (e.g. Vault). For example, to run Vault with the [run-vault 59 | module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault), you need to pass it the TLS certs: 60 | 61 | ``` 62 | /opt/vault/bin/run-vault --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem 63 | ``` 64 | 65 | We **strongly** recommend encrypting the private key file while it's in transit to the servers that will use it. Here 66 | are some of the ways you could do this: 67 | 68 | * Put your TLS cert in a secure Azure Container with encryption enabled. Give your Vault servers a role that allows them 69 | to download the certs from the Azure Container just before booting. 70 | * Manually upload the certificate to each Azure Instance with `scp`. 71 | 72 | 73 | ### Distributing TLS certs to your clients 74 | 75 | Distribute the CA public key (the file at `ca_public_key_file_path`) to any clients of those services so they can 76 | validate the server's TLS cert. Without the CA public key, the clients will reject any TLS connections: 77 | 78 | ``` 79 | vault read secret/foo 80 | 81 | Error initializing Vault: Get https://127.0.0.1:8200/v1/secret/foo: x509: certificate signed by unknown authority 82 | ``` 83 | 84 | Most TLS clients offer a way to explicitly specify extra public keys that you want to trust. For example, with 85 | Vault, you do this via the `-ca-cert` argument: 86 | 87 | ``` 88 | vault read -ca-cert=ca.crt.pem secret/foo 89 | 90 | Key Value 91 | --- ----- 92 | refresh_interval 768h0m0s 93 | value bar 94 | ``` 95 | 96 | As an alternative, you can configure the certificate trust on your server so that all TLS clients trust your CA 97 | public key by running the [update-certificate-store module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/update-certificate-store) on your server. Once 98 | you do that, your system will trust the public key without having to pass it in explicitly: 99 | 100 | ``` 101 | update-certificate-store --cert-file /opt/vault/tls/ca.crt.pem 102 | vault read secret/foo 103 | 104 | Key Value 105 | --- ----- 106 | refresh_interval 768h0m0s 107 | value bar 108 | ``` 109 | 110 | 111 | 112 | ## Background 113 | 114 | 115 | ### How TLS/SSL Works 116 | 117 | The industry-standard way to add encryption for data in motion is to use TLS (the successor to SSL). There are many 118 | examples online explaining how TLS works, but here are the basics: 119 | 120 | - Some entity decides to be a "Certificate Authority" ("CA") meaning it will issue TLS certificates to websites or 121 | other services 122 | 123 | - An entity becomes a Certificate Authority by creating a public/private key pair and publishing the public portion 124 | (typically known as the "CA Cert"). The private key is kept under the tightest possible security since anyone who 125 | possesses it could issue TLS certificates as if they were this Certificate Authority! 126 | 127 | - In fact, the consequences of a CA's private key being compromised are so disastrous that CA's typically create an 128 | "intermediate" CA keypair with their "root" CA key, and only issue TLS certificates with the intermediate key. 129 | 130 | - Your client (e.g. a web browser) can decide to trust this newly created Certificate Authority by including its CA 131 | Cert (the CA's public key) when making an outbound request to a service that uses the TLS certificate. 132 | 133 | - When CAs issue a TLS certificate ("TLS cert") to a service, they again create a public/private keypair, but this time 134 | the public key is "signed" by the CA. That public key is what you view when you click on the lock icon in a web 135 | browser and what a service "advertises" to any clients such as web browsers to declare who it is. When we say that 136 | the CA signed a public key, we mean that, cryptographically, any possessor of the CA Cert can validate that this same 137 | CA issued this particular public key. 138 | 139 | - The public key is more generally known as the TLS cert. 140 | 141 | - The private key created by the CA must be kept secret by the service since the possessor of the private key can 142 | "prove" they are whoever the TLS cert (public key) claims to be as part of the TLS protocol. 143 | 144 | - How does that "proof" work? Well, your web browser will attempt to validate the TLS cert in two ways: 145 | - First, it will ensure this public key (TLS cert) is in fact signed by a CA it trusts. 146 | - Second, using the TLS protocol, your browser will encrypt a message with the public key (TLS cert) that only the 147 | possessor of the corresponding private key can decrypt. In this manner, your browser will be able to come up with a 148 | symmetric encryption key it can use to encrypt all traffic for just that one web session. 149 | 150 | - Now your client/browser has: 151 | - declared which CA it will trust 152 | - verified that the service it's connecting to possesses a certificate issued by a CA it trusts 153 | - used that service's public key (TLS cert) to establish a secure session 154 | 155 | 156 | ### Commercial or Public Certificate Authorities 157 | 158 | For public services like banks, healthcare, and the like, it makes sense to use a "Commercial CA" like Verisign, Thawte, 159 | or Digicert, or better yet a widely trusted but free service like [Let's Encrypt](https://letsencrypt.org/). That's 160 | because every web browser comes pre-configured with a set of CA's that it trusts. This means the client connecting to 161 | the bank doesn't have to know anything about CA's at all. Instead, their web browser is configured to trust the CA that 162 | happened to issue the bank's certificate. 163 | 164 | Connecting securely to private services is similar to connecting to your bank's website over TLS, with one primary 165 | difference: **We want total control over the CA.** 166 | 167 | Imagine if we used a commercial CA to issue our private TLS certificate and that commercial or public CA--which we 168 | don't control--were compromised. Now the attackers of that commercial or public CA could impersonate our private server. 169 | And indeed, [it](https://www.theguardian.com/technology/2011/sep/05/diginotar-certificate-hack-cyberwar) [has]( 170 | https://www.schneier.com/blog/archives/2012/02/verisign_hacked.html) [happened]( 171 | http://www.infoworld.com/article/2623707/hacking/the-real-security-issue-behind-the-comodo-hack.html) 172 | multiple times. 173 | 174 | 175 | ### How We'll Generate a TLS Cert for Private Services 176 | 177 | One option is to be very selective about choosing a commercial CA, but to what benefit? What we want instead is 178 | assurance that our private service really was launched by people we trust. Those same people--let's call them our 179 | "operators"--can become their *own* CA and generate their *own* TLS certificate for the private service. 180 | 181 | Sure, no one else in the world will trust this CA, but we don't care because we only need our organization to trust 182 | this CA. 183 | 184 | So here's our strategy for issuing a TLS Cert for a private service: 185 | 186 | 1. **Create our own CA.** 187 | - If a client wishes to trust our CA, they need only reference this CA public key. 188 | - We'll deal with the private key in a moment. 189 | 190 | 1. **Using our CA, issue a TLS Certificate for our private service.** 191 | - Create a public/private key pair for the private service, and have the CA sign the public key. 192 | - This means anyone who trusts the CA will trust that the possessor of the private key that corresponds to this public 193 | key is who they claim to be. 194 | - We will be extremely careful with the TLS private key since anyone who obtains it can impersonate our private 195 | service! 196 | 197 | 1. **Freely advertise our CA's public key to all internal services.** 198 | - Any service that wishes to connect securely to our private service will need our CA's public key so it can declare 199 | that it trusts this CA, and thereby the TLS cert it issued to the private service. 200 | 201 | 1. **Throw away the CA private key.** 202 | - By erasing a CA private key it's impossible for the CA to be compromised, because there's no private key to steal! 203 | - Future certs can be generated with a new CA. 204 | 205 | -------------------------------------------------------------------------------- /modules/private-tls-cert/main.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # CREATE A CA CERTIFICATE 3 | # --------------------------------------------------------------------------------------------------------------------- 4 | 5 | resource "tls_private_key" "ca" { 6 | algorithm = "${var.private_key_algorithm}" 7 | ecdsa_curve = "${var.private_key_ecdsa_curve}" 8 | rsa_bits = "${var.private_key_rsa_bits}" 9 | } 10 | 11 | resource "tls_self_signed_cert" "ca" { 12 | key_algorithm = "${tls_private_key.ca.algorithm}" 13 | private_key_pem = "${tls_private_key.ca.private_key_pem}" 14 | is_ca_certificate = true 15 | 16 | validity_period_hours = "${var.validity_period_hours}" 17 | allowed_uses = ["${var.ca_allowed_uses}"] 18 | 19 | subject { 20 | common_name = "${var.ca_common_name}" 21 | organization = "${var.organization_name}" 22 | } 23 | 24 | # Store the CA public key in a file. 25 | provisioner "local-exec" { 26 | command = "echo '${tls_self_signed_cert.ca.cert_pem}' > '${var.ca_public_key_file_path}' && chmod ${var.permissions} '${var.ca_public_key_file_path}' && chown ${var.owner} '${var.ca_public_key_file_path}'" 27 | } 28 | } 29 | 30 | # --------------------------------------------------------------------------------------------------------------------- 31 | # CREATE A TLS CERTIFICATE SIGNED USING THE CA CERTIFICATE 32 | # --------------------------------------------------------------------------------------------------------------------- 33 | 34 | resource "tls_private_key" "cert" { 35 | algorithm = "${var.private_key_algorithm}" 36 | ecdsa_curve = "${var.private_key_ecdsa_curve}" 37 | rsa_bits = "${var.private_key_rsa_bits}" 38 | 39 | # Store the certificate's private key in a file. 40 | provisioner "local-exec" { 41 | command = "echo '${tls_private_key.cert.private_key_pem}' > '${var.private_key_file_path}' && chmod ${var.permissions} '${var.private_key_file_path}' && chown ${var.owner} '${var.private_key_file_path}'" 42 | } 43 | } 44 | 45 | resource "tls_cert_request" "cert" { 46 | key_algorithm = "${tls_private_key.cert.algorithm}" 47 | private_key_pem = "${tls_private_key.cert.private_key_pem}" 48 | 49 | dns_names = ["${var.dns_names}"] 50 | ip_addresses = ["${var.ip_addresses}"] 51 | 52 | subject { 53 | common_name = "${var.common_name}" 54 | organization = "${var.organization_name}" 55 | } 56 | } 57 | 58 | resource "tls_locally_signed_cert" "cert" { 59 | cert_request_pem = "${tls_cert_request.cert.cert_request_pem}" 60 | 61 | ca_key_algorithm = "${tls_private_key.ca.algorithm}" 62 | ca_private_key_pem = "${tls_private_key.ca.private_key_pem}" 63 | ca_cert_pem = "${tls_self_signed_cert.ca.cert_pem}" 64 | 65 | validity_period_hours = "${var.validity_period_hours}" 66 | allowed_uses = ["${var.allowed_uses}"] 67 | 68 | # Store the certificate's public key in a file. 69 | provisioner "local-exec" { 70 | command = "echo '${tls_locally_signed_cert.cert.cert_pem}' > '${var.public_key_file_path}' && chmod ${var.permissions} '${var.public_key_file_path}' && chown ${var.owner} '${var.public_key_file_path}'" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /modules/private-tls-cert/outputs.tf: -------------------------------------------------------------------------------- 1 | output "ca_public_key_file_path" { 2 | value = "${var.ca_public_key_file_path}" 3 | } 4 | 5 | output "public_key_file_path" { 6 | value = "${var.public_key_file_path}" 7 | } 8 | 9 | output "private_key_file_path" { 10 | value = "${var.private_key_file_path}" 11 | } 12 | 13 | -------------------------------------------------------------------------------- /modules/private-tls-cert/vars.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "ca_public_key_file_path" { 7 | description = "Write the PEM-encoded CA certificate public key to this path (e.g. /etc/tls/ca.crt.pem)." 8 | } 9 | 10 | variable "public_key_file_path" { 11 | description = "Write the PEM-encoded certificate public key to this path (e.g. /etc/tls/vault.crt.pem)." 12 | } 13 | 14 | variable "private_key_file_path" { 15 | description = "Write the PEM-encoded certificate private key to this path (e.g. /etc/tls/vault.key.pem)." 16 | } 17 | 18 | variable "owner" { 19 | description = "The OS user who should be given ownership over the certificate files." 20 | } 21 | 22 | variable "organization_name" { 23 | description = "The name of the organization to associate with the certificates (e.g. Acme Co)." 24 | } 25 | 26 | variable "ca_common_name" { 27 | description = "The common name to use in the subject of the CA certificate (e.g. acme.co cert)." 28 | } 29 | 30 | variable "common_name" { 31 | description = "The common name to use in the subject of the certificate (e.g. acme.co cert)." 32 | } 33 | 34 | variable "dns_names" { 35 | description = "List of DNS names for which the certificate will be valid (e.g. vault.service.consul, foo.example.com)." 36 | type = "list" 37 | } 38 | 39 | variable "ip_addresses" { 40 | description = "List of IP addresses for which the certificate will be valid (e.g. 127.0.0.1)." 41 | type = "list" 42 | } 43 | 44 | variable "validity_period_hours" { 45 | description = "The number of hours after initial issuing that the certificate will become invalid." 46 | } 47 | 48 | # --------------------------------------------------------------------------------------------------------------------- 49 | # OPTIONAL PARAMETERS 50 | # These parameters have reasonable defaults. 51 | # --------------------------------------------------------------------------------------------------------------------- 52 | 53 | variable "ca_allowed_uses" { 54 | description = "List of keywords from RFC5280 describing a use that is permitted for the CA certificate. For more info and the list of keywords, see https://www.terraform.io/docs/providers/tls/r/self_signed_cert.html#allowed_uses." 55 | type = "list" 56 | 57 | default = [ 58 | "cert_signing", 59 | "key_encipherment", 60 | "digital_signature", 61 | ] 62 | } 63 | 64 | variable "allowed_uses" { 65 | description = "List of keywords from RFC5280 describing a use that is permitted for the issued certificate. For more info and the list of keywords, see https://www.terraform.io/docs/providers/tls/r/self_signed_cert.html#allowed_uses." 66 | type = "list" 67 | 68 | default = [ 69 | "key_encipherment", 70 | "digital_signature", 71 | ] 72 | } 73 | 74 | variable "permissions" { 75 | description = "The Unix file permission to assign to the cert files (e.g. 0600)." 76 | default = "0600" 77 | } 78 | 79 | variable "private_key_algorithm" { 80 | description = "The name of the algorithm to use for private keys. Must be one of: RSA or ECDSA." 81 | default = "RSA" 82 | } 83 | 84 | variable "private_key_ecdsa_curve" { 85 | description = "The name of the elliptic curve to use. Should only be used if var.private_key_algorithm is ECDSA. Must be one of P224, P256, P384 or P521." 86 | default = "P256" 87 | } 88 | 89 | variable "private_key_rsa_bits" { 90 | description = "The size of the generated RSA key in bits. Should only be used if var.private_key_algorithm is RSA." 91 | default = "2048" 92 | } 93 | -------------------------------------------------------------------------------- /modules/run-vault/README.md: -------------------------------------------------------------------------------- 1 | # Vault Run Script 2 | 3 | This folder contains a script for configuring and running Vault on an [Azure](https://azure.microsoft.com/) server. This 4 | script has been tested on Ubuntu 16.04. There is a good chance it will work on other flavors of Debian as well. 5 | 6 | ## Quick start 7 | 8 | This script assumes you installed it, plus all of its dependencies (including Vault itself), using the [install-vault 9 | module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault). The default install path is 10 | `/opt/vault/bin`, so to start Vault in server mode, you run: 11 | 12 | ``` 13 | /opt/vault/bin/run-vault -azure-account-name my-account-name --azure-account-key [REDACTED] --azure-container "Vault" --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem 14 | ``` 15 | 16 | This will: 17 | 18 | 1. Generate a Vault configuration file called `default.hcl` in the Vault config dir (default: `/opt/vault/config`). 19 | See [Vault configuration](#vault-configuration) for details on what this configuration file will contain and how 20 | to override it with your own configuration. 21 | 22 | 1. Generate a [Supervisor](http://supervisord.org/) configuration file called `run-vault.conf` in the Supervisor 23 | config dir (default: `/etc/supervisor/conf.d`) with a command that will run Vault: 24 | `vault server -config=/opt/vault/config`. 25 | 26 | 1. Tell Supervisor to load the new configuration file, thereby starting Vault. 27 | 28 | We recommend using the `run-vault` command as part of [Custom 29 | Data](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/classic/inject-custom-data), so that it executes 30 | when the Azure Instance is first booting. After running `run-vault` on that initial boot, the `supervisord` configuration 31 | will automatically restart Vault if it crashes or the Azure instance reboots. 32 | 33 | See the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) for fully-working sample code. 34 | 35 | 36 | ## Command line Arguments 37 | 38 | The `run-vault` script accepts the following arguments: 39 | 40 | * `--azure-account-name` (required): Specifies the Azure storage account to use to store Vault data. 41 | * `--azure-account-key` (required): Specifies the Azure account key for the `--azure-account-name`. 42 | * `--azure-container` (required): Specifies the Azure container to use to store Vault data. 43 | * `--tls-cert-file` (required): Specifies the path to the certificate for TLS. To configure the listener to use a CA 44 | certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should 45 | appear first in the combined file. See [How do you handle encryption?](#how-do-you_handle-encryption) for more info. 46 | * `--tls-key-file` (required): Specifies the path to the private key for the certificate. See [How do you handle 47 | encryption?](#how-do-you_handle-encryption) for more info. 48 | * `--port` (optional): The port Vault should listen on. Default is `8200`. 49 | * `--log-level` (optional): The log verbosity to use with Vault. Default is `info`. 50 | * `--cluster-port` (optional): The port Vault should listen on for server-to-server communication. Default is 51 | `--port + 1`. 52 | * `config-dir` (optional): The path to the Vault config folder. Default is to take the absolute path of `../config`, 53 | relative to the `run-vault` script itself. 54 | * `user` (optional): The user to run Vault as. Default is to use the owner of `config-dir`. 55 | * `skip-vault-config`: If this flag is set, don't generate a Vault configuration file. This is useful if you have 56 | a custom configuration file and don't want to use any of of the default settings from `run-vault`. 57 | 58 | Example: 59 | 60 | ``` 61 | /opt/vault/bin/run-vault --azure-account-name my-account-name --azure-account-key [REDACTED] --azure-container my-container-name --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem 62 | ``` 63 | 64 | ## Vault configuration 65 | 66 | `run-vault` generates a configuration file for Vault called `default.hcl` that tries to figure out reasonable 67 | defaults for a Vault cluster in Azure. Check out the [Vault Configuration Files 68 | documentation](https://www.vaultproject.io/docs/configuration/index.html) for what configuration settings are 69 | available. 70 | 71 | 72 | ### Default configuration 73 | 74 | `run-vault` sets the following configuration values by default: 75 | 76 | * [storage](https://www.vaultproject.io/docs/configuration/index.html#storage): Configure Azure Container as the storage backend 77 | with the following settings: 78 | 79 | * [accountName](https://www.vaultproject.io/docs/configuration/storage/azure.html#accountname): Set to the 80 | `--azure-account-name` parameter. 81 | * [accountKey](https://www.vaultproject.io/docs/configuration/storage/azure.html#accountkey): Set to the 82 | `--azure-account-key` parameter. 83 | * [container](https://www.vaultproject.io/docs/configuration/storage/azure.html#container): Set to the 84 | `--azure-container` parameter. 85 | 86 | * [ha_storage](https://www.vaultproject.io/docs/configuration/index.html#ha_storage): Configure Consul as the [high 87 | availability](https://www.vaultproject.io/docs/concepts/ha.html) storage backend with the following settings: 88 | 89 | * [address](https://www.vaultproject.io/docs/configuration/storage/consul.html#address): Set the address to 90 | `127.0.0.1:8500`. This is based on the assumption that the Consul agent is running on the same server. 91 | * [scheme](https://www.vaultproject.io/docs/configuration/storage/consul.html#scheme): Set to `http` since our 92 | connection is to a Consul agent running on the same server. 93 | * [path](https://www.vaultproject.io/docs/configuration/storage/consul.html#path): Set to `vault/`. 94 | * [service](https://www.vaultproject.io/docs/configuration/storage/consul.html#service): Set to `vault`. 95 | * [redirect_addr](https://www.vaultproject.io/docs/configuration/storage/consul.html#redirect_addr): 96 | Set to `https://:` where `PRIVATE_IP` is the Instance's private IP and `CLUSTER_PORT` is 97 | the value passed to `--cluster-port`. 98 | * [cluster_addr](https://www.vaultproject.io/docs/configuration/storage/consul.html#cluster_addr): 99 | Set to `https://:` where `PRIVATE_IP` is the Instance's private IP and `CLUSTER_PORT` is 100 | the value passed to `--cluster-port`. 101 | 102 | * [listener](https://www.vaultproject.io/docs/configuration/index.html#listener): Configure a [TCP 103 | listener](https://www.vaultproject.io/docs/configuration/listener/tcp.html) with the following settings: 104 | 105 | * [address](https://www.vaultproject.io/docs/configuration/listener/tcp.html#address): Bind to `0.0.0.0:` 106 | where `PORT` is the value passed to `--port`. 107 | * [cluster_address](https://www.vaultproject.io/docs/configuration/listener/tcp.html#cluster_address): Bind to 108 | `0.0.0.0:` where `CLUSTER` is the value passed to `--cluster-port`. 109 | * [tls_cert_file](https://www.vaultproject.io/docs/configuration/listener/tcp.html#tls_cert_file): Set to the 110 | `--tls-cert-file` parameter. 111 | * [tls_key_file](https://www.vaultproject.io/docs/configuration/listener/tcp.html#tls_key_file): Set to the 112 | `--tls-key-file` parameter. 113 | 114 | 115 | ### Overriding the configuration 116 | 117 | To override the default configuration, simply put your own configuration file in the Vault config folder (default: 118 | `/opt/vault/config`), but with a name that comes later in the alphabet than `default.hcl` (e.g. 119 | `my-custom-config.hcl`). Vault will load all the `.hcl` configuration files in the config dir and merge them together 120 | in alphabetical order, so that settings in files that come later in the alphabet will override the earlier ones. 121 | 122 | For example, to set a custom `cluster_name` setting, you could create a file called `name.hcl` with the 123 | contents: 124 | 125 | ```hcl 126 | cluster_name = "my-custom-name" 127 | ``` 128 | 129 | If you want to override *all* the default settings, you can tell `run-vault` not to generate a default config file 130 | at all using the `--skip-vault-config` flag: 131 | 132 | ``` 133 | /opt/vault/bin/run-vault --azure-account-name my-account-name --azure-account-key [REDACTED] --azure-container my-container-name --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem --skip-vault-config 134 | ``` 135 | 136 | 137 | 138 | 139 | ## How do you handle encryption? 140 | 141 | Vault uses TLS to encrypt all data in transit. To configure encryption, you must do the following: 142 | 143 | 1. [Provide TLS certificates](#provide-tls-certificates) 144 | 1. [Consul encryption](#consul-encryption) 145 | 146 | 147 | ### Provide TLS certificates 148 | 149 | When you execute the `run-vault` script, you need to provide the paths to the public and private keys of a TLS 150 | certificate: 151 | 152 | ``` 153 | /opt/vault/bin/run-vault --azure-account-name my-account-name --azure-account-key [REDACTED] --azure-container my-container-name --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem 154 | ``` 155 | 156 | See the [private-tls-cert module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/private-tls-cert) for information on how to generate a TLS certificate. 157 | 158 | 159 | ### Consul encryption 160 | 161 | Since this Vault Module uses Consul as a high availability storage backend, you may want to enable encryption for 162 | Consul too. Note that Vault encrypts any data *before* sending it to a storage backend, so this isn't strictly 163 | necessary, but may be a good extra layer of security. 164 | 165 | By default, the Vault server nodes communicate with a local Consul agent running on the same server over (unencrypted) 166 | HTTP. However, you can configure those agents to talk to the Consul servers using TLS. Check out the [official Consul 167 | encryption docs](https://www.consul.io/docs/agent/encryption.html) and the Consul Azure Module [How do you handle 168 | encryption docs](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/run-consul#how-do-you-handle-encryption) 169 | for more info. 170 | -------------------------------------------------------------------------------- /modules/run-vault/run-vault: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is used to configure and run Vault on an Azure server. 3 | 4 | set -e 5 | 6 | readonly VAULT_CONFIG_FILE="default.hcl" 7 | readonly SUPERVISOR_CONFIG_PATH="/etc/supervisor/conf.d/run-vault.conf" 8 | 9 | readonly DEFAULT_PORT=8200 10 | readonly DEFAULT_LOG_LEVEL="info" 11 | 12 | readonly AZURE_INSTANCE_METADATA_URL="http://169.254.169.254/metadata/instance?api-version=2017-08-01" 13 | 14 | readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 15 | readonly SCRIPT_NAME="$(basename "$0")" 16 | 17 | function print_usage { 18 | echo 19 | echo "Usage: run-vault [OPTIONS]" 20 | echo 21 | echo "This script is used to configure and run Vault on an Azure server." 22 | echo 23 | echo "Options:" 24 | echo 25 | echo -e " --azure-account-name\tSpecifies the Azure Storage account name where Vault data should be stored. Required." 26 | echo -e " --azure-account-key\tSpecifies the Azure Storage account key for --azure-account-name. Required." 27 | echo -e " --azure-container\tSpecifies the Azure Storage Blob container name. Required." 28 | echo -e " --tls-cert-file\tSpecifies the path to the certificate for TLS. Required. To use a CA certificate, concatenate the primary certificate and the CA certificate together." 29 | echo -e " --tls-key-file\tSpecifies the path to the private key for the certificate. Required." 30 | echo -e " --port\t\tThe port for Vault to listen on. Optional. Default is $DEFAULT_PORT." 31 | echo -e " --cluster-port\tThe port for Vault to listen on for server-to-server requests. Optional. Default is --port + 1." 32 | echo -e " --config-dir\t\tThe path to the Vault config folder. Optional. Default is the absolute path of '../config', relative to this script." 33 | echo -e " --bin-dir\t\tThe path to the folder with Vault binary. Optional. Default is the absolute path of the parent folder of this script." 34 | echo -e " --log-dir\t\tThe path to the Vault log folder. Optional. Default is the absolute path of '../log', relative to this script." 35 | echo -e " --log-level\t\tThe log verbosity to use with Vault. Optional. Default is $DEFAULT_LOG_LEVEL." 36 | echo -e " --user\t\tThe user to run Vault as. Optional. Default is to use the owner of --config-dir." 37 | echo -e " --skip-vault-config\tIf this flag is set, don't generate a Vault configuration file. Optional. Default is false." 38 | echo 39 | echo "Example:" 40 | echo 41 | echo " run-vault --azure-account-name my-account-name --azure-account-key [REDACTED] --azure-container my-container-name --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem" 42 | } 43 | 44 | function log { 45 | local readonly level="$1" 46 | local readonly message="$2" 47 | local readonly timestamp=$(date +"%Y-%m-%d %H:%M:%S") 48 | >&2 echo -e "${timestamp} [${level}] [$SCRIPT_NAME] ${message}" 49 | } 50 | 51 | function log_info { 52 | local readonly message="$1" 53 | log "INFO" "$message" 54 | } 55 | 56 | function log_warn { 57 | local readonly message="$1" 58 | log "WARN" "$message" 59 | } 60 | 61 | function log_error { 62 | local readonly message="$1" 63 | log "ERROR" "$message" 64 | } 65 | 66 | function lookup_path_in_instance_metadata { 67 | local readonly path="$1" 68 | curl --silent --show-error --header Metadata:true --location "$AZURE_INSTANCE_METADATA_URL" | jq -r "$path" 69 | } 70 | 71 | function get_instance_ip_address { 72 | lookup_path_in_instance_metadata ".network.interface[0].ipv4.ipAddress[0].privateIpAddress" 73 | } 74 | 75 | 76 | # Based on code from: http://stackoverflow.com/a/16623897/483528 77 | function strip_prefix { 78 | local readonly str="$1" 79 | local readonly prefix="$2" 80 | echo "${str#$prefix}" 81 | } 82 | 83 | function assert_not_empty { 84 | local readonly arg_name="$1" 85 | local readonly arg_value="$2" 86 | 87 | if [[ -z "$arg_value" ]]; then 88 | log_error "The value for '$arg_name' cannot be empty" 89 | print_usage 90 | exit 1 91 | fi 92 | } 93 | 94 | function assert_is_installed { 95 | local readonly name="$1" 96 | 97 | if [[ ! $(command -v ${name}) ]]; then 98 | log_error "The binary '$name' is required by this script but is not installed or in the system's PATH." 99 | exit 1 100 | fi 101 | } 102 | 103 | function generate_vault_config { 104 | local readonly tls_cert_file="$1" 105 | local readonly tls_key_file="$2" 106 | local readonly port="$3" 107 | local readonly cluster_port="$4" 108 | local readonly config_dir="$5" 109 | local readonly user="$6" 110 | local readonly azure_account_name="$7" 111 | local readonly azure_account_key="$8" 112 | local readonly azure_container="$9" 113 | local readonly config_path="$config_dir/$VAULT_CONFIG_FILE" 114 | 115 | local instance_ip_address 116 | instance_ip_address=$(get_instance_ip_address) 117 | 118 | log_info "Creating default Vault config file in $config_path" 119 | cat > "$config_path" < "$supervisor_config_path" < 0 ]]; do 198 | local key="$1" 199 | 200 | case "$key" in 201 | --tls-cert-file) 202 | tls_cert_file="$2" 203 | shift 204 | ;; 205 | --tls-key-file) 206 | tls_key_file="$2" 207 | shift 208 | ;; 209 | --azure-account-name) 210 | azure_account_name="$2" 211 | shift 212 | ;; 213 | --azure-account-key) 214 | azure_account_key="$2" 215 | shift 216 | ;; 217 | --azure-container) 218 | azure_container="$2" 219 | shift 220 | ;; 221 | --port) 222 | assert_not_empty "$key" "$2" 223 | port="$2" 224 | shift 225 | ;; 226 | --cluster-port) 227 | assert_not_empty "$key" "$2" 228 | cluster_port="$2" 229 | shift 230 | ;; 231 | --config-dir) 232 | assert_not_empty "$key" "$2" 233 | config_dir="$2" 234 | shift 235 | ;; 236 | --bin-dir) 237 | assert_not_empty "$key" "$2" 238 | bin_dir="$2" 239 | shift 240 | ;; 241 | --log-dir) 242 | assert_not_empty "$key" "$2" 243 | log_dir="$2" 244 | shift 245 | ;; 246 | --log-level) 247 | assert_not_empty "$key" "$2" 248 | log_level="$2" 249 | shift 250 | ;; 251 | --user) 252 | assert_not_empty "$key" "$2" 253 | user="$2" 254 | shift 255 | ;; 256 | --skip-vault-config) 257 | skip_vault_config="true" 258 | ;; 259 | --help) 260 | print_usage 261 | exit 262 | ;; 263 | *) 264 | log_error "Unrecognized argument: $key" 265 | print_usage 266 | exit 1 267 | ;; 268 | esac 269 | 270 | shift 271 | done 272 | 273 | assert_not_empty "--tls-cert-file" "$tls_cert_file" 274 | assert_not_empty "--tls-key-file" "$tls_key_file" 275 | assert_not_empty "--azure-account-name" "$azure_account_name" 276 | assert_not_empty "--azure-account-key" "$azure_account_key" 277 | assert_not_empty "--azure-container" "$azure_container" 278 | 279 | assert_is_installed "supervisorctl" 280 | assert_is_installed "az" 281 | assert_is_installed "curl" 282 | assert_is_installed "jq" 283 | 284 | if [[ -z "$config_dir" ]]; then 285 | config_dir=$(cd "$SCRIPT_DIR/../config" && pwd) 286 | fi 287 | 288 | if [[ -z "$bin_dir" ]]; then 289 | bin_dir=$(cd "$SCRIPT_DIR/../bin" && pwd) 290 | fi 291 | 292 | if [[ -z "$log_dir" ]]; then 293 | log_dir=$(cd "$SCRIPT_DIR/../log" && pwd) 294 | fi 295 | 296 | if [[ -z "$user" ]]; then 297 | user=$(get_owner_of_path "$config_dir") 298 | fi 299 | 300 | if [[ -z "$cluster_port" ]]; then 301 | cluster_port=$(( $port + 1 )) 302 | fi 303 | 304 | if [[ "$skip_vault_config" == "true" ]]; then 305 | log_info "The --skip-vault-config flag is set, so will not generate a default Vault config file." 306 | else 307 | generate_vault_config "$tls_cert_file" "$tls_key_file" "$port" "$cluster_port" "$config_dir" "$user" "$azure_account_name" "$azure_account_key" "$azure_container" 308 | fi 309 | 310 | generate_supervisor_config "$SUPERVISOR_CONFIG_PATH" "$config_dir" "$bin_dir" "$log_dir" "$log_level" "$user" 311 | start_vault 312 | } 313 | 314 | run "$@" 315 | -------------------------------------------------------------------------------- /modules/update-certificate-store/README.md: -------------------------------------------------------------------------------- 1 | # Update Certificate Store 2 | 3 | This folder contains a script for adding a trusted, Certificate Authority (CA) certificate to an OS's certificate 4 | store. This allows you to establish TLS connections to services that use TLS certs signed by that CA without getting 5 | x509 certificate errors. This script has been tested on the following operating systems Ubuntu 16.04. There is a good 6 | chance it will work on other flavors of Debian as well. 7 | 8 | If you're unfamiliar with how TLS certificates work, check out the [Background 9 | section](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/private-tls-cert#background). 10 | 11 | ## Motivation 12 | 13 | Let's say you deployed a server (e.g. a Vault server) with a self-signed TLS certificate. If you try to make a request 14 | to that server using some sort of TLS client (e.g. a Vault client), you will get an error: 15 | 16 | ``` 17 | vault read secret/foo 18 | 19 | Error initializing Vault: Get https://127.0.0.1:8200/v1/secret/foo: x509: certificate signed by unknown authority 20 | ``` 21 | 22 | You can get around this error by explicitly telling the client to trust the public key of the CA that signed that TLS 23 | certificate: 24 | 25 | ``` 26 | vault read -ca-cert=/opt/vault/tls/ca.crt.pem secret/foo 27 | 28 | Key Value 29 | --- ----- 30 | refresh_interval 768h0m0s 31 | value bar 32 | ``` 33 | 34 | Having to pass the `-ca-cert` argument every time gets tedious. This module offers a way to configure the entire OS 35 | to trust this certificate. 36 | 37 | 38 | 39 | 40 | ## Quick start 41 | 42 | To use the `update-certificate-script`, use `git` to clone this repository at a specific tag (see the 43 | [releases page](../../../../releases) for all available tags) and run the `update-certificate-script` script: 44 | 45 | ``` 46 | git clone --branch https://github.com/hashicorp/terraform-azurerm-vault.git 47 | terraform-vault-azure/tree/master/modules/update-certificate-script/update-certificate-script --cert-file-path /opt/vault/tls/ca.cert.pem 48 | ``` 49 | 50 | That's it! 51 | 52 | Now you can make calls to services that use TLS cert signed by this CA, and you won't get any errors: 53 | 54 | ``` 55 | vault read secret/foo 56 | 57 | Key Value 58 | --- ----- 59 | refresh_interval 768h0m0s 60 | value bar 61 | ``` 62 | 63 | See the [vault-consul-image example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-consul-image) for working sample code. 64 | 65 | 66 | 67 | ## Command line Arguments 68 | 69 | The `run-vault` script accepts the following arguments: 70 | 71 | * `--cert-file-path` (required): The path to the CA certificate public key to add to the OS certificate store. 72 | * `--dest-file-name` (optional): This script will copy `--cert-file-path` to a file with this name in a shared 73 | certificate folder on the OS. The default file name is `custom.crt`, but you can use this parameter to customize 74 | it. The extension MUST be `.crt` or the OS will ignore the file. 75 | 76 | Example: 77 | 78 | ``` 79 | terraform-vault-azure/tree/master/modules/update-certificate-script/update-certificate-script --cert-file-path /opt/vault/tls/ca.cert.pem 80 | ``` 81 | 82 | -------------------------------------------------------------------------------- /modules/update-certificate-store/update-certificate-store: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is used to add a trusted, private CA certificate to an OS's certificate store. This allows you to 3 | # establish TLS connections to services that use TLS certs signed by that CA without getting x509 certificate errors. 4 | # This script has been tested with the following operating systems: 5 | # 6 | # 1. Ubuntu 16.04 7 | 8 | set -e 9 | 10 | readonly DEFAULT_DEST_FILE_NAME="custom.crt" 11 | 12 | readonly UPDATE_CA_CERTS_PATH="/usr/local/share/ca-certificates" 13 | readonly UPDATE_CA_TRUST_PATH="/etc/pki/ca-trust/source/anchors" 14 | 15 | readonly SCRIPT_NAME="$(basename "$0")" 16 | 17 | function print_usage { 18 | echo 19 | echo "Usage: update-certificate-store [OPTIONS]" 20 | echo 21 | echo "Add a trusted, private CA certificate to an OS's certificate store. This script has been tested with Ubuntu 16.04." 22 | echo 23 | echo "Options:" 24 | echo 25 | echo -e " --cert-file-path\tThe path to the CA certificate public key to add to the OS certificate store. Required." 26 | echo -e " --dest-file-name\tCopy --cert-file-path to a file with this name in a shared cert folder. The extension MUST be .crt. Optional. Default: $DEFAULT_DEST_FILE_NAME." 27 | echo 28 | echo "Example:" 29 | echo 30 | echo " update-certificate-store --cert-file /opt/vault/tls/ca.crt.pem" 31 | } 32 | 33 | function log { 34 | local readonly level="$1" 35 | local readonly message="$2" 36 | local readonly timestamp=$(date +"%Y-%m-%d %H:%M:%S") 37 | >&2 echo -e "${timestamp} [${level}] [$SCRIPT_NAME] ${message}" 38 | } 39 | 40 | function log_info { 41 | local readonly message="$1" 42 | log "INFO" "$message" 43 | } 44 | 45 | function log_warn { 46 | local readonly message="$1" 47 | log "WARN" "$message" 48 | } 49 | 50 | function log_error { 51 | local readonly message="$1" 52 | log "ERROR" "$message" 53 | } 54 | 55 | function command_exists { 56 | local readonly command_name="$1" 57 | [[ -n "$(command -v $command_name)" ]] 58 | } 59 | 60 | function update_certificate_store { 61 | local readonly cert_file_path="$1" 62 | local readonly dest_file_name="$2" 63 | 64 | log_info "Adding CA public key $cert_file_path to OS certificate store" 65 | 66 | if $(command_exists "update-ca-certificates"); then 67 | cp "$cert_file_path" "$UPDATE_CA_CERTS_PATH/$dest_file_name" 68 | update-ca-certificates 69 | elif $(command_exists "update-ca-trust"); then 70 | update-ca-trust enable 71 | cp "$cert_file_path" "$UPDATE_CA_TRUST_PATH/$dest_file_name" 72 | update-ca-trust extract 73 | else 74 | log_warn "Did not find the update-ca-certificates or update-ca-trust commands. Cannot update OS certificate store." 75 | fi 76 | } 77 | 78 | function assert_not_empty { 79 | local readonly arg_name="$1" 80 | local readonly arg_value="$2" 81 | 82 | if [[ -z "$arg_value" ]]; then 83 | log_error "The value for '$arg_name' cannot be empty" 84 | print_usage 85 | exit 1 86 | fi 87 | } 88 | 89 | function update { 90 | local cert_file_path 91 | local dest_file_name="$DEFAULT_DEST_FILE_NAME" 92 | 93 | while [[ $# > 0 ]]; do 94 | local key="$1" 95 | 96 | case "$key" in 97 | --cert-file-path) 98 | cert_file_path="$2" 99 | shift 100 | ;; 101 | --dest-file-name) 102 | dest_file_name="$2" 103 | shift 104 | ;; 105 | --help) 106 | print_usage 107 | exit 108 | ;; 109 | *) 110 | log_error "Unrecognized argument: $key" 111 | print_usage 112 | exit 1 113 | ;; 114 | esac 115 | 116 | shift 117 | done 118 | 119 | assert_not_empty "--cert-file-path" "$cert_file_path" 120 | assert_not_empty "--dest-file-name" "$dest_file_name" 121 | 122 | update_certificate_store "$cert_file_path" "$dest_file_name" 123 | } 124 | 125 | update "$@" -------------------------------------------------------------------------------- /modules/vault-cluster/README.md: -------------------------------------------------------------------------------- 1 | # Vault Cluster 2 | 3 | This folder contains a [Terraform](https://www.terraform.io/) module that can be used to deploy a 4 | [Vault](https://www.vaultproject.io/) cluster in [Azure](https://azure.microsoft.com/) on top of a Scale Set. This 5 | module is designed to deploy an [Azure Managed Image](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer) 6 | that had Vault installed via the [install-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault) module in this Module. 7 | 8 | ## How do you use this module? 9 | 10 | This folder defines a [Terraform module](https://www.terraform.io/docs/modules/usage.html), which you can use in your 11 | code by adding a `module` configuration and setting its `source` parameter to URL of this folder: 12 | 13 | ```hcl 14 | module "vault_cluster" { 15 | # TODO: update this to the final URL 16 | # Use version v0.0.1 of the vault-cluster module 17 | source = "github.com/hashicorp/terraform-azurerm-vault//modules/vault-cluster?ref=v0.0.1" 18 | 19 | # Specify the URI of the Vault Image. You should build this using the scripts in the install-vault module. 20 | image_uri = "/subscriptions/1d21e7f2-8614-4e78-bdfe-828bd654424f/resourceGroups/vault/providers/Microsoft.Compute/images/vault-consul-ubuntu-2017-09-11-222923" 21 | 22 | # This module uses an Azure Container as a storage backend 23 | storage_account_name="gruntworkconsul" 24 | storage_account_key = "RPJu0PSVOIc60WyLKZMQALQKL2ogdoRi75pXuIwDv/c2q/bDVb/vqobtZj55NCISMACsuOLE/VZWZ7DAcy33NA==" 25 | storage_container_name = "VaultConfig" 26 | 27 | # Configure and start Vault during boot. 28 | custom_data = <<-EOF 29 | #!/bin/bash 30 | /opt/vault/bin/run-vault --azure-account-name "${azure_account_name}" --azure-account-key "${azure_account_key}" --azure-container "${azure_container}" --tls-cert-file /opt/vault/tls/vault.crt.pem --tls-key-file /opt/vault/tls/vault.key.pem 31 | EOF 32 | 33 | # ... See vars.tf for the other parameters you must define for the vault-cluster module 34 | } 35 | ``` 36 | 37 | Note the following parameters: 38 | 39 | * `source`: Use this parameter to specify the URL of the vault-cluster module. The double slash (`//`) is intentional 40 | and required. Terraform uses it to specify subfolders within a Git repo (see [module 41 | sources](https://www.terraform.io/docs/modules/sources.html)). The `ref` parameter specifies a specific Git tag in 42 | this repo. That way, instead of using the latest version of this module from the `master` branch, which 43 | will change every time you run Terraform, you're using a fixed version of the repo. 44 | 45 | * `image_uri`: Use this parameter to specify the URI of a Vault [Azure Managed Image] 46 | (https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer) to deploy on each server in the 47 | cluster. You should install Vault in this image using the scripts in the [install-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault) module. 48 | 49 | * `storage_account_name, storage_account_key and storage_container_name`: This module creates an 50 | [Azure Storage Container](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-dotnet-how-to-use-blobs) to use 51 | as a storage backend for Vault. 52 | 53 | * `custom_data`: Use this parameter to specify a [Custom 54 | Data](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/classic/inject-custom-data) script that each 55 | server will run during boot. This is where you can use the [run-vault script](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault) to configure and 56 | run Vault. The `run-vault` script is one of the scripts installed by the [install-vault](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/install-vault) 57 | module. 58 | 59 | You can find the other parameters in [vars.tf](vars.tf). 60 | 61 | Check out the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) example for working sample code. 62 | 63 | ## How do you use the Vault cluster? 64 | 65 | To use the Vault cluster, you will typically need to SSH to each of the Vault servers. If you deployed the 66 | [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) example, the [vault-examples-helper.sh script](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/examples/vault-examples-helper/vault-examples-helper.sh) 67 | will do the lookup for you automatically (note, you must have the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) 68 | and [jq](https://stedolan.github.io/jq/) installed locally): 69 | 70 | ``` 71 | > ../vault-examples-helper/vault-examples-helper.sh 72 | 73 | Your Vault servers are running at the following IP addresses: 74 | 75 | 11.22.33.44 76 | 11.22.33.55 77 | 11.22.33.66 78 | ``` 79 | 80 | ### Initializing the Vault cluster 81 | 82 | The very first time you deploy a new Vault cluster, you need to [initialize the 83 | Vault](https://www.vaultproject.io/intro/getting-started/deploy.html#initializing-the-vault). The easiest way to do 84 | this is to SSH to one of the servers that has Vault installed and run: 85 | 86 | ``` 87 | vault init 88 | 89 | Key 1: 427cd2c310be3b84fe69372e683a790e01 90 | Key 2: 0e2b8f3555b42a232f7ace6fe0e68eaf02 91 | Key 3: 37837e5559b322d0585a6e411614695403 92 | Key 4: 8dd72fd7d1af254de5f82d1270fd87ab04 93 | Key 5: b47fdeb7dda82dbe92d88d3c860f605005 94 | Initial Root Token: eaf5cc32-b48f-7785-5c94-90b5ce300e9b 95 | 96 | Vault initialized with 5 keys and a key threshold of 3! 97 | ``` 98 | 99 | Vault will print out the [unseal keys](https://www.vaultproject.io/docs/concepts/seal.html) and a [root 100 | token](https://www.vaultproject.io/docs/concepts/tokens.html#root-tokens). This is the **only time ever** that all of 101 | this data is known by Vault, so you **MUST** save it in a secure place immediately! Also, this is the only time that 102 | the unseal keys should ever be so close together. You should distribute each one to a different, trusted administrator 103 | for safe keeping in completely separate secret stores and NEVER store them all in the same place. 104 | 105 | In fact, a better option is to initial Vault with [PGP, GPG, or 106 | Keybase](https://www.vaultproject.io/docs/concepts/pgp-gpg-keybase.html) so that each unseal key is encrypted with a 107 | different user's public key. That way, no one, not even the operator running the `init` command can see all the keys 108 | in one place: 109 | 110 | ``` 111 | vault init -pgp-keys="keybase:jefferai,keybase:vishalnayak,keybase:sethvargo" 112 | 113 | Key 1: wcBMA37rwGt6FS1VAQgAk1q8XQh6yc... 114 | Key 2: wcBMA0wwnMXgRzYYAQgAavqbTCxZGD... 115 | Key 3: wcFMA2DjqDb4YhTAARAAeTFyYxPmUd... 116 | ... 117 | ``` 118 | 119 | See [Using PGP, GPG, and Keybase](https://www.vaultproject.io/docs/concepts/pgp-gpg-keybase.html) for more info. 120 | 121 | 122 | ### Unsealing the Vault cluster 123 | 124 | Now that you have the unseal keys, you can [unseal Vault](https://www.vaultproject.io/docs/concepts/seal.html) by 125 | having 3 out of the 5 administrators (or whatever your key shard threshold is) do the following: 126 | 127 | 1. SSH to a Vault server. 128 | 1. Run `vault unseal`. 129 | 1. Enter the unseal key when prompted. 130 | 1. Repeat for each of the other Vault servers. 131 | 132 | Once this process is complete, all the Vault servers will be unsealed and you will be able to start reading and writing 133 | secrets. 134 | 135 | 136 | ### Connecting to the Vault cluster to read and write secrets 137 | 138 | There are three ways to connect to Vault: 139 | 140 | 1. [Access Vault from a Vault server](#access-vault-from-a-vault-server) 141 | 1. [Access Vault from other servers in the same Azure account](#access-vault-from-other-servers-in-the-same-azure-account) 142 | 1. [Access Vault from the public Internet](#access-vault-from-the-public-internet) 143 | 144 | 145 | #### Access Vault from a Vault server 146 | 147 | When you SSH to a Vault server, the Vault client is already configured to talk to the Vault server on localhost, so 148 | you can directly run Vault commands: 149 | 150 | ``` 151 | vault read secret/foo 152 | 153 | Key Value 154 | --- ----- 155 | refresh_interval 768h0m0s 156 | value bar 157 | ``` 158 | 159 | 160 | #### Access Vault from other servers in the same Azure account 161 | 162 | To access Vault from a different server in the same account, you need to specify the URL of the Vault cluster. You 163 | could manually look up the Vault cluster's IP address, but since this module uses Consul not only as a [storage 164 | backend](https://www.vaultproject.io/docs/configuration/storage/consul.html) but also as a way to register [DNS 165 | entries](https://www.consul.io/docs/guides/forwarding.html), you can access Vault 166 | using a nice domain name instead, such as `vault.service.consul`. 167 | 168 | To set this up, use the [install-dnsmasq 169 | module](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/install-dnsmasq) on each server that 170 | needs to access Vault. This allows you to access Vault from your Azure Instances as follows: 171 | 172 | ``` 173 | vault -address=https://vault.service.consul:8200 read secret/foo 174 | 175 | Key Value 176 | --- ----- 177 | refresh_interval 768h0m0s 178 | value bar 179 | ``` 180 | 181 | You can configure the Vault address as an environment variable: 182 | 183 | ``` 184 | export VAULT_ADDR=https://vault.service.consul:8200 185 | ``` 186 | 187 | That way, you don't have to remember to pass the Vault address every time: 188 | 189 | ``` 190 | vault read secret/foo 191 | 192 | Key Value 193 | --- ----- 194 | refresh_interval 768h0m0s 195 | value bar 196 | ``` 197 | 198 | Note that if you're using a self-signed TLS cert (e.g. generated from the [private-tls-cert 199 | module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/private-tls-cert)), you'll need to have the public key of the CA that signed that cert or you'll get 200 | an "x509: certificate signed by unknown authority" error. You could pass the certificate manually: 201 | 202 | ``` 203 | vault read -ca-cert=/opt/vault/tls/ca.crt.pem secret/foo 204 | 205 | Key Value 206 | --- ----- 207 | refresh_interval 768h0m0s 208 | value bar 209 | ``` 210 | 211 | However, to avoid having to add the `-ca-cert` argument to every single call, you can use the [update-certificate-store 212 | module](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/update-certificate-store) to configure the server to trust the CA. 213 | 214 | Check out the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) for working sample code. 215 | 216 | 217 | #### Access Vault from the public Internet 218 | 219 | We **strongly** recommend only running Vault in private subnets. That means it is not directly accessible from the 220 | public Internet, which reduces your surface area to attackers. If you need users to be able to access Vault from 221 | outside of Azure, we recommend using VPN to connect to Azure. 222 | 223 | ``` 224 | vault -address=https:// read secret/foo 225 | ``` 226 | 227 | Where `ELB_DNS_NAME` is the DNS name for your ELB, such as `vault.example.com`. You can configure the Vault address as 228 | an environment variable: 229 | 230 | ``` 231 | export VAULT_ADDR=https://vault.example.com 232 | ``` 233 | 234 | That way, you don't have to remember to pass the Vault address every time: 235 | 236 | ``` 237 | vault read secret/foo 238 | ``` 239 | 240 | 241 | 242 | ## What's included in this module? 243 | 244 | This module creates the following architecture: 245 | 246 | ![Vault architecture](https://raw.githubusercontent.com/hashicorp/terraform-azurerm-vault/master/_docs/architecture.png) 247 | 248 | 249 | ## How do you roll out updates? 250 | 251 | Please note that Vault does not support true zero-downtime upgrades, but with proper upgrade procedure the downtime 252 | should be very short (a few hundred milliseconds to a second depending on how the speed of access to the storage 253 | backend). See the [Vault upgrade guide instructions](https://www.vaultproject.io/docs/guides/upgrading/index.html) for 254 | details. 255 | 256 | If you want to deploy a new version of Vault across a cluster deployed with this module, the best way to do that is to: 257 | 258 | 1. Build a new Azure Image. 259 | 1. Set the `image_uri` parameter to the URL of the new Azure Image. 260 | 1. Run `terraform apply`. 261 | 262 | This updates the Launch Configuration of the Scale Set, so any new Instances in the Scale Set will have your new Image, 263 | but it does NOT actually deploy those new instances. To make that happen, you need to: 264 | 265 | 1. [Replace the standby nodes](#replace-the-standby-nodes) 266 | 1. [Replace the primary node](#replace-the-primary-node) 267 | 268 | 269 | ### Replace the standby nodes 270 | 271 | For each of the standby nodes: 272 | 273 | 1. SSH to the Azure Instance where the Vault standby is running. 274 | 1. Execute `sudo supervisorctl stop vault` to have Vault shut down gracefully. 275 | 1. Terminate the Azure Instance. 276 | 1. After a minute or two, the Scale Set should automatically launch a new Instance, with the new Azure Image, to replace the old one. 277 | 1. Have each Vault admin SSH to the new Azure Instance and unseal it. 278 | 279 | 280 | ### Replace the primary node 281 | 282 | The procedure for the primary node is the same, but should be done LAST, after all the standbys have already been 283 | upgraded: 284 | 285 | 1. SSH to the Azure Instance where the Vault primary is running. This should be the last server that has the old version 286 | of your Azure Image. 287 | 1. Execute `sudo supervisorctl stop vault` to have Vault shut down gracefully. 288 | 1. Terminate the Azure Instance. 289 | 1. After a minute or two, the Scale Set should automatically launch a new Instance, with the new Azure Image, to replace the old one. 290 | 1. Have each Vault admin SSH to the new Azure Instance and unseal it. 291 | 292 | ## What happens if a node crashes? 293 | 294 | There are two ways a Vault node may go down: 295 | 296 | 1. The Vault process may crash. In that case, `supervisor` should restart it automatically. At this point, you will 297 | need to have each Vault admin SSH to the Instance to unseal it again. 298 | 1. The Azure Instance running Vault dies. In that case, the Auto Scaling Group should launch a replacement automatically. 299 | Once again, the Vault admins will have to SSH to the replacement Instance and unseal it. 300 | 301 | Given the need for manual intervention, you will want to have alarms set up that go off any time a Vault node gets 302 | restarted. 303 | 304 | 305 | ## Security 306 | 307 | Here are some of the main security considerations to keep in mind when using this module: 308 | 309 | 1. [Encryption in transit](#encryption-in-transit) 310 | 1. [Encryption at rest](#encryption-at-rest) 311 | 1. [Dedicated instances](#dedicated-instances) 312 | 1. [Security groups](#security-groups) 313 | 1. [SSH access](#ssh-access) 314 | 315 | 316 | ### Encryption in transit 317 | 318 | Vault uses TLS to encrypt its network traffic. For instructions on configuring TLS, have a look at the 319 | [How do you handle encryption documentation](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/modules/run-vault#how-do-you-handle-encryption). 320 | 321 | 322 | ### Encryption at rest 323 | 324 | Vault servers keep everything in memory and does not write any data to the local hard disk. To persist data, Vault 325 | encrypts it, and sends it off to its storage backends, so no matter how the backend stores that data, it is already 326 | encrypted. By default, this Blueprint uses Consul as a storage backend, so if you want an additional layer of 327 | protection, you can check out the [official Consul encryption docs](https://www.consul.io/docs/agent/encryption.html) 328 | and the Consul Azure Module [How do you handle encryption 329 | docs](https://github.com/hashicorp/terraform-azurerm-consul/tree/master/modules/run-consul#how-do-you-handle-encryption) 330 | for more info. 331 | 332 | ### Consul 333 | 334 | This module configures Vault to use Consul as a high availability storage backend. This module assumes you already 335 | have Consul servers deployed in a separate cluster. We do not recommend co-locating Vault and Consul servers in the 336 | same cluster because: 337 | 338 | 1. Vault is a tool built specifically for security, and running any other software on the same server increases its 339 | surface area to attackers. 340 | 1. This Vault Module uses Consul as a high availability storage backend and both Vault and Consul keep their working 341 | set in memory. That means for every 1 byte of data in Vault, you'd also have 1 byte of data in Consul, doubling 342 | your memory consumption on each server. 343 | 344 | Check out the [Consul Azure Module](https://github.com/hashicorp/terraform-azurerm-consul) for how to deploy a Consul 345 | server cluster in Azure. See the [main example](https://github.com/hashicorp/terraform-azurerm-vault/tree/master/MAIN.md) for 346 | sample code that shows how to run both a Vault server cluster and Consul server cluster. 347 | 348 | 349 | ### Monitoring, alerting, log aggregation 350 | 351 | This module does not include anything for monitoring, alerting, or log aggregation. We especially recommend looking into Vault's [Audit 352 | backends](https://www.vaultproject.io/docs/audit/index.html) for how you can capture detailed logging and audit 353 | information. 354 | 355 | Given that any time Vault crashes, reboots, or restarts, you have to have the Vault admins manually unseal it (see 356 | [What happens if a node crashes?](#what-happens-if-a_node-crashes)), we **strongly** recommend configuring alerts that 357 | notify these admins whenever they need to take action! 358 | -------------------------------------------------------------------------------- /modules/vault-cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.10.0" 3 | } 4 | 5 | #--------------------------------------------------------------------------------------------------------------------- 6 | # CREATE STORAGE BUCKET 7 | # --------------------------------------------------------------------------------------------------------------------- 8 | resource "azurerm_storage_container" "vault" { 9 | name = "${var.storage_container_name}" 10 | resource_group_name = "${var.resource_group_name}" 11 | storage_account_name = "${var.storage_account_name}" 12 | container_access_type = "private" 13 | } 14 | 15 | #--------------------------------------------------------------------------------------------------------------------- 16 | # CREATE A LOAD BALANCER 17 | #--------------------------------------------------------------------------------------------------------------------- 18 | resource "azurerm_public_ip" "vault_access" { 19 | count = "${var.associate_public_ip_address_load_balancer ? 1 : 0}" 20 | name = "${var.cluster_name}_access" 21 | location = "${var.location}" 22 | resource_group_name = "${var.resource_group_name}" 23 | public_ip_address_allocation = "static" 24 | domain_name_label = "${var.cluster_name}" 25 | } 26 | 27 | resource "azurerm_lb" "vault_access" { 28 | count = "${var.associate_public_ip_address_load_balancer ? 1 : 0}" 29 | name = "${var.cluster_name}_access" 30 | location = "${var.location}" 31 | resource_group_name = "${var.resource_group_name}" 32 | 33 | frontend_ip_configuration { 34 | name = "PublicIPAddress" 35 | public_ip_address_id = "${azurerm_public_ip.vault_access.id}" 36 | } 37 | } 38 | 39 | resource "azurerm_lb_nat_pool" "vault_lbnatpool" { 40 | count = "${var.associate_public_ip_address_load_balancer ? 1 : 0}" 41 | resource_group_name = "${var.resource_group_name}" 42 | name = "ssh" 43 | loadbalancer_id = "${azurerm_lb.vault_access.id}" 44 | protocol = "Tcp" 45 | frontend_port_start = 2200 46 | frontend_port_end = 2299 47 | backend_port = 22 48 | frontend_ip_configuration_name = "PublicIPAddress" 49 | } 50 | 51 | resource "azurerm_lb_probe" "vault_probe" { 52 | resource_group_name = "${var.resource_group_name}" 53 | loadbalancer_id = "${azurerm_lb.vault_access.id}" 54 | name = "vault-running-probe" 55 | port = "${var.api_port}" 56 | } 57 | 58 | resource "azurerm_lb_backend_address_pool" "vault_bepool" { 59 | count = "${var.associate_public_ip_address_load_balancer ? 1 : 0}" 60 | resource_group_name = "${var.resource_group_name}" 61 | loadbalancer_id = "${azurerm_lb.vault_access.id}" 62 | name = "BackEndAddressPool" 63 | } 64 | 65 | resource "azurerm_lb_rule" "vault_api_port" { 66 | count = "${var.associate_public_ip_address_load_balancer ? 1 : 0}" 67 | resource_group_name = "${var.resource_group_name}" 68 | name = "vault-api" 69 | loadbalancer_id = "${azurerm_lb.vault_access.id}" 70 | protocol = "Tcp" 71 | frontend_port = "${var.api_port}" 72 | backend_port = "${var.api_port}" 73 | frontend_ip_configuration_name = "PublicIPAddress" 74 | backend_address_pool_id = "${azurerm_lb_backend_address_pool.vault_bepool.id}" 75 | probe_id = "${azurerm_lb_probe.vault_probe.id}" 76 | } 77 | 78 | #--------------------------------------------------------------------------------------------------------------------- 79 | # CREATE A VIRTUAL MACHINE SCALE SET TO RUN VAULT (WITHOUT LOAD BALANCER) 80 | # --------------------------------------------------------------------------------------------------------------------- 81 | 82 | resource "azurerm_virtual_machine_scale_set" "vault" { 83 | count = "${var.associate_public_ip_address_load_balancer ? 0 : 1}" 84 | name = "${var.cluster_name}" 85 | location = "${var.location}" 86 | resource_group_name = "${var.resource_group_name}" 87 | upgrade_policy_mode = "Manual" 88 | 89 | sku { 90 | name = "${var.instance_size}" 91 | tier = "${var.instance_tier}" 92 | capacity = "${var.cluster_size}" 93 | } 94 | 95 | os_profile { 96 | computer_name_prefix = "${var.vault_computer_name_prefix}" 97 | admin_username = "${var.vault_admin_user_name}" 98 | 99 | #This password is unimportant as it is disabled below in the os_profile_linux_config 100 | admin_password = "Passwword1234" 101 | custom_data = "${var.custom_data}" 102 | } 103 | 104 | os_profile_linux_config { 105 | disable_password_authentication = true 106 | 107 | ssh_keys { 108 | path = "/home/${var.vault_admin_user_name}/.ssh/authorized_keys" 109 | key_data = "${var.key_data}" 110 | } 111 | } 112 | 113 | network_profile { 114 | name = "VaultNetworkProfile" 115 | primary = true 116 | 117 | ip_configuration { 118 | name = "VaultIPConfiguration" 119 | subnet_id = "${var.subnet_id}" 120 | } 121 | } 122 | 123 | storage_profile_image_reference { 124 | id = "${var.image_id}" 125 | } 126 | 127 | storage_profile_os_disk { 128 | name = "" 129 | caching = "ReadWrite" 130 | create_option = "FromImage" 131 | os_type = "Linux" 132 | managed_disk_type = "Standard_LRS" 133 | } 134 | 135 | tags { 136 | scaleSetName = "${var.cluster_name}" 137 | } 138 | } 139 | 140 | #--------------------------------------------------------------------------------------------------------------------- 141 | # CREATE A VIRTUAL MACHINE SCALE SET TO RUN VAULT (WITH LOAD BALANCER) 142 | # --------------------------------------------------------------------------------------------------------------------- 143 | 144 | resource "azurerm_virtual_machine_scale_set" "vault_with_load_balancer" { 145 | count = "${var.associate_public_ip_address_load_balancer ? 1 : 0}" 146 | name = "${var.cluster_name}" 147 | location = "${var.location}" 148 | resource_group_name = "${var.resource_group_name}" 149 | upgrade_policy_mode = "Manual" 150 | 151 | sku { 152 | name = "${var.instance_size}" 153 | tier = "${var.instance_tier}" 154 | capacity = "${var.cluster_size}" 155 | } 156 | 157 | os_profile { 158 | computer_name_prefix = "${var.vault_computer_name_prefix}" 159 | admin_username = "${var.vault_admin_user_name}" 160 | 161 | #This password is unimportant as it is disabled below in the os_profile_linux_config 162 | admin_password = "Passwword1234" 163 | custom_data = "${var.custom_data}" 164 | } 165 | 166 | os_profile_linux_config { 167 | disable_password_authentication = true 168 | 169 | ssh_keys { 170 | path = "/home/${var.vault_admin_user_name}/.ssh/authorized_keys" 171 | key_data = "${var.key_data}" 172 | } 173 | } 174 | 175 | network_profile { 176 | name = "VaultNetworkProfile" 177 | primary = true 178 | 179 | ip_configuration { 180 | name = "VaultIPConfiguration" 181 | subnet_id = "${var.subnet_id}" 182 | load_balancer_backend_address_pool_ids = ["${azurerm_lb_backend_address_pool.vault_bepool.id}"] 183 | load_balancer_inbound_nat_rules_ids = ["${element(azurerm_lb_nat_pool.vault_lbnatpool.*.id, count.index)}"] 184 | } 185 | } 186 | 187 | storage_profile_image_reference { 188 | id = "${var.image_id}" 189 | } 190 | 191 | storage_profile_os_disk { 192 | name = "" 193 | caching = "ReadWrite" 194 | create_option = "FromImage" 195 | os_type = "Linux" 196 | managed_disk_type = "Standard_LRS" 197 | } 198 | 199 | tags { 200 | scaleSetName = "${var.cluster_name}" 201 | } 202 | } 203 | 204 | #--------------------------------------------------------------------------------------------------------------------- 205 | # CREATE A SECURITY GROUP AND RULES FOR SSH 206 | # --------------------------------------------------------------------------------------------------------------------- 207 | 208 | resource "azurerm_network_security_group" "vault" { 209 | name = "${var.cluster_name}" 210 | location = "${var.location}" 211 | resource_group_name = "${var.resource_group_name}" 212 | } 213 | 214 | resource "azurerm_network_security_rule" "ssh" { 215 | count = "${length(var.allowed_ssh_cidr_blocks)}" 216 | 217 | access = "Allow" 218 | destination_address_prefix = "*" 219 | destination_port_range = "22" 220 | direction = "Inbound" 221 | name = "SSH${count.index}" 222 | network_security_group_name = "${azurerm_network_security_group.vault.name}" 223 | priority = "${100 + count.index}" 224 | protocol = "Tcp" 225 | resource_group_name = "${var.resource_group_name}" 226 | source_address_prefix = "${element(var.allowed_ssh_cidr_blocks, count.index)}" 227 | source_port_range = "1024-65535" 228 | } 229 | 230 | -------------------------------------------------------------------------------- /modules/vault-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "scale_set_name" { 2 | value = "${var.cluster_name}" 3 | } 4 | 5 | output "admin_user_name" { 6 | value = "${var.vault_admin_user_name}" 7 | } 8 | 9 | output "cluster_size" { 10 | value = "${var.cluster_size}" 11 | } 12 | 13 | output "storage_containter_id" { 14 | value = "${azurerm_storage_container.vault.id}" 15 | } 16 | 17 | output "load_balancer_ip_address" { 18 | value = "${azurerm_public_ip.vault_access.ip_address}" 19 | } -------------------------------------------------------------------------------- /modules/vault-cluster/vars.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "location" { 7 | description = "The location that the resources will run in (e.g. East US)" 8 | } 9 | 10 | variable "resource_group_name" { 11 | description = "The name of the resource group that the resources for consul will run in" 12 | } 13 | 14 | variable "storage_account_name" { 15 | description = "The name of the storage account that will be used for images" 16 | } 17 | 18 | variable "subnet_id" { 19 | description = "The id of the subnet to deploy the cluster into" 20 | } 21 | 22 | variable "cluster_name" { 23 | description = "The name of the Consul cluster (e.g. consul-stage). This variable is used to namespace all resources created by this module." 24 | } 25 | 26 | variable "storage_container_name" { 27 | description = "The name of the Azure Storage Container where secrets will be kept." 28 | } 29 | 30 | variable "image_id" { 31 | description = "The URL of the Image to run in this cluster. Should be an image that had Consul installed and configured by the install-consul module." 32 | } 33 | 34 | variable "instance_size" { 35 | description = "The size of Azure Instances to run for each node in the cluster (e.g. Standard_A0)." 36 | } 37 | 38 | variable "key_data" { 39 | description = "The SSH public key that will be added to SSH authorized_users on the consul instances" 40 | } 41 | 42 | variable "custom_data" { 43 | description = "A Custom Data script to execute while the server is booting. We remmend passing in a bash script that executes the run-consul script, which should have been installed in the Consul Image by the install-consul module." 44 | } 45 | 46 | # --------------------------------------------------------------------------------------------------------------------- 47 | # OPTIONAL PARAMETERS 48 | # These parameters have reasonable defaults. 49 | # --------------------------------------------------------------------------------------------------------------------- 50 | 51 | variable "instance_tier" { 52 | description = "Specifies the tier of virtual machines in a scale set. Possible values, standard or basic." 53 | default = "standard" 54 | } 55 | 56 | variable "consul_computer_name_prefix" { 57 | description = "The string that the name of each instance in the cluster will be prefixed with" 58 | default = "consul" 59 | } 60 | 61 | variable "vault_computer_name_prefix" { 62 | description = "The string that the name of each instance in the cluster will be prefixed with" 63 | default = "vault" 64 | } 65 | 66 | variable "consul_admin_user_name" { 67 | description = "The name of the administrator user for each instance in the cluster" 68 | default = "consuladmin" 69 | } 70 | 71 | variable "vault_admin_user_name" { 72 | description = "The name of the administrator user for each instance in the cluster" 73 | default = "vaultadmin" 74 | } 75 | 76 | variable "instance_root_volume_size" { 77 | description = "Specifies the size of the instance root volume in GB. Default 40GB" 78 | default = 40 79 | } 80 | 81 | variable "cluster_size" { 82 | description = "The number of nodes to have in the Consul cluster. We strongly recommended that you use either 3 or 5." 83 | default = 3 84 | } 85 | 86 | variable "cluster_tag_key" { 87 | description = "Add a tag with this key and the value var.cluster_tag_value to each Instance in the ASG. This can be used to automatically find other Consul nodes and form a cluster." 88 | default = "consul-servers" 89 | } 90 | 91 | variable "cluster_tag_value" { 92 | description = "Add a tag with key var.clsuter_tag_key and this value to each Instance in the ASG. This can be used to automatically find other Consul nodes and form a cluster." 93 | default = "auto-join" 94 | } 95 | 96 | variable "subnet_ids" { 97 | description = "The subnet IDs into which the Azure Instances should be deployed. We recommend one subnet ID per node in the cluster_size variable. At least one of var.subnet_ids or var.availability_zones must be non-empty." 98 | type = "list" 99 | default = [] 100 | } 101 | 102 | variable "allowed_ssh_cidr_blocks" { 103 | description = "A list of CIDR-formatted IP address ranges from which the Azure Instances will allow SSH connections" 104 | type = "list" 105 | default = [] 106 | } 107 | 108 | variable "associate_public_ip_address_load_balancer" { 109 | description = "If set to true, create a public IP address with back end pool to allow SSH publically to the instances." 110 | default = false 111 | } 112 | 113 | variable "root_volume_type" { 114 | description = "The type of volume. Must be one of: standard, gp2, or io1." 115 | default = "standard" 116 | } 117 | 118 | variable "root_volume_size" { 119 | description = "The size, in GB, of the root EBS volume." 120 | default = 50 121 | } 122 | 123 | variable "root_volume_delete_on_termination" { 124 | description = "Whether the volume should be destroyed on instance termination." 125 | default = true 126 | } 127 | 128 | variable "api_port" { 129 | description = "The port to use for Vault API calls" 130 | default = 8200 131 | } -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "vault_cluster_size" { 2 | value = "${var.num_vault_servers}" 3 | } 4 | 5 | output "vault_admin_user_name" { 6 | value = "${module.vault_servers.admin_user_name}" 7 | } 8 | 9 | output "load_balancer_ip_address" { 10 | value = "${module.vault_servers.load_balancer_ip_address}" 11 | } 12 | -------------------------------------------------------------------------------- /vars.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # --------------------------------------------------------------------------------------------------------------------- 4 | 5 | variable "subscription_id" { 6 | description = "The Azure subscription ID" 7 | } 8 | 9 | variable "tenant_id" { 10 | description = "The Azure tenant ID" 11 | } 12 | 13 | variable "client_id" { 14 | description = "The Azure client ID" 15 | } 16 | 17 | variable "secret_access_key" { 18 | description = "The Azure secret access key" 19 | } 20 | 21 | variable "resource_group_name" { 22 | description = "The name of the Azure resource group consul will be deployed into. This RG should already exist" 23 | } 24 | 25 | variable "storage_account_name" { 26 | description = "The name of an Azure Storage Account. This SA should already exist" 27 | } 28 | 29 | variable "storage_account_key" { 30 | description = "The key for storage_account_name." 31 | } 32 | 33 | variable "image_uri" { 34 | description = "The URI to the Azure image that should be deployed to the consul cluster." 35 | } 36 | 37 | variable "key_data" { 38 | description = "The SSH public key that will be added to SSH authorized_users on the consul instances" 39 | } 40 | 41 | variable "allowed_inbound_cidr_blocks" { 42 | description = "A list of CIDR-formatted IP address ranges from which the Azure Instances will allow connections to Consul" 43 | type = "list" 44 | } 45 | 46 | # --------------------------------------------------------------------------------------------------------------------- 47 | # OPTIONAL PARAMETERS 48 | # These parameters have reasonable defaults. 49 | # --------------------------------------------------------------------------------------------------------------------- 50 | variable "location" { 51 | description = "The Azure region the consul cluster will be deployed in" 52 | default = "East US" 53 | } 54 | 55 | variable "address_space" { 56 | description = "The supernet for the resources that will be created" 57 | default = "10.0.0.0/16" 58 | } 59 | 60 | variable "subnet_address" { 61 | description = "The subnet that consul resources will be deployed into" 62 | default = "10.0.10.0/24" 63 | } 64 | 65 | variable "consul_cluster_name" { 66 | description = "What to name the Consul cluster and all of its associated resources" 67 | default = "consul-example" 68 | } 69 | 70 | variable "vault_cluster_name" { 71 | description = "What to name the Vault cluster and all of its associated resources" 72 | default = "vault-example" 73 | } 74 | 75 | variable "instance_size" { 76 | description = "The instance size for the servers" 77 | default = "Standard_A0" 78 | } 79 | 80 | variable "num_consul_servers" { 81 | description = "The number of Consul server nodes to deploy. We strongly recommend using 3 or 5." 82 | default = 3 83 | } 84 | 85 | variable "num_vault_servers" { 86 | description = "The number of Vault server nodes to deploy. We strongly recommend using 3 or 5." 87 | default = 3 88 | } 89 | --------------------------------------------------------------------------------