Was ist meine IP - selfhosted
whatismyip.com auf dem eigenen Server.
Zugegeben: Es gibt viele Anbieter, die diesen Dienst anbieten. Ich habe jedoch viele virtuelle Instanzen in meinem Netz, die gelegentlich ihre öffentliche IP wissen wollen. Wenn diese dann zufällig innerhalb einer Minute ihre Anfragen an z.B. https://whatismyipaddress.com/ richten, kann es vorkommen, dass das Rate-Limiting der Seite die Antwort verweigert, da alle Anfragen von derselben Source-IP kommen.
Um nicht in diese Falle zu treten, habe ich mir diesen Dienst selbst nachgebaut. Eine Anleitung dazu befindet sich auf github:
Da ich, wie wohl inzwischen offensichtlich sein dürfte, ein Freund der Virtualisierung bin, habe ich auch dieses Projekt mit einem weiteren Container in meiner Web-Landschaft umgesetzt. Es integriert sich nahtlos in mein Web-Services Framework, sodass sich auch dieses Modul direkt hinter den hier beschriebenen Reverse-Proxy stellen lässt.
Doch diesmal muss man etwas genauer darüber nachdenken, welchen Weg die Netzwerkpakete nehmen.
So sollte es aussehen.
Wenn das Projekt ersteinmal läuft, soll es so aussehen:
Neue Herausforderungen
Da der Reverse-Proxy zwischen dem aufrufenden Client und dem neuen Docker-Container steht, erfüllt der Proxy natürlich seine Aufgabe, indem er die Netzwerkverbindung unterbricht, um sie untersuchen zu können. Auf Clientseite erzwingt er https
, was er zum Container hin als normale http
-Verbindung neu aufbaut. Aus diesem Grund wird der Container die Pakete nie vom rufenden Client kommen sehen, sondern vom Proxy. So kann er seine Aufgabe nicht erfüllen, da er immer antwortet der Proxy habe ihn gerufen.
Ich musste mich daher mit den HTTP-Headern auseinandersetzen. Wie ich dieses Problem gelöst habe, werden wir später im Verlauf der Konfiguration sehen.
Der Container
Wie immer ist die Konfiguration des Containers extrem einfach. Lediglich seine Beschreibung an das docker-compose.yml
-File anhängen:
echoip:
image: mpolden/echoip
container_name: echoip
environment:
- TZ=Europe/Berlin # Zeitzone, optional
command: -r -p -H X-Real-IP -a /geoip/GeoLite2-ASN.mmdb -c /geoip/GeoLite2-City.mmdb -f /geoip/GeoLite2-Country.mmdb
expose:
- "8080"
volumes:
- "./ifconfig:/geoip" # Verzeichnis für GeoIP-Datenbanken mounten
restart: unless-stopped
networks:
- web
labels:
- "com.centurylinklabs.watchtower.enable=true"
Wie man jedoch diesmal sieht, gibt es ein paar Besonderheiten, die ich im Einzelnen durchgehe:
echoip:
Dies ist der Name des Docker-Services, der hier definiert wird. Er ist frei gewählt.
image: mpolden/echoip
Dies gibt das Docker-Image an, das verwendet wird, um den Container zu erstellen.
container_name: echoip
Hier wird der Name des Containers festgelegt.
environment:
Hier werden Umgebungsvariablen festgelegt. In diesem Fall wird die Zeitzone auf Europe/Berlin
gesetzt.
command:
Dies legt die Parameter fest, die beim Start des Containers an echoip übergeben werden. Die Optionen haben folgende Bedeutung:
-r
: Verwendung der Reverse-Lookup-Funktion.-p
: Hören auf Anfragen.-H X-Real-IP
: Hier ist die Besonderheit: Die Erkennung der echten IP-Adresse des Clients erfolgt aus dem HeaderX-Real-IP
, den wir mit der realen IP des aufrufenden Clients befüllen müssen.-a /geoip/GeoLite2-ASN.mmdb
: Pfad zur ASN-Datenbank.-c /geoip/GeoLite2-City.mmdb
: Pfad zur Stadt-Datenbank.-f /geoip/GeoLite2-Country.mmdb
: Pfad zur Länderdatenbank.
expose:
Hier wird der Port 8080 für den Container freigegeben, sodass andere Dienste innerhalb des Docker-Netzwerks darauf zugreifen können.
volumes:
Dies definiert ein Volume, das auf dem Host-System gemountet wird. Das Verzeichnis ./ifconfig
auf dem Host wird in /geoip
im Container verfügbar gemacht, was bedeutet, dass GeoIP-Datenbanken hier abgelegt werden müssen.
restart: unless-stopped
Dies gibt an, dass neu gestartet werden soll, es sei denn, er wurde manuell gestoppt. Das sorgt dafür, dass der Dienst im Falle eines Fehlers oder nach einem Systemneustart automatisch wieder gestartet wird.
networks:
Hier wird angegeben, dass der Container dem web
-Netzwerk beitreten soll, welches sich hinter dem NPM befindet.
labels:
Labels sind Metadaten für den Container. In diesem Fall wird eine Label für die Integration mit Watchtower gesetzt, einem Tool, das Docker-Container automatisch aktualisiert. Das Label aktiviert Watchtower für diesen Container.
Geolocation hinzufügen
Um Informationen darüber zu erhalten, wo sich die abgefragte IP ungefähr befindet, müssen die Geodaten in den im Compose-File angegebenen Ordner abgelegt werden. Da ich diesen Dienst später ifconfig
nennen möchte, benenne ich den Ordner entsprechend
mkdir ifconfig
cd ifconfig
license_key=[get_your_own_at_www.maxmind.com]
wget -qO - "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=${license_key}&suffix=tar.gz" | tar -xvzf -
wget -qO - "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${license_key}&suffix=tar.gz" | tar -xvzf -
wget -qO - "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${license_key}&suffix=tar.gz" | tar -xvzf -
mv $(find . -name '*.mmdb' 2>/dev/null) .
rm -rf GeoLite2-*_*
Einen license_key erhält man auf der Webseite von Maxmind.
Damit wären alle Vorbereitungen abgeschlossen und wir können den Container aus dem Ordner, in dem auch das Compose-File liegt, starten.
docker-compose up -d echoip
Konfiguration des Nginx-Proxy-Managers
Als nächstes müssen wir noch den Dienst im Internet anbieten. Das machen wir wie gewohnt:
Neu sind diese Parameter:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
Diese Anweisungen sind Teil der Konfiguration des Reverse Proxys. Sie sorgen dafür, dass bestimmte HTTP-Header gesetzt werden, wenn Anfragen an Backend-Server weitergeleitet werden.
proxy_set_header X-Real-IP $remote_addr;
Dies setzt den Header X-Real-IP
auf die IP-Adresse des ursprünglichen Clients ($remote_addr
).
Dieser Header ermöglicht es dann dem Backend-Server zu erkennen, von welcher IP-Adresse die Anfrage ursprünglich gesendet wurde, anstatt nur die IP-Adresse des Proxys zu sehen, wie eingangs erklärt.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Dies setzt den Header X-Forwarded-For
auf eine Liste von IP-Adressen. Wenn die Anfrage über mehrere Proxys geleitet wird, werden alle vorherigen IP-Adressen hinzugefügt. Dieser Header wird häufig verwendet, um eine Kette von Proxys zu verfolgen, die die Anfrage weitergeleitet haben. So kann der Backend-Server die gesamte Historie der IP-Adressen sehen, die an der Anfrage beteiligt waren.
proxy_set_header X-Forwarded-Proto $scheme;
Dies setzt den Header X-Forwarded-Proto
auf das Protokoll (HTTP oder HTTPS), das der Client ursprünglich verwendet hat ($scheme
) . Es informiert den Backend-Server darüber, welches Protokoll für die ursprüngliche Anfrage verwendet wurde, was wichtig sein kann, wenn der Server entscheidet, wie er mit dieser Anfrage umgeht (z. B. bei der Erstellung von Links oder der Verwaltung von Redirects).
Aufruf an der Konsole
In der Shell kann man so leicht Informationen über die eigene oder auch fremde IPs erhalten:
curl https://ifconfig.meine-domain.de/json | jq .
{
"ip": "212.19.61.165",
"ip_decimal": 3558030757,
"country": "Germany",
"country_iso": "DE",
"country_eu": true,
"region_name": "Rheinland-Pfalz",
"region_code": "RP",
"zip_code": "55218",
"city": "Ingelheim",
"latitude": 49.9686,
"longitude": 8.0641,
"time_zone": "Europe/Berlin",
"asn": "AS12306",
"asn_org": "Plus.line AG",
"user_agent": {
"product": "curl",
"version": "8.10.1",
"raw_value": "curl/8.10.1"
}
}
curl https://ifconfig.meine-domain.de/city
Ingelheim
https://ifconfig.its-meister.de/port/443
{
"ip": "185.101.174.192",
"port": 443,
"reachable": true
}
$ curl https://ifconfig.meine-domain.de/country
Elbonia
$ curl https://ifconfig.meine-domain.de/country-iso
EB
$ curl https://ifconfig.meine-domain.de/city
Bornyasherk
$ curl https://ifconfig.meine-domain.de/asn
AS59795
$ curl https://ifconfig.meine-domain.de/asn-org
Hosting4Real
Wie man sieht, leistet dieser Container gute Dienste - z.B. im Zusammenhang mit Dynamischen DNS-Diensten.
Fazit
Dieser Blogpost ist eigentlich keine große Aktion. Er entstand lediglich, um dem Rate-Limiting öffentlicher Anbieter zu entgehen. Aber vielleicht nützt es nicht nur mir, sondern auch einem meiner Leser, die diesen Dienst ebenfalls verwenden können. 😃