Direkt zum Hauptinhalt

Hauptkonfiguration - bffh.dhall

BFFH verwendet Dhall für die Struktur der Konfigurationsdateien. Die Konfiguration von BFFH befindet sich in der Datei bffh.dhall. Die Datei kann auch umbenannt werden. Wichtig ist, dass sie dann überall korrekt referenziert wird (z.B. in Service Scripts).

Tipps & Tricks in Dhall

Dhall ist eine umfangreiche Sprache für die Konfiguration und erlaubt äußerst komplexe Datenstrukturen. Vieles davon wird für FabAccess vermutlich nur im kleineren Rahmen benötigt, jedoch besteht die grundlegende Kompatibilität auch für komplizierte Gebilde. Im Rahmen dieser Dokumentationen gehen wir nur auf grundlegende, wichtige und besonders praktische Konfigurationsaspekte ein.

Kommentare

Zeilen, die mit -- beginnen, werden automatisch als Kommentar gewertet

Zeichenketten erzeugen

Werte können zusammengefügt werden (concatenation). Das geht zum Beispiel mit dem Syntax ++ wie folgt:

certfile = env:BFFH_CFG_PATH as Text ++ "/cert/bffh-selfsigned.crt"

Oder als Verkettungsbefehl:

hobbies = concatSep ", " [ "piano", "reading", "skiing" ]

Dhall-Konfigurationen verschlanken

Folgende Möglichkeiten der Vereinfachung und Granulierung von Dhall-Dateien stellen wir vor. Wir berufen uns unter anderem auf die Dokumentation https://github.com/dhall-lang/dhall-lang/blob/master/standard/imports.md

Variablen/Konstanten innerhalb einer Datei setzen

Folgendes Beispiel erzeugt ein Listing VARS, welches drei Variablen enthält, die vereinheitlicht angesprochen werden können. Variablen lassen sich auf diese Art und Weise beliebig tief schachteln. Zu beachten ist das kleine Wörtchen in, welches im Block danach eine Ersetzung bzw. Referenzierung anstrebt.

let VARS = {
	BFFH_CFG_PATH = "/opt/fabinfra/bffh-data/",
	MQTT_USER = "fabinfra101",
	MQTT_PASSWD = "fablocal"
	}
in 
{
    listens = [
        { address = "0.0.0.0", port = 59661 },
    ],
    certfile = VARS.BFFH_CFG_PATH ++ "cert/bffh-selfsigned.crt",
    keyfile = VARS.BFFH_CFG_PATH ++ "cert/bffh-selfsigned.key",
    mqtt_url = "mqtt://" ++ VARS.MQTT_USER ++ ":" ++ VARS.MQTT_PASSWD ++ "@0.0.0.0:1883",
    db_path = VARS.BFFH_CFG_PATH ++ "bffh.db"
}

Umgebungsvariablen verwenden

Wir setzen z.B. ein einer Bash-Umgebung einen Variablenwert und führen ein Kommando aus, welches die nachfolgende Dhall-Datei verwendet. Diese Dhall-Datei sucht in ihrer übergebenen Umgebung (Environment) nach passenden Variablen und ersetzt die Werte entsprechend.

Als Rohtext:

# bash
export BFFH_CFG_PATH="/opt/fabinfra/bffh-data"
bffh_cfg = env:BFFH_CFG_PATH as Text

oder als aufgelöster Pfad:

# bash
export BFFH_CFG_PATH="~/bffh-data"
bffh_cfg = env:BFFH_CFG_PATH as Location

Inhalte aus URLs importieren

Folgender Syntax lädt den Content der entfernten URL als Variableninhalt:

let concatSep = http://prelude.dhall-lang.org/Prelude/Text/concatSep sha256:fa909c0b2fd4f9edb46df7ff72ae105ad0bd0ae00baa7fe53b0e43863f9bd34a

Importe können mit Integritätsprüfungen geschützt werden, wenn ein SHA-256-Hash angehängt wird (wie beim obigen concatSep-Import). 

Dhall-Dateien innerhalb anderer Dhall-Dateien referenzieren (verschachteln)

Importierte Ausdrücke können transitiv andere Ausdrücke importieren. Eine exemplarische Datei  ./schema.dhall kann andere Dateien importieren:

{ 
    name : ./schema/name.dhall, 
    age  : ./schema/age.dhall, 
    hobbies : ./schema/hobbies.dhall
}

... und wenn ./schema/hobbies.dhall einen relativen Import enthielte wie z.B.:

List ./hobby.dhall

... dann würde sich der relative Import von ./hobby.dhall tatsächlich auf ./schema/hobby.dhall beziehen. Dies ist als "Importverkettung" bekannt: die Auflösung von Importen relativ zur Position des aktuellen Ausdrucks.

Dhall-Dateien parsen, prüfen und formatieren

Dhall-Dateien können mit einer eigenen Bibliothek geparsed werden und auf Fehler geprüft werden. Das Resultat kann per neu formatiert ausgegeben werden - auch unter den Stichworten Beautify oder Prettyprint bekannt. Zunächst muss dafür die Bibliothek dhall installiert werden.

sudo apt install dhall

Das Folgende Kommado setzt zunächst die Konfigurationsdatei als Varaible. Wenn diese Datei erfolgreich geparsed werden konnte, dann formatieren wir diese und überschreiben dann die Originaldatei:

CFG="/opt/fabinfra/bffh-data/config/bffh.dhall" && \
dhall format < $CFG && \
dhall format < $CFG > $CFG.bup && \
rm $CFG && \
mv $CFG.bup $CFG

Syntax-Highlighting: Dhall-Dateien schöner bearbeiten

Für verschiedene Editoren gibt es verschiedener Highlighter-Plugins. Dabie unter anderem für Vim, VS Code und Emacs. Siehe hier.

Für Vim funktioniert das zum Beispiel so:

curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
vim ~/.vimrc
call plug#begin()
Plug 'vmchale/dhall-vim'
call plug#end()

Beim nächsten Öffnen des Editors führen wir das Kommando :PlugInstall aus und öffnen dann beliebige Dhall-Dateien und sehen, dass uns Syntax-Highlighting zur Verfügung steht.

Damit die Datei sauber als Dhall erkannt wird, muss die Dateiendung *.dhall lauten.

grafik.png
Beispiel-Screenshot für vim

Die Konfiguration von bffh

In der Config gespeicht werden grundlegende, pflichtmäßig anzugebende Informationen zu

Allgemeine Einstellungen

listens

Enthält die Adressen, auf die BFFH bei der Verbindung für die API hört. Standardport für BFFH ist 59661

Beispiel:

listens = 
[ 
    { address = "127.0.0.1", port = Some 59661 }
]

mqtt_url

Enthält die Adresse des MQTT-Servers, mit dem sich BFFH verbindet.

Die Adresse hat das Format <protocol>://[user]:[password]@<server>:[port]

  • protocol wird benötigt und kann eins der folgenden Werte annehmen: mqtt,tcp,mqtts,ssl
  • user und password sind optional
  • server ist erforderlich und kann eine IP-Adresse oder ein Hostname sein
  • port ist optional. Der Standardport ist 59661

Beispiele:

mqtt_url = "tcp://localhost:1883" 
mqtt_url = "mqtts://user:password@server.tld:port"

certfile und keyfile

TLS-Zertifikat. Details, wie man ein TLS-Zertifikat installiert: Server - Anleitung zum selber kompilieren

certfile = "/opt/fabinfra/bffh-data/cert/bffh-selfsigned.crt"
keyfile = "/opt/fabinfra/bffh-data/cert/bffh-selfsigned.key"

db_path

Enthält den Pfad für die interne Datenbank, die BFFH verwendet. BFFH wird zwei Dateien erstellen: <db_path> und <db_path>-lock. Es sollte sichergestellt werden, dass BFFH Schreibzugriff auf das entsprechende Verzeichnis hat. In der internen Datenbank werden die aktuellen Maschinenzustände gespeichert (welche Maschine ist z.B. gerade durch einen Nutzer INUSE) und welche Nutzer mit Rollen, Passwörtern und Cardkeys zugreifen dürfen. Diese interne Datenbank kann teilweise exportiert werden (Benutzerdatenbank kann als *.toml Datei ausgegeben werden). Wir empfehlen die Ablage im Standardverzeichnis /var/lib/bffh/.

Beispiel:

db_path = "/var/lib/bffh/bffh.db"

auditlog_path

Pfadangabe zur Auditdatei, die BFFH im laufenden Prozess schreibt. Die Ausgabe erfolgt im JSON-Format. Siehe Audit Log (Revisionsprotokoll) für Details zum Audit. Die Datei kann zum Beispiel mit Tools wie Grafana Loki ausgelesen werden. Wir empfehlen deshalb den Pfad so zu definieren, dass entsprechende Dienste korrekte Leseberechtigungen auf die Datei haben. Der bei Linux-Systemen verwendete Standardpfad for Log-Dateien ist per se /var/log/.

auditlog_path = "/var/log/bffh-audit.json"

Falls bffh wegen einem Berechtigungsfehler nicht startet, kann folgender Trick angewendet werden.

Error:
  × audit log failed
  ╰─▶ Permission denied (os error 13)

Lösung:

sudo touch /var/log/bffh-audit.json
sudo chown bffh:root /var/log/bffh-audit.json
sudo chmod 750 /var/log/bffh-audit.json

Alternativ kann auch mit setfacl eine ACL angelegt werden. Hierzu gibt es ein nettes Tool im unter https://techgeeks.io/unix/acl-generator.

sudo setfacl -R -m u:bffh:rw-,d:u:bffh:rw- /var/log/bffh-audit.json

Berechtigungen (Permissions)

Standardberechtigungen

BFFH verfügt über einige Standardberechtigungen, die der Verwaltung und den Admin-Rechten zugewiesen werden können.

bffh.users.info - Nutzerliste bekommen und Infos über diese Accounts erhalten
bffh.users.manage - Nutzerliste bekommen und Nutzer verwalten
bffh.users.admin - Globale Administration: Nutzer hinzufügen, löschen, ändern (z.B. Passwort-Reset)

Modellieren von Berechtigungen

Allgemeines Schema: space.type.category.permission.model

Administrator
space.machines.printers.*

Offene Berechtigung
space.machines.printers.read.*

BFFH verwendet eine pfadähnliche Zeichenkette als Erlaubnisformat, getrennt durch einen . Punkt. So besteht zum Beispiel this.is.a.permission aus den Teilen this, is, a und permission. Bei der Anforderung von Berechtigungen, z. B. in Maschinen, muss immer eine genaue Berechtigung angegeben werden, also z. B. test.write. Bei der Erteilung von Berechtigungen, z. B. in Rollen, können entweder eine genaue Berechtigung angegeben oder die beiden Platzhalter * und + verwendet werden. Diese Wildcards verhalten sich ähnlich wie Regex- oder Bash-Wildcards:

  • * gewährt alle Berechtigungen in diesem Teilbaum. So wird perms.read.* für jedes von passen: 
    • perms.read
    • perms.read.machineA
    • perms.read.machineB
    • perms.read.machineC.manage
  • + gewährt alle Berechtigungen unter des Wertes. So wird perms.read.+* für jedes von passen: 
    • perms.read.machineA
    • perms.read.machineB
    • perms.read.machineC.manage
    • aber nicht perms.read

Wildcards sind wahrscheinlich am nützlichsten, um Maschinen zu gruppieren, z.B. 3D-Drucker und eine Bandsäge:

  1. Write (schreiben) Berechtigungen
    • machines.printers.write.prusa.sl1
    • machines.printers.write.prusa.i3
    • machines.printers.write.anycubic
    • machines.bandsaws.write.bandsaw1
  2. Manage (verwalten) Berechtigungen
    • machines.printers.manage.prusa.sl1
    • machines.printers.manage.prusa.i3
    • machines.printers.manage.anycubic
    • machines.bandsaws.manage.bandsaw1
  3. Admin Berechtigungen
    • machines.printers
      • Für alle Drucker
    • machines.bandsaws
      • Für alle Bandsägen

Dann erteilen wir den Rollen die entsprechenden Rechte:

  • Nutze beliebige 3D-Drucker:
    • machines.printers.write.+
  • Erlaube nur die Nutzung "billiger" Drucker:
    • machines.printers.write.anycubic.*
    • machines.printers.write.prusa.i3
  • Erlaube das Verwalten der Drucker:
    • machines.printers.+
  • Erlaubte das Administrieren aller Drucker:
    • machines.printers.*

Auf diese Weise klappt es trotzdem mit der Aufteilung, wenn später ein weitere Anycubic Drucker gekauft wird:

  • machines.printers.write.anycubic.i3
  • machines.printers.write.anycubic.megax

Konfiguration von Maschinen

machines

Enthält eine Liste der definierten Maschinen. Die Maschinen haben verschiedene Wahrnehmungsebenen, mit denen interagiert werden kann:

  • disclose (offenlegen): Benutzer kann die Maschine in der Maschinenliste sehen
  • read (lesen): Der Benutzer kann Informationen über die Maschine und ihren Zustand lesen
  • write (schreiben): Der Benutzer kann die Maschine benutzen
  • manage (verwalten): Der Benutzer kann als Manager mit dem System interagieren (Prüfen, Freigeben, Transferieren)

Jede Maschine muss eine ID haben, um in anderen Teilen dieser Konfiguration oder über die API auf die Maschine verweisen zu können. Und jede Maschine muss einen Namen haben.

Optionale Informationen

Um weitere Informationen über die Maschine bereitzustellen, können diesen Beschreibungen hinzugefügt oder ein externer Wiki-Link bereitstellt werden. Beide Attribute sind nur optional und müssen nicht gesetzt werden.

Beispiel:

machines = 
{ 
    machine123 = 
    { 
        name = "Testmachine",
        description = Some "A test machine",
        wiki = "https://someurl"

        disclose = "lab.test.read",
        read = "lab.test.read",
        write = "lab.test.write",
        manage = "lab.test.admin"
    }
}

“machine123” is in this case the “Machine-ID”

Konfiguration von Rollen (roles)

Die Rollen werden in der Datei bffh.dhall konfiguriert. Wenn die Datei roles.toml im Verzeichnis vorhanden ist, kann sie gelöscht werden und kann nicht zur Verwaltung von Rollen verwendet werden.

roles

Enthält die Liste der definierten Rollen. Rollen haben eine Liste von Berechtigungen und können vererbt werden. Die Berechtigung kann ein Platzhalter in der Berechtigungsliste sein.

Beispiel:

roles =
{
    testrole = 
    {
        permissions = [ "lab.test.*" ]
    },
    somerole =
    { 
        parents = [ "testparent" ],
        permissions = [ "lab.some.admin" ]
    }, 
    testparent =
    {
        permissions =
        [
            "lab.some.write",
            "lab.some.read",
            "lab.some.disclose"
        ]
    }
}

Konfiguration von Aktoren (actors)

actors

Enthält eine Liste von Aktoren. Aktoren werden durch ein Modul und einen oder mehrere Parameter definiert. Aktuell von Haus aus unterstützte Aktoren (ohne zusätzliche Plugins) sind:

Dummy Actor

Für Testzwecke kann ein interner Dummy-Initiator genutzt werden:

Der „Dummy“-Initiator versucht alle paar Sekunden, einen Rechner als den angegebenen Benutzer zu verwenden und zurückzugeben. Er ist gut geeignet, um das System zu testen, führt aber zu Spam im Log und ist daher standardmäßig deaktiviert.   

actors = { 
   Dummy_123 = 
      { 
          module = "Dummy", 
          params = {=}
      } 
   }

Shelly Actor

Dieser Aktor verbindet BFFH über einen MQTT-Server mit einem Shelly Gerät.

Der topic Parameter des Shelly muss auf das Shelly-spezifische MQTT-Topic gesetzt werden.

Anleitung zum Auffinden des Shelly Topic

Beispiel:

actors = 
{
    Shelly_123 = 
    { 
        module = "Shelly", 
        params = 
        {
            topic = "shellyplug-s-123456"
        }
    }
}

„Shelly_123“ ist in diesem Fall die "Actor-ID".

Process Actor

Dieser Aktor ermöglicht es, eigene Prozesse (z.B. Python, Bash, Perl, Java ...) mit dem BFFH Server zu verbinden. Im Beispiel heißt unser Aktor Bash_123.

cmd = Pfad der ausführbaren Datei
args = Argumente der ausführbaren Datei

Beispiel:

actors = 
{
    Bash_123 =
    { 
        module = "Process", 
        params =
            { 
                cmd = "./examples/actor.sh",
                args = "your ad could be here"
            }
    }
}

Konkret nutzbare Aktoren-Beispiele finden sich hier.

actor_connections

Verbindet den Aktor mit einer Maschine. Eine Maschine kann mehrere Aktoren haben. Verwenden Sie die "Machine-ID" (machine) und "Actor-ID" (actor).

Beispiele:

actor_connections = [] : List { machine : Text, initiator : Text },
actor_connections = 
[
    { machine = "Testmachine", actor = "Shelly_123" },
    { machine = "Another", actor = "Bash_123" },
    { machine = "Yetmore", actor = "Bash_234" }
]

Konfiguration von Initiatoren (initiators)

Initiatoren werden fast genauso konfiguriert wie Aktoren.

initiators

Enthält eine Liste von Initiatoren. Initiatoren werden durch ein Modul und einen oder mehrere Parameter definiert. Die Liste kann bzw. muss leer sein, wenn keine Initiatoren verwendet werden:

initiators = {=}

Sonst kann die Liste einen oder mehrere Initiator mit ihrem Name (Im Beispiel Initiator_123) enthalten:

Dummy Initiator

Für Testzwecke kann ein interner Dummy-Initiator genutzt werden:

Der „Dummy“-Initiator versucht alle paar Sekunden, einen Rechner als den angegebenen Benutzer zu verwenden und zurückzugeben. Er ist gut geeignet, um das System zu testen, führt aber zu Spam im Log und ist daher standardmäßig deaktiviert.   

initiators = { 
   Initiator_123 = 
      { 
          module = "Dummy", 
          params = 
          { 
              uid = "Testuser" 
          } 
      } 
   }
Process Initiator

Dieser Initiator ermöglicht es, eigene Prozesse (z.B. Python, Bash, Perl, Java ...) mit dem BFFH Server zu verbinden. Im Beispiel heißt unser Initiator Bash_567.

cmd = Pfad der ausführbaren Datei
args = Argumente der ausführbaren Datei

Beispiel:

initiators = 
{
    Bash_567 =
    { 
        module = "Process", 
        params =
            { 
                cmd = "./examples/init.py",
                args = "your ad could be here"
            }
    }
}

Konkret nutzbare Initiatoren-Beispiele finden sich hier.

init_connections

Verknüpfung von Maschinen mit Initiatoren. Ähnlich wie bei Aktoren können einer Maschine mehrere Initiatoren zugewiesen werden, aber ein Initiator kann nur einer Maschine zugewiesen werden. Verwenden Sie für die Zuweisung die "Machine-ID" (machine) und "Initiator-ID" (initiator).

Beispiele:

init_connections = [] : List { machine : Text, initiator : Text },
init_connections = [
    { machine = "Testmachine", initiator = "Initiator_123" }
],

FabFire

spacename

Der Name des Spaces (die offene Werkstatt, das FabLab, der HackerSpace, etc.) wird im URN-Schema urn:fabaccess:lab:{spacename} verwendet. Wird er nicht definiert, wird der Wert "generic" vergeben. Diese Angaben benötigen wir für QR-Codes von Maschinen oder für DESFire Karten zur Nutzung von FabFire.

instanceurl

Wird für eine allgemeine Space Info genutzt und als URN im Code genutzt: urn:fabaccess:lab:{spacename}\x00{instanceurl}. Dieser Wert wird aktuell nicht verwendet, muss jedoch ausgefüllt werden, damit die Konfiguration bffh.dhall valide ist!

Minimal funktionierende Standardkonfiguration

Wie bereits eingangs erwähnt, werden alle obigen Parameter für die Konfiguration benötigt. Für die minimale Startfähigkeit eines BFFH Servers kann folgendes Konfigurationssample verwendet werden. Dabei muss auch der MQTT-Server unbedingt laufen und erreichbar sein. Ein Standardsample (etwas abweichend) kann auch per bffhd --print-default ausgegeben werden (siehe Cheat Sheet).

{
spacename = "fabaccess.sample.space",
instanceurl = "https://fabaccess.sample.space", 
listens = [{address = "127.0.0.1"}],
certfile = "/etc/bffh/bffh-selfsigned.cert",
keyfile = "/etc/bffh/bffh-selfsigned.key",
mqtt_url = "mqtt://127.0.0.1:1883",
db_path = "/var/lib/bffh/bffh.db",
auditlog_path = "/var/log/bffh-audit.json", 
roles = {=},
machines = {=},
actors = {=},
actor_connections = [] : List { machine : Text, initiator : Text },
initiators = {=},
init_connections = [] : List { machine : Text, initiator : Text },
}

Konfiguration per Config Generator

Siehe Einfache Konfiguration mit dem FabAccess Config Generator

Konfiguration prüfen

Konfiguration erstellt, aber unsicher, ob sie syntaktisch korrekt ist? Wir können das per bffh --check --config <Pfad zu bffh.dhall> überprüfen. Siehe hier. Das Kommando kann mit dem --check Parameter unabhängig und vor dem Neustart von bffh ausgeführt werden und verhindert so die Unverfügbarkeit des Service bei Fehlkonfiguration (alternativ kann auch ein Staging-System für derartige Tests angelegt werden).