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  }