PowerShell/CloudFlare/cloudflare_ddns.ps1
2024-05-01 14:11:08 +02:00

236 lines
8.1 KiB
PowerShell

# gitea.RdzeN.net
# DDNS Cloudflare - KeepUpIP
#
<#
The script checks the current external IP based on defined websites.
In case of a change in the external IP, it updates the CloudFlare service using a dedicated API.
It requires providing 'api_key' and 'dns_zone' IDs.
Parameters, such as the number of logs set to 10k lines, can be customized.
The frequency of execution should be defined in cron/schedule.
#>
# Variable section
$location = Get-Location
$path_ip = "$($location)\cloudflare_ddns_ip" # store last set IP
$path_log = "$($location)\cloudflare_ddns_log" # store logs
$path_sites = "$($location)\cloudflare_ddns_sites"
$log_limit = 10000
$ip = $null
$ip_actuall = $null
$ip_ddns = $null
$api_key = 'enter_api_key' # https://dash.cloudflare.com/profile/api-tokens
$dns_zone = 'enter_dns_zone_id' # cloudflare -> websites -> your site -> dns_zone
# if as Page list;
# Alternatively, it checks whether the file already exists.
# The file changes the order of pages by rotating like a die.
# The first element becomes the last, and so on, in a rolling cube fashion.
if (-not (Test-Path $path_sites)) {
$ip_sites = @(
'http://ipinfo.io/ip'
'http://ifconfig.me/ip'
'http://icanhazip.com'
'http://ident.me'
'https://api.ipify.org'
'https://ip2location.io/ip'
)
} else {
[array]$ip_sites = Get-Content $path_sites
}
# function for logs.
$logs = @()
function log {
param (
$l_level,
$l_message
)
$l_timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.ffff K"
switch ($l_level) {
0 {
$log_message = "$($l_timestamp) | [INFO] | $($l_message)"
}
1 {
$log_message = "$($l_timestamp) | [WARNING] | $($l_message)"
}
2 {
$l_error = $Error[0].Exception
$log_message = "$($l_timestamp) | [ERROR] | $($l_message) | $l_error"
}
}
return $log_message
}
# function for storing logs.
function log_store {
$logs >> $path_log
if (Test-Path $path_log) {
$tmp = Get-Content $path_log -Tail $log_limit
$tmp | Set-Content $path_log
}
}
# 01 - Get the actual IP
function get-ip {
for ($i = 0; $i -lt $ip_sites.Count; $i++) {
switch ($ip_sites[$i]) {
'http://ipinfo.io/ip' {
$uri = $ip_sites[$i]
try {
$test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 2
$ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim()
}
catch {
$global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)"
continue
}
}
'http://ifconfig.me/ip' {
$uri = $ip_sites[$i]
try {
$test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 1
$ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim()
}
catch {
$global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)"
continue
}
}
'http://icanhazip.com' {
$uri = $ip_sites[$i]
try {
$test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 1
$ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim()
}
catch {
$global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)"
continue
}
}
'http://ident.me' {
$uri = $ip_sites[$i]
try {
$test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 1
$ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim()
}
catch {
$global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)"
continue
}
}
'https://api.ipify.org' {
$uri = $ip_sites[$i]
try {
$test = Test-Connection -TargetName $uri.Replace("https://","").Split("/")[0] -Count 1
$ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim()
}
catch {
$global:logs += log -l_level 1 -l_message "IP: ERROR | URL: $($uri)"
continue
}
}
'https://ip2location.io/ip' {
$uri = $ip_sites[$i]
try {
$test = Test-Connection -TargetName $uri.Replace("https://","").Split("/")[0] -Count 1
$ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim()
}
catch {
$global:logs += log -l_level 1 -l_message "IP: ERROR | URL: $($uri)"
continue
}
}
}
if ($null -ne $ip) {
$global:logs += log -l_level 0 -l_message "IP: $($ip) | URL: $($uri)"
$ip_sites = $ip_sites[1..($ip_sites.Count)] + $ip_sites[0]
$ip_sites > $path_sites
break
}
}
return $ip
}
$ip_actuall = get-ip
# If the retrieved IP is NULL or does not match the regex, then...
if (($null -eq $ip_actuall) -or ($ip_actuall -notmatch "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$")) {
$logs += log -l_level 2 -l_message "Error durring geting public IP | EXIT"
log_store
exit
}
# Verification of the existence of a file with the saved IP;
# If the file exists, then verify if the received IP matches the saved one. If yes, close.
if (Test-Path $path_ip) {
$tmp = Get-Content $path_ip
if ($tmp -eq $ip_actuall) {
$logs += log -l_level 0 -l_message "IP MATCH! | EXIT"
log_store
exit
}
}
# 02 - Get the actuall DNS record from cloudflare
$headers= @{
"Content-Type" = "application/json"
"Authorization" = "Bearer $api_key"
}
# "Try" for fetching a list of DNS records from Cloudflare.
try {
$response = Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$($dns_zone)/dns_records" -Method GET -Headers $headers
$cloudflare_dns = $response.result
$logs += log -l_level 0 -l_message "Downloaded $([array]$cloudflare_dns.count) records."
}
# In case of an error, log the details to a file and close.
catch {
$logs += log -l_level 2 -l_message "Error durring geting dns_records from cloud_flare | EXIT"
log_store
exit
}
# "Processing each entry from Cloudflare.
# If the script encounters the current IP, it will terminate with log recording.
# The script is designed to update all entries with a consistent IP in case of a change.
for ($i = 0; $i -lt $cloudflare_dns.Count; $i++) {
$id = $cloudflare_dns[$i].id
$address = $cloudflare_dns[$i].name
$ip_ddns = $cloudflare_dns[$i].content
$proxied = $cloudflare_dns[$i].proxied
if ($ip_ddns -eq $ip_actuall) {
$logs += log -l_level 0 -l_message "IP MATCH! | EXIT"
log_store
exit
}
$body = @{
content = $ip_actuall
name = $address
type = "A"
proxied = $proxied
comment = log -l_level 0 -l_message "Updated from $($ip_ddns)"
}
$body = $body | ConvertTo-Json
$url = "https://api.cloudflare.com/client/v4/zones/$($dns_zone)/dns_records/$($id)"
try {
$response = Invoke-RestMethod -Uri $url -Method Put -Headers $headers -Body $body
$logs += log -l_level 0 -l_message "Updated from $($ip_ddns) to $($ip_actuall) at $($address)"
}
catch {
$logs += log -l_level 2 -l_message "Not updated from $($ip_ddns) to $($ip_actuall) at $($address)"
}
}
# Saving the new IP to a file for future verification.
$ip_actuall > $path_ip
log_store
exit