├── CODEOWNERS ├── modules └── generate-cert │ ├── outputs.tf │ ├── main.tf │ └── vars.tf ├── .gitignore ├── LICENSE.txt └── README.md /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @brikis98 @josh-padnick 2 | -------------------------------------------------------------------------------- /modules/generate-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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Gruntwork, LLC 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /modules/generate-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/generate-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/my-app.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/my-app.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. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Private TLS Cert 2 | 3 | This repository contains a [Terraform](https://www.terraform.io/) module that can be used to generate self-signed TLS 4 | certificate. To be more accurate, the module generates the following: 5 | 6 | * A Certificate Authority (CA) public key 7 | * The public and private keys of a TLS certificate signed by the CA 8 | 9 | This TLS certificate is meant to be used with **private** services, such as a web service used only within your 10 | company. For publicly-accessible services, especially services you access through a web browser, you should NOT use 11 | this module, and instead get certificates from a commercial Certificate Authority, such as 12 | [Let's Encrypt](https://letsencrypt.org/). 13 | 14 | If you're unfamiliar with how TLS certificates work, check out the [Background section](#background). 15 | 16 | 17 | 18 | 19 | ## Quick start 20 | 21 | *Note: This Terraform module uses bash commands in a `local-exec` provisioner to copy the generate certificates to 22 | files, so currently, it will only work on Linux, Unix, and OS X.* 23 | 24 | 1. `git clone` this repo to your computer and go into the `modules/generate-cert` folder. 25 | 26 | 1. Open `vars.tf` and fill in the variables that do not have a default. 27 | 28 | 1. DO NOT configure Terraform remote state storage for this code. You do NOT want to store the state files as they 29 | will contain the private keys for the certificates. 30 | 31 | 1. Run `terraform init`. 32 | 33 | 1. Run `terraform apply`. The output will show you the paths to the generated files: 34 | 35 | ``` 36 | Outputs: 37 | 38 | ca_public_key_file_path = ca.key.pem 39 | private_key_file_path = my-app.key.pem 40 | public_key_file_path = my-app.crt.pem 41 | ``` 42 | 43 | 1. Delete your local Terraform state: 44 | 45 | ``` 46 | rm -rf terraform.tfstate* 47 | ``` 48 | 49 | The Terraform state will contain the private keys for the certificates, so it's important to clean it up! 50 | 51 | You can now use the TLS certs with your applications! To inspect a certificate, you can use OpenSSL: 52 | 53 | ``` 54 | openssl x509 -inform pem -noout -text -in my-app.crt.pem 55 | ``` 56 | 57 | 58 | 59 | 60 | ## Background 61 | 62 | 63 | ### How TLS/SSL Works 64 | 65 | The industry-standard way to add encryption for data in motion is to use TLS (the successor to SSL). There are many 66 | examples online explaining how TLS works, but here are the basics: 67 | 68 | - Some entity decides to be a "Certificate Authority" ("CA") meaning it will issue TLS certificates to websites or 69 | other services 70 | 71 | - An entity becomes a Certificate Authority by creating a public/private key pair and publishing the public portion 72 | (typically known as the "CA Cert"). The private key is kept under the tightest possible security since anyone who 73 | possesses it could issue TLS certificates as if they were this Certificate Authority! 74 | 75 | - In fact, the consequences of a CA's private key being compromised are so disastrous that CA's typically create an 76 | "intermediate" CA keypair with their "root" CA key, and only issue TLS certificates with the intermediate key. 77 | 78 | - Your client (e.g. a web browser) can decide to trust this newly created Certificate Authority by including its CA 79 | Cert (the CA's public key) when making an outbound request to a service that uses the TLS certificate. 80 | 81 | - When CAs issue a TLS certificate ("TLS cert") to a service, they again create a public/private keypair, but this time 82 | 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 83 | browser and what a service "advertises" to any clients such as web browsers to declare who it is. When we say that 84 | the CA signed a public key, we mean that, cryptographically, any possessor of the CA Cert can validate that this same 85 | CA issued this particular public key. 86 | 87 | - The public key is more generally known as the TLS cert. 88 | 89 | - The private key created by the CA must be kept secret by the service since the possessor of the private key can 90 | "prove" they are whoever the TLS cert (public key) claims to be as part of the TLS protocol. 91 | 92 | - How does that "proof" work? Well, your web browser will attempt to validate the TLS cert in two ways: 93 | - First, it will ensure this public key (TLS cert) is in fact signed by a CA it trusts. 94 | - Second, using the TLS protocol, your browser will encrypt a message with the public key (TLS cert) that only the 95 | possessor of the corresponding private key can decrypt. In this manner, your browser will be able to come up with a 96 | symmetric encryption key it can use to encrypt all traffic for just that one web session. 97 | 98 | - Now your client/browser has: 99 | - declared which CA it will trust 100 | - verified that the service it's connecting to possesses a certificate issued by a CA it trusts 101 | - used that service's public key (TLS cert) to establish a secure session 102 | 103 | 104 | ### Commercial or Public Certificate Authorities 105 | 106 | For public services like banks, healthcare, and the like, it makes sense to use a "Commercial CA" like Verisign, Thawte, 107 | or Digicert, or better yet a widely trusted but free service like [Let's Encrypt](https://letsencrypt.org/). That's 108 | because every web browser comes pre-configured with a set of CA's that it trusts. This means the client connecting to 109 | the bank doesn't have to know anything about CA's at all. Instead, their web browser is configured to trust the CA that 110 | happened to issue the bank's certificate. 111 | 112 | Connecting securely to private services is similar to connecting to your bank's website over TLS, with one primary 113 | difference: **We want total control over the CA.** 114 | 115 | Imagine if we used a commercial CA to issue our private TLS certificate and that commercial or public CA--which we 116 | don't control--were compromised. Now the attackers of that commercial or public CA could impersonate our private server. 117 | And indeed, [it](https://www.theguardian.com/technology/2011/sep/05/diginotar-certificate-hack-cyberwar) [has]( 118 | https://www.schneier.com/blog/archives/2012/02/verisign_hacked.html) [happened]( 119 | http://www.infoworld.com/article/2623707/hacking/the-real-security-issue-behind-the-comodo-hack.html) 120 | multiple times. 121 | 122 | 123 | ### How We'll Generate a TLS Cert for Private Services 124 | 125 | One option is to be very selective about choosing a commercial CA, but to what benefit? What we want instead is 126 | assurance that our private service really was launched by people we trust. Those same people--let's call them our 127 | "operators"--can become their *own* CA and generate their *own* TLS certificate for the private service. 128 | 129 | Sure, no one else in the world will trust this CA, but we don't care because we only need our organization to trust 130 | this CA. 131 | 132 | So here's our strategy for issuing a TLS Cert for a private service: 133 | 134 | 1. **Create our own CA.** 135 | - If a client wishes to trust our CA, they need only reference this CA public key. 136 | - We'll deal with the private key in a moment. 137 | 138 | 1. **Using our CA, issue a TLS Certificate for our private service.** 139 | - Create a public/private key pair for the private service, and have the CA sign the public key. 140 | - This means anyone who trusts the CA will trust that the possessor of the private key that corresponds to this public 141 | key is who they claim to be. 142 | - We will be extremely careful with the TLS private key since anyone who obtains it can impersonate our private 143 | service! For this reason, we recommend immediately encrypting the private key with 144 | [KMS](https://aws.amazon.com/kms/). 145 | 146 | 1. **Freely advertise our CA's public key to all internal services.** 147 | - Any service that wishes to connect securely to our private service will need our CA's public key so it can declare 148 | that it trusts this CA, and thereby the TLS cert it issued to the private service. 149 | 150 | 1. **Throw away the CA private key.** 151 | - By erasing a CA private key it's impossible for the CA to be compromised, because there's no private key to steal! 152 | - Future certs can be generated with a new CA. 153 | 154 | 155 | 156 | 157 | ## License 158 | 159 | This code is released under the MIT License. See [LICENSE.txt](/LICENSE.txt). 160 | --------------------------------------------------------------------------------