diff --git a/README.md b/README.md deleted file mode 100644 index 46773d3..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# DIY-DynDNS-Powershell - -Automatically updating the Cloudflare DNS records for your self-hosted domain - with Powershell \ No newline at end of file diff --git a/fixDNS.ps1 b/fixDNS.ps1 new file mode 100644 index 0000000..89bba44 --- /dev/null +++ b/fixDNS.ps1 @@ -0,0 +1,39 @@ +#Dealing with my Cloudflare domains and my dynamic IP +$progressPreference = "silentlyContinue" +$timeStamp = (get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss") +$cfEmail = "cloudflare@sorensiim.dk" +$cloudflareAccountId = "5781ba314730623cc65f8e943e1bf215" +$cfToken = "Md8Na4WkayyONozTp8i7OvS2PdFt63fdP1ZR_lTt" +$myIp = curl "api.ipify.org" #Curl is easier for this one +$cfAPIBaseURI = "https://api.cloudflare.com/client/v4/" +#Get an auth token +$cfHeaders = @{ + "Authorization" = "Bearer $cfToken" +} + +#Pull the DNS zones +$zones = ((Invoke-WebRequest -URI "$($cfAPIBaseURI)zones" -Method GET -Headers $cfHeaders -UseBasicParsing).content | ConvertFrom-JSON).Result +#$zones + +#Get the DNS records for the zones, except the TailScale records +$zones | Foreach-Object{ + $zoneId = $_.id + $dnsRecords = ((Invoke-WebRequest -URI "$($cfAPIBaseURI)zones/$($_.id)/dns_records" -Method GET -Headers $cfHeaders -UseBasicParsing).content | ConvertFrom-JSON).Result | Where-Object{($_.type -eq "A") -and ($_.content -notMatch "^100")} | Select-Object id, name, type, content + #$dnsRecords | Where-Object{($_.type -eq "A") -and ($_.content -notMatch "^100")} | Select-Object id, name, type, content + #break + #Update the DNS record to use the correct IP + $dnsRecords | where-Object{$_.content -ne $myIp} | Foreach-object{ + $oldIp = $_.content + $recordBody = @{ + comment = "$TimeStamp | Updated automatically from $oldIp to $myIp" + content = "$myIp" + } | ConvertTo-JSON + $ipFix = Invoke-WebRequest -URI "$($cfAPIBaseURI)zones/$($zoneId)/dns_records/$($_.id)" -Method PATCH -Body $recordBody -Headers $cfHeaders + If($ipFix.statuscode -match "20."){ + Write-output "Successfully changed the A-record $($_.name) from $oldIp to $myIp." + } + If($ipFix.statuscode -notMatch "20."){ + Write-error "Failed to change the A-record $($_.name) from $oldIp to $myIp." + } + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..f569242 --- /dev/null +++ b/readme.md @@ -0,0 +1,83 @@ +### Prerequisites + - Use Cloudflare DNS + - [Powershell installed](obsidian://open?vault=Glamdring%202&file=PowerShell%20on%20Debian) + + +### Step 1 - Get a Token +Get a Cloudflare API token with the necessary permissions. Log in to your CloudFlare account, go to `Manage Account` and to `Account API Tokens`. Click that big, blue `Create Token` button and select the `Edit zone DNS` template. + +Select the relevant Zone Resources, I just gave it permissions to edit all my zones: +![[Pasted image 20250415131506.png]] + +Once you've configured it all, continue to summary, click `Create Token` and remember to save it. It will only be shown once, so if you forget to save it you'll have to create a new token. + +### Step 2 - The Script +Create the PowerShell script file on whatever server you want to run this. Using your web server would be a good choice. +``` +nano /etc/ipfixer.ps1 +``` +Create the file whereever you want, I'm not your mom. + +Paste in the script, remember to add the token you created in step 1: +``` +#Dealing with my Cloudflare domains and my dynamic IP +$ProgressPreference = "SilentlyContinue" +$timeStamp = (get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss") +$cfToken = "MkayyyYourTokenGoesHere" +$myIp = curl "api.ipify.org" #Curl is easier for this one +$cfAPIBaseURI = "https://api.cloudflare.com/client/v4/" +#Get an auth token +$cfHeaders = @{ + "Authorization" = "Bearer $cfToken" +} + +#Pull the DNS zones +$zones = ((Invoke-WebRequest -URI "$($cfAPIBaseURI)zones" -Method GET -Headers $cfHeaders -UseBasicParsing).content | ConvertFrom-JSON).Result + +#Get the DNS records for the zones, except the TailScale records +$zones | Foreach-Object{ + $zoneId = $_.id + $dnsRecords = ((Invoke-WebRequest -URI "$($cfAPIBaseURI)zones/$($_.id)/dns_records" -Method GET -Headers $cfHeaders -UseBasicParsing).content | ConvertFrom-JSON).Result | Where-Object{($_.type -eq "A") -and ($_.content -notMatch "^100")} | Select-Object id, name, type, content + + #Update the DNS record to use the correct IP + $dnsRecords | where-Object{$_.content -ne $myIp} | Foreach-object{ + $oldIp = $_.content + $recordBody = @{ + comment = "$TimeStamp | Updated automatically from $oldIp to $myIp" + content = "$myIp" + } | ConvertTo-JSON + $ipFix = Invoke-WebRequest -URI "$($cfAPIBaseURI)zones/$($zoneId)/dns_records/$($_.id)" -Method PATCH -Body $recordBody -Headers $cfHeaders + If($ipFix.statuscode -match "20."){ + Write-output "Successfully changed the A-record $($_.name) from $oldIp to $myIp." + } + If($ipFix.statuscode -notMatch "20."){ + Write-error "Failed to change the A-record $($_.name) from $oldIp to $myIp." + } + } +} +``` +A few things to note in the script above: + - All TailScale IPs (or other IPs starting with 100) are ignored, as is everything but A-records. + - If a record is updated, a comment is added to it on Cloudflare: + ![[Pasted image 20250415132520.png]] + +### Step 3 - Automation (cron) +The entire point was to automate everything, so let's set up a cron job (scheduled task) +``` +crontab -e +``` +This uses your default text editor. If it's something insane like Vim, you might want to [change it](https://linuxsimply.com/linux-basics/text-editors/default/). + +Just like Regex, cron is pretty simple once you've learned it. Every line added to the file consists of 6 space-separated properties: +1. The minute +2. The hour +3. The day of the month +4. The month of the year +5. The day of the week +6. The command to run + +This means that to run the command `pwsh /etc/ipfixer.ps1` every hour, on the hour, you need to add this line: +``` +0 * * * * pwsh /etc/ipfixer.ps1 +``` +Feel free to get creative with the schedule if you like, [good info here](https://vitux.com/how-to-setup-a-cron-job-in-debian/). \ No newline at end of file