Pick your OS / firewall. Each setup is a drop-in script plus a cron / scheduled-task line. You already have your auth URL + server URL — paste the server key in the script, save, schedule, done.
Tested on Ubuntu 22.04 / 24.04, Debian 12. The most common modern Linux setup.
Drop this at /usr/local/bin/ipauth-update.sh. Replace YOUR_SERVER_KEY with the server key from your pair; change PORT if you're not gating SSH.
#!/bin/sh
SERVER_URL="https://ipauth.net/serverquery/?key=YOUR_SERVER_KEY"
PORT=22
CURIP=$(curl --max-time 5 -fsS "$SERVER_URL" | grep -oE '"ipaddress":"[0-9.]+' | cut -d'"' -f4)
[ -z "$CURIP" ] && exit 0
if ! ufw status | grep -q "$CURIP.*$PORT/tcp"; then
ufw allow from "$CURIP" to any port "$PORT" proto tcp comment "ipauth"
fi
sudo chmod +x /usr/local/bin/ipauth-update.sh
sudo crontab -e →
*/2 * * * * /usr/local/bin/ipauth-update.sh
Click your auth URL, wait 2 min, then check:
sudo ufw status | grep ipauth
For systems without ufw — older distros, embedded Linux, or anything where you manage iptables directly. Persistence depends on your distro (iptables-persistent, netfilter-persistent, or saved via init).
Save as /usr/local/bin/ipauth-update.sh:
#!/bin/sh
SERVER_URL="https://ipauth.net/serverquery/?key=YOUR_SERVER_KEY"
PORT=22
COMMENT="ipauth"
CURIP=$(curl --max-time 5 -fsS "$SERVER_URL" | grep -oE '"ipaddress":"[0-9.]+' | cut -d'"' -f4)
[ -z "$CURIP" ] && exit 0
# Remove any prior ipauth allow rules for this port, then add fresh.
iptables -S INPUT | grep -- "--comment \"$COMMENT\"" | sed 's/^-A /-D /' | while read R; do
iptables $R 2>/dev/null
done
iptables -I INPUT -p tcp --dport "$PORT" -s "$CURIP" -m comment --comment "$COMMENT" -j ACCEPT
sudo chmod +x /usr/local/bin/ipauth-update.sh
*/2 * * * * /usr/local/bin/ipauth-update.sh
If you use iptables-persistent, also run netfilter-persistent save after the rule lands, OR add a second cron entry to do it.
sudo iptables -L INPUT -n --line-numbers | grep ipauth
Uses Windows Firewall via PowerShell + Task Scheduler. Run PowerShell as Administrator.
Save as C:\Scripts\ipauth-update.ps1:
# ipauth-update.ps1
$ServerUrl = "https://ipauth.net/serverquery/?key=YOUR_SERVER_KEY"
$Port = 22
$RuleName = "IPAuth-Allow-$Port"
try {
$resp = Invoke-RestMethod -Uri $ServerUrl -TimeoutSec 5
if (-not $resp.ipaddress) { exit 0 }
$ip = $resp.ipaddress
} catch { exit 0 }
Get-NetFirewallRule -DisplayName $RuleName -ErrorAction SilentlyContinue | Remove-NetFirewallRule
New-NetFirewallRule -DisplayName $RuleName `
-Direction Inbound -Action Allow `
-Protocol TCP -LocalPort $Port `
-RemoteAddress $ip `
-Profile Any | Out-Null
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\ipauth-update.ps1"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes 2) `
-RepetitionDuration (New-TimeSpan -Days 3650)
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
Register-ScheduledTask -TaskName "IPAuth-Update" `
-Action $action -Trigger $trigger -Principal $principal `
-Description "Updates Windows Firewall rule from IPAuth"
Get-NetFirewallRule -DisplayName "IPAuth-Allow-*" |
Get-NetFirewallAddressFilter |
Select-Object RemoteAddress
If you're using IPAuth from a Windows laptop (not a server), you probably want it to register your IP every time you open PowerShell — so new SSH sessions to your remote boxes work immediately after a network change. This is what we use ourselves.
Open your $PROFILE:
notepad $PROFILE # (if the file doesn't exist yet, PowerShell will offer to create it)
Add a $Servers hash table and a startup loop that hits each auth URL (not server URL — this is the client-side click equivalent) and prints a status line:
$Servers = @{
'webprod' = @{
IPAuthAuth = 'https://ipauth.net/whitelist/?key=YOUR_AUTH_KEY_HERE'
HealthURL = 'https://web.example.com/'
}
# add more boxes as @{ IPAuthAuth = '...'; HealthURL = '...' }
}
try {
$publicIP = Invoke-RestMethod 'https://api.ipify.org' -TimeoutSec 5
} catch { $publicIP = '(lookup failed)' }
Write-Host "Your public IP is: " -NoNewline
Write-Host $publicIP -ForegroundColor Cyan
$statusParts = @()
foreach ($name in $Servers.Keys | Sort-Object) {
$cfg = $Servers[$name]
try { Invoke-RestMethod $cfg.IPAuthAuth -TimeoutSec 5 | Out-Null; $ipauthOK = $true }
catch { $ipauthOK = $false }
try {
$r = Invoke-WebRequest $cfg.HealthURL -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop
$healthOK = $r.StatusCode -eq 200
} catch { $healthOK = $false }
$color = if ($ipauthOK -and $healthOK) { 'Green' } elseif ($healthOK) { 'Yellow' } else { 'Red' }
$state = if ($healthOK) { 'UP' } else { 'DOWN' }
if (-not $ipauthOK) { $state += '(no-ipauth)' }
$statusParts += @{ Name = $name; State = $state; Color = $color }
}
$first = $true
foreach ($p in $statusParts) {
if (-not $first) { Write-Host " | " -NoNewline }
Write-Host "$($p.Name): " -NoNewline
Write-Host $p.State -ForegroundColor $p.Color -NoNewline
$first = $false
}
Write-Host ""
Save and close. Every new PowerShell window now auto-registers your current IP with every server's IPAuth key in $Servers, prints your public IP, and shows a per-server up/down summary. If you switch networks (coffee shop → home), just open a new PS window and you're back in within the server's poll window.
Server side still needs the cron from steps 1-3 above — this client-side helper just clicks the auth URL for you. The two pieces work together.
macOS ships pf out of the box (BSD heritage). Useful for hardened Macs or a Mac mini acting as a small server. Requires enabling pf and adding an anchor.
Edit /etc/pf.conf (sudo):
anchor "ipauth" load anchor "ipauth" from "/etc/pf.anchors/ipauth"
Create /etc/pf.anchors/ipauth:
table <ipauth_allow> persist pass in proto tcp from <ipauth_allow> to any port 22 keep state
Enable pf and load:
sudo pfctl -e sudo pfctl -f /etc/pf.conf
Save as /usr/local/bin/ipauth-update.sh:
#!/bin/sh SERVER_URL="https://ipauth.net/serverquery/?key=YOUR_SERVER_KEY" CURIP=$(curl --max-time 5 -fsS "$SERVER_URL" | grep -oE '"ipaddress":"[0-9.]+' | cut -d'"' -f4) [ -z "$CURIP" ] && exit 0 pfctl -a ipauth -t ipauth_allow -T replace "$CURIP" 2>/dev/null
sudo chmod +x /usr/local/bin/ipauth-update.sh
Save as /Library/LaunchDaemons/net.ipauth.update.plist:
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>Label</key><string>net.ipauth.update</string>
<key>ProgramArguments</key>
<array><string>/usr/local/bin/ipauth-update.sh</string></array>
<key>StartInterval</key><integer>120</integer>
<key>RunAtLoad</key><true/>
</dict>
</plist>
sudo launchctl load -w /Library/LaunchDaemons/net.ipauth.update.plist
sudo pfctl -a ipauth -t ipauth_allow -T show
Uses a pf table — pfctl updates it in-place, no daemon reload needed. Tested on OpenBSD 7.5+.
Add to /etc/pf.conf:
table <ipauth_allow> persist pass in on egress proto tcp from <ipauth_allow> to any port 22 keep state
doas pfctl -f /etc/pf.conf
#!/bin/sh SERVER_URL="https://ipauth.net/serverquery/?key=YOUR_SERVER_KEY" CURIP=$(ftp -V -M -o - "$SERVER_URL" 2>/dev/null | grep -oE '"ipaddress":"[0-9.]+' | cut -d'"' -f4) [ -z "$CURIP" ] && exit 0 pfctl -t ipauth_allow -T replace "$CURIP" 2>/dev/null
doas chmod +x /usr/local/bin/ipauth-update.sh
Edit root's crontab via doas crontab -e:
*/2 * * * * /usr/local/bin/ipauth-update.sh
doas pfctl -t ipauth_allow -T show
FreeBSD's native packet filter. Use a single rule that points at a table the script updates in-place. FreeBSD also supports pf if you'd rather — same flow as the OpenBSD/macOS tabs.
Append to /etc/ipfw.rules (or wherever your ruleset lives):
ipfw table 10 create type addr ipfw add allow tcp from table\(10\) to any 22 in
Reload your ruleset (typically /etc/rc.d/ipfw restart).
#!/bin/sh SERVER_URL="https://ipauth.net/serverquery/?key=YOUR_SERVER_KEY" CURIP=$(fetch -q -T 5 -o - "$SERVER_URL" | grep -oE '"ipaddress":"[0-9.]+' | cut -d'"' -f4) [ -z "$CURIP" ] && exit 0 # Replace table 10 contents with just the current IP. ipfw table 10 flush ipfw table 10 add "$CURIP"
chmod +x /usr/local/bin/ipauth-update.sh
Root's crontab:
*/2 * * * * /usr/local/bin/ipauth-update.sh
ipfw table 10 list
The flow is the same anywhere: poll the server URL, parse the IP, update your allow list, schedule it every couple of minutes. If you're running on something exotic (nftables on Alpine, OPNsense, pfSense, IPFire, Synology, etc.) the script body changes but the structure doesn't.
Ask us about your setup →