Inhaltsverzeichnis

Home-Server

DynDNS

Gelegentlich ist es eine gute Idee, einen Server zu Hause einzurichten und ihn dann aus dem Internet erreichbar zu machen. Das nennt sich "Home-Server".

Hat man seinen Internetzugang bei der Telekom oder bei 1und1, dann ist das mit einem DynDNS-Account ganz einfach zu realisieren. Komplizierter wird es, wenn man seinen Internetzugang bei Unitymedia hat.

Mit einem Internetzugang von Unitymedia, bei dem man eine Fritz!Box bekommen hat, benötigt man einen DynDNS-Anbieter, der IPv6 unterstützt.

deutsche DynDNS-Anbieter:

ddclient

Achtung: ddclient ist ziemlich buggy, wenn Cloudflare als DNS zum Einsatz kommt, empfehle ich das selbstgeschriebene Skript weiter unten (Stand ddclient 3.9.1)

Konfigurationsdateien

/etc/ddclient/ddclientv4.conf
daemon=0
syslog=yes
pid=@runstatedir@/ddclient.pid
ssl=yes
ipv6=no
 
login=E-MAIL
password=API-KEY-TOKEN
 
use=cmd, cmd='curl --ipv4 http://checkip.dyndns.org/', cmd-skip='Current IP Address: '
#use=web, web='http://checkip.dyndns.org/', web-skip='Current IP Address: '
 
protocol=cloudflare, zone=EXAMPLE.COM, ttl=1, EXAMPLE.COM
/etc/ddclient/ddclientv6.conf
daemon=0
syslog=yes
pid=@runstatedir@/ddclient.pid
ssl=yes
ipv6=yes
 
login=E-MAIL
password=API-KEY-TOKEN
 
use=cmd, cmd='curl --ipv6 http://checkipv6.dyndns.org/', cmd-skip='Current IP Address: '
#use=web, web='http://checkipv6.dyndns.org/', web-skip='Current IP Address: '
 
protocol=cloudflare, zone=EXAMPLE.COM, ttl=1, EXAMPLE.COM

IPv6 auf der von Debian gepatchten Version (z.B. Ubuntu)

/etc/ddclient/ddclientv6.conf
daemon=0
syslog=yes
pid=@runstatedir@/ddclient.pid
ssl=yes
ipv6=yes
 
login=E-MAIL
password=API-KEY-TOKEN
 
### usev6 only works with debian's patched version in apt repos
usev6=if, if='eth0', if-skip='inet6 '
 
protocol=cloudflare, zone=EXAMPLE.COM, ttl=1, EXAMPLE.COM

Benutzung

Aufruf (IPv4 & IPv6):

ddclient -daemon 0 -file /etc/ddclient/ddclientv4.conf -force && ddclient -daemon 0 -file /etc/ddclient/ddclientv6.conf -force

Debuggen (IPv4 & IPv6)

ddclient -daemon=0 -file /etc/ddclient/ddclientv4.conf -force -debug -noquiet -verbose && ddclient -daemon 0 -file /etc/ddclient/ddclientv6.conf -force -debug -noquiet -verbose

ddclient soll stündlich statt täglich ausgeführt werden

mv /usr/local/etc/periodic/daily/ddclient_force /usr/local/etc/periodic/hourly/

alternativ: nur den einzelnen Befehl in crontab hinzufügen

0 * * * * ddclient -daemon 0 -file /etc/ddclient/ddclientv4.conf -force && ddclient -daemon 0 -file /etc/ddclient/ddclientv6.conf -force

DynDNS-Client selbst gebaut

Cloudflare

neuste Version auf GitHub: https://github.com/masterflitzer/cloudflare-ddns.git

Abhängigkeiten: links und jq

Cron:

@reboot root cloudflare-ddns.sh > /var/log/cloudflare-ddns.log 2>&1
@hourly root cloudflare-ddns.sh > /var/log/cloudflare-ddns.log 2>&1

Skript

> apt install links curl jq
cloudflare-ddns.ini
API_TOKEN=1234567893feefc5f0q5000bfo0c38d90bbeb
ZONE_NAME=example.com
cloudflare-ddns.sh
#!/bin/bash
 
##### Variables
 
CONFIG_FILE="$(dirname "$0")/$(basename -- "$0" .sh).ini"
INTERVAL="5"
COUNTER="10"
 
# boolean
# if "true" the dns record for the root domain will be updated
UPDATE_ROOT_DOMAIN="true"
# multiple record names (subdomains) to be updated
# separated by space, e.g. "www mail smtp"
RECORD_NAME_V4=""
RECORD_NAME_V6=""
TTL="1"
PROXIED="false"
 
# stable base URL for all Version 4 HTTPS endpoints
API_ENDPOINT="https://api.cloudflare.com/client/v4"
 
# custom API-Token (not global API-Key)
# permissions needed: #dns_records:edit
API_TOKEN="$(cat "$CONFIG_FILE" | grep -E "^API_TOKEN=" | head -1 | cut -d "=" -f2)"
 
# when you want to update "www.example.com", "www" is the RECORD_NAME and "example.com" is the ZONE_NAME
ZONE_NAME="$(cat "$CONFIG_FILE" | grep -E "^ZONE_NAME=" | head -1 | cut -d "=" -f2)"
 
COUNTER_V4="$COUNTER"
COUNTER_V6="$COUNTER"
 
 
##### Functions
 
links_IPv4() {
    IP_V4="$(links -dump http://checkip.dyndns.org/ | tr -s '[ :]' '\n' | egrep '[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+')"
    #IPv4="1.1.1.1"
}
 
links_IPv6() {
    IP_V6="$(links -dump http://checkipv6.dyndns.org/ | tr -s ' ' '\n' | egrep '[0-9a-f]+[:]+[0-9a-f]+[:]*')"
    #IPv6="2606:4700:4700::1111"
}
 
 
##### Script
 
### Get IPs
printf "\n#==============================================================================#\n\n"
 
echo -n "Determining IPv4 address"
links_IPv4
while test -z "$IP_V4" -a "$COUNTER_V4" -gt "0"
do
    echo -n '.'
    COUNTER_V4="$(echo "$COUNTER_V4"|awk '{print $1-1}')"
    sleep $INTERVAL
    links_IPv4
done
echo
 
echo -n "Determining IPv6 address"
links_IPv6
while test -z "$IP_V6" -a "$COUNTER_V6" -gt "0"
do
    echo -n '.'
    COUNTER_V6="$(echo "$COUNTER_V6"|awk '{print $1-1}')"
    sleep $INTERVAL
    links_IPv6
done
echo
 
echo "IPv4: $IP_V4"
echo "IPv6: $IP_V6"
 
printf "\n#==============================================================================#\n\n"
 
### Get Zone ID
ZONE_ID="$(curl -s -X GET "${API_ENDPOINT}/zones" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" 2>/dev/null | jq -r ".result[] | select(.name == \"${ZONE_NAME}\") | .id")"
 
### Get Record ID (IPv4)
RECORD_ID_V4="$(curl -s -X GET "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" 2>/dev/null | jq -r ".result[] | select((.name == \"${ZONE_NAME}\") and (.type == \"A\")) | .id")"
 
### Get Record ID (IPv6)
RECORD_ID_V6="$(curl -s -X GET "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" 2>/dev/null | jq -r ".result[] | select((.name == \"${ZONE_NAME}\") and (.type == \"AAAA\")) | .id")"
 
echo "Record Name      = $ZONE_NAME"
echo "ZONE_ID          = $ZONE_ID"
echo "Record ID (IPv4) = $RECORD_ID_V4"
echo "Record ID (IPv6) = $RECORD_ID_V6"
 
 
### Set IP
printf "\n#==============================================================================#\n\n"
 
if test $UPDATE_ROOT_DOMAIN == "true"
then
    echo "IPv4: Updating DNS Record to '$IP_V4'"
    curl -s -X PUT "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records/${RECORD_ID_V4}" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" --data "{\"type\":\"A\",\"name\":\"${ZONE_NAME}\",\"content\":\"${IP_V4}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"; echo
 
    printf "\n#------------------------------------------------------------------------------#\n\n"
 
    echo "IPv6: Updating DNS Record to '$IP_V6'"
    curl -s -X PUT "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records/${RECORD_ID_V6}" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" --data "{\"type\":\"AAAA\",\"name\":\"${ZONE_NAME}\",\"content\":\"${IP_V6}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"; echo
fi
 
### Set IP for Record Names
printf "\n#==============================================================================#\n\n"
 
if test ! -z "${RECORD_NAME_V4// }"; then printf "IPv4: Subdomains\n\n"; fi
 
COUNTER="0"
for RECORD_NAME in $RECORD_NAME_V4
do
    if test $COUNTER -gt 0; then printf "\n#------------------------------------------------------------------------------#\n\n"; fi
 
    RECORD_ID="$(curl -s -X GET "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" 2>/dev/null | jq -r ".result[] | select((.name == \"${RECORD_NAME}.${ZONE_NAME}\") and (.type == \"A\")) | .id")"
 
    echo "Record Name      = $RECORD_NAME.$ZONE_NAME"
    echo "Record ID        = $RECORD_ID"
 
    echo "Updating DNS Record to '$IP_V4'"
    curl -s -X PUT "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records/${RECORD_ID}" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" --data "{\"type\":\"A\",\"name\":\"${RECORD_NAME}.${ZONE_NAME}\",\"content\":\"${IP_V4}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"; echo
 
    let "COUNTER += 1"
done
 
if test $COUNTER -gt 0; then
    printf "#==============================================================================#\n\n"
fi
 
if test ! -z "${RECORD_NAME_V6// }"; then printf "IPv6: Subdomains\n\n"; fi
 
COUNTER="0"
for RECORD_NAME in $RECORD_NAME_V6
do
    if test $COUNTER -gt 0; then printf "\n#------------------------------------------------------------------------------#\n\n"; fi
 
    RECORD_ID="$(curl -s -X GET "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" 2>/dev/null | jq -r ".result[] | select((.name == \"${RECORD_NAME}.${ZONE_NAME}\") and (.type == \"AAAA\")) | .id")"
 
    echo "Record Name      = $RECORD_NAME.$ZONE_NAME"
    echo "Record ID        = $RECORD_ID"
 
    echo "Updating DNS Record to '${IP_V6}'"
    curl -s -X PUT "${API_ENDPOINT}/zones/${ZONE_ID}/dns_records/${RECORD_ID}" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" --data "{\"type\":\"AAAA\",\"name\":\"${RECORD_NAME}.${ZONE_NAME}\",\"content\":\"${IP_V6}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"; echo
 
    let "COUNTER += 1"
done
 
if test $COUNTER -gt 0; then
    printf "#==============================================================================#\n\n"
fi

Selfhost

Wird dagegen der Internetzugang per Horizon-Box bereitgestellt, dann kann man dort keinen DynDNS-Account eintragen und muss sich einen DynDNS-Client selber bauen.

hierfür sind folgende Schritte abzuarbeiten:

  1. Home-Server mit fester IP im LAN (z.B. 192.168.0.2) konfigurieren, der Server darf keine DHCP-Adresse bekommen, da sich diese u.u. ändern kann;
  2. in der Horizon-Box muss unter FORTGESCHRITTEN / Weiterleitung eine Port-Weiterleitung eingerichtet werden, z.B.:
    • | 80 | 192.168.0.2 | 80 | TCP |
  3. sinnvoll wäre auch ein Portweiterleitung für den Port 443 aber natürlich nur, wenn man seine Web-Seite SSL-verschlüsselt hat
  4. jetzt wird ein DynDNS-Zugang benötigt, den kann man u.a. bei selfhost bekommen;
  5. beim DynDNS-Anbieter eine Umleitung einrichten und das vergebene Passwort merken, man kann sich bei selfhost auch schon eine Update-URL generieren, von der nur die IP entsprechend jedesmal angepasst werden muss;
    1. beispielsweise sieht das bei selfhost so aus:
    2. man kann es auch automatisieren, dann würde der Kommandozeilenaufruf von Linux aus so aussehen:
    3. die Kommandozeile kann man dann in die crontab eintragen und einmal am Tag ausführen lassen, damit wäre der selbst gebaute DynDNS-Client fertig;
      1. Wichtig ist hierbei noch, dass er nicht zu oft ausgeführt wird, da einige Anbieter nur ein IP-Update pro Tag zulassen!

hier noch ein paar alternative Kommandozeilenaufrufe für Linux, die theoretisch alle das gleiche bewirken sollten:

links -dump http://3075846:geheim@carol.selfhost.de/nic/update?myip=$(links -dump http://www.ip-secrets.info/ | awk '/Ihre aktuelle IP-Adresse:/{print $NF}')
links -dump http://3075846:geheim@carol.selfhost.de/nic/update?myip=$(links -dump http://www.wieistmeineip.de/ | sed -n '/Ihre IP-Adresse lautet:/,/[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*/p' | awk '/[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*/{print $NF}')
links -dump http://3075846:geheim@carol.selfhost.de/nic/update?myip=$(links -dump http://www.meine-aktuelle-ip.de/ | awk '/Ihre aktuelle IP Adresse:/{print $NF}')
links -dump http://3075846:geheim@carol.selfhost.de/nic/update?myip=$(links -dump http://www.meineip.de/ | sed -n '/Meine IP-Adresse lautet:/,/[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*/p' | awk '/[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*/{print $NF}')
links -dump http://3075846:geheim@carol.selfhost.de/nic/update?myip=$(links -dump http://meineipadresse.de/ | fgrep 'Meine o:ffentliche' | tr -s ' ' '\n' | awk '/[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*/{print $NF}')
links -dump http://3075846:geheim@carol.selfhost.de/nic/update?myip=$(links -dump http://www.heise.de/netze/tools/meine-ip-adresse/ | awk '/Ihre Anfrage kommt von der IP-Adresse:/{print $NF}')

Statt "3075846" und "geheim" sind die eigenen Zugangsdaten für den DynDNS-Account anzugeben.

Und nicht vergessen!
Es muss unbedingt der PATH angegeben werden, denn sonst kann der CRON-Dienst das Programm links nicht finden.
Da diese Kommandos mit Linux und FreeBSD funktionieren sollen, kann ich hier den absoluten Pfad nicht angeben. Denn der links-Pfad ist bei Linux und FreeBSD unterschiedlich.