From 55434521a97b8c67f62720354e0eeca7e42bc38e Mon Sep 17 00:00:00 2001 From: The Magician Date: Wed, 22 May 2019 15:28:40 -0700 Subject: [PATCH] DNSSec on Managed Zones (#737) Signed-off-by: Modular Magician --- google-beta/resource_dns_managed_zone.go | 255 ++++++++++++++++++ google-beta/resource_dns_managed_zone_test.go | 8 + website/docs/r/dns_managed_zone.html.markdown | 41 +++ 3 files changed, 304 insertions(+) diff --git a/google-beta/resource_dns_managed_zone.go b/google-beta/resource_dns_managed_zone.go index 5fced64120..027fbaa10f 100644 --- a/google-beta/resource_dns_managed_zone.go +++ b/google-beta/resource_dns_managed_zone.go @@ -19,6 +19,7 @@ import ( "fmt" "log" "reflect" + "strconv" "time" "github.com/hashicorp/terraform/helper/hashcode" @@ -59,6 +60,66 @@ func resourceDnsManagedZone() *schema.Resource { Optional: true, Default: "Managed by Terraform", }, + "dnssec_config": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_key_specs": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "algorithm": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"ecdsap256sha256", "ecdsap384sha384", "rsasha1", "rsasha256", "rsasha512", ""}, false), + }, + "key_length": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "key_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"keySigning", "zoneSigning", ""}, false), + }, + "kind": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "dns#dnsKeySpec", + }, + }, + }, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "dns#managedZoneDnsSecConfig", + }, + "non_existence": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"nsec", "nsec3", ""}, false), + }, + "state": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"off", "on", "transfer", ""}, false), + }, + }, + }, + }, "forwarding_config": { Type: schema.TypeList, Optional: true, @@ -196,6 +257,12 @@ func resourceDnsManagedZoneCreate(d *schema.ResourceData, meta interface{}) erro } else if v, ok := d.GetOkExists("dns_name"); !isEmptyValue(reflect.ValueOf(dnsNameProp)) && (ok || !reflect.DeepEqual(v, dnsNameProp)) { obj["dnsName"] = dnsNameProp } + dnssecConfigProp, err := expandDnsManagedZoneDnssecConfig(d.Get("dnssec_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("dnssec_config"); !isEmptyValue(reflect.ValueOf(dnssecConfigProp)) && (ok || !reflect.DeepEqual(v, dnssecConfigProp)) { + obj["dnssecConfig"] = dnssecConfigProp + } nameProp, err := expandDnsManagedZoneName(d.Get("name"), d, config) if err != nil { return err @@ -283,6 +350,9 @@ func resourceDnsManagedZoneRead(d *schema.ResourceData, meta interface{}) error if err := d.Set("dns_name", flattenDnsManagedZoneDnsName(res["dnsName"], d)); err != nil { return fmt.Errorf("Error reading ManagedZone: %s", err) } + if err := d.Set("dnssec_config", flattenDnsManagedZoneDnssecConfig(res["dnssecConfig"], d)); err != nil { + return fmt.Errorf("Error reading ManagedZone: %s", err) + } if err := d.Set("name", flattenDnsManagedZoneName(res["name"], d)); err != nil { return fmt.Errorf("Error reading ManagedZone: %s", err) } @@ -410,6 +480,80 @@ func flattenDnsManagedZoneDnsName(v interface{}, d *schema.ResourceData) interfa return v } +func flattenDnsManagedZoneDnssecConfig(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["kind"] = + flattenDnsManagedZoneDnssecConfigKind(original["kind"], d) + transformed["non_existence"] = + flattenDnsManagedZoneDnssecConfigNonExistence(original["nonExistence"], d) + transformed["state"] = + flattenDnsManagedZoneDnssecConfigState(original["state"], d) + transformed["default_key_specs"] = + flattenDnsManagedZoneDnssecConfigDefaultKeySpecs(original["defaultKeySpecs"], d) + return []interface{}{transformed} +} +func flattenDnsManagedZoneDnssecConfigKind(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenDnsManagedZoneDnssecConfigNonExistence(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenDnsManagedZoneDnssecConfigState(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenDnsManagedZoneDnssecConfigDefaultKeySpecs(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "algorithm": flattenDnsManagedZoneDnssecConfigDefaultKeySpecsAlgorithm(original["algorithm"], d), + "key_length": flattenDnsManagedZoneDnssecConfigDefaultKeySpecsKeyLength(original["keyLength"], d), + "key_type": flattenDnsManagedZoneDnssecConfigDefaultKeySpecsKeyType(original["keyType"], d), + "kind": flattenDnsManagedZoneDnssecConfigDefaultKeySpecsKind(original["kind"], d), + }) + } + return transformed +} +func flattenDnsManagedZoneDnssecConfigDefaultKeySpecsAlgorithm(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenDnsManagedZoneDnssecConfigDefaultKeySpecsKeyLength(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenDnsManagedZoneDnssecConfigDefaultKeySpecsKeyType(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenDnsManagedZoneDnssecConfigDefaultKeySpecsKind(v interface{}, d *schema.ResourceData) interface{} { + return v +} + func flattenDnsManagedZoneName(v interface{}, d *schema.ResourceData) interface{} { return v } @@ -553,6 +697,117 @@ func expandDnsManagedZoneDnsName(v interface{}, d TerraformResourceData, config return v, nil } +func expandDnsManagedZoneDnssecConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedKind, err := expandDnsManagedZoneDnssecConfigKind(original["kind"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKind); val.IsValid() && !isEmptyValue(val) { + transformed["kind"] = transformedKind + } + + transformedNonExistence, err := expandDnsManagedZoneDnssecConfigNonExistence(original["non_existence"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNonExistence); val.IsValid() && !isEmptyValue(val) { + transformed["nonExistence"] = transformedNonExistence + } + + transformedState, err := expandDnsManagedZoneDnssecConfigState(original["state"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedState); val.IsValid() && !isEmptyValue(val) { + transformed["state"] = transformedState + } + + transformedDefaultKeySpecs, err := expandDnsManagedZoneDnssecConfigDefaultKeySpecs(original["default_key_specs"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDefaultKeySpecs); val.IsValid() && !isEmptyValue(val) { + transformed["defaultKeySpecs"] = transformedDefaultKeySpecs + } + + return transformed, nil +} + +func expandDnsManagedZoneDnssecConfigKind(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDnsManagedZoneDnssecConfigNonExistence(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDnsManagedZoneDnssecConfigState(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDnsManagedZoneDnssecConfigDefaultKeySpecs(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAlgorithm, err := expandDnsManagedZoneDnssecConfigDefaultKeySpecsAlgorithm(original["algorithm"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAlgorithm); val.IsValid() && !isEmptyValue(val) { + transformed["algorithm"] = transformedAlgorithm + } + + transformedKeyLength, err := expandDnsManagedZoneDnssecConfigDefaultKeySpecsKeyLength(original["key_length"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKeyLength); val.IsValid() && !isEmptyValue(val) { + transformed["keyLength"] = transformedKeyLength + } + + transformedKeyType, err := expandDnsManagedZoneDnssecConfigDefaultKeySpecsKeyType(original["key_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKeyType); val.IsValid() && !isEmptyValue(val) { + transformed["keyType"] = transformedKeyType + } + + transformedKind, err := expandDnsManagedZoneDnssecConfigDefaultKeySpecsKind(original["kind"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKind); val.IsValid() && !isEmptyValue(val) { + transformed["kind"] = transformedKind + } + + req = append(req, transformed) + } + return req, nil +} + +func expandDnsManagedZoneDnssecConfigDefaultKeySpecsAlgorithm(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDnsManagedZoneDnssecConfigDefaultKeySpecsKeyLength(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDnsManagedZoneDnssecConfigDefaultKeySpecsKeyType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDnsManagedZoneDnssecConfigDefaultKeySpecsKind(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandDnsManagedZoneName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } diff --git a/google-beta/resource_dns_managed_zone_test.go b/google-beta/resource_dns_managed_zone_test.go index 8ce1a68f31..946e4206fa 100644 --- a/google-beta/resource_dns_managed_zone_test.go +++ b/google-beta/resource_dns_managed_zone_test.go @@ -125,6 +125,14 @@ resource "google_dns_managed_zone" "private" { networks { network_url = "${google_compute_network.%s.self_link}" } + dnssec_config { + state = "on" + default_key_specs { + algorithm = "rsasha1" + key_length = "128" + key_type = "zoneSigning" + } + } } } diff --git a/website/docs/r/dns_managed_zone.html.markdown b/website/docs/r/dns_managed_zone.html.markdown index ed154dbfe1..ab50ac8d48 100644 --- a/website/docs/r/dns_managed_zone.html.markdown +++ b/website/docs/r/dns_managed_zone.html.markdown @@ -212,6 +212,10 @@ The following arguments are supported: (Optional) A textual description field. Defaults to 'Managed by Terraform'. +* `dnssec_config` - + (Optional) + DNSSEC configuration Structure is documented below. + * `labels` - (Optional) A set of key/value label pairs to assign to this ManagedZone. @@ -242,6 +246,43 @@ The following arguments are supported: If it is not provided, the provider project is used. +The `dnssec_config` block supports: + +* `kind` - + (Optional) + Identifies what kind of resource this is + +* `non_existence` - + (Optional) + Specifies the mechanism used to provide authenticated denial-of-existence responses. Output only while state is not OFF. + +* `state` - + (Optional) + Specifies whether DNSSEC is enabled, and what mode it is in + +* `default_key_specs` - + (Optional) + Specifies parameters that will be used for generating initial DnsKeys for this ManagedZone. Output only while state is not OFF Structure is documented below. + + +The `default_key_specs` block supports: + +* `algorithm` - + (Optional) + String mnemonic specifying the DNSSEC algorithm of this key + +* `key_length` - + (Optional) + Length of the keys in bits + +* `key_type` - + (Optional) + Specifies whether this is a key signing key (KSK) or a zone signing key (ZSK). Key signing keys have the Secure Entry Point flag set and, when active, will only be used to sign resource record sets of type DNSKEY. Zone signing keys do not have the Secure Entry Point flag set and will be used to sign all other types of resource record sets. + +* `kind` - + (Optional) + Identifies what kind of resource this is + The `private_visibility_config` block supports: * `networks` -