Unix: add dns static ip blog
This commit is contained in:
parent
7ec3d37186
commit
aa9c63256d
1 changed files with 166 additions and 6 deletions
|
@ -24,25 +24,25 @@ easily get *inward* access to any port.
|
|||
|
||||
The easiest method, and probably recommended for most users, is using Software
|
||||
Driven WAN (SDWAN). This is similar to a VPN, but unlike Wireguard, ZeroTier One
|
||||
doesn't [[1]] require a centralized server.
|
||||
doesn't<sup>1</sup> require a centralized server.
|
||||
|
||||
Advantages:
|
||||
- Free version is good enough for most small groups
|
||||
- Very simple setup
|
||||
- Very fast through UDP hole-punching
|
||||
- Very low latency through UDP hole-punching
|
||||
- Free (for up to 25 clients)
|
||||
|
||||
Drawbacks:
|
||||
- Limit of 25 clients (on the [free
|
||||
version](https://www.zerotier.com/pricing/))
|
||||
- Requires installing separate software on all clients
|
||||
- Sometimes fails to connect for up to an hour... very hard to debug when it
|
||||
happens
|
||||
- Can only be used by clients on the VPN. For example, a public webserver won't
|
||||
be able to use this
|
||||
- Limit of 25 clients (on the [free
|
||||
version](https://www.zerotier.com/pricing/))
|
||||
|
||||
To use ZeroTier:
|
||||
1. Sign up for an account at [zerotier.com](https://www.zerotier.com/)
|
||||
2. Under "Networks" create a network and give it a name. Ensure access control
|
||||
2. Under "Networks" create a network and give it a name. Ensure Access Control
|
||||
is private.
|
||||
3. Install ZeroTier One on all clients you'd like to connect. You can add more
|
||||
later.
|
||||
|
@ -168,3 +168,163 @@ ssh -p 8022 emiliko@172.27.10.10
|
|||
```
|
||||
|
||||
This will be forwarded to port 22 on the server!
|
||||
|
||||
### Reverse Proxy without a Static IP
|
||||
|
||||
If you're planning to use a home computer, you'll quickly find that most
|
||||
internet service providers do not offer static IPs for consumer plans. Luckily,
|
||||
there's a very simple way around this: Domain Name Servers (DNS).
|
||||
|
||||
You will need a domain to achieve this. Domains should not cost more than
|
||||
$20/year. Here I'll use the domain `example.com` as an example.
|
||||
|
||||
The idea is that a domain will point to a specific IP, but this IP is determined
|
||||
through a lookup to the DNS. This means that if we change the IP the DNS has
|
||||
every time our computer's IP changes, we'll appear to have a static IP!
|
||||
|
||||
First, put your nameservers on a good DNS provider. I use
|
||||
[Cloudflare](https://pages.cloudflare.com/), it's free and fast. You'll need to
|
||||
find the DNS page. The URL will look something like:
|
||||
|
||||
```
|
||||
https://dash.cloudflare.com/<long-project-id>/example.com/dns/records
|
||||
```
|
||||
|
||||
There, you'll want to add an "A Record". The name will be the subdomain. So if
|
||||
my computer is called `mycomputer` and that's in the name field, it'll be
|
||||
accessible at `mycomputer.example.com`.
|
||||
|
||||
Now you need to identify your IP address. This is your PUBLIC IP address, not
|
||||
your LOCAL IP address. One easy way to do this is `curl -q
|
||||
https://ifconfig.me/ip`.
|
||||
|
||||
Make sure "Proxy Status" is OFF. Proxying the connection appears to make this
|
||||
whole idea break down very quickly, so don't.
|
||||
|
||||
With that "A Record" set, try `host mycomputer.example.com` to see when the DNS
|
||||
updates. This can take up to 4 hour, but usually takes under a minute in
|
||||
practice. With this, you should be able to access your computer using the
|
||||
domain! Of course, make sure your router's ports are forwarding to your
|
||||
computer.
|
||||
|
||||
We now need to make your computer update Cloudflare's DNS, whenever the IP
|
||||
changes. I use the script below to do this. Fill in the `HOST`, `DOMAIN`,
|
||||
`TOKEN`, `ZONE_ID`. The `TOKEN` is your Cloudflare application token:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
declare wan_ip_record wan_ip cf_records host_record cf_host_ip cf_rec_id
|
||||
|
||||
declare -r HOST='mycomputer'
|
||||
declare -r DOMAIN='example.com'
|
||||
declare -r TOKEN='...'
|
||||
declare -r ZONE_ID='...'
|
||||
|
||||
#╔─────────────────────────────────────────────────────────────────────────────╗
|
||||
#│ Gετ WΛN IP |
|
||||
#╚─────────────────────────────────────────────────────────────────────────────╝
|
||||
if ! wan_ip_record="$(curl ifconfig.me)"; then
|
||||
echo "Hosts timed out" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wan_ip="$wan_ip_record"
|
||||
#wan_ip="$(echo "$wan_ip_record" | tail -n1 | awk '{ split($0, a, " "); print a[4] }')"
|
||||
|
||||
#╔─────────────────────────────────────────────────────────────────────────────╗
|
||||
#│ Gετ Λ rεcδrd δη Clδμdflαrε |
|
||||
#╚─────────────────────────────────────────────────────────────────────────────╝
|
||||
if ! cf_records="$(curl -s --request GET \
|
||||
--url https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $TOKEN")"
|
||||
then
|
||||
echo "Failed to retrive cloudflare zone records" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in {0..4000}; do # Assuming 4000 is enough
|
||||
record="$(echo "$cf_records" | jq --raw-output ".result[${i}].name")"
|
||||
|
||||
if [[ "$record" == "${HOST}.${DOMAIN}" ]]; then
|
||||
host_record="$(echo "$cf_records" | jq -r ".result[${i}]")"
|
||||
break
|
||||
elif [[ "$record" == null ]]; then
|
||||
echo "No record found for ${HOST}.${DOMAIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
#╔─────────────────────────────────────────────────────────────────────────────╗
|
||||
#│ Sετ Λ rεcδrd τδ cμrrεητ WΛN |
|
||||
#╚─────────────────────────────────────────────────────────────────────────────╝
|
||||
cf_host_ip="$(echo "$host_record" | jq -r '.content')"
|
||||
cf_rec_id="$(echo "$host_record" | jq -r '.id')"
|
||||
|
||||
if [[ -z "$cf_host_ip" || "$cf_host_ip" == null ]]; then
|
||||
echo "Failed to find content of A record for ${HOST}.${DOMAIN}" >&2
|
||||
exit 1
|
||||
elif [[ -z "$cf_rec_id" || "$cf_rec_id" == null ]]; then
|
||||
echo "Failed to find A record ID for ${HOST}.${DOMAIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$cf_host_ip" == "$wan_ip" ]]; then
|
||||
echo "Cloudflare is up to date @ $(date)" >&2
|
||||
else
|
||||
echo "Updating Cloudflare's A record from $cf_host_ip to $wan_ip" >&2
|
||||
|
||||
patch_response="$(curl -s --request PATCH \
|
||||
--url "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${cf_rec_id}" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $TOKEN" \
|
||||
--data '{
|
||||
"comment": "'"${HOST} @ $(date)"'",
|
||||
"content": "'"$wan_ip"'",
|
||||
"name": "'"${HOST}.${DOMAIN}"'",
|
||||
"proxied": false,
|
||||
"ttl": 1
|
||||
}')"
|
||||
|
||||
if [[ "$(echo "$patch_response" | jq -r '.success')" == true ]]; then
|
||||
echo "Update to $wan_ip succeeded @ $(date)" >&2
|
||||
else
|
||||
echo "Failed to update A record. DUMP:"
|
||||
echo "$patch_response"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Now we need a systemd-timer to run this script. I run it once every 15 minutes.
|
||||
Please refer to the [systemd-timers]() blog for more information, but breifly I
|
||||
use:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Wants=update_a_record.timer
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/set_a_records.sh
|
||||
```
|
||||
|
||||
Timer:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Requires=update_a_record.service
|
||||
|
||||
[Timer]
|
||||
Unit=update_a_record.service
|
||||
OnCalendar=*-*-* *:00,15,30,45:00
|
||||
RandomizedDelaySec=15min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
Then start it with `systemctl enable update_a_record.service`. The name of the
|
||||
service will be different based on what you called the files.
|
||||
|
|
Loading…
Reference in a new issue