Einfacher Reverse Proxy mit dem Nginx Webserver

Vor einiger Zeit habe ich über einen Reverse Proxy mit Apache geschrieben. Das klappt natürlich auch bei Nginx. Es wird dazu das Modul ngx_http_proxy_module benötig. Da es zu den Standards gehört ist es eigentlich immer mit dabei und wird auch automatisch beim kompilieren gebaut.

Es hat eine ganze Reihe von Optionen, aber die wichtigste ist mit Sicherheit proxy_pass. Alle Optionen werden im location { … } Kontext gesetzt.

Ein einfaches Beispiel wie man es im server { … } Kontext verwenden kann.

location /feed {
 proxy_pass https://problempod.de/feed/mp3;
 proxy_pass_request_headers off;
 proxy_pass_request_body off;
 proxy_ssl_verify on;
 proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
 resolver 8.8.8.8 ipv6=off;
}

Das Ganze ist etwas einfacher als bei Apache. Der Inhalt von „https://problempod.de/feed/mp3“ wird durch den Proxy unter meiner Domain und /feed bereitgestellt. Man kann bei proxy_pass mit http oder https URLs arbeiten. Die beiden Optionen proxy_pass_request_headers und proxy_pass_request_body mit dem Wert „off“ verhindern das irgendwelche Request Informationen des Clients an das Proxy Ziel weiter gegeben werde. Das ist kein muss und manchmal möchte man auch Information vom Client mit geben. Oder gar eigene Header senden. Bei diesem einfachen Beispiel ist das aber nicht der Fall.

Bei https URLs in proxy_pass kann es Sinn machen das Zertifikat zu überprüfen. Bei Default würde Nginx einfach alles akzeptieren. Das kann bei internen Seiten gut und wichtig sein. Aber wenn das Ziel valide Zertifikate verwendet, kann man diese auch überprüfen. Das geht mit proxy_ssl_verify und dem Wert „on“. Das alleine reicht aber nicht. Nginx braucht auch eine Liste mit vertrauenswürdigen Zertifikaten. Diese übergibt man mit proxy_ssl_trusted_certificate „/pfad/zum/cert“. Auf vielen System gibt es mit „/etc/ssl/certs/ca-certificates.crt“ solche eine Liste. Diese enthält oft alle vertrauenswürdigen Zertifikate. Man kann das auch etwas eingrenzen wenn man die CA des Zieles kennt. Zum Beispiel mit „/etc/ssl/certs/COMODO_RSA_Certification_Authority.pem“. Falls die Überprüfung zu einem negativen Ergebnis kommt, wird das als Error ins Log geschrieben und man erhält einen 502 Bad Gateway.

Die resolver Option ist vielseitig einsetzbar. Diese Option gehört streng genommen nicht zu den Proxy Optionen aber kann hier hilfreich sein. Sie definiert wie  Nginx an die IP Adresse der Domain in proxy_pass kommt. In diesem Beispiel geht der DNS Request an Googles DNS. Man kann aber auch jeden beliebigen DNS Resolver wählen. Mit dem „ipv6=off“ Wert kann verhindert werden das neben dem A auch ein AAAA Abruf gemacht wird. Standardmäßig werden IPv4 und IPv6 Adressen aufgelöst. Das kann helfen wenn das Ziel zwar über einen AAAA Record verfügt aber der nicht zu erreichen ist. Neben einem externen kann auch ein interner DNS Server gewählt werden. Fehlt die resolver Option fällt Nginx auf die OS Einstellungen zurück. In den meisten Fällen wird das der Nameserver in „/etc/resolv.conf“ sein.

Der einfachste Proxy bei Nginx wäre so etwas

location /feed2 {
 proxy_pass https://problempod.de/feed/m4a;
}

Das reicht völlig aus. Aber in den meisten fällen möchte man trotzdem von den vielen Option Gebrauch machen um ein best möglichstes Ergebnis zu erhalten.

Brotil und Nginx

Ich verwende seit einiger Zeit das ngx_brotli Modul im Nginx.  Da ich Ubuntu Xenial verwende setze ich mein PPA ein. Dort wird das Modul gegen ein aktuelles brotli Paket gebaut.

Die Konfiguration ist relativ einfach. In der nginx.conf im http Bereich reichen folgende Optionen.

brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/rss+xml application/x-javascript text/javascript image/svg+xml;

Wirklich nötig ist eigentlich nur „brotli on;“ , die anderen Optionen kann man nach belieben verändern.

Äußerst interessant ist „brotli_comp_level“, der Standard Wert ist 6. Je höher der Wert ist je stärker wird komprimiert. Der Nachteil ist, es dauert länger und die Webseite wird langsam. Man muss also einen guten Mittelwert finden.

Dazu habe ich eine relativ große Text Datei bei jedem Level mehrmals heruntergeladen und gemessen wie lange der Server braucht die Datei komprimiert bereitzustellen. Der Cash muss natürlich ausgeschaltet werden und ein wenig schwanken die Werte schon. Eine große Hilfe war curl mit der -w Option. Die Komprimierungszeit habe ich berechnet aus „time_starttransfer – time_pretransfer“.

Man kann erkennen das mit Level 10 die Komprimierungszeit fast eine Sekunde beträgt. Das ist natürlich viel zu lange. Daher nochmal ohne Level 10.

Man kann gut erkennen das die Levels 5 bis 9 alle eine fast gleiche Komprimierungsratio haben. Daher lohnt es sich in den meisten Fällen nicht ein Level höher als 5 zu wählen. Die Zeit von 0,74 Sekunden bei Level 5 ist ein guter Kompromiss, bei einer Größe von nur 7,51 %. Eine Alternative wäre Level 2, dort dauert es nur 0,51 Sekunden, es werden trotzdem Dateien mit nur 9,8 % ihrer eigentlichen Größe erreicht. Je kleiner die zu komprimierenden Dateien sind umso schneller ist es. Daher sehen die Diagramme bei jeder Datei ein wenig anders aus. Aber ich denke mit meiner Beispieldatei konnte ich viele Fälle abdecken.

Pakete aus bestimmten PPAs bevorzugen

PPAs können eine schöne Sache sein, aber manchmal kann die Verwendung von PPAs auch ganz schön nerven. Zum Beispiel wenn in zwei PPAs die man verwenden möchte unter anderem die selben Pakete enthalten sind. Natürlich in unterschiedlichen Versionen oder Builds, aber apt wird immer die höchste Version installieren.

Zum Glück kann man aber apt mit preferences (Präferenzen) sagen welche Pakete aus welchem PPA installiert werden sollen. Eigentlich sind die preferences dazu gedacht bei einer bestimmten Version zu bleiben, aber man kann damit auch für bestimmte Pakete ein PPA bevorzugen.

Um in apt preferences zu verwenden wird eine Datei in /etc/apt/preferences.d angelegt. Jede Datei kann mehrere Einträge enthalten. Ein Eintrag besteht immer aus drei Zeilen. Die erste ist der Paket Name „Package“. Dort funktionieren auch Wildcards wie *. Die zweite Zeile ist „Pin“, dort wird fest gelegt von wo das Paket kommen soll, welche Version bevorzugt wird oder ähnliches. Die letzte Zeile besteht aus der „Pin-Priority“. Dort wird über Zahlenwerte die Wichtigkeit des Eintrags festgelegt. Ich wollte wirklich immer mein PPA bevorzugen. Auch sollten schon installierte Pakete ersetzt werden. Deshalb habe ich den Wert 1001 genommen. Eine Bevorzugung würde aber auch schon mit 991 stattfinden, jedoch bleiben dann bereits installiere Version bestehen.

Ein Beispiel für Openssl

Package: openssl
Pin: release n=xenial,o=LP-PPA-carsten-uppenbrink-net-nginx
Pin-Priority: 1001

Durch das „release“ in der Pin Zeile wirkt das Pinning auf die Quellen und nicht auf eine bestimmte Version des Paketes. Durch n=xenial reduziere ich die Präferenz auf xenial Pakete. Durch o= bevorzuge ich ein bestimmtes PPA. Die Syntax für o bei PPAs ist etwas anders als bei den Einträgen in der sources.list. Es folgt dem Schema

o=LP-PPA-LP-BENUTZER-PPA-NAME

In dem Beispiel oben ist der LP Benutzer (carsten-uppenbrink-net) und der PPA Name (nginx). Die Einträge sind ab dem Speichern sofort aktiv. Überprüfen kann man das ganze recht einfach mit

$ apt-cache policy openssl 
openssl:
 Installed: 1.1.0g-3~ubuntu16.04.3+ppa.carsten+1
 Candidate: 1.1.0g-3~ubuntu16.04.3+ppa.carsten+1
 Version table:
     1.1.0g-3~ubuntu16.04.3+ppa.carsten.weak+1 500
        500 http://ppa.launchpad.net/carsten-uppenbrink-net/postfix/ubuntu xenial/main amd64 Packages
 *** 1.1.0g-3~ubuntu16.04.3+ppa.carsten+1 1001
        500 http://ppa.launchpad.net/carsten-uppenbrink-net/nginx/ubuntu xenial/main amd64 Packages
        100 /var/lib/dpkg/status
     1.0.2g-1ubuntu4.10 500
        500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages
        500 http://security.ubuntu.com/ubuntu xenial-security/main amd64 Packages
     1.0.2g-1ubuntu4 500
        500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages

Man sieht das 1.1.0g-3~ubuntu16.04.3+ppa.carsten+1 aus dem Nginx PPA installiert ist, obwohl es theoretisch eine neuere Version im Postfix PPA gibt.

Durch die Verwendung von Wildcards in der „Package“ Ziele kann man sich Einträge sparen. Jedoch kann man sicherheitshalber auch einfach für jedes doppelte Paket ein extra Eintrag anlegen.

Der Eintrag im Ubuntuusers Wiki zeigt noch weitere Möglichkeiten.

Nginx Geoblocking mit dem GeoIP2 Modul

In einem anderen Artikel beschreibe ich wie man das ngx_http_geoip2_module für Nginx einrichtet. Hier geht es nun um das erstellen von einer Blockade von chinesischen IP Adressen. Das bedeutet natürlich das keiner mit einer chinesischen IP Adresse auf die Webseite zugreifen kann. Falls es sich um eine Private Seite handelt, sperrt man so wahrscheinlich viele Bots aus aber eher wenige echt User. Auch ist zu beachten das die Einträge in der GeoLite2 Datenbank falsch sein können.

In der Variable $geoip2_city_country_code wird der Landes Code von einer zugreifenden IP Adresse abgelegt. Durch ein Mapping kann man nun bestimmen ob die Variable $blocked _country mit „ja“ oder „nein“ gefüllt wird. Das Mapping befindet sich noch im http { … } Kontext.

map $geoip2_city_country_code $blocked_country {
 default nein;
 CN ja;
}

Im server { … } Kontext kann nun folgenden Abfrage eingetragen werden. Wenn Möglich relativ weit oben.

if ($blocked_country = ja) {
 return 403;
}

Sollte die Variable $blocked_country „ja“ enthalten, wird nur der Status Code 403 Forbidden zurück geben und nicht die eigentliche Seite ausgeliefert.

Falls einem Länder nicht groß genug sind kann man auch gleich einen ganzen Kontinent blockieren. In meinem Geoip2 Artikel nenne ich die Kontinent Variable  $geoip2_city_continent_code. So könnte man zum Beispiel NA (Nord Amerika) und AS (Asien) blockieren

map $geoip2_city_continent_code $blocked_continent {
 default nein;
 NA ja;
 AS ja;
}

Der Mapping Eintrag muss wieder im http { … } Kontext sein. Das eigentliche blockieren dann im server { … } Kontext.

if ($blocked_continent = ja) {
 return 403;
}

Nginx mit dem GeoIP2 Modul

Seit ich von Apache auf Nginx als Webserver umgestiegen bin, verwende ich das ngx_http_geoip_module. Die Variablen kann man einerseits an PHP weitergeben, aber auch zum Beispiel einzelne Webseiten nur für bestimmt Länder freigeben. Das ist nicht immer super genau, aber um auf privaten Seiten ein paar chinesische Bots auszusperren funktioniert es ganz gut.

Um von der IP auf Geo Informationen schließen zu können werden die GeoLite Datenbanken von MaxMind benötigt. Jedoch hat MaxMind angekündigt ab dem 1. April 2018 die GeoLite Datenbanken nicht mehr zu aktualisieren. Ab dann wird es nur noch Updates für die neueren GeoLite2 Datenbanken geben. Leider kann das ngx_http_geoip_module nur mit den alten Datenbanken umgehen. Zum Glück gibt es aber das ngx_http_geoip2_module welches mit den neuen GeoLite2 Datenbanken umgehen kann.

Fall ihr selber euren Nginx aus dem Source Code baut ist die Installation sehr einfach. Es muss nur ein weiteres Modul mit –add-dynamic-module= oder –add-module= bei der Konfiguration hinzugefügt werden. Zusätzlich muss auch die libmaxminddb von MaxMind installiert sein. Falls ihr die Distributionsversion von Nginx nehmt ist das ein Problem. Hier müsst ihr entweder selber bauen oder ein weiteres Repo finden, dass das Modul mit baut.

Für Ubuntu Xenial habe ich ein PPA mit der Mainline Version von Nginx angelegt. Dort muss einfach das Paket libnginx-mod-http-geoip2 zusätzlich zu den normalen Nginx Paketen installiert werden.

Nach der Installation und dem Download der GeoLite2 Datenbanken kann es los gehen. Die Konfiguration ist hier etwas anders als bein dem alten Modul.

Zum Beispiel für die ASN Datenbank, sieht der Eintrag in die nginx.conf so aus. Der Variablen Name ist hierbei frei wählbar, nur was nach der Variable kommt ist fest. Das ganze geschieht im http { … } Kontext.

geoip2 /etc/nginx/geo2/GeoLite2-ASN.mmdb {
 $geoip2_asn_system_number autonomous_system_number;
 $geoip2_asn_system_organization autonomous_system_organization;
}

Es gibt in der GeoLite2 City jetzt auch Einträge in weiteren Sprachen als nur Englisch. Ein paar Beispiele mit der GeoLite2 City Datenbank. Auch hier sind die Variablen Namen frei wählbar, falls sie nicht schon reserviert sind. Nur der Pfad hinter den Variablen muss auf einen eindeutigen Eintrag verweisen. Eine Auswahl an möglichen Pfaden für Variablen der GeoLite2 City Datenbank.

geoip2 /etc/nginx/geo2/GeoLite2-City.mmdb {
 $geoip2_city_city_name city names en;
 $geoip2_city_city_name_de city names de;
 $geoip2_city_continent_code continent code;
 $geoip2_city_continent_name continent names en;
 $geoip2_city_continent_name_de continent names de;
 $geoip2_city_country_code country iso_code;
 $geoip2_city_country_name country names en;
 $geoip2_city_country_name_de country names de;
 $geoip2_city_subdivisions_code0 subdivisions 0 iso_code;
 $geoip2_city_subdivisions_name0 subdivisions 0 names en;
 $geoip2_city_subdivisions_name0_de subdivisions 0 names de;
 $geoip2_city_subdivisions_code1 subdivisions 1 iso_code; 
 $geoip2_city_subdivisions_name1 subdivisions 1 names en;
 $geoip2_city_subdivisions_name1_de subdivisions_de 1 names de;
}

Am einfachsten lassen sich mögliche Pfade mit dem von MaxMind erstellen Tool mmdb-bin oder mmdblookup herausfinden. So in etwa wird die Ausgabe aussehen.

{
 "continent": 
  {
    "code": 
       "NA" <utf8_string>
    "geoname_id": 
       6255149 <uint32>
    "names": 
     {
       "de": 
          "Nordamerika" <utf8_string>
       "en": 
          "North America" <utf8_string>
     }
  }
 "country": 
  {
    "geoname_id": 
       6252001 <uint32>
    "iso_code": 
       "US" <utf8_string>
    "names": 
     {
       "de": 
          "USA" <utf8_string>
       "en": 
          "United States" <utf8_string>
  }
 }
...

Dort kann man dann den Pfad „continent“ –> „names“ –> „de“ folgen und kommt zum Eintrag „Nordamerika“. Der Eintrage in der nginx.conf sieht dann in etwa so aus: $geoip2_city_continent_name_de continent names de; Der Inhalt von $geoip2_city_continent_name_de wäre in diesem Beispiel „Nordamerika“.

Dem Kernel mit printk_devkmsg das Rate-Limit bei Logs verbieten

Vor kurzem habe ich über das unterdrücken von Meldungen durch Journald geschrieben. Mit dem beschrieben Trick lassen sich alle Meldungen von Systemd Units ins Journal eintragen. Doch hilft das nicht bei Meldungen die vom Kernel unterdrückt werden. Das sieht im Journal etwa so aus

kernel: systemd-shutdow: 42 output lines suppressed due to ratelimiting

Da hilft RateLimitBurst= und RateLimitIntervalSec= im journald.conf nicht. Sondern man muss auf einen Kernel Boot Parameter zurück greifen. Dazu fügt man

printk.devkmsg=on

zu seinen Boot Parametern hinzu und starte sein System neu. Standard mäßig ist printk_devkmsg auf „ratelimit“ gesetzt. Wenn man printk_devkmsg auf „on“ setzt wird das Rate-Limit aufgehoben und Journald kann auch beim Herunterfahren alle Zeilen aufzeichnen.

Systemd Journald verwirft Nachrichten wegen einem Rate-Limit

In letzter Zeit hatte ich viele Meldungen im Journal die sich in etwa so anhören.

systemd-journald[325]: Suppressed 927 messages from postfix.service

oder

systemd-journald[310]: Suppressed 521 messages from dovecot.service

Das ist natürlich nicht besonders schön. In den verworfenen Nachrichten hätten vielleicht wichtige Informationen stecken können.

Zum Glück kann man Journald dazu bringen keine Nachrichten mehr zu verwerfen oder zumindest über einen kurzen Zeitraum mehr Zeilen zuzulassen.

Konfiguriert wird das ganze in /etc/systemd/journald.conf und genauer geht es um RateLimitIntervalSec= und RateLimitBurst=.

Die Standardeinstellungen sind maximal 1000 Nachrichten in 30 Sekunden. Es gibt jetzt verschiedene Möglichkeiten das Limit zu ändern. Es ist möglich zu sagen Journald soll 5000 Nachrichten in 30 Sekunden zulassen. Dann müsste man

RateLimitBurst=5000

in /etc/systemd/journald.conf eintragen. Genau so gut könnte man sagen Journald soll in 5 Sekunden bis zu 1000 Nachrichten von einem Dienst zulassen. Dann fügt man einfach nur

RateLimitIntervalSec=5s

in die /etc/systemd/journald.conf ein. Man kann auch RateLimitBurst und RateLimitIntervalSec mit eigenen Werten kombinieren.

Um das Rate-Limit komplett abzuschalten kann man auch einfach einen der beiden Optionen auf 0 setzten. Grundsätzlich wird das aber nicht empfohlen.

RateLimitBurst=0

Noch eine Bemerkung zu alten Systemd Versionen. Die Systemd Version in Ubuntu 16.04 (Version 229) kennt noch nicht RateLimitIntervalSec. Dort heißt die Option RateLimitInterval, macht aber genau das selbe wie RateLimitIntervalSec und bekommt auch die selben Werte zugewiesen.

Bluetooth mit dem Broadcom BCM20702

In meinem Laptop ist ein WLAN Chip von Broadcom. Nach dem Etikett ist es ein BCM943228 HMB , mit einem integrierten Bluetooth Chip BCM20702. Der Linux Kernel identifiziert die PCIe Karte als BCM43228 und im dmesg taucht noch BCM4359 auf.  Für den WLan Teil gibt es keine Open Source Linux Treiber. Der von Broadcom bereitgestellt Linux STA Treiber, kurz wl, funktioniert aber ganz gut.

Anders sieht es beim Bluetooth aus, hier funktionierte lange Zeit erstmal gar nichts. Mit den neueren Kernel Versionen (mindestes ab 4.14 vielleicht auch schon früher) gab es aber eine kleine Verbesserung. Im dmesg tauchten plötzlich folgende Zeilen auf.

kernel: Bluetooth: hci1: BCM: chip id 63
kernel: Bluetooth: hci1: BCM: features 0x07
kernel: Bluetooth: hci1: BCM20702A
kernel: Bluetooth: hci1: BCM20702A1 (001.002.014) build 0000
kernel: bluetooth hci1: Direct firmware load for brcm/BCM20702A1-105b-e066.hcd failed with error -2
kernel: Bluetooth: hci1: BCM: Patch brcm/BCM20702A1-105b-e066.hcd not found

Der Kernel hat den Bluetooth Chip BCM20702A1 erkannt kann aber keine Firmware finden. Leider ist aus Lizenz rechtlichen Gründen die Firmware meist nicht in den normalen Distribution Firmware Pakten enthalten.

Doch es gibt in verschieden Quellen im Internet die diese Firmware bereitstellen. Die größte Sammlung scheint es in einem GitHub Repository zu geben

https://github.com/winterheart/broadcom-bt-firmware

Im Ordner brcm gibt es viele verschiedene Versionen. Um die benötigte Version zu finden einfach den Namen der hcd-Datei aus dem Log suchen. Ich brauche BCM20702A1-105b-e066.hcd, alternativ kann man auch einfach alle herunterladen.

Nach dem Download, die hcd-Datei in den Ordner /lib/firmware/brcm/ kopieren. Falls es den Ordner noch nicht gibt selbst erstellen.

Danach kann man mittels

modprobe -r btusb 

modprobe btusb

das Modul neu laden oder den Laptop neu starten. Jetzt sollte Bluetooth funktionieren.

Desktop Benachrichtigungen aus Root Bashscripts starten

Für ein Bashscript wollte ich eine Desktop Benachrichtigung ausgeben. Normalerweise ist das recht einfach.

notify-send 'Backup finished!' 'Umount all Backup shares'

Damit wird eine Benachrichtigung an den laufenden Benachrichtigungs-Deamon geschickt und auf dem Desktop angezeigt. Nur leider funktioniert das nicht wenn das Script als ein anderer Benutzer läuft. In den meisten fällen ist das Root.

Doch es gibt einen Trick wie man trotzdem eine Benachrichtigung auf dem Desktop ausgeben kann.

sudo -u carsten DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send 'Backup finished!' 'Umount all Backup shares'

Dazu wird der Befehl mit „sudo -u benutername“ gestartet und folgende Variablen angehängt

DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

„DISPLAY=:0“ bezieht sich auf den laufenden XServer und über die DBus Adresse wird der richtige Benutzer gewählt. Gegebenenfalls muss die UID des Benutzers angepasst werden, in dem Beispiel ist es die 1000. Dann kommt der eigentliche Befehl in der selben Zeile.

Damit kann man dann Benachrichtigungen aus Scripten anzeigen die eigentlich von Root gestartet wurden, wie zum Beispiel ein Backup Script.

Squashfs aus LEDE und OpenWRT Images extrahieren

Vor kurzem musste ich ein altes LEDE bin Image entpacken. Da es sich zum Großteil um ein Squashfs komprimiertes Dateisystem handelt kann es einfach gemountet werden, jedoch enthalten die Bin Dateien noch weitere Datenfelder. Das ist zum Beispiel ein Bootloader oder ein proprietärer Datenblob. Diese Teile müssen vorher abgetrennt werden.

Als erstes kann mit binwalk der Inhalt eines Images aufgelistet werden.

$ binwalk lede-ar71xx-generic.bin

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
512     0x200       LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 4284740 bytes
1321680 0x142AD0    Squashfs filesystem, little endian, version 4.0, compression:xz, size: 6400476 bytes, 2185 inodes, blocksize: 262144 bytes, created: 2017-04-11 14:36:24

Am Anfang sind komprimierte Daten enthalten und danach kommt das Squashfs Dateisystem. Um den Anfang abzutrennen kann dd verwendend werden.  Alle dazu benötigten Parameter zeigt binwalk gleich mit an.

$ dd if=lede-ar71xx-generic.bin bs=1 skip=1321680 count=6400476 of=lede-ar71xx-generic.squashfs

Das Offset von Squashfs wird bei skip eingetragen, wodurch alles davor übersprungen wird. Der count bei dd gibt die Länge bzw. Größe an und zwar in Bytes, dass binwalk auch in der Beschreibung vom Squashfs anzeigt.

Danach kann die erzeugte .squashfs Datei gemountet oder mit unsquashfs entpackt werden.

$ sudo mount -t squashfs -o loop lede-ar71xx-generic-tl-wr1043nd-v2.squashfs /mnt

$ unsquashfs lede-ar71xx-generic-tl-wr1043nd-v2.squashfs