Single-Dialect Code

What about a codebase that feels the same everywhere?

any 3+ years Scala codebase I’ve seen so far has been a terrible hodge-podge of dialects (one per contributor over time). (source)

I’ve seen a few code bases where I could feel who originally wrote which code, just by looking at the code. This individual style does not only consists of formatting* and naming, but also exists on an architectual level like the used idioms, function signature design, when to split concerns into individual functions, data- vs objectorientation, concept abstraction, use of library functionality and more. (*assuming it’s not enforced)

Each set of these properties could be classified as a dialect. Dialects are naturally ephemeral and fuzzy, change by coder, time of day, experience, distraction level, problem at hand, etc.

Maintaining or integrating code with different dialects probably creates some kind of mental overhead due to the impedance mismatch. I wonder how much of a real-world impact this really has, but I feel that it’s greater than zero especially with long lived codebases. I will collect instances of this problem here when I stumble upon them in the future.

Could we somehow get rid of dialects and make a codebase (or a language) that just allows for one single dialect? Some languages have this ‚one single way of doing a thing‚, but in my opinion this covers only pretty low level architectural choices.

With the advent of LLMs it may be possible and worth exploring: Have a LLM based linter that is able to explain how and why code is not idiomatic, and even suggest improvements.

ESP32 Breakout über den UART des Raspberry Pi 3 flashen

Die Esp-32 Chips gibts als Devboards mit USB-Schnittstelle und integriertem USB/Serial-Wandler oder als „Buttoned Breakout Boards“ ohne zusätzliche Beschaltung.

Nur was macht man ohne einen extra 3.3-Volt RS232-Wandler? Man benutzt seinen Raspberry Pi.

Verkabelt wird wie folgt:

Raspberry Pi ESP-32
Tx Rx
Rx Tx
Ground Gnd
3.3V 3.3V

Mein Programm kompiliere ich auf meinem großen PC, den Raspberry benutze ich nur zum Programmieren, denn es ist ziemlich kompliziert, die ESP32-Toolchain auf dem Raspberry Pi zu installieren.

Also auf dem PC erstmal ein ESP-32 Programm erstellen und kompilieren. Ich nehme statt Arduino lieber platformio, weil.

cd ~
mkdir esp32
cd esp32
virtualenv --python=python2.7 venv_platformio
. ./esp32/venv_platformio/bin/activate
pip install platformio
mkdir wifitest
cd wifitest
platformio init

Nun die Datei ~/esp32/wifitest/platformio.ini so ergänzen:

[platformio]
env_default = esp32


[env:esp32]
platform = espressif32
framework = espidf
monitor_baud = 115200
board = lolin32
board_flash_mode = dio

Das ausgewählte Board ist egal.

Eine Datei ~/esp32/wifitest/src/main.c nach dem offiziellen Template anlegen:

#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "nvs_flash.h"
#include "driver/gpio.h"

esp_err_t event_handler(void *ctx, system_event_t *event)
{
    return ESP_OK;
}

void app_main(void)
{
    nvs_flash_init();
    tcpip_adapter_init();
    ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    wifi_config_t sta_config = {
        .sta = {
            .ssid = "SSID",
            .password = "passwrd",
            .bssid_set = false
        }
    };
    ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config) );
    ESP_ERROR_CHECK( esp_wifi_start() );
    ESP_ERROR_CHECK( esp_wifi_connect() );

    gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
    int level = 0;
    while (true) {
        gpio_set_level(GPIO_NUM_4, level);
        level = !level;
        vTaskDelay(300 / portTICK_PERIOD_MS);
    }
}

mit

platformio run

kompilieren. Bei allen platformio Befehlen darauf achten, dass das virtual environment aktiviert ist, in das platformio hinein installiert wurde. Aktivieren mit . ./esp32/venv_platformio/bin/activate, deaktivieren mit deactivate.

Auf dem Raspberry Pi muss das esp-idf Toolkit installiert werden, denn darin befindet sich das Tool zum Flashen.

# Raspberry
sudo apt-get install git python python-serial
cd ~
mkdir esp32
cd esp32
git clone --recursive https://github.com/espressif/esp-idf.git

Außerdem muss der Serielle Port freigegeben werden, der ist derzeit noch als Login-Shell unter Benutzung.

sudo raspi-config
# -> Interfacing-Options
#   -> Serial
#     -> Would you like a login shell to be accessible over serial? -> No
#     -> Would you like the serial port hardware to be enabled? -> Yes
# -> Finish
#   -> Reboot now

Seit dem Raspberry Version 3 liegt die Serielle Schnittstelle nicht mehr unter /dev/ttyAMA0 sondern unter /dev/ttyS0. AMA0 ist stattdessen die serielle Schnittstelle der Bluetoothfunktionalität.

Dann legen wir auf dem Raspberry Pi ein Script an, dass sich die Dateien vom großen PC holt und auf den ESP-32 flasht: ~/esp32/get_and_flash.

#!/bin/bash

REMOTEBUILDDIR=daniel@yak.local:esp32/wifitest/.pioenvs/esp32/

mkdir -p tmp
scp ${REMOTEBUILDDIR}partitions_table.bin tmp
scp ${REMOTEBUILDDIR}firmware.bin tmp
scp ${REMOTEBUILDDIR}bootloader.bin tmp

. ~/esp32/venv_platformio/bin/activate

python2.7 ~/esp32/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port "/dev/ttyS0" --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 tmp/bootloader.bin 0x8000 tmp/partitions_table.bin 0x10000 tmp/firmware.bin

Die Adressen bzw den ganzen Befehl habe ich aus dem Kommando platformio run --target upload -v extrahiert.

Jetzt muss nur noch irgendwie eine OTA-Flash-Möglichkeit her.

Raspberry Pi mit Domain hinter Unitymedia Router (IPv6)

Das war mal wieder komplizierter als gedacht. So richtet man sein Netzwerk so ein, dass man einen Raspberry Pi hinter einem Unitymedia-Router via Dyndns erreichen kann.

Setup:
* Internet von Unitymedia via der Connectbox
* Eine Dyndns-Subdomain (bei mir von inwx)
* Ein via WLAN verbundener Raspberry Pi mit Debian Stretch (9.3)

Probleme, die es zu lösen gilt:
* Die Verbindung muss über IPv6 ablaufen, da Unitymedia keine allein genutzten IPv4 Adressen vergibt
* Der Raspberry Pi soll ein autoreconnect zum WLAN machen
* Die Connectbox muss die Pakete weiterleiten
* Der Raspberry Pi darf keine IPv6 Privacy Extensions verwenden (zufällige IP suffixe), weil sonst nach einer Woche die Filterregeln der Connectbox ihre Gültigkeit verlieren.
* Let’s Encrypt-Zertifikate für eine abhörsichere Verbindung
* Zugriffsbeschränkung
* Mein Smartphone sollte die Verbindung aufbauen können

Ich gehe mal davon aus, dass der Raspberry-Pi soweit funktioniert. Alle $ Befehle beziehen sich auf die Linux-Shell des Raspberry Pi. Das $ Zeichen darf nicht mit eingegeben werden. Wie man Konfigurationsdateien bearbeitet, erklärt Paul McWhorter hier.

Mein Raspberry läuft mit Debian Stretch (9.3). Die Debian-Version lässt sich über
cat /etc/debian_version
herausfinden.

Wlan

Der offizielle Guide zeigt, wie man sich mit einem WLAN verbindet.

Um bei WLAN-Ausfall automatisch wiederverbunden zu werden, erstellt man eine neue Konfigurationsdatei ($ sudo nano /etc/network/interfaces.d/99-wlan) mit folgendem Inhalt:

allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp

IPv6 Adresse

Im Gegensatz zu den bekannten und quasi aufgebrauchten IPv4-Adressen (z.B. 221.14.142.35) gibt es von den IPv6-Adressen einfach unglaublich viele (z.B. 2a02:8071:1ec:d900:ba27:ebff:fef3:2eaf). So viele, dass Netzwerken ganze Teilbereiche zugewiesen werden, aus denen sich dann die Teilnehmer (z.B. der Raspberry) eine raussuchen können. Am Ende setzt sich so eine Adresse folgendermaßen zusammen:

  2a02:8071:1ec:d900:ba27:ebff:fef3:2eaf
  <  prefix        > < interface       >
                       identifier

Das Präfix bekommt die Connectbox von Unitymedia zugewiesen und der Interface-Identifier enthält die MAC-Adresse des Teilnehmers (plus ff:fe in der Mitte und einen Bitkipper im achten Bit). Da das nicht sehr Datenschutzfreundlich ist, gibt es die Privacy-Extensions, die den Interface-Indentifier zufällig machen und alle x Stunden wechseln. Und weil Unitymedia selbst ein bisschen Datenschutz machen will, wechselt auch das Präfix alle paar Tage. Das genaue Ablaufdatum sieht man auf der Connectbox (192.168.0.1) unter Admin -> Information -> IPv6 lease expire.

Der Raspberry (bzw Raspian) ist jetzt standardmäßig so eingestellt, dass er zwar nicht die Privacy-Extensions verwendet, aber sich seinen Interface-Identifier zumindest halb-zufällig erstellt, nämlich aus einer Kombination des Präfixes, der Mac-Adresse und der Uhrzeit. Das bedeutet, dass sich der Interface-Identifier jedes mal ändert, wenn sich das Präfix ändert. Da wir gleich Portfilter in der Connectbox einrichten wollen, muss zumindest der Interface-Identifier konstant bleiben. Die Connectbox ist schlau genug, den Präfix der IPv6-Adressen im Portfilter automatisch zu aktualisieren.

Dazu editieren wir $ sudo nano /etc/dhcpcd.conf, suchen die Zeile

# Generate Stable Private IPv6 Addresses instead of hardware based ones
slaac private

und kommentieren die slaac private Einstellung aus:

# Generate Stable Private IPv6 Addresses instead of hardware based ones
#slaac private

Nach einem Neustart sollte die IPv6-Adresse ($ ip -6 addr show dev wlan0) die MAC-Adresse des WLAN-Adapters ($ ip link show dev wlan0) enthalten (mit der oben beschriebenen Verwurstelung). Ersterer Befehl zeigt zwei Adressen an. Diejenige, die mit fe80: startet ist die lokale Adresse, wie früher 192.168.x.x.

Portfreigabe

In der Oberfläche der Connectbox (192.168.0.1) unter Erweiterte Einstellungen -> Sicherheit -> Ip und Port Filter -> IPv6 Port Filter erstellen wir eine neue eingehende Port-Regel. Wer schon früher Portregeln für IPv4 erstellt hat, muss aufpassen. Die Quellportangabe hat hier eine andere Bedeutung.

[x] Aktiviert    [ ] Deaktiviert
Traffic policy: [x] Ja    [ ] Nein
Protokoll: TCP
Quell IP-Adresse: Alle
Ziel IP-Adresse: <Die aktuelle Adresse des Raspberry Pi.
                  Diejenige, die NICHT mit fe80 anfängt.>
Quell Port Range: Beliebig !!!
Ziel Port Range: Manuell. Start: 80. Ende: 80.

Dann ein Klick auf „Regel übernehmen“ und ganz wichtig danach auf „Änderungen übernehmen“.

Dann noch eine zweite Portfreigabe für SSL-Verbindungen einrichten (SSL ist nicht SSH):

Ziel Port Range: Manuell. Start: 443. Ende: 443.

Dyndns

Da wird es jetzt komplizierter, da es verschiedene Anbieter gibt. Ich habe bei mir darauf geachtet, dass es für meine (Sub-)Domain nur einen AAAA-Record gibt und keinen A-Record. (Der A-Record übersetzt einen Domainnamen in eine IPv4-Adresse, der AAAA-Record in eine IPv6-Adresse). Ich will damit erreichen, dass Verbindungen von außen gar nicht erst versuchen, eine IPv4-Verbindung aufzubauen. Abgesehen davon, wohin sollte ich die leiten? 0.0.0.0?

Mein Anbieter ist inwx.de, da habe ich für 5 Euro im Jahr eine Domain. Dazu gibt es eine „kostenlose“ Dyndns-Funktion, bei der man dann den AAAA-Record per URL-Aufruf aktualisieren kann. Ich hab die DynDns-Funktion einfach für eine neue Subdomain eingerichtet. Es wurde automatisch ein A und ein AAAA-Record erstellt. Den A-Record hab ich dann einfach wieder gelöscht. Solange ich bei dem URL-Aufruf den Parameter myip=ipaddr weglasse, wird der auch nicht mehr neu erstellt, so wie ich das wollte. Bei der Dyndns-Konfiguration erscheint interessanterweise die Fehlermeldung DynDNS-Record gelöscht, aber es funktioniert trotzdem noch, den AAAA-Record zu aktualisieren.

Bei allen folgenden Befehlen/Scripten/Konfigurationsdateien musst du deinen eigenen Domainnamen eintragen. Meiner ist radi.invisibeltower.de. Geheimhalten bringt nichts, denn alle Domains, für die ein Zertifikat ausgestellt wird, werden in einer großen Liste veröffentlicht.

Auf dem Raspberry-Pi erstellen wir ein Script, das die Dyndns-Url aktualisiert:

$ mkdir ~/domain
$ touch ~/domain/dyndns_update.sh
$ chmod +x ~/domain/dyndns_update.sh 
$ nano ~/domain/dyndns_update.sh

mit dem folgenden Inhalt:

#!/bin/bash

HOSTNAME=radi.invisibletower.de
DYNDNSUSER=hier_username_eintragen
DYNDNSPW=hier_passwort_eintragen
DYNDNSURL=hier_die_update_url_bis_zum_Fragezeichen_aber_ohne_das_Fragezeichen_eintragen

CURRENT_IP6=$(ip address show dev wlan0 | grep ff:fe | grep -v fe80: | awk '{print $2}' | sed 's/^\([0-9a-f:]*\).*/\1/g')
DNS_IP6=$(host -t AAAA ${HOSTNAME} | tr " " "\n" | grep ff:fe)

if [[ "${CURRENT_IP6}" == "" ]]; then
  date
  echo ERROR - COULD NOT GET OWN IP
  exit
fi

if [[ "${CURRENT_IP6}" == "${DNS_IP6}" ]]; then
  # Printing if everything is ok just spams the log.
  # echo OK.
  :
else
  date
  echo The DNS knows ip: ${DNS_IP6}
  echo The real ipv6 address is: ${CURRENT_IP6}
  echo Renew DNS ...
  curl ${DYNDNSURL}\?myipv6\=${CURRENT_IP6}\&hostname\=${HOSTNAME}\&username\=${DYNDNSUSER}\&password\=${DYNDNSPW}
  echo
  echo Done. Sleep 60 seconds.
  sleep 60
  echo Lookup again ...
  DNS_IP6=$(host -t AAAA ${HOSTNAME} | tr " " "\n" | grep ff:fe)
  if [[ "${CURRENT_IP6}" == "${DNS_IP6}" ]]; then
    echo DNS ok
  else
    echo ERROR - COULD NOT UPDATE DNS \(or dns TTL is longer than expected\)
  fi
fi

Ausführen via $ ~/domain/dyndns_update.sh . Jetzt sollte der DNS-Teil schonmal funktionieren.

Wenn nicht, hilft vielleicht unboundtest.com bei der Fehlersuche. Mein Fehler war ein simpler Buchstabendreher im Domainnamen. Wenn was schief geht, hilft es oft auch, einfach 3600 Sekunden zu warten. Vor allem wenn es mit unbound funktioniert, mit host -t AAAA domain.de aber nicht.

Damit das automatisch funktioniert, legen wir für dieses script einen cronjob an. Cronjobs führen Scripte regelmäßig ohne unser Zutun aus.

Mit $ sudo crontab -e öffnen wir die Tabelle aller Cronjobs und fügen ans Ende folgende Zeile ein:

*/5 * * * * /home/pi/domain/dyndns_update.sh >> /var/log/dyndns_update.log

Let’s Encrypt Nr 1

Zu Beginn brauchen wir erstmal ein SSL-Zertifikat, das wir dann im nächsten Schritt benutzen können. Dazu hab ich mich an dieser Anleitung orientiert.

Let’s encrypt stellt ein sich selbst aktualisierendes Tool mit dem Namen certbot-auto bereit. Das installieren wir erstmal:

$ cd ~/domain
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
$ ./certbot-auto --version

Zum Aktualisieren der Zertifikate erstellen wir ein Script:

$ cd ~/domain
$ touch letsencrypt_init.sh
$ chmod +x letsencrypt_init.sh
$ nano letsencrypt_init.sh

mit folgendem Inhalt:

#!/bin/bash

DOMAIN=radi.invisibletower.de

service haproxy stop
/home/pi/domain/certbot-auto certonly --standalone -d $DOMAIN

mkdir -p /etc/haproxy/certs
cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/letsencrypt/live/$DOMAIN/privkey.pem > /etc/haproxy/certs/$DOMAIN.pem
chmod -R go-rwx /etc/haproxy/certs

service haproxy start

Da kommt auch schon der haproxy drin vor. Die beiden service-Befehle werden erstmal fehlschlagen, aber das ist ok. Ausführen mit sudo ~/domain/letsencrypt_init.sh .

Die Zertifikate sollten jetzt zu finden sein: $ sudo ls -l /etc/haproxy/certs

Haproxy

Ich benutze weder apache noch nginx als Webserver. Stattdessen läuft ein Proxy auf Port 80 und 443 der einzelne Unterverzeichnisse an andere Serverprogramme auf dem Raspberry Pi (z.B. Octoprint) weiterleitet. Der Proxy heißt Haproxy. Er kümmert sich um sowohl um SSL als auch um die Authentifizierung.

Erstmal installieren mit $ sudo apt-get install haproxy. Dann die Konfigurationsdatei $ sudo nano /etc/haproxy/haproxy.cfg anpassen:

global
        daemon
        log 127.0.0.1 local0 debug
	tune.ssl.default-dh-param 2048

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option redispatch
        option http-server-close
        option forwardfor
        maxconn 2048
        timeout connect 5s
        timeout client  15min
        timeout server  15min

frontend http-in
	bind :::80 v4v6

        acl is_letsencrypt path_beg /.well-known/acme-challenge/

        use_backend letsencrypt-backend if is_letsencrypt
        redirect scheme https code 301 if !{ ssl_fc } !is_letsencrypt

frontend https-in
        bind :::443 v4v6 ssl crt /etc/haproxy/certs/radi.invisibletower.de.pem
	reqadd X-Forwarded-Proto:\ https
        option forwardfor
	
	acl myvaliduser http_auth(L1)

	use_backend webcam if myvaliduser { path_beg /webcam/ } 
	use_backend octoprint if myvaliduser { path_beg /octoprint/ } 
        use_backend octoprint_socket if myvaliduser { path_beg /sockjs/ }
	use_backend askforauth if !myvaliduser

backend webcam
	reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2
	server webcam1  127.0.0.1:8080

backend octoprint
	reqrep ^([^\ :]*)\ /octoprint/(.*)  \1\ /\2
	reqadd X-Script-Name:\ /octoprint
	option forwardfor
	reqadd X-Scheme:\ https if { ssl_fc }
	server octoprint1 127.0.0.1:5000

backend askforauth
	http-request auth realm Hallo

backend octoprint_socket
	reqrep ^([^\ :]*)\ /(.*)     \1\ /\2
	server octoprint1 127.0.0.1:5000

backend letsencrypt-backend
	server letsencrypt 127.0.0.1:54321

userlist L1
    group G1
    user meinusername insecure-password meinpasswort groups G1
    user zweiter_username insecure-password anderespasswort groups G1

Das sieht kompliziert aus (Doku), aber ein bisschen muss man sich da reinfuchsen. Deswegen hier ein bisschen Kontext.

Ruft man eine Internetseite im Browser auf, passiert das über das sogenannte HTTP-Protokoll. Das legt fest, wie genau der Browser nach einer bestimmten URL fragt und wie der Server darauf antworten muss. Die Frage nennt man HTTP-Request, die Antwort HTTP-Response. Beide bestehen aus Klartext, erst kommen einige sogenannte Header, darauf folgt der eigentliche Inhalt, der bei Request meist leer ist und bei Responses den HTML-Code enthält. Der HTTP-Request, mit dem ich diesen Beitrag aufrufe, sieht beispielsweise so aus:

GET /?p=241&preview=true HTTP/1.1
Host: invisibletower.de
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://invisibletower.de/
Cookie: wordpress_logged_in=geheim; wp-settings-1=geheim; wp-settings-time-1=geheim
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

Die erste Zeile darin gibt den Typ (GET/POST/..) und den Pfad an. Jede weitere Zeile darin ist ein Header und erfüllt irgendeine Funktion. Ein Webserver macht nichts anderes, als einen beliebigen Port zu öffnen (standardmäßig Port 80) und auf HTTP-Requests zu warten.

Der haproxy öffnet ebenendiesen Port 80, nimmt alle Anfragen an, schaut sich die Header an und leitet die Anfragen quasi als Mittelsmann nach den eigenstellten Regeln an andere Server weiter. Die Regeln können beispielsweise den aufgerufenen Pfad abfragen oder ob in den Headern ein korrektes Passwort mitgeschickt wurde. Außerdem kann haproxy den Request manipulieren, z.B. neue Header einfügen oder den Pfad verändern. Die „anderen“ Server können auf anderen Computern oder auf dem gleichen Computer, dann aber auf anderen Ports laufen. Ein Server ist quasi nur ein Programm, dass einen Port geöffnet hat. (PS: haproxy kann auch ganz normale Netzwerkverbindungen routen, die nicht das HTTP-Protokoll verwenden.)

Haproxy ist quasi ein Router. Um das intern umzusetzen, hat der haproxy sogenannte Frontends und Backends. Ein Frontend sagt, wo der haproxy Anfragen annimmt und an welches Backend sie weitergeleitet werden und das Backend sagt, wie der dahinterstehende Server erreichbar ist.

Gehen wir mal die Konfigurationsdatei durch.

frontend http-in
	bind :::80 v4v6

        acl is_letsencrypt path_beg /.well-known/acme-challenge/

        use_backend letsencrypt-backend if is_letsencrypt
        redirect scheme https code 301 if !{ ssl_fc } !is_letsencrypt

frontend sagt, dass jetzt ein neues Frontend anfängt. http-in ist der frei wählbare Name des Frontends. Der Name ist eigentlich nur wichtig für die Logdateien. bind :::80 v4v6 sagt, dass dieses Frontend Port 80 auf allen ipv4 und ipv6 adressen dieses Computers öffnen soll. acl regelname regel legt eine neue Regel an, die später im Zusammenhang mit if Anweisungen benutzt werden kann. In diesem Fall ist die Regel is_letsencrypt erfüllt, wenn der Pfad mit /.well-known/acme-challenge/ anfängt. Die use_backend-Zeile sorgt dafür, dass das Backend mit dem Namen letsencrypt-backend benutzt wird, wenn die Regel is_letsencrypt erfüllt ist. In allen anderen Fällen tritt die redirect-Anweisung in Kraft. Die liefert dem anfragenden Browser eine Antwort mit einem HTTP-Fehlercode zurück, der dem Browser sagt, er soll es mit der verschlüsselten Variante (https://) versuchen.

Was hier absichtlich fehlt sind weitere use_backend-Angaben: Damit kann eine unverschlüsselte Anfrage an Port 80 nichts schlimmes anrichten, denn hier wird weder verschlüsselt noch authentifiziert.

frontend https-in
        bind :::443 v4v6 ssl crt /etc/haproxy/certs/radi.invisibletower.de.pem
	reqadd X-Forwarded-Proto:\ https
        option forwardfor
	
	acl myvaliduser http_auth(L1)

	use_backend letsencrypt-backend if { path_beg /.well-known/acme-challenge/ }
	use_backend webcam if myvaliduser { path_beg /webcam/ } 
	use_backend octoprint if myvaliduser { path_beg /octoprint/ } 
        use_backend octoprint_socket if myvaliduser { path_beg /sockjs/ }
	use_backend askforauth if !myvaliduser

Das nächste Frontend, diesmal lauscht es auf Port 443 und macht eine SSL-Verschlüsselung mit dem angegebenen privaten Zertifikat. reqadd X-Forwarded-Proto:\ https fügt dem HTTP-Request einen X-Forwarded-Proto Header hinzu. Welche Bedeutung der genau hat muss man googlen. (PS: Der Schrägstrich escapet das Leerzeichen). option forwardfor fügt ebenfalls irgendeinen (wichtigen?) Header hinzu. acl myvaliduser http_auth(L1) legt eine neue Regel mit dem frei wählbaren Namen myvaliduser an. Die Regel ist erfüllt, wenn http_auth(L1) stimmt. http_auth schaut sich die Request-Header an, sucht nach einer Username:Passwort-Kombination und vergleicht die mit der Liste L1. Die use_backend Zeilen nutzen das Ergebnis dieser Überprüfung für ihre Entscheidung, an welches Backend sie den Request weiterleiten sollen. Das Backend askforauth bekommt alle Anfragen mit falschem oder fehlendem Passwort (das ! bedeutet Negation). Die Regeln können übrigens entweder über eine acl-Anweisung angegeben werden oder innerhalb von geschweiften Klammern { .. } nach dem if stehen. Zwischen den einzelnen Regeln braucht nur ein Leerzeichen zu stehen, sie werden behandelt, als würde dazwischen ein logisches UND stehen.

Das Passwort wird zwar über die Leitung geschickt, ist aber wegen der SSL-Verschlüsselung für alle anderen verschlüsselt. Damit niemand beispielsweise in einem öffentlichen WLAN das Passwort abgreifen kann, sollte man am besten keine Authentifizierung im http-in-Frontend einfordern, nur wie hier im https-in-Frontend.

backend webcam
	reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2
	server webcam1  127.0.0.1:8080

Dieses Backend leitet alle Anfragen an den Server weiter, der lokal auf Port 8080 lauscht. (127.0.0.1 ist immer die IP-Adresse des Servers selbst). webcam1 ist wieder ein frei wählbarer Name. Die Zeile mit reqrep schneidet den /webcam/ Teil des Pfades aus der Anfrage heraus.

backend askforauth
	http-request auth realm Hallo

Dieses Backend antwortet fordert mit einem speziellen HTTP-Response-Header eine Username:Passwort-Kombination vom Browser an.

userlist L1
	group G1
	....

Ist die Liste der erlaubten User. Selbsterklärend.

Mit diesem Wissen sollte man jetzt selbst weitere Backends anlegen können.

Hat man die Konfiguration geändert, kann man haproxy so neustarten:

$ sudo service haproxy stop
$ sudo service haproxy start
$ sudo service haproxy status 

Mit $ journalctl -xe kann man sich mehr Details zu Fehlern der Konfigurationsdatei anschauen.

Let’s Encrypt 2

Jetzt wo der Proxy steht, kann man die regelmäßige Let’s Encrypt Zertifikatserneuerung auch über den Proxy abwickeln. Auch diese Anleitung ist schamlos abgeschrieben.

Dazu erstellen wir einen Cronjob, der jede Nacht $ certbot renew --deploy-hook letsencrypt_renewhook.sh aufruft, welches bei erfolgtem Renewal das Script letsencrypt_renewhook.sh aufruft, welches wiederum die neuen Zertifikate für haproxy zusammenkopiert.

Als erstes erstellen wir das Script mit $ sudo nano ~/domain/letsencrypt_renewhook.sh:

 
#!/bin/sh

SITE=radi.invisibletower.de

# move to the correct let's encrypt directory
cd /etc/letsencrypt/live/$SITE

# cat files to make combined .pem for haproxy
cat fullchain.pem privkey.pem > /etc/haproxy/certs/$SITE.pem

# reload haproxy
service haproxy reload

Jetzt noch das Script ausführbar machen: $ sudo chmod u+x ~/domain/letsencrypt_renewhook.sh und mit $ sudo /home/pi/domain/letsencrypt_renewhook.sh testen. Es sollte ohne Fehler durchlaufen.

Jetzt muss noch certbot so eingestellt werden, dass er hinter dem haproxy läuft. Dazu öffnen wir die Konfigurationsdatei mit $ sudo nano /etc/letsencrypt/renewal/radi.invisibletower.de.conf und fügen in die Kategorie [renewalparams] folgende Einstellung ein:

http01_port = 54321

Den Prozess kann testen wir mit $ sudo certbot renew --dry-run. Es sollte kein roter Fehlertext auftauchen.

Mit $ sudo crontab -e öffnen wir die Tabelle aller Cronjobs und fügen ans Ende folgende Zeile ein:

30 2 * * * /home/pi/domain/certbot-auto renew --deploy-hook "/home/pi/domain/letsencrypt_renewhook.sh" >> /var/log/le-renewal.log

Android

Jetzt hatte ich noch das Problem, dass mein Handy die Seite nicht aufrufen wollte (Congstar im Telekom-Netz). Das Problem war bzw. ist, dass standardmäßig kein IPv6 unterstützt wird. Sagt man zumindest im Congstar-Forum. Aber:

Gehe im Handy in die Einstellungen. Dann nach Mobilfunknetze (das befindet sich bei mir unter Drahtlose Netzwerke => Mehr). Dann nach Zugangspunkte und wähle den aktiven Zugangspunkt aus. In der Liste ganz nach unten scrollen und APN-Protokoll sowie APN-Roaming-Protokoll auf IPv4/IPv6 stellen. Eventuell kurz den Flugzeugmodus an- und wieder ausschalten. Tadaa.

Sicherheit

Die Konfiguration hat jetzt folgende Probleme:
– Aus dem internen Netzwerk sind die „Unterserver“ auch auf ihren Spezialports zu erreichen. Das ist nicht unbedingt schlimm. Abhilfe würde dieser Befehl schaffen:

$ sudo apt-get install iptables-persistent
$ PORT=8080 sudo /sbin/iptables -A INPUT -p tcp -i wlan0 ! -s 127.0.0.1 --dport ${PORT} -j DROP && sudo /sbin/ip6tables -A INPUT -p tcp -i wlan0 ! -s ::1 --dport ${PORT} -j DROP && sudo /sbin/iptables-save | sudo tee /etc/iptables/rules.v4 && sudo /sbin/ip6tables-save | sudo tee /etc/iptables/rules.v6

– Wer die Dateien certbot-auto, dyndns_update.sh oder letsencrypt_renewhook.sh editiert hat wegen des cronjobs automatisch root-Rechte, auch wenn man ein Passwort für root vergibt. Abhilfe schafft:

$ sudo chown -R root:root ~/domain
$ sudo chmod -R 755  ~/domain

Fazit

So, jetzt sind alle Informationen mal an einer Stelle zusammengefasst. Fehler in der Anleitung bitte in die Kommentare.

Daniel.

UEFI Eintrag wiederherstellen (Grub)

Windows 7/8/10 Updates und der Biosbatteriewechsel löschen machmal ungefragt den UEFI-Eintrag von meinem Linux (Dualboot) aus dem BIOS. Bisher hab ich dass immer über ein Rettungslinux auf dem USB-Stick repariert, aber es gibt eine elegantere Methode, die ich nicht mehr vergessen will, also wird gebloggt.

Man muss wissen, dass es nicht langt, .EFI-Dateien auf der EFI-Partition zu haben, um diese booten zu können. Die UEFI-Firmware hat eine eigene Liste, in der die bootbaren Betriebssysteme stehen müssen. (Ich meine die Liste, die beim Drücken der -Taste (bei mir F11) während dem Booten erscheint.

Schritt 1: Linux (bei mir Fedora) über die UEFI-Shell starten:

  • F11
  • UEFI-Shell wählen
  • EFI-Partition auswählen: Anzeige aller Partitionen mit mm oder map. Die Partitionen heißen bei mir fs0 bis fs4 bzw blk0 bis blk4. Wechseln auf die Partition mit:
  • Shell> fs0:
  • (der Doppelpunkt liegt im englischen Tastaturlayout auf SHIFT-Ö)
  • Testen ob es die richtige Partition war, denn auf der EFI-Partition gibt es ein Verzeichnis namens EFI
  • Shell> cd EFI
  • Wenn das fehlschlägt, eine andere Partition verwenden, z.B. mit Shell> fs1:, dann wieder Shell> cd EFI ausprobieren.
  • Die richtige .EFI-Datei suchen
  • fs0> cd <tabtaste mehrfach drücken, bis FEDORA oder UBUNTU oder der Name deines Linux erscheint>
  • fs0> <tabtaste mehrfach drücken und sich alle *.EFI Dateien merken>
  • (tab completion funktioniert hier, auch wenn man noch keine Buchstaben eingegeben hat)
  • Die Efi-Dateien durchprobieren, bei mir klappen sowohl GRUBX64.EFI als auch SHIM.EFI
  • fs0> GRUBX64.EFI
  • Das genutzte Verzeichnis und die genutzte Datei merken!

Jetzt einen Booteintrag in UEFI hinterlegen (auf der Konsole des gestarteten Linux)

  • Evtl efibootmgr installieren:
  • sudo apt-get install efibootmgr…
  • sudo dnf install efibootmgr-…
  • Booteinträge und Bootreihenfolge anschauen:
  • $ sudo efibootmgr
    BootCurrent: 0003
    Timeout: 1 seconds
    BootOrder: 0001,0002,0003
    Boot0001* WINDOWS BOOT MANAGER
    Boot0002* Hard Drive
    Boot0003* UEFI: Built-in EFI Shell
  • Partition und Laufwerk der EFI-Partition herausfinden
  • $ mount | grep efi
    efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,......)
    /dev/sdb1 on /boot/efi type vfat (rw,......)

  • Laufwerk ist hier /dev/sdb (ohne Zahl) und die Partition ist 1 (die Zahl)
  • Eintrag für Linux wiederherstellen
  • sudo efibootmgr -c -L "Mein Linux" -l /EFI/FEDORA/GRUBX64.EFI -d <Laufwerk> -p <Partitionsnummer>

Bootreihenfolge anpassen

  • sudo efibootmgr
  • Die Zeile „BootOrder: 0004, 0001, …“ gibt die aktuelle Bootreihenfolge an, die erste Nummer wird als erstes gestartet. Die Nummern beziehen sich auf die BootXXXX Zeilen.
  • Jetzt eine neue Reihenfolge überlegen und aktivieren
  • sudo efibootmgr -o 1,0,2,3

So läuft es zumindest für mein ASRock Mainboard.
Funktioniert das nicht? Ab in die Kommentare.

WordPress Pitfalls

WordPress hat so seine Eigenheiten, die ich hier für mich mal dokumentiere:

– Benutzt man wp_register_script mit Abhängigkeiten ($deps=array(..)), und die Abhängigkeiten sind über Footer ($footer) und Header verteilt, funktioniert garnix mehr. Keine Fehlermeldung etc.

Testbericht: Rapoo E9090P

Ein Review in 3 Teilen

Teil 1: Technik

Pro:
  • Tasten F1 bis F12 sind die Defaultzuordnung der oberen Reihe, die Extras wie Lautstärke erreicht man über die Fn-Taste.
  • Alle Sonderfunktionstasten senden Standard-Keycodes und funktionieren wie gewollt in Linux.
  • Die Tastatur hat große Pfeiltasten, auch im deutschen Tastaturlayout.
  • Die Beleuchtung, einstellbar in 2 Helligkeitsstufen über Fn+Tab.
  • Nach ca. 14 Sekunden ohne Aktivität geht die Hintergrundbeleuchtung aus.
  • Es wird ein Micro-USB-Kabel für die Ladestation mitgeliefert.
  • Auf der Unterseite befindet sich ein Ein- und Ausschalter.
  • Für die Funkanbindung gibt es einen kleinen seperaten USB-Dongle.
  • Die Gesten des Touchpads: In Linux (Fedora 20 in meinem Fall) kommen für die Gesten brauchbare Tastaturevents an,einige haben sogar schon eine sinnvolle Zuordnung, bei anderen muss man noch nachhelfen:
    • Zweifingerscroll in alle vier Richtungen: Button 4,5,6,7 events, wie bei anderen 4-Wege Mausrägern auch
    • Zweifingerzoom: Button 4 bzw 5 events, aber mit anderem State (0x814 und 0x1014 statt 0x810 und 0x1010, das entspricht genau dem normalen Zweifingerscroll nach oben/unten mit gedrückter Steuerungstaste Control_L)
    • Dreifingerscroll nach oben: Windows-Taste links (auch bekannt als Super_L)
    • Dreifingerscroll nach unten: Tastenkombination Super_L+d
    • Dreifingerscroll nach rechts bzw links: Button 8 und 9 events, das entspricht den Daumentasten, die manche Mäuse haben und aktiviert im Firefox standardmäßig das Zurück- und Vorwärtsspringen der vorher besuchten Seiten.
    • Vierfingerscroll nach unten: Super_L+M
    • Vierfingerscroll nach oben: Super_L+Shift+M
    • Den Vierfingerscroll nach rechts und links gibt es hier nicht.
    • Von oben hereinwischen: Control_L+Super_L+XF86TouchpadOff (XF86TouchpadOff entspricht 0x2c3 bzw ist Keycode 201 zugordnet)
    • Von rechts hereinwischen: Alt_L+Super_L+XF86TouchpadOff
    • Von links hereinwischen: Control_L+Super_L+Backspace
    • Von unten hereinwischen ist keine Geste, die untersten anderthalb cm entsprechen der rechten bzw linken Maustaste, sind aber nicht physisch klickbar sondern nur speziell makierte Touchflächen.
    • Der Tip mit 2 bzw 3 Fingern ohne Bewegung erzeugt den Rechtsklick bzw den Klick mit der mittleren Maustaste.
    • Tip, Loslassen, Tip und anschließendes Gedrückthalten entsprechen wie bei vielen Touchpads dem Gedrücktlassen der linken Maustaste für Drag’n’Drop oder zum Textmakieren. Gleiches funktioniert auch angemehm wenn man mit dem Daumen den Touchbereich für die linke Maustaste berührt.
Cons:
  • Das Touchpad ist annehmbar aber nicht gut. Wie viele Touchpads ist es bei Microbewegungen ungenau bzw. man erhält keine Reaktion, d.h. sie ist nichts für pixelgenaues Arbeiten.
  • Das kabellose Laden erzeugt ein leises Pfeifen, ähnlich dem mancher Handyladegeräte (mal mit dem Ohr ganz nah drangehen). Ist es sonst still oder relativ leise im Raum, nervt es wirklich, vor allem weil es ständig die Tonhöhe wechselt. Aber man kann ja laden, wenn man gerade nicht im Raum ist.
  • Schaut man schräg von vorne auf die Tastatur, sieht man unter jeder Taste einen störenden hellen Strich.
  • Manchmal kommt man mit dem kleinen Finger beim Drücken der Entertaste auf das Touchpad was dann einen ungewollten Klick auslöst.
  • Es gibt keine Extra-Ladebuchse an der Tastatur, die Ladestation ist die einzige Auflademöglichkeit.
  • Man benötigt 2 USB-Ports, einen für das Funkmodul und einen zum Laden der Tastatur.

Teil 2: Schreibgefühl

Ich warte mit meiner entgültigen Einschätzung noch ein oder zwei Wochen, bis ich mich vollständig an die Tastatur gewöhnt habe. Normales, blindes Zehnfingerschreiben klappt schon jetzt am Anfang schon sehr gut und schnell. Die Anordnung der Shift- und Pfeil-nach-oben-Taste sind auf dieser Tastatur alledings genau invers zu meiner Laptoptastatur, sodass ich statt einem Doppelpunkt erst versehentlich einen Punkt in die darüberliegende Zeile setze. Aber ich glaube nach einer gewissen Eingewöhnungszeit dürfte sich das geben. Auch schaffe ich es meist nicht, die Sonderzeichen der oberen Zeile zu benutzen, weil die rechte Shift-Taste ungewöhnlich kurz ist.

Teil 3: Programmiereignung

.. wird sich auch in den nächsten Wochen zeigen. Für einen ersten Test zähle ich einmal die Vertipper für die nächsten Zeilen:

int* main(char[] argv, int argn) {
println(argv[1]);
}

3 Vertipper, davon sind zwei der Shifttaste geschuldet.

Rechte auf dem uberspace

Durch einen kleinen Fehler hatte ich mir alle Rechte im Verzeichnis /var/www/virtual/username überschrieben.

Wie stellt man wieder alles richtig ein?

cd /var/www/virtual/$USER

# Rechte für Ordner (type = directory)  (rwxr-xr-x)
find * -type d -print0 | xargs -0 chmod 755

# Rechte für Dateien (type = file)  (rw-r--r--)
find * -type f -print0 | xargs -0 chmod 644

# FCGI-Scripte müssen ausführbar sein
chmod 700 fcgi-bin/*

# Der Ordner . also /var/www/virtual/$USER sollte die Rechte 750 haben, also
ll /var/www/virtual | grep $USER
# drwxr-x--- 18 username apache 4096 3. Jul 16:06 username

 

Wichtig dabei ist, dass der Webserver (Gruppe: apache) lesend auf alle Dateien zugreifen kann. (php Scripte laufen als fcgi Script im Namen von euch, aber alle anderen Dateien, zB statische Bilder werden direkt vom Webserver (apache) gelesen und weitergeleitet.)

/var/www/virtual/$USER  hat die Gruppe apache und Leserechte für die Gruppe
/var/www/virtual/$USER/html  hat Eintrittsrechte (x) für den Rest (und damit apache)
/var/www/virtual/$USER/html/.htaccess  hat Leserechte (r) für den Rest (und damit apache)

Außerdem dürfen die fcgi-Scripte aus Sicherheitsgründen keine Schreibrechte für die Gruppe oder jeden haben, euer User muss sie aber ausführen können (z.B. 700, rwx——)

Die Leserechte aller anderen Dateien für den Rest (o) stellen kein Sicherheitsproblem dar, da andere spionierende User schon beim Verzeichnis

/var/www/virtual/$USER

ausgesperrt werden.

Kleine Übersicht über das Rechtesystem in Linux:

Für jede Datei können Leserechte (r), Schreibrechte (w) und Ausführ/Eintrittsrechte (x), jeweils getrennt für einen Benutzer, eine Gruppe und den Rest (also alle die Weder der Benutzer sind noch der Gruppe angehören). Das x steht bei Dateien dafür, dass man die Datei direkt ausführen kann und entspricht der Dateiendung .exe unter Windows. Bei Ordnern gibt das x an, ob demjenigen Erlaubt wird, den Ordnerinhalt zu sehen.

ls -l irgendein_Ordner
-rw-r--r--  1 peter gedoens   418 28. Okt 2013  datei1.txt
-rwxrwxrwx  1 peter gedoens   418 28. Okt 2013  datei-die-jeder-bearbeiten-
                                                und-ausführen-kann.sh
-rw-r--r--  1 peter gedoens   418 28. Okt 2013  datei-ohne-endung
|\ /\ /\ /
| |  |  |
| |  |  |-- o Rechte für jeden, der nicht der User peter ist
| |  |        oder der Gruppe gedoens angehört.
| |  |----- g Rechte für Mitglieder der Gruppe gedoens.
| |-------- u Rechte für den User peter, der damit auch 
|             "Besitzer" der Datei oder des Ordners ist.
|----------   Typ des Elements, meist d für Ordner oder 
              f für Dateien. (l für Links, c für character
              devices, siehe /dev, s für gesetztes suid bit)

Was bedeuten die Nummern, zB 755 oder 750?

Die Nummern sind nur eine andere Schreibweise für die rwxrwxrwx Schreibweise.
Man teilt rwxrwxrwx in drei Dreiergruppen auf und weist den Positionen Zahlen zu:

                     rwx rwx rwx
                     421 421 421

Jetzt addiert man einfach die entsprechenden zahlen für die Rechte, die man aktivieren will, zB

r = 4    rw = 6    x = 1    rwx = 7     rx = ?

Wenn man sich das nicht merken will, akzeptiert der chmod Befehl, mit dem man die Rechte ändert auch folgende Schreibweise:

chmod o-r irgendeine-datei.txt

Statt dem absoluten 755 gehen auch relative Änderungen: + steht für das Hinzufügen, – für das Entfernen der Rechte und = für das setzten der Rechte.

Vor +, = oder - steht u g oder o für user, group oder other.
Nach +, = oder - stehen die Rechte, also r w oder x.

Beispiele:    uo+w    o-rwx     u+w    ugo+rwx    u=rwx     u+r,g=r

Ich will 3 Leuten Schreibrechte und einem 4. Leserechte gewähren

Für den normalen Gebrauch als Einzelperson ist dieses Prinzip des Rechtemanagements relativ einfach verständlich, noch überschaubar und trotz vieler Limitationen ausreichend. Wer viele Benutzer hat, kann jedoch ACLs benutzen, mit denen man die Rechte sehr viel feiner zwischen vielen Usern verteilen kann: http://www.knilse.de/download/acl.html

Festival Lautsprecher

Der Bau eines zweiten Festivallautsprechers steht an. Für Nachbauwillige hier ein wenig Dokumentation:

BOM (Bill of Materials) (meist Amazon Links, ich hab noch Prime-Probe :):

  • Lautsprecher: Beyma 8AG/N (bei Hobbyhifiladen (die lassen es direkt über beyma schicken). Bei amazon gibts auch einen 8AG, aber ich weiß nicht ob das der richtige ist) (40,80€ + 5,90€)
  • Verstärker: Lepai LP-2020A+ (Tripath) (26,99 €)
  • Holz: MDF 1,2mm aus dem Baumarkt, direkt zuschneiden lassen (~9€)
  • Schrauben: 4×35 T20 für das Gehäuse (~5€)
  • Schaniere und Schließvorrichtungen für den Deckel (2*2,72€ + 2*2,83€)
  • Schrauben: klein, um innen Verstärker etc festzuschrauben, auch für Schaniere. Alle aus dem Baumarkt von nebenan.
  • Ladegerät: für 12V Bleiakkus, portables Steckerteil zB AL800 ebay oder AL600 amazon (16,99€)
  • Batteriewächter: Kemo (18,50 €)
  • Batterie: 9Ah 12V deep cycle Blei-Gel-Akku (24,85€)
  • USB-Stromversorgung: 12V zu 5V Wandler (7,98€)
  • USB-Verlängerungskabel (je nach 12V zu 5V USB Wandler)
  • Chinch-Panel: 4 Fach, braucht man nicht unbedingt (1,99 €)
  • robustes Cinch-Klinke-Kabel: (13,19€)
  • Kabel, wer keine hat
  • Switch (2€)
  • Sicherung (0,50€)
  • Sicherungsfassung (2€)
  • + viele Versandkosten

Macht 184,86€ + Versand der oben nicht eingerechnet ist (Bei mir +5,50€).

Wallabag auf dem Uberspace

Update: 5.08.2016 (Wallabag Version 2.0.5)

Wallabag (ehemals Poche) ist ein Open-Source-Ersatz für Pocket.

Wallabag wird auf dem eigenen Webserver installiert und kann dann von Überall verwendet werden. Hier die Anleitung zur Installation auf dem eigenen uberspace.

Vor dem Updaten von Version 1.0 auf 2.x die Datenbank exportieren!

Erste Installation:

* Php version testen (5.6.x ist gut): php -v. Ansonsten wie weiter unten beschrieben umstellen.
* Composer installieren. (Siehe weiter unten)

 # ins Webverzeichnis wechseln
cd /var/www/virtual/$USER

# ein Verzeichnis für den Code erstellen
mkdir wallabag.code
cd wallabag.code

# wallabag quellcode downloaden (nur einmal)
git clone https://github.com/wallabag/wallabag.git .

Mit Git wird der Quellcode verwaltet. Hierdurch hat man nicht nur Zugriff auf die aktuelle (instabile) Entwicklerversion sondern auch auf alle Softwareversionen davor. Wir wollen die neuste stabile Version. Dazu suchen wir in dieser Liste die neuste Versionsnummer und bauen sie in den folgenden Befehl ein. (Derzeit 2.0.5)

# neuste version auschecken
git checkout 2.0.5

# Der folgende Befehl fragt nach einiger Zeit:"Some parameters are missing. Please provide them."
# -> dann einfach immer wieder Enter drücken bis "secret(...):" erscheint.
# -> Dann wild auf der Tastatur herumtippen und mit Enter bestätigen. Noch ein paar mal Enter drücken.
# -> Ein paar grüne Haken und Balken sollten folgen.
# -> Alle gemachten Einstellungen lassen sich später in der Datei app/config/parameters.yml ändern.
SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist

# Dieser Befehl fragt 
# -> nach dem Anlegen einer neuen Datenbank (y),
# -> ob ein neuer Adminuser angelegt werden soll (y),
# -> User, Passwort und Email des neuen Wallabag-Admins.
# -> Beim vertippen etc, den Befehl nochmal ausführen und dabei die Datenbank löschen.
php bin/console wallabag:install --env=prod

Ich benutze für wallabag eine Subdomain (wallabag.invisibletower.de), aber ein Unterverzeichnis in auf der Hauptdomain (z.B. /var/www/virtual/$USER/html/wallabag) geht natürlich auch.

# Für die Subdomain einfach das web-verzeichnis linken
ln -s /var/www/virtual/$USER/wallabag.code/web /var/www/virtual/$USER/wallabag.invisibletower.de

# und kreiert sich für den einfacheren
# Zugriff einen Link ins Homeverzeichnis
ln -s /var/www/virtual/$USER/wallabag.invisibletower.de /home/$USER/live.wallabag
ln -s /var/www/virtual/$USER/wallabag.code              /home/$USER/live.wallabag.code

Anmerkung: Im Gegensatz zur alten Anleitung liegt hier der Code wieder im Webverzeichnis und nicht im Homeverzeichnis. Er liegt allerdings im Verzeichnis wallabag.code, für das Apache keinen direkten Zugriff von außen bietet. (Es sei denn du besitzt die Domain wallabag.code).
Damit sollte die wallabag.sql-Datei mit den sensiblen Daten dennoch sicher sein.

Update

Wird eine neue Wallabag-Version veröffentlicht, kann man mittels git leicht updaten:

cd ~/live.wallabag.code
git pull
git pull --tags
git checkout 2.0.??????
SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist
php bin/console cache:clear --env=prod

Auf neuere PHP-Version umstellen

Wallabag benötigt php in Version 5.5.x oder 5.6.x. Eventuell musst du deinen Uberspace account umstellen. (Kann u.u. Probleme bereiten, lässt sich dann aber einfach rückgängig machen).

Um zu testen, welche Version du verwendest: php -v

Um die verfügbaren Versionen zu sehen: ls -d /package/host/localhost/php*
Wie man sieht, verweisen die Versionen mit nur zwei Ziffern immer auf die aktuellste Unterversion.
Die beste Wahl ist wohl 5.6.

Version ändern via nano ~/etc/phpversion. Die Datei ist sehr einfach aufgebaut, einfach die zweite Zeile in PHPVERSION=5.6 ändern. Speichern geht mit Strg+O, Schließen mit Strg+X. Danach noch ein killall php-cgi ausführen, um alle laufenden php-Interpreter zu killen (die starten sich dann automatisch mit der neuen Versionseinstellung neu).
Jetzt die SSH Verbindung trennen und neu aufbauen. Test mit php -v. Evtl wieder ins richtige Verzeichnis wechseln cd ~/live.wallabag.code.

Composer im uberspace installieren

Am besten ins ~/bin Verzeichnis installieren, das ist praktisch, weil man den composer dann ohne Pfadangabe nutzen kann. (~/bin ist beim uberspace standardmäßig schon im $PATH).

curl -sS https://getcomposer.org/installer | php -- --filename=composer --install-dir=$HOME/bin

Falls schon installiert, kann man composer upgraden via

composer selfupdate

Update von Version 1 (Wenn installiert wie hier früher beschrieben wurde)

* Zunächst Daten via Webinterface als JSON exportiern.
* Alle Überbleibsel der letzten Installation einfach löschen 😀

rm -rf ~/live.wallabag.code ~/live.wallabag ~/live.wallabag.updatewebdir.sh 
rm -rf /var/www/virtual/$USER/wallabag.invisibletower.de

Und neu installieren. Danach Daten importieren.

Tipp: Truecrypt verschlüsselte Laufwerke überprüfen

Das Problem: Bei truecrypt-verschlüsselten Partitionen führt das Betriebssystem standardmäßig keine Dateisystemchecks durch.

Dateisystemchecks dienen dazu, eventuell aufgetretene Fehler in der Dateisystemstruktur zu beheben, die zB durch Systemabstürze im falschen Augenblick entstehen können. Um diese Fehler zu beheben oder ihnen entgegenzuwirken gibt es grundsätzlich zwei Strategien.

Strategie Eins setzt auf sogenannte transaktions-basierte Dateisysteme. Hier wird vor jedem Schreibzugriff an zentraler Stelle der jetzt folgende Schreibzugriff vermerkt. Danach wird der eigentliche Schreibzugriff ausgeführt und daraufhin die Notiz entfernt. Stürzt jetzt das System während des Schreibzugriffs ab, bleibt die Notiz bestehen. Beim nächsten mounten (einhängen) des Dateisystems wird dann anhand der Notiz versucht, den Fehler zu beheben. Bei Truecrypt-Laufwerken kann dies jedoch manchmal in die Hose gehen, weil die eigentliche Verschlüsselung noch dazwischen hängt und quasi einen Buffer bildet.

Dennoch gibt es eine zweite Strategie. Diese führt nach jeden X. mounten (meist nach jedem 30. mal) beim Systemstart einen kompletten Dateisystemcheck durch, bevor das Laufwerk gemountet wird.

Dieser komplette Check kann bei Truecrypt-Systemen jedoch nicht stattfinden, da sie einfach viel zu spät erst entschlüsselt werden. Das heißt man muss manuell ran.

Truecrypt-verschlüsselte Partitionen funktionieren folgendermaßen: Die Partition /sdx9 ist komplett verschlüsselt, d.h. auch das Dateisystem ist verschlüsselt. Truecrypt übersetzt diese verschlüsselte Partition und stellt eine unverschlüsselte Partition unter /dev/mapper/truecrypt1 zu Verfügung. Erst diese Partition verhält sich wie die anderen unverschlüsselten Partitionen, z.B. wie /sda1. Die Partition /dev/mapper/truecrypt1 wird dann mit normalen Linux-Boardmitteln in ein beliebiges Verzeichnis gemountet, zB /home/ich/crypt.

Um die Partition mal zu überprüfen muss man sie erst mal frei geben, d.h. alle offenen Dokumente speichern und die Programme die sie benutzen am Besten schließen. Danach aushängen mit dem umount-Befehl:

sudo umount /dev/mapper/truecrypt1

Das Überprüfen funktioniert dann folgendermaßen:

sudo fsck /dev/mapper/truecrypt1

Sollte alles glatt gehen sieht man dann eine Meldung ähnlich meiner:

e2fsck 1.42.5 (29-Jul-2012)
Pool wurde 130 mal ohne Überprufung eingehängt, Prüfung erzwungen.
Durchgang 1: Prüfe Inodes, Blocks, und Größen
Durchgang 2: Prüfe Verzeichnis Struktur
Durchgang 3: Prüfe Verzeichnis Verknüpfungen
Durchgang 4: Überprüfe die Referenzzähler
Durchgang 5: Überprüfe Gruppe
Zusammenfassung
Pool: 96390/8323072 Dateien (12.1% nicht zusammenhängend), 20707462/33280583 Blöcke

Danach wird die Partition wieder eingehangen:

sudo mount /dev/mapper/truecrypt1 /home/ich/crypt

Wer die Partition mit speziellen Parametern gemountet hat, kennt sich bestimmt gut aus und benötigt deshalb wahrscheinlich keine weiterführende Erklärung.

In diesem Fall ist bei mir übrigens alles gut gegangen. Als ich meine Letzte Überprüfung vor einigen Jahren durchgeführt hatte sah die Sache allerdings anders aus. Da fand fsck ca 100 Fehler, die es aber glücklicherweise automatisch reparieren konnte.

Um Dateisystemfehler zu vermeiden sollte man auch diesen Tipp beherzigen:

Bei Systemaufhängern sollte seinem Linux die Möglichkeit geben, alle zwischengespeicherten Daten auf die Festplatte zu schreiben und nicht einfach durch 5-sekündiges Drücken des Power-Knopfes den Strom abschalten. In den meisten Fällen ist der Kernel nämlich durchaus noch ansprechbar, auch wenn man das von außen nicht bemerkt.

Um den sogenannten Emergency Sync durchzuführen, bedient man sich der SysRQ-Shortcuts:

Alt + Druck gedrückt lassen, und dann nacheinander R, E, I, S, U, B

R: Tastaturzugriff entziehen

E, I: SIGTERM und SIGKILL an alle Prozesse senden, d.h. alle Programme beenden

S: Emergency Sync, d.h. alle gepufferten Daten auf die Festplatte schreiben

U, B: Auswerfen aller Laufwerke und anschließendes Ausschalten

Wer die Tastenkombination schon mal ausprobieren möchte, kann sich ohne Probleme des S-Teils bedienen:

Zum sehen der Kernelmeldung am besten auf eine Shell wechseln:

Strg + Alt + F1

dann den Sync ausführen:

Alt + Druck + S

Dann die ausgegebene Meldung bewundern, das Festplattenlicht beim Schreiben beobachten und mit

Strg + Alt + F7

wieder zurück zum Grafischen Teil deines Linuxes wechseln.