apache2 Proxy mit Cache
apacheIch nutze im ioBroker als Wetter Adapter Weather Undergound. Damit rasch das eigene Wetter abgefragt und in vis-2 mit Material-Design als HTML-Ausgabe dargestellt. Einfach, schlicht – tut es mir. Nur beziehe ich die Icons direkt von Weather Underground. Das muss doch auch anders gehen: Also hab ich mal eben einen apache2 Proxy davorgesetzt. Damit sind meine Zugriffe lokal und zusätzlich noch mit einem Cache. Es lohnt ja nicht jedes Bild immer und immer wieder zu laden – wenn man es doch in einem Cache ablegen könnte.
Bevor einer fragt: Ja, das ist sicherlich nicht sicher einfach so alles ohne irgendwelche Sicherheitsmaßnahmen umzusetzen. Das weiß ich auch. Zum einen läuft das bei mir nur intern und nicht öffentlich im Internet, zum anderen ging es mir um die Funktion. Ich wollte das einfach mal mit apache2 umgesetzt haben. Nicht mehr, nicht weniger 😉 Und betrachte dies als Beispiel: Bei mir war die Ausgangslage ioBroker. Wer weiß, was es bei Dir ist 😛
Das hier will ich haben:

Das aktuelle Wetter inklusive Vorhersage 😉
Und so ist es gelöst – einfach folgenden HTML-Code in einem „Vis 2 Material-Widgets HTML-Vorlage“ einfügen:
<style>
.grid-container {
display: grid;
grid-template-columns: 64px auto;
gap: 5px;
padding: 5px;
font-size: 0.8em;
}
.grid-container div {
margin: auto 0;
}
.grid-container img {
width: 64px;
height: 64px;
}
</style>
<div class="grid-container">
<div>
<img src="{v:weatherunderground.0.forecast.0d.iconURL;alt:0_userdata.0.Wetter.Tagesvorhersage.VorhersageIcon;v?v:alt}">
</div>
<div>
<span style="font-weight:bold;">Jetzt</span><br>
{weatherunderground.0.forecast.current.temp} °C
</div>
<div>
<img src="{v:weatherunderground.0.forecast.0d.iconURL;alt:0_userdata.0.Wetter.Tagesvorhersage.VorhersageIcon;v?v:alt}">
</div>
<div>
<span style="font-weight:bold;">Heute: {weatherunderground.0.forecast.0d.date;date(DD.MM.YYYY)}</span>
<br>
{weatherunderground.0.forecast.0d.tempMin}
-
{weatherunderground.0.forecast.0d.tempMax} °C
({weatherunderground.0.forecast.0d.cloudCover} % Wolkendichte)
<br>
Regen: {weatherunderground.0.forecast.0d.precipitationChance} % ({weatherunderground.0.forecast.0d.precipitationAllDay} mm)
<br>
{weatherunderground.0.forecast.0d.state}
</div>
<div>
<img src="{weatherunderground.0.forecast.1d.iconURL}">
</div>
<div>
<span style="font-weight:bold;">{weatherunderground.0.forecast.1d.date;date(DD.MM.YYYY)}</span>
<br>
{weatherunderground.0.forecast.1d.tempMin}
-
{weatherunderground.0.forecast.1d.tempMax} °C
({weatherunderground.0.forecast.1d.cloudCover} % Wolkendichte)
<br>
Regen: {weatherunderground.0.forecast.1d.precipitationChance} % ({weatherunderground.0.forecast.1d.precipitationAllDay} mm)
<br>
{weatherunderground.0.forecast.1d.state}
</div>
<div>
<img src="{weatherunderground.0.forecast.2d.iconURL}">
</div>
<div>
<span style="font-weight:bold;">{weatherunderground.0.forecast.2d.date;date(DD.MM.YYYY)}</span>
<br>
{weatherunderground.0.forecast.2d.tempMin}
-
{weatherunderground.0.forecast.2d.tempMax} °C
({weatherunderground.0.forecast.2d.cloudCover} % Wolkendichte)
<br>
Regen: {weatherunderground.0.forecast.2d.precipitationChance} % ({weatherunderground.0.forecast.2d.precipitationAllDay} mm)
<br>
{weatherunderground.0.forecast.2d.state}
</div>
<div>
<img src="{weatherunderground.0.forecast.3d.iconURL}">
</div>
<div>
<span style="font-weight:bold;">{weatherunderground.0.forecast.3d.date;date(DD.MM.YYYY)}</span>
<br>
{weatherunderground.0.forecast.3d.tempMin}
-
{weatherunderground.0.forecast.3d.tempMax} °C
({weatherunderground.0.forecast.3d.cloudCover} % Wolkendichte)
<br>
Regen: {weatherunderground.0.forecast.3d.precipitationChance} % ({weatherunderground.0.forecast.3d.precipitationAllDay} mm)
<br>
{weatherunderground.0.forecast.3d.state}
</div>
<div>
<img src="{weatherunderground.0.forecast.4d.iconURL}">
</div>
<div>
<span style="font-weight:bold;">{weatherunderground.0.forecast.4d.date;date(DD.MM.YYYY)}</span>
<br>
{weatherunderground.0.forecast.4d.tempMin}
-
{weatherunderground.0.forecast.4d.tempMax} °C
({weatherunderground.0.forecast.4d.cloudCover} % Wolkendichte)
<br>
Regen: {weatherunderground.0.forecast.4d.precipitationChance} % ({weatherunderground.0.forecast.4d.precipitationAllDay} mm)
<br>
{weatherunderground.0.forecast.4d.state}
</div>
<div>
<img src="{weatherunderground.0.forecast.5d.iconURL}">
</div>
<div>
<span style="font-weight:bold;">{weatherunderground.0.forecast.5d.date;date(DD.MM.YYYY)}</span>
<br>
{weatherunderground.0.forecast.5d.tempMin}
-
{weatherunderground.0.forecast.5d.tempMax} °C
({weatherunderground.0.forecast.5d.cloudCover} % Wolkendichte)
<br>
Regen: {weatherunderground.0.forecast.5d.precipitationChance} % ({weatherunderground.0.forecast.5d.precipitationAllDay} mm)
<br>
{weatherunderground.0.forecast.5d.state}
</div>
</div>
Im Objekt {weatherunderground.0.forecast.0d.iconURL} stehen URLs wie diese hier: https://www.wunderground.com/static/i/c/v4/28.svg. Und ich hätte gerne, dass daraus das hier wird: https://10.96.100.66:8082/proxy.0/img/https://www.wunderground.com/static/i/c/v4/28.svg. Damit vis-2 keinen externen Apache direkt aufruft, nutze ich den Proxy-Adapter. In diesem ist folgender Pfad gesetzt: aus /proxy.0/img/… mache https://10.96.100.66:4444/img/… wie in folgendem Screenshot gezeigt:

ioBroker Proxy-Adapter Pfad-Einstellung
So wie ich vis-2 in meinem Webbrowser öffne und mir die Wettervorhersage anschaue, werden im Webbrowser die Bilder über die gleiche Adresse geladen, wie vis-2 selbst. Im Hintergrund setzt der Proxy-Adapter, wie oben gezeigt, die Anfragen auf den lokal installieren apache2 um – welcher die Proxy Anfragen an Port 4444 annimmt.
Dazu habe ich in der apache2 Konfig einen neuen vhost angelegt. Dieser lauscht auf Port 4444 und ist via https erreichbar. Als Cache nutze ich das apache2 Modul cache_disk. Damit das korrekt funktioniert, lasse ich die eventuell von der eigentlichen Anfrage gelieferten Header zum Caching durch meine eigenen überschreiben. Als nächstes wird die URL umgeschrieben. Aus https://<meine ioBroker>:4444/img/<https://das eigentliche Bild> soll einfach der Teil nach /img/ extrahiert und als eigentliche Bild-Quelle verwendet werden. Hier gibt es lediglich einen kleinen Haken: apache2 ersetzt // bei https:// durch einen /. Es wird aus der URL also http:/<wohin auch immer>. Deswegen wird die neue URL aus dem Protokoll, verbunden durch einen / und der eigentlichen Anfrage, zusammengesetzt. Sollte es zu einem Fehler kommen, lasse ich ein fallback.svg anzeigen. Und zuletzt lasse ich noch in die Standardlogdaten entsprechende Hinweise eintragen.
So sieht das ganze aus:
Listen 4444
<VirtualHost *:4444>
ServerName imgprx.gehirn-mag.net.internal
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/fullchain.pem
SSLCertificateKeyFile /etc/apache2/ssl/key.pem
# --- Caching ---
Header unset Cache-Control
Header unset Pragma
Header set Cache-Control "public, max-age=86400"
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/mod_cache-lock
CacheIgnoreHeaders Set-Cookie Cache-Control Pragma Expires
CacheRoot /var/cache/apache2/mod_cache_disk
CacheEnable disk /
CacheDisable /fallback.svg
CacheDefaultExpire 604800
CacheMaxExpire 604800
# --- URL Rewrite für /img/<https://asfasfdsa> ---
# Apache macht aus https://bla... https:/bla... <-- Split in zwei Teile
SSLProxyEngine On
RewriteEngine On
RewriteRule ^/img/(https?:)(.+)$ - [E=TARGET:$1/$2,E=TPROTO:$1,E=TURL:$2]
RewriteCond %{ENV:TARGET} !=""
RewriteRule ^(.*)$ %{ENV:TARGET} [P,L]
# --- Fallback bei Fehlern ---
ProxyErrorOverride On
ErrorDocument 404 /fallback.svg
ErrorDocument 500 /fallback.svg
ErrorDocument 502 /fallback.svg
ErrorDocument 503 /fallback.svg
ErrorDocument 504 /fallback.svg
Alias /fallback.svg /var/www/vhosts/proxy/fallback.svg
# --- Log ---
CustomLog /var/log/apache2/other_vhosts_access.log "%h %l %u %t \"%r\" %>s %b \"cache:%{cache-status}e\" \"target:%{TARGET}e\""
ErrorLog /var/log/apache2/error.log
</VirtualHost>
Im HTML-Vorlage Widget vom ioBroker habe ich die URLs nun wie folgen geändert: <img src=“/proxy.0/img/{weatherunderground.0.forecast.1d.iconURL}“>. Also lediglich ein /proxy.0/img vorangestellt. Bekommen habe ich also, dass im Webbrowser die externen Bilder ebenfalls über vis-2 geladen werden. Allerdings geht im Hintergrund ein apache2 ran, welcher die gewünschten Bilder aus dem Internet im Sinne eines Proxys lädt und diese sogar noch in einem Cache ablegt.
Und, nochmals erwähnt: Das läuft bei mir nur intern. Letztendlich lädt dieser Proxy alles aus dem Netz, was hinter /img/ steht. Sollte man das also wirklich ungeschützt ins Internet packen wollen, dann wären noch diverse weitere Einstellungen zum Thema Sicherheit sehr sinnvoll. Für meine Zwecke, wo ich im ersten Schritt nur wissen wollte, ob und wie man so etwas in apache2 lösen kann, tut es das mehr wie Dicke. Ich weiß, dass es geht – denn ich habe eine lauffähige Konfig 😀