Skip to content

Commit

Permalink
Add WEDOS plugin and guide (#533)
Browse files Browse the repository at this point in the history
  • Loading branch information
rmbolger authored Jan 14, 2024
1 parent 625c7ec commit fb403a7
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 0 deletions.
308 changes: 308 additions & 0 deletions Posh-ACME/Plugins/WEDOS.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
function Get-CurrentPluginType { 'dns-01' }

function Add-DnsTxt {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$RecordName,
[Parameter(Mandatory,Position=1)]
[string]$TxtValue,
[Parameter(Mandatory)]
[pscredential]$WedosCredential,
[Parameter(ValueFromRemainingArguments)]
$ExtraParams
)

$zone = Find-Zone $RecordName $WedosCredential
if (-not $zone) {
throw "Unable to find zone for $RecordName in WEDOS"
}
Write-Debug "Found zone $zone"

# setup a tracking variable for zones we need to "commit"
if (-not $script:WedosZonesToSave) { $script:WedosZonesToSave = @() }

if (-not (Find-TxtRec $RecordName $TxtValue $zone $WedosCredential)) {
# empty string short names are ok for zone apex
$recShort = ($RecordName -ireplace [regex]::Escape($zone), [string]::Empty).TrimEnd('.')
$data = @{
domain = $zone
name = $recShort
type = 'TXT'
rdata = $TxtValue
ttl = 300 # minimum
}
Invoke-Wedos dns-row-add $WedosCredential -Data $data
if ($zone -notin $script:WedosZonesToSave) {
$script:WedosZonesToSave += $zone
}
}
else {
Write-Debug "Record $RecordName already contains $TxtValue. Nothing to do."
}

<#
.SYNOPSIS
Add a DNS TXT record to WEDOS
.DESCRIPTION
Add a DNS TXT record to WEDOS
.PARAMETER RecordName
The fully qualified name of the TXT record.
.PARAMETER TxtValue
The value of the TXT record.
.PARAMETER WedosCredential
The account username and API password.
.PARAMETER ExtraParams
This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
.EXAMPLE
Add-DnsTxt '_acme-challenge.example.com' 'txt-value' -WedosCredential (Get-Credential)
Adds a TXT record for the specified site with the specified value.
#>
}

function Remove-DnsTxt {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$RecordName,
[Parameter(Mandatory,Position=1)]
[string]$TxtValue,
[Parameter(Mandatory)]
[pscredential]$WedosCredential,
[Parameter(ValueFromRemainingArguments)]
$ExtraParams
)

$zone = Find-Zone $RecordName $WedosCredential
if (-not $zone) {
throw "Unable to find zone for $RecordName in WEDOS"
}
Write-Debug "Found zone $zone"

# setup a tracking variable for zones we need to "commit"
if (-not $script:WedosZonesToSave) { $script:WedosZonesToSave = @() }

if (-not ($rec = Find-TxtRec $RecordName $TxtValue $zone $WedosCredential)) {
Write-Debug "Record $RecordName with value $TxtValue doesn't exist. Nothing to do."
}
else {
$data = @{
domain = $zone
row_id = $rec.ID
}
Invoke-Wedos dns-row-delete $WedosCredential -Data $data
if ($zone -notin $script:WedosZonesToSave) {
$script:WedosZonesToSave += $zone
}
}

<#
.SYNOPSIS
Remove a DNS TXT record from WEDOS
.DESCRIPTION
Remove a DNS TXT record from WEDOS
.PARAMETER RecordName
The fully qualified name of the TXT record.
.PARAMETER TxtValue
The value of the TXT record.
.PARAMETER WedosCredential
The account username and API password.
.PARAMETER ExtraParams
This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
.EXAMPLE
Remove-DnsTxt '_acme-challenge.example.com' 'txt-value' -WedosCredential (Get-Credential)
Removes a TXT record for the specified site with the specified value.
#>
}

function Save-DnsTxt {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[pscredential]$WedosCredential,
[Parameter(ValueFromRemainingArguments)]
$ExtraParams
)

foreach ($zone in $script:WedosZonesToSave) {
Write-Verbose "Applying changes for $zone zone"
$data = @{
name = $zone
}
Invoke-Wedos dns-domain-commit $WedosCredential -Data $data
}
$script:WedosZonesToSave = @()

<#
.SYNOPSIS
Commit changes to WEDOS zones.
.DESCRIPTION
Commit changes to WEDOS zones.
.PARAMETER WedosCredential
The account username and API password.
.PARAMETER ExtraParams
This parameter can be ignored and is only used to prevent errors when splatting with more parameters than this function supports.
.EXAMPLE
Save-DnsTxt -WedosCredential (Get-Credential)
Commits changes to zones modified by Add-DnsTxt and Save-DnsTxt
#>
}

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

# https://kb.wedos.com/cs/wapi-api-rozhrani/zakladni-informace-wapi-api-rozhrani/wapi-zakladni-informace/
# https://kb.wedos.com/en/wapi-api-interface/wdns-en/wapi-wdns/

function Invoke-Wedos {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$Command,
[Parameter(Mandatory,Position=1)]
[pscredential]$Credential,
[hashtable]$Data,
[int[]]$AltGoodCodes=@()
)

# The auth protocol for this API is rather...unique.

# Get a SHA1 hash of the password
$sha1 = [Security.Cryptography.SHA1CryptoServiceProvider]::new()
$hashBytes = $sha1.ComputeHash([Text.Encoding]::UTF8.GetBytes($Credential.GetNetworkCredential().Password))
$pHash = [BitConverter]::ToString($hashBytes).Replace('-','').ToLower()

# For some reason, the auth protocol requires the current 00-24 hour
# with leading zeros specifically in the Europe/Prague time zone.
$nowUtc = [DateTime]::UtcNow
$hour = [TimeZoneInfo]::ConvertTimeFromUtc(
$nowUtc,
# Despite being "Standard" time, this will auto-convert
# to "Summer" time when appropriate.
[TimeZoneInfo]::FindSystemTimeZoneById('Central Europe Standard Time')
).ToString('HH')

# Concatenate the username, hashed password, and hour
# and then SHA1 hash the whole thing
$authRaw = '{0}{1}{2}' -f $Credential.Username,$pHash,$hour
Write-Debug "authRaw = $authRaw"
$hashBytes = $sha1.ComputeHash([Text.Encoding]::UTF8.GetBytes($authRaw))
$auth = [BitConverter]::ToString($hashBytes).Replace('-','').ToLower()

# Build the request object
$req = @{
request = @{
user = $Credential.UserName
auth = $auth
clTRID = "Posh-ACME $(New-Guid)" # client request ID
command = $Command
}
}
if ($Data) {
$req.request.data = $Data
}

$queryParams = @{
Uri = 'https://api.wedos.com/wapi/json'
Method = 'POST'
# Send the JSON request as a value of "request" that is
# application/x-www-form-urlencoded instead of just raw JSON
Body = @{request=($req | ConvertTo-Json -Compress -Depth 10)}
Verbose = $false
ErrorAction = 'Stop'
}
Write-Debug "POST $($queryParams.Uri)`n$($req|ConvertTo-Json -Depth 10)"
$resp = Invoke-RestMethod @queryParams @script:UseBasic
Write-Debug "Response:`n$($resp|ConvertTo-Json -Depth 10)"

if ($resp.response.code -ne 1000 -and $resp.response.code -notin $AltGoodCodes) {
"WEDOS API Error $($resp.response.code): $($resp.response.result)"
}
return $resp.response.data
}

function Find-Zone {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$RecordName,
[Parameter(Mandatory,Position=1)]
[pscredential]$Credential
)

# setup a module variable to cache the record to zone mapping
# so it's quicker to find later and uses fewer API calls
if (!$script:WedosRecordZones) { $script:WedosRecordZones = @{} }

# check for the record in the cache
if ($script:WedosRecordZones.ContainsKey($RecordName)) {
return $script:WedosRecordZones.$RecordName
}

# Get all of the domains on the account
$zones = Invoke-Wedos dns-domains-list $Credential | Select-Object -Expand domain
if (-not $zones) {
Write-Warning "No WEDOS hosted domains found."
return
}

# find the zone for the closest/deepest sub-zone that would contain the record.
$pieces = $RecordName.Split('.')
for ($i=0; $i -lt ($pieces.Count-1); $i++) {
$zoneTest = $pieces[$i..($pieces.Count-1)] -join '.'
Write-Debug "Checking $zoneTest"

$match = $zones | Where-Object { $zoneTest -eq $_.name } | Select-Object -First 1
if ($match) {
$script:WedosRecordZones.$RecordName = $zoneTest
return $zoneTest
}
}
}

function Find-TxtRec {
[CmdletBinding()]
param(
[Parameter(Mandatory,Position=0)]
[string]$RecordName,
[Parameter(Mandatory,Position=1)]
[string]$TxtValue,
[Parameter(Mandatory,Position=2)]
[string]$Zone,
[Parameter(Mandatory,Position=3)]
[pscredential]$Credential
)

# Get all of the records in the zone
$recs = Invoke-Wedos dns-rows-list $Credential -Data @{domain=$Zone} | Select-Object -Expand row
if (-not $recs) {
Write-Warning "No WEDOS records found."
return
}

return $recs | Where-Object {
$RecordName -eq "$($_.name).$Zone".Trim('.') -and
$_.rdtype -eq 'TXT' -and
$_.rdata -eq $TxtValue
}
}
1 change: 1 addition & 0 deletions Posh-ACME/Private/Import-PluginDetail.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function Import-PluginDetail {
'UKFast' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'dns-01'; Path = ''; Name = 'UKFast'}
'WebRoot' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'http-01'; Path = ''; Name = 'WebRoot'}
'WebSelfHost' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'http-01'; Path = ''; Name = 'WebSelfHost'}
'WEDOS' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'dns-01'; Path = ''; Name = 'WEDOS'}
'Windows' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'dns-01'; Path = ''; Name = 'Windows'}
'Yandex' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'dns-01'; Path = ''; Name = 'Yandex'}
'Zilore' = [pscustomobject]@{PSTypeName = 'PoshACME.PAPluginDetail'; ChallengeType = 'dns-01'; Path = ''; Name = 'Zilore'}
Expand Down
26 changes: 26 additions & 0 deletions docs/Plugins/WEDOS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
title: WEDOS

# How To Use the WEDOS DNS Plugin

This plugin works against the [WEDOS](https://www.wedos.com/zone/) DNS provider. It is assumed that you have already setup an account and added or registered the DNS domain(s) you will be working against.

## Setup

The WAPI (Web API) needs to be explicitly enabled on your account before you can use it. If you haven't already, follow the [instructions here](https://kb.wedos.com/en/wapi-api-interface/wapi-activation-and-settings/).

First it will have you agree to the ToS. Then double check that the IP address you will be connecting from is in the `Allowed IP addresses` field and the preferred protocol is `JSON`. You'll also need to set a password specifically for API access (which should be different than your primary account password).

## Using the Plugin

You will need to provide your account username/email and API password as a PSCredential object to the `WedosCredential` plugin parameter.

!!! warning
WEDOS seems to have an unusually long propagation time between when your DNS changes are committed and when they are reflected on their authoritative nameservers. The GUI warns that changes could take up to 60 minutes. But testing has show the average time is around 4-8 minutes. Be sure to use the `-DnsSleep` parameter with an appropriately long timeout
to avoid validation failures.

```powershell
$pArgs = @{
WedosCredential = (Get-Credential)
}
New-PACertificate example.com -Plugin WEDOS -PluginArgs $pArgs -DnsSleep 600
```
1 change: 1 addition & 0 deletions docs/Plugins/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ SimplyCom | [Simply.com](https://www.simply.com/) (formerly UnoEuro) | [Usage Gu
SSHProxy | Custom SSH Script | [Usage Guide](SSHProxy.md) | :white_check_mark:
TotalUptime | [TotalUptime](https://totaluptime.com/solutions/cloud-dns-service/) | [Usage Guide](TotalUptime.md) | :white_check_mark:
UKFast | [UKFast](https://ukfast.co.uk) | [Usage Guide](UKFast.md) | :white_check_mark:
WEDOS | [WEDOS](https://www.wedos.com/zone/) | [Usage Guide](WEDOS.md) | :white_check_mark:
Windows | [Microsoft Windows DNS](https://www.microsoft.com/en-us/cloud-platform/windows-server) | [Usage Guide](Windows.md) | Windows-Only<sup>[1](#windows)</sup>
Yandex | [Yandex DNS](https://connect.yandex.com/) | [Usage Guide](Yandex.md) | :white_check_mark:
Zilore| [Zilore](https://zilore.com/?r=1f752c82378516890a5200006eae8469) | [Usage Guide](Zilore.md) | :white_check_mark:
Expand Down

0 comments on commit fb403a7

Please sign in to comment.