Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DynECT DNS provider #271

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Posh-ACME/DnsPlugins/DynECT-Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# How To Use the DynECT DNS Plugin

This plugin works against DynECT DNS provider.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the provider name a link to their site somewhere generic. Normally it would be the service's homepage but since that doesn't really exist anymore, perhaps the Getting Started Guide for Managed DNS? https://help.dyn.com/managed-dns-gsg/

It requires PoshDynDnsApi powershell module to be installed in order to work correctly. - However, if this module is missing, it will be installed on first run (You will be prompted if you want to install or not.)

## Setup

In addition to your username and password, you will also need a "customer" name, in order to make a successful connection.
Customer name can be found on the home dashboard when logged into the dyn ECT portal.

### Any OS

```powershell
$pass = Read-Host -Prompt "Password" -AsSecureString
$params = @{
user='myusername'
pass=$pass
customer='examplecustomer'
zone='example.com'
}
New-PACertificate *.test.example.com -DnsPlugin DynECT -PluginArgs $params
```
237 changes: 237 additions & 0 deletions Posh-ACME/DnsPlugins/DynECT.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
function Add-DnsTxtDynECT {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$RecordName,
[Parameter(Mandatory,Position=1)]
[string]$TxtValue,
[Parameter(Mandatory,Position=2)]
[string]$zone,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring a zone parameter doesn't work for Posh-ACME DNS plugins because plugin parameters are saved account-wide. So if you ever request a cert in a different zone than your original one, it will overwrite the saved value for the original one and you'll break your renewals. Plugins generally derive the zone name from the $RecordName value.

[Parameter(Mandatory,Position=3)]
[string]$user,
[Parameter(Mandatory,Position=4)]
[securestring]$pass,
[Parameter(Mandatory,Position=5)]
[string]$customer
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The credential parameter names are too generic and could potentially conflict with other plugins. Add a provider specific prefix like Dyn, DynManaged, or DM. So they'd become $DynUser, $DynPass, and $DynCustomer for example.

You're also missing the required $ExtraParams parameter from the example plugin. It and the $RecordName and $TxtValue need to exist in all plugins to ensure everything works together.

)

Add-DynModule

If ($user -and $customer -and $pass) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because all of your parameters are set to Mandatory, you don't need to check that they exist here. PowerShell will enforce this for you.

Write-Verbose "All arguments for authentication has been set"
Write-Verbose "Trying to establish connection to DynECT"
Connect-DynDnsSession -User $user -Customer $customer -Password $pass

If (Test-DynDnsSession) {
Write-Verbose "Successfully generated auth token to DynECT"
} Else {
Write-Warning "Token could not be generated, connection to DynECT has failed"
Return
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informing the user about what went wrong here is good. But a simple return without actually throwing an error is going to make the module think the TXT record was created successfully. I'd change this to something like:

throw "Dyn connection failed. Token could not be generated."

}

If ($zone -and $RecordName -and $TxtValue) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because all of your parameters are set to Mandatory, you don't need to check that they exist here. PowerShell will enforce this for you.

Write-Verbose "All arguments for updating DNS has been set"
Write-Verbose "Trying to add DNS record to DynECT"
Add-DynDnsRecord -Zone $zone -Node $RecordName -DynDnsRecord (New-DynDnsRecord -Text $TxtValue) -Confirm:$false
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the TXT record already exists when you call this? Does the Get-DynDnsZoneChanges call below just return nothing? Just want to make sure.


If (Get-DynDnsZoneChanges -Zone $zone) {
Write-Verbose "DNS Zone has new changes"
} Else {
Write-Warning "DNS Zone do not have any new changes to publish"
}
} Else {
Write-Warning "Missing arguments for updating DNS zone"
Write-Warning "Following arguments are needed: `nzone `nRecordName `nTxtValue"
Return
}

} Else {
Write-Warning "Missing arguments for authentication"
Write-Warning "Following arguments are needed: `nuser `ncustomer `npass"
Return
}

<#
.SYNOPSIS
Add a DNS TXT record to a DynECT hosted zone.

.DESCRIPTION
This plugin require PoShDynDnsApi powershell module.

.PARAMETER RecordName
The fully qualified name of the TXT record.

.PARAMETER TxtValue
The value of the TXT record.

.PARAMETER Zone
The zone is the root domain e.g. example.com

.PARAMETER user
The user is the username that has permissions to DynECT API

.PARAMETER pass
The pass is the password of the user that has permissions to DynECT API

.PARAMETER customer
The customer is the DynECT customer registered name, this is needed to generate authentication token

.EXAMPLE
Add-DnsTxtDynECT '_acme-challenge.example.com' 'asdfqwer12345678' -Zone 'example.com' -user 'username' -pass (ConvertTo-SecureString -AsPlainText 'password' -Force) -customer 'customername'

.EXAMPLE
$seckey = Read-Host -Prompt 'Secret Key:' -AsSecureString
Add-DnsTxtDynECT '_acme-challenge.example.com' 'asdfqwer12345678' -Zone 'example.com' -user 'username' -pass $seckey -customer 'customername

Add a TXT record using an explicit Access Key and Secret key from Windows.
#>

}

function Remove-DnsTxtDynECT {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$RecordName,
[Parameter(Mandatory,Position=1)]
[string]$TxtValue,
[Parameter(Mandatory,Position=2)]
[string]$zone,
[Parameter(Mandatory,Position=3)]
[string]$user,
[Parameter(Mandatory,Position=4)]
[securestring]$pass,
[Parameter(Mandatory,Position=5)]
[string]$customer
)

If ($user -and $customer -and $pass) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because all of your parameters are set to Mandatory, you don't need to check that they exist here. PowerShell will enforce this for you.

Write-Verbose "All arguments for authentication has been set"
Write-Verbose "Trying to establish connection to DynECT"
Connect-DynDnsSession -User $user -Customer $customer -Password $pass

If (Test-DynDnsSession) {
Write-Verbose "DynECT session is alive"

If ($zone -and $RecordName) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because all of your parameters are set to Mandatory, you don't need to check that they exist here. PowerShell will enforce this for you.

Write-Verbose "Trying to remove DNS record"
$txtToRemove = Get-DynDnsRecord -Zone $zone -RecordType TXT -Node $RecordName
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does Dyn deal with multi-valued TXT records? It's a fairly common case when creating wildcard certs that you need to create 2 TXT records with the same name and different values. Most plugins need to distinguish what to delete based on the $TxtValue rather than just $RecordName. We don't want to delete anything except explicitly what we were asked to delete.


If ($txtToRemove) {
Write-Verbose "Record found, removing record: $txtToRemove"
Remove-DynDnsRecord -DynDnsRecord $txtToRemove -Confirm:$false

If (Get-DynDnsZoneChanges -Zone $zone) {
Write-Verbose "DNS Zone has new changes"
} Else {
Write-Warning "DNS Zone do not have any new changes to publish"
}
} Else {
Write-Warning "No records to remove was found. Skipping removal"
}
} Else {
Write-Warning "Missing arguments for removal of DNS Zone."
Write-Warning "Make sure both 'zone' and 'RecordName' is set"
Return
}
} Else {
Write-Warning "DynECT session has been terminated. unable to remove record"
}
} Else {
Write-Warning "Missing arguments for authentication"
Write-Warning "Following arguments are needed: `nuser `ncustomer `npass"
Return
}

<#
.SYNOPSIS
Removes DNS record from DynECT hosted zone.

.DESCRIPTION


.PARAMETER RecordName
The fully qualified name of the TXT record.

.PARAMETER Zone
The zone is the root domain e.g. example.com

.EXAMPLE
Remove-DnsTxtDynECT '_acme-challenge.example.com' -Zone 'example.com'
#>
}

function Save-DnsTxtDynECT {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$zone,
[Parameter(Mandatory,Position=1)]
[string]$user,
[Parameter(Mandatory,Position=2)]
[securestring]$pass,
[Parameter(Mandatory,Position=3)]
[string]$customer
)

If ($zone) {
Write-Verbose "All arguments has been set for publishing zone: $zone"
Publish-DynDnsZoneChanges -Zone $zone -Force -Confirm:$false

If (!(Get-DynDnsZoneChanges -Zone $zone)) {
Write-Verbose "Zone: $zone has been published. no missing changes"
} Else {
Write-Warning "Zone: $zone still has missing changes to publish"
}

Write-Verbose "Disconnecting session to DynECT"
Disconnect-DynDnsSession

If (Test-DynDnsSession) {
Write-Warning "Unable to disconnect session to DynECT"
} Else {
Write-Verbose "Successfully disconnected to DynECT"
}
}
<#
.SYNOPSIS
Publish DNS changes to DynECT hosted zone.

.DESCRIPTION

.PARAMETER Zone
The zone is the root domain e.g. example.com

.EXAMPLE
Save-DnsTxtDynECT -Zone 'example.com'
#>

}

Function Add-DynModule {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally, I'd try to avoid 3rd party module requirements for something with a standard REST API. But since the service is going away soon, I'm ok with it in this instance. However, the plugin shouldn't be responsible for installing the module if it doesn't exist. Just throw an error if it's not found.

$Module = Get-Module -ListAvailable -name "PoShDynDnsApi"

If ($Module.Count -ge 1) {
Write-Verbose "PoShDynDnsApi powershell module is present"
Import-Module -Name PoShDynDnsApi
} Else {
Try {
Write-Verbose "PoShDynDnsApi powershell module is missing, installing"
Install-Module -Name PoShDynDnsApi -Scope CurrentUser
Write-Verbose "Successfully installed PoShDynDnsApi module"
Import-Module -Name "PoShDynDnsApi"
} Catch {
Write-Warning "Module was unable to be installed"
Return
}
}
}

############################
# Helper Functions
############################

# Add additional functions here if necessary.
# Make sure they're uniquely named and try to follow
# verb-noun naming guidelines.
# https://msdn.microsoft.com/en-us/library/ms714428