github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/tls/resource_self_signed_cert.go (about) 1 package tls 2 3 import ( 4 "crypto/rand" 5 "crypto/x509" 6 "encoding/pem" 7 "fmt" 8 "math/big" 9 "net" 10 "time" 11 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 var keyUsages map[string]x509.KeyUsage = map[string]x509.KeyUsage{ 16 "digital_signature": x509.KeyUsageDigitalSignature, 17 "content_commitment": x509.KeyUsageContentCommitment, 18 "key_encipherment": x509.KeyUsageKeyEncipherment, 19 "data_encipherment": x509.KeyUsageDataEncipherment, 20 "key_agreement": x509.KeyUsageKeyAgreement, 21 "cert_signing": x509.KeyUsageCertSign, 22 "crl_signing": x509.KeyUsageCRLSign, 23 "encipher_only": x509.KeyUsageEncipherOnly, 24 "decipher_only": x509.KeyUsageDecipherOnly, 25 } 26 27 var extKeyUsages map[string]x509.ExtKeyUsage = map[string]x509.ExtKeyUsage{ 28 "any_extended": x509.ExtKeyUsageAny, 29 "server_auth": x509.ExtKeyUsageServerAuth, 30 "client_auth": x509.ExtKeyUsageClientAuth, 31 "code_signing": x509.ExtKeyUsageCodeSigning, 32 "email_protection": x509.ExtKeyUsageEmailProtection, 33 "ipsec_end_system": x509.ExtKeyUsageIPSECEndSystem, 34 "ipsec_tunnel": x509.ExtKeyUsageIPSECTunnel, 35 "ipsec_user": x509.ExtKeyUsageIPSECUser, 36 "timestamping": x509.ExtKeyUsageTimeStamping, 37 "ocsp_signing": x509.ExtKeyUsageOCSPSigning, 38 "microsoft_server_gated_crypto": x509.ExtKeyUsageMicrosoftServerGatedCrypto, 39 "netscape_server_gated_crypto": x509.ExtKeyUsageNetscapeServerGatedCrypto, 40 } 41 42 func resourceSelfSignedCert() *schema.Resource { 43 return &schema.Resource{ 44 Create: CreateSelfSignedCert, 45 Delete: DeleteSelfSignedCert, 46 Read: ReadSelfSignedCert, 47 48 Schema: map[string]*schema.Schema{ 49 50 "dns_names": &schema.Schema{ 51 Type: schema.TypeList, 52 Optional: true, 53 Description: "List of DNS names to use as subjects of the certificate", 54 ForceNew: true, 55 Elem: &schema.Schema{ 56 Type: schema.TypeString, 57 }, 58 }, 59 60 "ip_addresses": &schema.Schema{ 61 Type: schema.TypeList, 62 Optional: true, 63 Description: "List of IP addresses to use as subjects of the certificate", 64 ForceNew: true, 65 Elem: &schema.Schema{ 66 Type: schema.TypeString, 67 }, 68 }, 69 70 "validity_period_hours": &schema.Schema{ 71 Type: schema.TypeInt, 72 Required: true, 73 Description: "Number of hours that the certificate will remain valid for", 74 ForceNew: true, 75 }, 76 77 "early_renewal_hours": &schema.Schema{ 78 Type: schema.TypeInt, 79 Optional: true, 80 Default: 0, 81 Description: "Number of hours before the certificates expiry when a new certificate will be generated", 82 ForceNew: true, 83 }, 84 85 "is_ca_certificate": &schema.Schema{ 86 Type: schema.TypeBool, 87 Optional: true, 88 Description: "Whether the generated certificate will be usable as a CA certificate", 89 ForceNew: true, 90 }, 91 92 "allowed_uses": &schema.Schema{ 93 Type: schema.TypeList, 94 Required: true, 95 Description: "Uses that are allowed for the certificate", 96 ForceNew: true, 97 Elem: &schema.Schema{ 98 Type: schema.TypeString, 99 }, 100 }, 101 102 "key_algorithm": &schema.Schema{ 103 Type: schema.TypeString, 104 Required: true, 105 Description: "Name of the algorithm to use to generate the certificate's private key", 106 ForceNew: true, 107 }, 108 109 "private_key_pem": &schema.Schema{ 110 Type: schema.TypeString, 111 Required: true, 112 Description: "PEM-encoded private key that the certificate will belong to", 113 ForceNew: true, 114 StateFunc: func(v interface{}) string { 115 return hashForState(v.(string)) 116 }, 117 }, 118 119 "subject": &schema.Schema{ 120 Type: schema.TypeList, 121 Required: true, 122 Elem: nameSchema, 123 ForceNew: true, 124 }, 125 126 "cert_pem": &schema.Schema{ 127 Type: schema.TypeString, 128 Computed: true, 129 }, 130 131 "validity_start_time": &schema.Schema{ 132 Type: schema.TypeString, 133 Computed: true, 134 }, 135 136 "validity_end_time": &schema.Schema{ 137 Type: schema.TypeString, 138 Computed: true, 139 }, 140 }, 141 } 142 } 143 144 func CreateSelfSignedCert(d *schema.ResourceData, meta interface{}) error { 145 keyAlgoName := d.Get("key_algorithm").(string) 146 var keyFunc keyParser 147 var ok bool 148 if keyFunc, ok = keyParsers[keyAlgoName]; !ok { 149 return fmt.Errorf("invalid key_algorithm %#v", keyAlgoName) 150 } 151 keyBlock, _ := pem.Decode([]byte(d.Get("private_key_pem").(string))) 152 if keyBlock == nil { 153 return fmt.Errorf("no PEM block found in private_key_pem") 154 } 155 key, err := keyFunc(keyBlock.Bytes) 156 if err != nil { 157 return fmt.Errorf("failed to decode private_key_pem: %s", err) 158 } 159 160 notBefore := time.Now() 161 notAfter := notBefore.Add(time.Duration(d.Get("validity_period_hours").(int)) * time.Hour) 162 163 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 164 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 165 if err != nil { 166 return fmt.Errorf("failed to generate serial number: %s", err) 167 } 168 169 subjectConfs := d.Get("subject").([]interface{}) 170 if len(subjectConfs) != 1 { 171 return fmt.Errorf("must have exactly one 'subject' block") 172 } 173 subjectConf := subjectConfs[0].(map[string]interface{}) 174 subject, err := nameFromResourceData(subjectConf) 175 if err != nil { 176 return fmt.Errorf("invalid subject block: %s", err) 177 } 178 179 cert := x509.Certificate{ 180 SerialNumber: serialNumber, 181 Subject: *subject, 182 NotBefore: notBefore, 183 NotAfter: notAfter, 184 BasicConstraintsValid: true, 185 } 186 187 keyUsesI := d.Get("allowed_uses").([]interface{}) 188 for _, keyUseI := range keyUsesI { 189 keyUse := keyUseI.(string) 190 if usage, ok := keyUsages[keyUse]; ok { 191 cert.KeyUsage |= usage 192 } 193 if usage, ok := extKeyUsages[keyUse]; ok { 194 cert.ExtKeyUsage = append(cert.ExtKeyUsage, usage) 195 } 196 } 197 198 dnsNamesI := d.Get("dns_names").([]interface{}) 199 for _, nameI := range dnsNamesI { 200 cert.DNSNames = append(cert.DNSNames, nameI.(string)) 201 } 202 ipAddressesI := d.Get("ip_addresses").([]interface{}) 203 for _, ipStrI := range ipAddressesI { 204 ip := net.ParseIP(ipStrI.(string)) 205 if ip == nil { 206 return fmt.Errorf("invalid IP address %#v", ipStrI.(string)) 207 } 208 cert.IPAddresses = append(cert.IPAddresses, ip) 209 } 210 211 if d.Get("is_ca_certificate").(bool) { 212 cert.IsCA = true 213 } 214 215 certBytes, err := x509.CreateCertificate(rand.Reader, &cert, &cert, publicKey(key), key) 216 if err != nil { 217 fmt.Errorf("Error creating certificate: %s", err) 218 } 219 certPem := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})) 220 221 validFromBytes, err := notBefore.MarshalText() 222 if err != nil { 223 return fmt.Errorf("error serializing validity_start_time: %s", err) 224 } 225 validToBytes, err := notAfter.MarshalText() 226 if err != nil { 227 return fmt.Errorf("error serializing validity_end_time: %s", err) 228 } 229 230 d.SetId(serialNumber.String()) 231 d.Set("cert_pem", certPem) 232 d.Set("validity_start_time", string(validFromBytes)) 233 d.Set("validity_end_time", string(validToBytes)) 234 235 return nil 236 } 237 238 func DeleteSelfSignedCert(d *schema.ResourceData, meta interface{}) error { 239 d.SetId("") 240 return nil 241 } 242 243 func ReadSelfSignedCert(d *schema.ResourceData, meta interface{}) error { 244 245 endTimeStr := d.Get("validity_end_time").(string) 246 endTime := time.Now() 247 err := endTime.UnmarshalText([]byte(endTimeStr)) 248 if err != nil { 249 // If end time is invalid then we'll just throw away the whole 250 // thing so we can generate a new one. 251 d.SetId("") 252 return nil 253 } 254 255 earlyRenewalPeriod := time.Duration(-d.Get("early_renewal_hours").(int)) * time.Hour 256 endTime = endTime.Add(earlyRenewalPeriod) 257 258 if time.Now().After(endTime) { 259 // Treat an expired certificate as not existing, so we'll generate 260 // a new one with the next plan. 261 d.SetId("") 262 } 263 264 return nil 265 }