From 2b8bbbe0243e938e2c4c8e644552805c81bd3c1a Mon Sep 17 00:00:00 2001 From: Jonathan Creasy Date: Mon, 3 Apr 2023 08:58:49 -0400 Subject: [PATCH] Added dnssec provider (#26) * Added dnssec provider based on API documentation: https://www.name.com/api-docs/types/dnssec --------- Signed-off-by: Aleksey Sviridkin Co-authored-by: Aleksey Sviridkin --- README.md | 92 ++++----- namedotcom/provider.go | 2 +- namedotcom/resource_namedotcom_dnssec.go | 229 +++++++++++++++++++++++ 3 files changed, 276 insertions(+), 47 deletions(-) create mode 100644 namedotcom/resource_namedotcom_dnssec.go diff --git a/README.md b/README.md index c0921038..f8828b1e 100644 --- a/README.md +++ b/README.md @@ -6,91 +6,72 @@ Special thanks to @mhumeSF for the original [name.com provider](https://github.c Supported features: -- Set DNS records -- Set NS records +- DNS records +- NS records +- DNSSEC ## Usage -### How to install the provider - ```HCL +# Set up the provider terraform { required_providers { namedotcom = { source = "lexfrei/namedotcom" version = "1.1.6" } + aws = { + source = "hashicorp/aws" + version = "3.38.0" + } } } -``` -### How to create the provider +provider "aws" { + region = "us-west-2" +} -Username and token must be generated from your account, [here](https://www.name.com/account/settings/api). +resource "aws_route53_zone" "example_com" { + name = "example.com" +} -```HCL +# Create the provider with your account details provider "namedotcom" { username = var.namedotcom_username token = var.namedotcom_token } -``` - -### Example usage - -```HCL -// example.com CNAME -> bar.com +# Example usage for creating DNS records resource "namedotcom_record" "bar" { domain_name = "example.com" - host = "" + host = "" record_type = "cname" - answer = "bar.com" + answer = "foo.com" } -// foo.example.com -> 10.1.2.3 - resource "namedotcom_record" "foo" { domain_name = "example.com" - host = "foo" + host = "foo" record_type = "A" - answer = "10.1.2.3" + answer = "1.2.3.4" } -``` - -Many records per domain example -```HCL +# Example usage for creating many records per domain resource "namedotcom_record" "domain-me" { domain_name = "domain.me" record_type = "A" for_each = { - "" = local.t6 - www = local.t8 - www1 = local.t8 - www2 = local.t9 + "" = "1.2.3.4" + www = "2.3.4.5" + www1 = "3.4.5.6" + www2 = "4.5.6.7" } - host = each.key + host = each.key answer = each.value } -``` - -Setting nameservers from a generated hosted_zone - -```HCL -provider "aws" { - region = "us-west-2" -} - -provider "namedotcom" { - token = "..." - username = "..." -} - -resource "aws_route53_zone" "example_com" { - name = "example.com" -} +# Example usage for setting nameservers from a generated hosted_zone resource "namedotcom_domain_nameservers" "example_com" { domain_name = "example.com" nameservers = [ @@ -100,6 +81,25 @@ resource "namedotcom_domain_nameservers" "example_com" { "${aws_route53_zone.example_com.name_servers.3}", ] } + +# Example usage for using DNSSEC +resource "aws_route53_key_signing_key" "dnssec" { + name = data.aws_route53_zone.example_com.name + hosted_zone_id = data.aws_route53_zone.example_com.id + key_management_service_arn = aws_kms_key.dnssec.arn + lifecycle { + create_before_destroy = true + } +} + +resource "namedotcom_dnssec" "dnssec" { + domain_name = aws_route53_zone.example_com.name + key_tag = aws_route53_key_signing_key.dnssec.key_tag + algorithm = aws_route53_key_signing_key.dnssec.signing_algorithm_type + digest_type = aws_route53_key_signing_key.dnssec.digest_algorithm_type + digest = aws_route53_key_signing_key.dnssec.digest_value +} + ``` ### How to import record diff --git a/namedotcom/provider.go b/namedotcom/provider.go index c48c872f..a69b4f1e 100644 --- a/namedotcom/provider.go +++ b/namedotcom/provider.go @@ -30,7 +30,7 @@ func Provider() *schema.Provider { // "namedotcom_domain_autorenew": resourceDomainAutoRenew(), // "namedotcom_domain_contact": resourceDomainContact(), // "namedotcom_domain_lock": resourceDomainLock(), - // "namedotcom_dnssec": resourceDnssec(), + "namedotcom_dnssec": resourceDNSSEC(), // "namedotcom_email_forwarding": resourceEmailForwarding(), // "namedotcom_transfer": resourceTransfer(), // "namedotcom_url_forwarding": resourceUrlForwarding(), diff --git a/namedotcom/resource_namedotcom_dnssec.go b/namedotcom/resource_namedotcom_dnssec.go new file mode 100644 index 00000000..0b2487da --- /dev/null +++ b/namedotcom/resource_namedotcom_dnssec.go @@ -0,0 +1,229 @@ +package namedotcom + +import ( + "strings" + + "github.com/pkg/errors" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/namedotcom/go/v4/namecom" +) + +func resourceDNSSEC() *schema.Resource { + return &schema.Resource{ + Create: resourceDNSSECCreate, + Read: resourceDNSSECRead, + Delete: resourceDNSSECDelete, + Importer: &schema.ResourceImporter{ + State: resourceDNSSECImporter, + }, + + Schema: map[string]*schema.Schema{ + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "DomainName is the zone that the DNSSEC belongs to", + }, + "key_tag": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + Description: "KeyTag contains the key tag value of the DNSKEY RR that validates this signature.", + }, + "algorithm": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + Description: "Algorithm is an integer identifying the algorithm used for signing. ", + }, + "digest_type": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + Description: "DigestType is an integer identifying the algorithm used to create the digest.", + }, + "digest": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Digest is a digest of the DNSKEY RR that is registered with the registry.", + }, + }, + } +} + +// resourceDNSSECCreate creates a new DNSSEC in the Name.com API. +func resourceDNSSECCreate(data *schema.ResourceData, meta interface{}) error { + _, err := meta.(*namecom.NameCom).CreateDNSSEC( + &namecom.DNSSEC{ + DomainName: data.Get("domain_name").(string), + KeyTag: data.Get("key_tag").(int32), + Algorithm: data.Get("algorithm").(int32), + DigestType: data.Get("digest_type").(int32), + Digest: data.Get("digest").(string), + }, + ) + if err != nil { + return errors.Wrap(err, "Error CreateDNSSEC") + } + + domainNameString, ok := data.Get("domain_name").(string) + if !ok { + return errors.New("Error getting domain_name") + } + + data.SetId(domainNameString) + + return resourceDNSSECRead(data, meta) +} + +// resourceDNSSECImporter import existing DNSSEC from the Name.com API. +func resourceDNSSECImporter(data *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + client, ok := meta.(*namecom.NameCom) + if !ok { + return nil, errors.New("Error getting client") + } + + importDomainName, importDigest, err := resourceDNSSECImporterParseID(data.Id()) + if err != nil { + return nil, err + } + + request := namecom.GetDNSSECRequest{ + DomainName: importDomainName, + Digest: importDigest, + } + + DNSSEC, err := client.GetDNSSEC(&request) + if err != nil { + return nil, errors.Wrap(err, "Error GetDNSSECRequest") + } + + err = data.Set("domain_name", DNSSEC.DomainName) + if err != nil { + return nil, errors.Wrap(err, "Error setting domain_name") + } + + err = data.Set("key_tag", int(DNSSEC.KeyTag)) + if err != nil { + return nil, errors.Wrap(err, "Error setting key_tag") + } + + err = data.Set("algorithm", int(DNSSEC.Algorithm)) + if err != nil { + return nil, errors.Wrap(err, "Error setting algorithm") + } + + err = data.Set("digest_type", int(DNSSEC.DigestType)) + if err != nil { + return nil, errors.Wrap(err, "Error setting digest_type") + } + + err = data.Set("digest", DNSSEC.Digest) + if err != nil { + return nil, errors.Wrap(err, "Error setting digest") + } + + data.SetId(importDomainName) + + return []*schema.ResourceData{data}, nil +} + +// resourceDNSSECImporterParseID parses the ID of the DNSSEC. +func resourceDNSSECImporterParseID(id string) (domainName, digest string, err error) { + parts := strings.SplitN(id, "_", 2) + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", errors.New("unexpected format of ID, expected DomainName_Digest") + } + + return parts[0], parts[1], nil +} + +// resourceDNSSECRead reads a DNSSEC from the Name.com API. +func resourceDNSSECRead(data *schema.ResourceData, meta interface{}) error { + client, ok := meta.(*namecom.NameCom) + if !ok { + return errors.New("Error getting client") + } + + domainNameString, ok := data.Get("domain_name").(string) + if !ok { + return errors.New("Error getting domain_name") + } + + digestString, ok := data.Get("digest").(string) + if !ok { + return errors.New("Error getting digest") + } + + request := namecom.GetDNSSECRequest{ + DomainName: domainNameString, + Digest: digestString, + } + + DNSSEC, err := client.GetDNSSEC(&request) + if err != nil { + return errors.Wrap(err, "Error GetDNSSECRequest") + } + + err = data.Set("domain_name", DNSSEC.DomainName) + if err != nil { + return errors.Wrap(err, "Error setting domain_name") + } + + err = data.Set("key_tag", int(DNSSEC.KeyTag)) + if err != nil { + return errors.Wrap(err, "Error setting key_tag") + } + + err = data.Set("algorithm", int(DNSSEC.Algorithm)) + if err != nil { + return errors.Wrap(err, "Error setting algorithm") + } + + err = data.Set("digest_type", int(DNSSEC.DigestType)) + if err != nil { + return errors.Wrap(err, "Error setting digest_type") + } + + err = data.Set("digest", DNSSEC.Digest) + if err != nil { + return errors.Wrap(err, "Error setting digest") + } + + return nil +} + +// resourceDNSSECDelete deletes a DNSSEC from the Name.com API. +func resourceDNSSECDelete(data *schema.ResourceData, meta interface{}) error { + client, ok := meta.(*namecom.NameCom) + if !ok { + return errors.New("Error getting client") + } + + domainNameString, ok := data.Get("domain_name").(string) + if !ok { + return errors.New("Error getting domain_name") + } + + digestString, ok := data.Get("digest").(string) + if !ok { + return errors.New("Error getting digest") + } + + deleteRequest := namecom.DeleteDNSSECRequest{ + DomainName: domainNameString, + Digest: digestString, + } + + _, err := client.DeleteDNSSEC(&deleteRequest) + if err != nil { + return errors.Wrap(err, "Error DeleteDNSSEC") + } + + data.SetId("") + + return nil +}