blob: 88eb0a65955295ed3e85420c566363043a1966d8 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package dns
import (
"fmt"
"log"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
"google.golang.org/api/dns/v1"
)
// DNSSEC Algorithm Numbers: https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
// The following are algorithms that are supported by Cloud DNS
var dnssecAlgoNums = map[string]int{
"rsasha1": 5,
"rsasha256": 8,
"rsasha512": 10,
"ecdsap256sha256": 13,
"ecdsap384sha384": 14,
}
// DS RR Digest Types: https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
// The following are digests that are supported by Cloud DNS
var dnssecDigestType = map[string]int{
"sha1": 1,
"sha256": 2,
"sha384": 4,
}
func DataSourceDNSKeys() *schema.Resource {
return &schema.Resource{
Read: dataSourceDNSKeysRead,
Schema: map[string]*schema.Schema{
"managed_zone": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"key_signing_keys": {
Type: schema.TypeList,
Computed: true,
Elem: kskResource(),
},
"zone_signing_keys": {
Type: schema.TypeList,
Computed: true,
Elem: dnsKeyResource(),
},
},
}
}
func dnsKeyResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"algorithm": {
Type: schema.TypeString,
Computed: true,
},
"creation_time": {
Type: schema.TypeString,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"digests": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"digest": {
Type: schema.TypeString,
Optional: true,
},
"type": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"id": {
Type: schema.TypeString,
Computed: true,
},
"is_active": {
Type: schema.TypeBool,
Computed: true,
},
"key_length": {
Type: schema.TypeInt,
Computed: true,
},
"key_tag": {
Type: schema.TypeInt,
Computed: true,
},
"public_key": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func kskResource() *schema.Resource {
resource := dnsKeyResource()
resource.Schema["ds_record"] = &schema.Schema{
Type: schema.TypeString,
Computed: true,
}
return resource
}
func generateDSRecord(signingKey *dns.DnsKey) (string, error) {
algoNum, found := dnssecAlgoNums[signingKey.Algorithm]
if !found {
return "", fmt.Errorf("DNSSEC Algorithm number for %s not found", signingKey.Algorithm)
}
digestType, found := dnssecDigestType[signingKey.Digests[0].Type]
if !found {
return "", fmt.Errorf("DNSSEC Digest type for %s not found", signingKey.Digests[0].Type)
}
return fmt.Sprintf("%d %d %d %s",
signingKey.KeyTag,
algoNum,
digestType,
signingKey.Digests[0].Digest), nil
}
func flattenSigningKeys(signingKeys []*dns.DnsKey, keyType string) []map[string]interface{} {
var keys []map[string]interface{}
for _, signingKey := range signingKeys {
if signingKey != nil && signingKey.Type == keyType {
data := map[string]interface{}{
"algorithm": signingKey.Algorithm,
"creation_time": signingKey.CreationTime,
"description": signingKey.Description,
"digests": flattenDigests(signingKey.Digests),
"id": signingKey.Id,
"is_active": signingKey.IsActive,
"key_length": signingKey.KeyLength,
"key_tag": signingKey.KeyTag,
"public_key": signingKey.PublicKey,
}
if signingKey.Type == "keySigning" && len(signingKey.Digests) > 0 {
dsRecord, err := generateDSRecord(signingKey)
if err == nil {
data["ds_record"] = dsRecord
}
}
keys = append(keys, data)
}
}
return keys
}
func flattenDigests(dnsKeyDigests []*dns.DnsKeyDigest) []map[string]interface{} {
var digests []map[string]interface{}
for _, dnsKeyDigest := range dnsKeyDigests {
if dnsKeyDigest != nil {
data := map[string]interface{}{
"digest": dnsKeyDigest.Digest,
"type": dnsKeyDigest.Type,
}
digests = append(digests, data)
}
}
return digests
}
func dataSourceDNSKeysRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*transport_tpg.Config)
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
if err != nil {
return err
}
fv, err := tpgresource.ParseProjectFieldValue("managedZones", d.Get("managed_zone").(string), "project", d, config, false)
if err != nil {
return err
}
project := fv.Project
managedZone := fv.Name
if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error setting project: %s", err)
}
d.SetId(fmt.Sprintf("projects/%s/managedZones/%s", project, managedZone))
log.Printf("[DEBUG] Fetching DNS keys from managed zone %s", managedZone)
response, err := config.NewDnsClient(userAgent).DnsKeys.List(project, managedZone).Do()
if err != nil && !transport_tpg.IsGoogleApiErrorWithCode(err, 404) {
return fmt.Errorf("error retrieving DNS keys: %s", err)
} else if transport_tpg.IsGoogleApiErrorWithCode(err, 404) {
return nil
}
log.Printf("[DEBUG] Fetched DNS keys from managed zone %s", managedZone)
if err := d.Set("key_signing_keys", flattenSigningKeys(response.DnsKeys, "keySigning")); err != nil {
return fmt.Errorf("Error setting key_signing_keys: %s", err)
}
if err := d.Set("zone_signing_keys", flattenSigningKeys(response.DnsKeys, "zoneSigning")); err != nil {
return fmt.Errorf("Error setting zone_signing_keys: %s", err)
}
return nil
}