# FabAccess Konfiguration

Nach jeder [FabAccess Installation](https://docs.fab-access.org/books/fabaccess-installation "FabAccess Installation") ist eine Anpassung (Konfiguration) an die nötigen Umstände sinnvoll und wünschenswert.

# Benutzerkonfiguration - users.toml

## Wichtige Informationen zur Benutzerdatenbank

BFFH selbst nutzt eine interne Datenbank in `bffh.db` für die Benutzer, jedoch wird diese erstmalig über die Datei `users.toml` gespeist.

Das Speisen von Benutzern kann auch über die Python-API [pyfabapi](https://gitlab.com/fabinfra/fabaccess/pyfabapi) erfolgen (siehe [Beispielscript zum Hinzufügen und Löschen von Benutzern](https://gitlab.com/fabinfra/fabaccess/pyfabapi/-/blob/main/add_del_user.py?ref_type=heads)).

Nachdem mindestens eine erste Verwaltungsrolle inkl. zugewiesenem Benutzer (Admin, Manager) zum System hinzugefügt wurde, kann anschließend auch die Benutzerverwaltung der Client-Anwendung [Borepin](https://docs.fab-access.org/books/fabaccess-konfiguration/page/client-benutzen-und-typische-konfigurationsfehler-bei-server-und-clients) genutzt werden. Über diese lassen sich Benutzer hinzufügen und entfernen, sowie Rollen und Passwörter managen.

<p class="callout warning">Die in der App hinzugefügten bzw. modifizierten Benutzer **werden nicht automatisch** in die erstmalig genutzte `users.toml` geschrieben, sondern direkt in die interne Benutzerdatenbank. Das Exportieren erfolgt explizit durch `/usr/bin/bffhd --dump-users`. Für unsere Handhabe bedeutet das, dass die parallele Bearbeitung der Benutzerdatenbank per App und `users.toml` sorgfältig getätigt werden sollte, um etwaige Änderungen nicht ungewollt rückgängig zu machen.</p>

## Intro

In der [TOML](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-toml)-Datei `users.toml` werden die Benutzer, sowie ihre jeweiligen Passwörter, Rollen und [Kartenschlüssel](https://docs.fab-access.org/books/fabfire-und-nfc-tags "NFC Key Cards und Transponder (FabCard / FabFire)") gespeichert. Die Datei befindet sich üblicherweise in `<span class="pre">/etc/bffh/</span>`. Die Datei wird nicht automatisch von BFFH geladen - egal, ob sie komplett neu ist oder nur modifiziert wurde. Das Importieren der `users.toml` erfolgt durch `/usr/bin/bffhd --load-users`.

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2025-02/scaled-1680-/NKVmINIqnmKbgINE-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2025-02/NKVmINIqnmKbgINE-grafik.png)

*Offizielles TOML-Logo*

## Aufbau einer users.toml

Der grundlegende Aufbau folgt dem oben verlinkten TOML-Standard.

### Benutzer

Jeder Nutzer wird dabei durch eine eigene Sektion `userid` angegeben (in einem Paar eckiger Klammern, z.B. `[AdminUser]`). Die `userid` Kennungen werden von BFFH z.B. beim Ausführen von Aktionen verwendet ([Aktoren](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-actors-config), [Initiatoren](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-konfiguration-von-in)) - dort werden sie in die Befehlskette hinten angefügt.

Der Benutzername kann direkt in eckige Klammern ohne Hochkommas geschrieben werden, solange keine Sonderzeichen wie Umlaute enthalten sind. Wir empfehlen aus genau diesem Grund prinzipiell jeden Nutzer ausschließlich in Hochkommas zu schreiben. So kann der Nutzer `[Ö]` von BFFH nicht erfolgreich importiert werden (da ungültige Deklaration), der Nutzer `["Ö"]` jedoch schon. Bei Nichtbeachtung folgt eine Fehlermeldung:

```bash
Error:   × unexpected character found: `\u{d6}` at line x column y
```

<p class="callout danger">**Achtung**: Der Name kann unter anderem auch Leerzeichen und Sonderzeichen enthalten, sowie nahezu beliebig viele Zeichen. Folgender Username ist beispielsweise gültig:  
`["Ein seeeeeeeeeeeeeeeeeeeeeeeeeehr                        langer                                                 Nutzername mit Sond@rze!chen_    "]`. Dem Administrator wird angeraten eine Nutzernamenkonvention aufzustellen, die z.B. verschiedene Sonderzeichen vermeidet, die häufig auch bei LDAP-gestützten Anwendungen Probleme bereiten. Es wird im Zweifelsfall empfohlen sich auf Klein- und Großbuchstaben, sowie Ziffern und einfache Zeichen wie `.-_` zu beschränken.  
</p>

### Rollen, Passwörter, Cardkeys

Unter jeder `userid` werden Schlüssel-Wert-Paare abgelegt. Die aktuell **genutzten** Schlüsselnamen lauten:

- `roles` (Rollen) - diese Rollen werden in der [Hauptkonfiguration](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall "Hauptkonfiguration - bffh.dhall") definiert und dann der `users.toml` zugewiesen
- `passwd` (Passwort - unverschlüsselt (plaintext) oder als [Argon2](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-argon2)-Hash)
- `cardkey` (DESFire EV2 [UUID](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-uuid-%28universally-un)) - diese wird entweder aus der Borepin App oder über das [FabFire Provisioning Tool](https://docs.fab-access.org/books/plugins-aktoren-initiatoren/page/fabfire-tools#bkmrk-fabfire-provisioning "FabFire Tools") erzeugt
- [`cardtoken`](https://gitlab.com/fabinfra/fabaccess/bffh/-/blame/development/bffhd/capnp/user.rs?ref_type=heads#L261) - eine anonyme Alternative zur `userid` und gilt as Mapping zwischen `userid` und `cardtoken`. So muss die `userid` auf einer DESFire EV2 Karte nicht gespeichert werden. Siehe auch [Datei 0003](https://docs.fab-access.org/books/fabfire-und-nfc-tags/page/fabfire-funktionsprinzip-grundlagen#bkmrk-datei-0003-erm%C3%B6glich).

### Weitere Schlüssel und Kommentare

<p class="callout info">Beliebige weitere Schlüssel können in der `users.toml` angelegt werden und werden ebenso von BFFH geladen (key-value store) und auch wieder exportiert. Sie werden jedoch nicht vom System verwendet!  
</p>

Kommentare können mit `#` vorangestellt erzeugt werden oder auf gleicher Zeile hinter dem Wert ("Inline comment"):

```ini
[Admin1]
# this is the admin role
roles = ["Admin"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" #some inline comment
```

Kommentare werden spätestens beim Laden innerhalb der BFFH-Datenbank verworfen. Es empfiehlt sich im Falle des Kommentarbedarfs mit Exportstabilität einen Pseudoschlüssel anzulegen, zum Beispiel `comment`:

```ini
[Admin1]
roles = ["Admin"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
comment = "this is the admin role"
```

## Beispielbenutzer und -rollen

Rollen werden in der [bffh.dhall-Konfiguration](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall "Hauptkonfiguration mit bffh.dhall") definiert. Das Docker-compose Repository [https://gitlab.com/fabinfra/fabaccess/dockercompose](https://gitlab.com/fabinfra/fabaccess/dockercompose) hat ein gutes [Beispiel](https://gitlab.com/fabinfra/fabaccess/dockercompose/-/blob/main/config/bffh/users.toml):

```ini
[Admin1]
roles = ["Admin"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[Admin2]
roles = ["Admin"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[ManagerA1]
roles = ["ManageA", "UseA", "ReadA", "DiscloseA"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[ManagerA2]
roles = ["ManageA", "UseA", "ReadA", "DiscloseA"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[ManagerB1]
roles = ["ManageB", "UseB", "ReadB", "DiscloseB"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[ManagerB2]
roles = ["ManageB", "UseB", "ReadB", "DiscloseB"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[ManagerC1]
roles = ["ManageC", "UseC", "ReadC", "DiscloseC"]
passwd = "secret"
cardkey = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
```

# Manuelles Anlegen neuer Benutzer  


Lasse alle neuen Nutzer ihre Passwörter verschlüsselt an den Admin übertragen, indem diese ihr Passwort als verschlüsselten [Argon2](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-argon2) String übermitteln. Prinzipiell unterstützt FabAccess die Typen

- Argon2i
- Argon2d
- Argon2id

Die Passwörter können beliebig komplex werden. Es gibt verschiedene Parameter, die ein Passwort besonders sicher machen. Folgende Werte und ihrer [empfohlenen Standardwerte](https://github.com/sru-systems/rust-argon2/blob/master/src/config.rs#L58) können genutzt werden. Mit folgenden Settings hashed BFFH die Passwörter intern und exportiert sie auch so (`bffhd --export`):

- Salt: eine zufällige Zeichenkette , z.B. `16` bytes lang ([Empfehlung](https://docs.rs/argon2/latest/argon2/constant.RECOMMENDED_SALT_LEN.html))
- Parallelism Factor: `1`
- Memory Cost: `4096` kilobytes
- Iterations: `3`
- Hash Length: `32` bytes

<p class="callout info">Je sicherer das Passwort gewählt wird, desto höher ist die benötigte CPU-Leistung zum Entschlüsseln und das Einloggen in FabAccess dauert entsprechend länger. Ein Passwort mit Parallelism Factor 10, Memory Cost 100000, Iterations 20 und Hash Length 100 benötigt auf einem Raspberry Pi 3 B+ ca. 15 Sekunden zur Entschlüsselung. Siehe auch [https://www.ory.sh/choose-recommended-argon2-parameters-password-hashing](https://www.ory.sh/choose-recommended-argon2-parameters-password-hashing) und [https://de.wikipedia.org/wiki/Argon2#Empfohlene\_Parameter](https://de.wikipedia.org/wiki/Argon2#Empfohlene_Parameter)</p>

### Argon2 Passwort auf der Kommandozeile erzeugen ...  


#### ... mit Ubuntu Linux

```bash
apt install argon2
PASSWORD="mypassword" && echo | argon2 $PASSWORD -i -k 4096 -p 1 -t 3 -l 32
```

#### ... mit Windows

Eine Implementierung für Windows existiert zum Beispiel unter [https://github.com/philr/argon2-windows](https://github.com/philr/argon2-windows/releases)

Wir laden das Release-Zip herunter, entpacken es und führen `Argo2Opt.exe` aus:

```bash
set PASSWORD="mypassword"
echo %PASSWORD% | Argon2Opt.exe %PASSWORD% -i -k 4096 -p 1 -t 3 -l 32
```

Wir übermitteln die kodierte Zeichenkette in die `users.toml`, hier im Beispiel: `$argon2i$v=19$m=4096,t=3,p=1$UEFTU1dPUkQ$VMwncjCWdW+f6x8qzshLaA`. Der Eintrag in der `users.toml` könnte so lauten:

```ini
[vmario]
roles = ["mitglied"]
passwd = "$argon2i$v=19$m=4096,t=3,p=1$UEFTU1dPUkQ$VMwncjCWdW+f6x8qzshLaA"
cardkey = "70AFE9E6B1D6352313C2D336ADC2777A"
```

### Argon2 Passwort mit [Argon2 Hash Generator](https://argon2.online/) Tool

Argon2 Hashes können auch online erstellt werden:

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2025-01/scaled-1680-/PdP4PesecdEgL4ML-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2025-01/PdP4PesecdEgL4ML-grafik.png)

Wir übermitteln dem Administrator von FabAccess letztlich die kodierte Zeichenkette, hier im Beispiel:  
`$argon2i$v=19$m=4096,t=3,p=1$VHJCT0xBRkpIZGdyQkY4WA$7V9o957iuOYX/UoEHMpYncmTCNJ06dQ9YOJyZnxv3OU`

## Benutzerkonfiguration in BFFH importieren

Eine einmal angelegte `users.toml` kann und muss in die BFFH-interne Datenbank importiert werden. Details siehe

- [Cheat Sheet - Wichtigste Befehle (Übersicht)](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht "Cheat Sheet - Wichtigste Befehle (Übersicht)")
- [Nutzerdatenbank laden / hashen / prüfen](https://docs.fab-access.org/books/fabaccess-konfiguration/page/nutzerdatenbank-laden-hashen-prufen)

## Benutzerkonfiguration aus BFFH exportieren

Die in der BFFH-Datenbank gespeicherten Benutzer können mit dem Zusatz `--dump-users` in eine `users.toml` Datei exportiert werden (hier im Beispiel nach `/etc/bffh/config_backup/users.toml`):

```bash
mkdir -p /etc/bffh/config_backup/
/usr/bin/bffhd --verbose --config /etc/bffh/bffh.dhall --dump-users /etc/bffh/config_backup/users.toml --force
```

Siehe auch [Cheat Sheet - Wichtigste Befehle (Übersicht)](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht "Cheat Sheet - Wichtigste Befehle (Übersicht)")

## Weitere Tipps und und Tricks mit TOML Dateien  


Ein paar weitere Tools, die das digitale Leben ggf. vereinfachen.

### TOML-Dateien sortieren (nach Sektionen, Arrays, etc.) mit toml-sort

[https://pypi.org/project/toml-sort](https://pypi.org/project/toml-sort)

```bash
cd /opt/fabinfra/scripts/
python3 -m venv env
. env/bin/activate #activate venv
/opt/fabinfra/scripts/env/bin/pip3 install toml-sort
chown -R bffh:bffh /opt/fabinfra/scripts/env/
```

```bash
toml-sort --help
usage: toml-sort [-h] [--version] [-o OUTPUT] [-i] [-I] [-a] [--no-sort-tables] [--sort-table-keys]
                 [--sort-inline-tables] [--sort-inline-arrays] [--sort-first KEYS] [--no-header] [--no-comments]
                 [--no-header-comments] [--no-footer-comments] [--no-inline-comments] [--no-block-comments]
                 [--spaces-before-inline-comment {1,2,3,4}] [--spaces-indent-inline-array {2,4,6,8}]
                 [--trailing-comma-inline-array] [--check]
                 [F ...]
```

Mit folgendem Kommando lassen sich `users.toml` Dateien sortieren und gleichzeitig überschreiben.

```bash
toml-sort --in-place --all /etc/bffh/users.toml
```

### TOML-Dateien als JSON ausgeben/verarbeiten

Analog zum bekannten Werkzeug [`jq`](https://jqlang.github.io/jq/) (JSON-Parser) gibt es das Tool `yq` für das TOML-Format (als Wrapper für `jq`): [https://kislyuk.github.io/yq/#toml-support](https://kislyuk.github.io/yq/#toml-support)

```bash
cd /opt/fabinfra/scripts/
python3 -m venv env
. env/bin/activate #activate venv
/opt/fabinfra/scripts/env/bin/pip3 install yq
chown -R bffh:bffh /opt/fabinfra/scripts/env/
```

<p class="callout info">Durch dieses Paket wird ebenfalls die Binary `tomlq` automatisch mit installiert. Bitte beachten: Das gleichnamige Paket `tomlq` gibt es separat auf [PyPI](https://pypi.org/project/tomlq/), ist jedoch stark veraltet und inkompatibel zu neuen Python Versionen. Es wird daher empfohlen das beinhaltende, aktuellere Paket `yq` zu installieren.</p>

Unsere `users.toml` als JSON-Objekt ausgeben:

```bash
tomlq '.' /etc/bffh/users.toml
```

Mit dem nachfolgenden Befehl überschreiben wir eine `users.toml` Datei so, dass z.B. alle Kommentare entfernt werden:

```bash
tomlq -t -i '.' /etc/bffh/users.toml
```

# Benutzer mit externen Systemen synchronisieren

Grundsätzlich gibt es mehrere Möglichkeiten, um Benutzer in FabAccess anzulegen:

- über eine [`users.toml`](https://docs.fab-access.org/books/fabaccess-konfiguration/page/benutzerkonfiguration-userstoml "Benutzerkonfiguration - users.toml") Datei
- direkt über die Client App (als Administrator)
- über die API. Hierzu gibt es eine Referenzimplementation [pyfabi](https://gitlab.com/fabinfra/fabaccess/pyfabapi) mit einem Beispielscript `<a href="https://gitlab.com/fabinfra/fabaccess/pyfabapi/-/blob/main/add_del_user.py?ref_type=heads">add_del_user.py</a>`

## Hinweise zum Arbeiten mit users.toml Dateien

Wer mit einer automatisierten Erstellung einer `users.toml` aus externen Nutzerquellen arbeitet, der sollte folgenden Workflow erarbeiten:

1. BFFH Server herunterfahren
2. Aktuelle Benutzerkonfiguration per `/usr/bin/bffhd --dump-users users.toml` sichern
3. die soeben gesicherte `users.toml` Datei benutzen, um die aktuellsten Änderungen an der Benutzerbasis hinzuzufügen, zu löschen oder zu ändern. Hier muss selbst entschieden werden, welche Benutzerdaten aktuell sein sollen: Die durch die App geänderten Daten, oder die aus der externen Quelle? Eine von beiden muss die andere überschreiben bzw. überlagern. Entsprechend muss das verwendete Script programmiert sein: bei Vorhandensein von Nutzern, Rollen, Passwörter, Cardkeys rückfragen, ob bei Differenzen der bestehende oder der neue Datensatz verwendet werden soll oder per Config-Parameter automatisiert durchführen.
4. die neue `users.toml` Datei laden: `/usr/bin/bffhd --load-users users.toml` (siehe auch [Nutzerdatenbank laden / hashen / prüfen](https://docs.fab-access.org/books/fabaccess-konfiguration/page/nutzerdatenbank-laden-hashen-prufen))
5. BFFH Server starten

<p class="callout success">Unter Umständen ist die Arbeit mit der [pyfabapi](https://gitlab.com/fabinfra/fabaccess/pyfabapi) sinnvoller!</p>

## LDAP Anbindung

Hierzu gibt es einen [dedizierten Artikel](https://docs.fab-access.org/books/schnittstellen-und-apis/page/ldap-anbindung).

## Authentik

Nutzer geben ein zweites Passwort an und ein Skript checkt jede Minute Authentik ob es neue Nutzer gibt, sich Gruppen/Passwort geändert haben und nimmt entsprechend Änderungen über die FabAccess API vor. Das Passwort wird dann aus den Nutzerattributen gelöscht. Die Daten liegen in einer MySQL Datenbank.

<p class="callout info">Mögliche Ansprechartner dafür könnten das [Creative Lab der Hochschule München](https://clab-hm.de/safety/fabaccess) oder Michael Prange vom [MakerSpace Gütersloh](https://docs.fab-access.org/books/makerspace-gutersloh "Makerspace Gütersloh") sein!  
</p>

## CiviCRM

CiviCRM erlaubt den Export von Nutzern als CSV-Tabelle. Diese kann verwendet werden, um Nutzer in FabAccess zu importieren.

<p class="callout info">Roy Böttcher vom [MakerSpace Leipzig](https://docs.fab-access.org/books/makerspace-leipzig "MakerSpace Leipzig") fragen! </p>

## VereinOnline

Nutzer und Gruppen aus [VereinOnline](https://www.vereinonline.org) lassen sich mit dem Tool [csv-to-fabaccess-user-toml](https://github.com/Sternenlabor/csv-to-fabaccess-user-toml) vom [Sternenlabor Plauen](https://docs.fab-access.org/books/sternenlabor-ev "Sternenlabor e.V.") direkt als `users.toml` ausgeben. Dazu wird eine Datei namens `Mitglieder.csv` mit Spaltenseparator `;` und den Spalten `Login` und `Gruppen` (mit Separator `,`) erzeugt. Für das Tool muss [Node.js](https://nodejs.org/en) installiert sein.

```bash
cd ~
git clone https://github.com/Sternenlabor/csv-to-fabaccess-user-toml.git
cd csv-to-fabaccess-user-toml/

sudo apt install nodejs

npm install csv-parser
```

Mitglieder-CSV Datei - Beispiel:

[https://docs.fab-access.org/data-csv-to-fabaccess-user-toml/Mitglieder.csv](https://docs.fab-access.org/data-csv-to-fabaccess-user-toml/Mitglieder.csv)

```bash
# Ausführen
node run.mjs

[
  { Login: 'Anton', Gruppen: 'Admins' },
  { Login: 'Peter', Gruppen: 'Mitglieder,Admins' },
  { Login: 'Jonny', Gruppen: 'Mitglieder' },
  { Login: 'Sarah', Gruppen: 'Mitglieder' }
]
users: 4
File created successfully
```

Beispiel-Output:

```bash
cat user.toml
```

```ini
[Admin1]
roles = [ "Admin" ]
passwd = "secret"

[Anton]
roles = [ "User", "Admins" ]
passwd = "secret"

[Peter]
roles = [ "User", "Mitglieder,Admins" ]
passwd = "secret"

[Jonny]
roles = [ "User", "Mitglieder" ]
passwd = "secret"

[Sarah]
roles = [ "User", "Mitglieder" ]
passwd = "secret"
```

## easyVerein

Für das Programm easyVerein sind noch keine Schnittstellen in der Community bekannt. Jedoch nutzen es viele offene Werkstätten.

# Hauptkonfiguration - bffh.dhall

BFFH verwendet [Dhall](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-dhall) für die Struktur von Ressourcen (z.B. Maschinen), Rollen, Aktoren und Initiatoren. Die Konfiguration von BFFH befindet sich in der Datei `<span class="pre">bffh.dhall</span>`. Die Datei kann auch umbenannt werden. Wichtig ist, dass sie dann überall korrekt referenziert wird (z.B. in Service Scripts). Der betreffende Quellcode findet sich in [GitLab](https://gitlab.com/fabinfra/fabaccess/bffh/-/blob/main/bffhd/config/dhall.rs?ref_type=heads).

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2025-02/scaled-1680-/1EBzdHIzCwSBbKoT-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2025-02/1EBzdHIzCwSBbKoT-grafik.png)

*Offizielles Dhall Logo*

## Tipps &amp; 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 Kompatibilität auch für komplizierte Gebilde. Im Rahmen dieser Dokumentationen gehen wir nur auf grundlegende, wichtige und besonders praktische Konfigurationsaspekte ein.

<p class="callout success">Ein nutzbringenden Cheatsheet für Dhall findet sich unter [https://docs.dhall-lang.org/howtos/Cheatsheet.html](https://docs.dhall-lang.org/howtos/Cheatsheet.html)</p>

### Literal für leere Einträge

Dhall-Records können auch leer übergeben werden. Dafür gibt es eine spezielle Syntax: `{=}`. Siehe [https://hackage.haskell.org/package/dhall-1.42.1/docs/Dhall-Tutorial.html#g:6](https://hackage.haskell.org/package/dhall-1.42.1/docs/Dhall-Tutorial.html#g:6)

**Beispiel:**

```ini
initiators = {=}
```

### Literal "Some"

Die folgenden beiden Angaben sind inhaltlich gleich. Das Wörtchen "Some" macht in unserem Beispiel den Wert `port` optional. Diese Notation kann verwendet werden, um innerhalb einer Konfiguration darauf hinzuweisen, dass ein Wert fest gesetzt werden könnte, aber aktuell nicht von Nöten ist. Siehe [https://hackage.haskell.org/package/dhall-1.42.1/docs/Dhall-Tutorial.html#g:5](https://hackage.haskell.org/package/dhall-1.42.1/docs/Dhall-Tutorial.html#g:5)

**Beispiel:**

```ini
listens = [{ address = "127.0.0.1" }]
```

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

### Kommentare  


<p class="callout info">Siehe [https://docs.dhall-lang.org/tutorials/Language-Tour.html#comments](https://docs.dhall-lang.org/tutorials/Language-Tour.html#comments)</p>

Zeilen, die mit `--` beginnen, werden automatisch als Kommentar gewertet. Das kann auch verwendet werden, um z.B. nicht mit dem Literal "`Some`" zu arbeiten. So können wir z.B. eine Originalzeile auskommentieren und darunter unsere angepasste Zeile schreiben.

**Beispiel Zeilenkommentar:**

```ini
--listens = [{ address = "127.0.0.1", port = 59661 }]
listens = [{ address = "127.0.0.1" }]
```

Kommentare können auch in Blöcken geschrieben werden, wenn sie in einem Block beginnend `{-` und endend mit `-}` stehen. So werden diese bei etwaigen Formatieroperationen mit `dhall format <Datei>` nicht entfernt!

**Beispiel Blockkommentar**:

```ini
{- Main configuration file for bffh
   ================================
  
   In this configuration file you configure almost all parts of how bffh operates, but most importantly:
        * Machines
        * Initiators and Actors
        * Which Initiators and Actors relate to which machine(s)
        * Roles and the permissions granted by them
 -}
```

**Beispiel Blockkommentar an beliebiger Stelle**:

```ini
    roles = ./roles.dhall, {- Kommentar mitten im Text -}
```

### Zeichenketten erzeugen

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

```ini
certfile = env:BFFH_CFG_PATH as Text ++ "/cert/bffh.crt"
```

Oder als Verkettungsbefehl:

```ini
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](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 darauffolgenden Block (Record) eine Ersetzung bzw. Referenzierung anstrebt.

```ini
let VARS = {
	BFFH_CFG_PATH = "/etc/bffh/",
	MQTT_USER = "fabinfra101",
	MQTT_PASSWD = "fablocal"
	}
in {
    listens = [
        { address = "0.0.0.0", port = 59661 }
        ],
    certfile = VARS.BFFH_CFG_PATH ++ "certs/bffh.crt",
    keyfile = VARS.BFFH_CFG_PATH ++ "certs/bffh.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. Der Prozess, der die Dhall-Datei interpretiert, sucht in seiner übergebenen Umgebung (Environment) nach passenden Variablen und ersetzt die Werte entsprechend.

Als Rohtext in Dhall:

```bash
# bash
export BFFH_CFG_PATH="/etc/bffh"
```

```ini
bffh_cfg = env:BFFH_CFG_PATH as Text
```

oder als aufgelöster Pfad in Dhall:

```bash
# bash
export BFFH_CFG_PATH="~/bffh"
```

```ini
bffh_cfg = env:BFFH_CFG_PATH as Location
```

#### Inhalte aus URLs importieren

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

```ini
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). Dadurch verhindern wir Manipulation von außerhalb.

#### 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:

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

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

```ini
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.

### Auf Kommas achten

In verschiedenen Konfigurationssprachen wie JSON, TOML und Dhall verhalten sich die Syntaxregeln unterschiedlich. In Dhall-Dateien ist es für einige Parser schädlich, wenn innerhalb einer List oder eines Records ein "trailing" Komma auftaucht. Folgendes sollte vermieden werden:

```ini
listens = [
    { address = "127.0.0.1" },
    { address = "::1" },
    ]
```

Syntaktisch korrekt ist:

```ini
listens = [
    { address = "127.0.0.1" },
    { address = "::1" }
    ]
```

<p class="callout info">BFFH kommt mit diesem Kommaproblem aus, jedoch der offizielle [dhall-haskell](https://github.com/dhall-lang/dhall-haskell) Parser nicht.</p>

### Kurze oder lange Schreibweise

Wir können die Unterlemente `Dummy_123`, `Dummy_234` und `Dummy_345` im Beispiel in ein neues Klammernpaar schreiben oder direkt mit einem Punkt abtrennen und auf die gleiche Zeile schreiben:

```ini
actors = { 
    Dummy_123 = { 
          module = "Dummy", 
          params = {=}
          },
    Dummy_234 = { 
          module = "Dummy", 
          params = {=}
          },
    Dummy_345 = { 
          module = "Dummy", 
          params = {=}
          }
    }
```

Oder entsprechend eingekürzt:

```ini
actors.Dummy_123 = { 
    module = "Dummy", 
    params = {=}
    },
actors.Dummy_234 = { 
    module = "Dummy", 
    params = {=}
    },
actors.Dummy_345 = { 
    module = "Dummy", 
    params = {=}
    }
```


Oder bei Rollen:

```ini
roles = {
    Admin = {
       permissions =  [
               "bffh.users.manage",
               "bffh.users.info",
               "bffh.users.admin"
             ]
        }
    }
```

... eingekürzt:

```ini
roles.Admin.permissions =  [
    "bffh.users.manage",
    "bffh.users.info",
    "bffh.users.admin"
    ]
```

<p class="callout info">Das eignet sich bei [Rollen](#bkmrk-roles-1), [Aktoren](#bkmrk-actors%3A%3Arecord) und [Initiatoren](#bkmrk-initiators%C2%A0%28object%29)! Wie es verwendet werden soll ist hier Geschmackssache.</p>

### Dhall-Dateien parsen, prüfen und formatieren

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

```bash
sudo apt install dhall
```

Falls die Installation scheitert, kann das Paket auch manuell installiert werden (siehe [https://pkgs.org/search/?q=dhall)](https://pkgs.org/search/?q=dhall)) - Beispiel für Raspberry Pi mit Debian Bookworm:

```bash
wget http://ports.ubuntu.com/pool/universe/h/haskell-dhall/dhall_1.32.0-1build1.1_arm64.deb
dpkg -i dhall_1.32.0-1build1.1_arm64.deb
```

Das Folgende Kommando setzt zunächst die Konfigurationsdatei als Variable. Wenn diese Datei erfolgreich geparsed werden konnte, dann formatieren wir diese und überschreiben dann die Originaldatei - beim Scheitern unternehmen wir nichts.

<p class="callout warning">**Warnung:**  
Durch das automatische Formatieren werden alle etwaigen Kommentare entfernt (bekanntes Problem von Dhall, siehe [hier](https://github.com/dhall-lang/dhall-haskell/issues/145)), es sei denn diese werden in als Blockkommentar an den **Anfang** der Datei esetzt ([siehe oben](#bkmrk-kommentare)). Falls der [FabAccess Config Generator](https://docs.fab-access.org/books/fabaccess-konfiguration/page/einfache-konfiguration-mit-dem-fabaccess-config-generator "Einfache Konfiguration mit dem FabAccess Config Generator") genutzt wird, gehen dabei u.a. auch die Anfangs- und End-Tags `--- ||| GENERATOR START` und `--- ||| GENERATOR END` verloren!  
  
Unterreferenzierte Dhall-Dateien werden nicht automatisch mitgeprüft. Eine Rekursion findet also **nicht** statt. D.h. jede Dhall-Datei muss einzeln geprüft/formatiert werden.  
</p>

Wir nutzen folgende Kommandos zum Formatieren:

```bash
CFG="/etc/bffh/bffh.dhall" && \
dhall format < $CFG && \
dhall format < $CFG > $CFG.bup && \
rm $CFG && \
mv $CFG.bup $CFG
```

Es können jedoch auch die offiziellen Flags verwendet werden. Siehe:

```bash
Usage: dhall format [--inplace FILE] [--check]
  Standard code formatter for the Dhall language

Available options:
  --inplace FILE           Modify the specified file in-place
  --check                  Only check if the input is formatted
  -h,--help                Show this help text
```

```bash
#nur formatieren
dhall format --inplace /etc/bffh/bffh.dhall --check

# formatieren und überschreiben
dhall format --inplace /etc/bffh/bffh.dhall
```

<p class="callout info">Siehe auch [Konfiguration von BFFH auf Synax und Inhalt prüfen](#bkmrk-konfiguration-auf-in) für eine BFFH-spezifische Kontrolle der Daten.  
</p>

### Syntax-Highlighting: Dhall-Dateien schöner bearbeiten

Für verschiedene Editoren gibt es Highlighter-Plugins, dabei unter anderem für Vim, VS Code und Emacs. Siehe [hier](https://github.com/dhall-lang/awesome-dhall?tab=readme-ov-file#text-editor-support). Eine Referenz findet sich auch in [https://docs.dhall-lang.org/howtos/Text-Editor-Configuration.html](https://docs.dhall-lang.org/howtos/Text-Editor-Configuration.html).

Für Vim funktioniert das zum Beispiel so:

```bash
curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
```

```bash
vim ~/.vimrc
```

```ini
call plug#begin()
Plug 'vmchale/dhall-vim'
call plug#end()
```

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

<p class="callout info">Damit die Datei als Dhall erkannt wird, muss die Dateiendung \*.dhall lauten.</p>

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-12/scaled-1680-/uuqU8K5FopchuDY7-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-12/uuqU8K5FopchuDY7-grafik.png)  
*Beispiel-Screenshot für Vim*

## Die Konfiguration von BFFH  


Den FabAccess Server zu konfigurieren ist zweifellos eine der wichtigsten Aufgaben und erfordert die Auseinandersetzung mit einigen [Konzepten](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/chapter/grundkonzepte "Grundkonzepte").

<p class="callout danger">Bei jeder Änderung der Konfigurationsdatei muss BFFH neugestartet werden, um die Änderungen zu übernehmen!</p>

### Übersicht über alle Einstellungen

In der Config gespeichert werden grundlegende, überwiegend **pflichtmäßig** anzugebende Informationen zu ...

- allgemeine Einstellungen (z.B. Interface, Port, Zertifikate, Audit, Datenbank, etc.)  
    
    - [`listens`](#bkmrk-listens)
    - [`db_path`](#bkmrk-db_path-%28string%29)
    - [`certfile`](#bkmrk-certfile%C2%A0%28string%29-un)
    - [`keyfile`](#bkmrk-certfile%C2%A0%28string%29-un)
    - `<a href="#bkmrk-tls_min_version%3A%3Astr">tls_min_version</a>` (optional)
    - [`ciphers`](#bkmrk-ciphers%3A%3Astring-%28opt) (optional)
    - [`mqtt_url`](#bkmrk-mqtt_url)
    - [`auditlog_path`](#bkmrk-auditlog_path-%28strin)
- Rollen, Berechtigungen, Ressourcen (Maschinen) 
    - [`roles`](#bkmrk-roles-1)
    - [`machines`](#bkmrk-machines%3A%3Arecord)
- Aktoren, Initiatoren und ihre Mappings 
    - [`actors`](#bkmrk-actors%3A%3Arecord)
    - [`initiators`](#bkmrk-initiators%C2%A0%28object%29)
    - [`actor_connections`](#bkmrk-actor_connections-1)
    - [`init_connections`](#bkmrk-init_connections-1)
- Föderation &amp; FabFire 
    - [`spacename`](#bkmrk-spacename)
    - [`instanceurl`](#bkmrk-instanceurl)

### Allgemeine Einstellungen

#### `<span class="pre">listens</span>`::List

Enthält eine Liste aus Einträgen mit einer Adresse (`address`::String (notwendig)), auf die BFFH bei der Verbindung für die API hört sowie einer Portangabe (`port`::Integer (optional)). Die Adresse kann eine IPv4, IPv6 oder ein Hostname sein. Wird der Port nicht angegeben, dann wird der Standardport verwendet. Dieser Port lautet für BFFH `<span class="pre">59661</span>` und dabei handelt es sich um einen dynamischen/privaten Port, der auch ohne Root-Zugriff genutzt werden kann (siehe auch "[registrierte Ports](https://www.digitalocean.com/community/tutorials/opening-a-port-on-linux#introduction)").

Es ist möglich mehrere Einträge (verschiedene Adressen und Ports) anzugeben. BFFH versucht alle Verbindungen zu etablieren. BFFH kann also z.B. auch gleichzeitig auf `0.0.0.0:59661` und `0.0.0.0:5961` lauschen.

<p class="callout warning">**Achtung:** Wenn BFFH keinen Port für die angegebene Kombination binden kann, wird ein Fehler protokolliert, aber mit den übrigen Ports fortgefahren.  
</p>

**Beispiele für listens:**

```ini
listens = [
    { address = "127.0.0.1", port = Some 59661 },
    { address = "::1", port = 59661 },
    { address = "steak.fritz.box", port = 59661 } ]
```

<div class="document" id="bkmrk--1" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section"><div class="section" id="bkmrk--2"></div></div></div></div></div>#### `<span class="pre">mqtt_url</span>`::String  


Enthält die Adresse des [MQTT](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-mqtt)-Servers, mit dem sich BFFH verbindet.

Die Adresse hat das Format `<span class="pre"><protocol>://[user]:[password]@<server>:[port]</span>`

<div class="document" id="bkmrk-protocol-is-required" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section"><div class="section">- `<span class="pre">protocol</span>` (notwendig) - kann eins der folgenden Werte annehmen: `<span class="pre">mqtt</span>`,`<span class="pre">tcp</span>`,`<span class="pre">mqtts</span>`,`<span class="pre">ssl</span>`
- `<span class="pre">user</span>` (optional)
- `<span class="pre">password</span>` (optional)
- `<span class="pre">server</span>` (notwendig) - kann IP-Adresse oder Hostname sein
- `<span class="pre">port</span>` (optional) - Der Standardport ist `1883`

</div></div></div></div></div>**Beispiele für mqtt\_url:**

```ini
mqtt_url = "tcp://localhost:1883" 
```

```ini
mqtt_url = "mqtts://user:password@server.tld:port"
```

<p class="callout warning">**Achtung:** Der MQTT-Server muss laufen und erreichbar sein, sonst startet BFFH nicht. Wird die MQTT-Verbindung getrennt, während BFFH läuft, stürzt BFFH nicht ab und protokolliert stattdessen die Verbindungsfehler in die Log-Ausgabe.</p>

#### `certfile`::String und `keyfile`::String


Unsere Kommunikation über [Cap'n Proto](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/capn-proto-api "Cap'n Proto API") erfolgt grundsätzlich verschlüsselt. Dafür benötigen wir ein [TLS](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-tls)-Zertifikat. Details, wie man ein TLS-Zertifikat generiert: [Server - Anleitung zum selber kompilieren](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren#bkmrk-zertifikat-erzeugen--1 "Server - Anleitung zum selber kompilieren").

BFFH benötigt ein [PEM](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-pem-%28privacy-enhance)-kodiertes Zertifikat und den zugehörigen Schlüssel als zwei separate Dateien. BFFH verwendet TLS standardmäßig und ausschließlich in Version [1.2 bzw 1.3](https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html#method.with_safe_default_protocol_versions). Dabei implementiert es die Rust-Bibliothek [*rustls*](https://docs.rs/rustls/latest/rustls/) im [Code](https://gitlab.com/fabinfra/fabaccess/bffh/-/blob/development/bffhd/tls.rs?ref_type=heads).

**Beispiel für certfile und keyfile:**

```ini
certfile = "/etc/bffh/certs/bffh.crt"
```

```ini
keyfile = "/etc/bffh/certs/bffh.key"
```

<div class="document" id="bkmrk--3" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section"><div class="section" id="bkmrk--4"></div></div></div></div></div>#### `ciphers`::String (optional)

Es kann eine [Cipher Suite](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-cipher-suite) vorgegeben werden, die genutzt werden soll.

<p class="callout info">Wir empfehlen diesen Wert nicht zu vergeben und beim Standard zu belassen (standardmäßig wird diese Option in bffh.dhall nicht gesetzt).</p>

<p class="callout warning">Die Definition erfolgt als String, nicht als List und kann deshalb aktuell nur eine einzelne Cipher Suite enthalten. Siehe [Issue 109](https://gitlab.com/fabinfra/fabaccess/bffh/-/issues/109).</p>

**Beispiel für ciphers:**

```ini
ciphers = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
```

[Mögliche Cipher Suites](https://gitlab.com/fabinfra/fabaccess/bffh/-/blob/development/bffhd/tls.rs?ref_type=heads#L18) sind:

- TLS v1.2 + TLS v1.3 
    - [`TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`](https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256/)
    - [`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`](https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384/)
    - [`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`](https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256/)
    - [`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`](https://ciphersuite.info/cs/TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256/)
    - [`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`](https://ciphersuite.info/cs/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384/)
    - [`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`](https://ciphersuite.info/cs/TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256/)
- TLS v.1.3 only 
    - [`TLS13_AES_128_GCM_SHA256`](https://ciphersuite.info/cs/TLS_AES_128_GCM_SHA256/)
    - [`TLS13_AES_256_GCM_SHA384`](https://ciphersuite.info/cs/TLS_AES_256_GCM_SHA384/)
    - [`TLS13_CHACHA20_POLY1305_SHA256`](https://ciphersuite.info/cs/TLS_CHACHA20_POLY1305_SHA256/)

#### `tls_min_version`::String (optional)

Die [TLS](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-tls) Version von BFFH kann erzwungen werden.

<p class="callout info">Wir empfehlen diesen Wert nicht zu vergeben und beim Standard zu belassen (standardmäßig wird diese Option in bffh.dhall nicht gesetzt).</p>

Mögliche Werte:

- `tls12` für TLS v1.2
- `tls13` für TLS v1.3

**Beispiel für tls\_min\_version:**

```ini
tls_min_version = "tls12"
```

#### `<span class="pre">protocols</span>`::List (optional)

Gibt an, welche Protokolle durch BFFH genutzt werden dürfen.

<p class="callout danger">Diese Einstellung kann gesetzt werden, hat aber keine Auswirkungen und wird im Code aktuell nicht genutzt. Sie wird nur zur Vollständigkeit hier gelistet. Es ist nicht bekannt, welche Protokolle unterstützt werden sollen!</p>

**Beispiel für protocols:**

```ini
protocols = ["tcp"],
```

#### `<span class="pre">db_path</span>`::String


Enthält den Pfad für die [interne Datenbank auf LMDB-Basis](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/datenbank-konzept-lmdb "Datenbank-Konzept (LMDB)"), die BFFH verwendet. BFFH wird beim Start zwei Dateien erstellen, falls nicht bereits existent: `/var/lib/bffh/bffh.db` und `/var/lib/bffh/bffh.db-lock`. BFFH erstellt selber keine fehlenden Verzeichnisse. Es sollte sichergestellt werden, dass BFFH Schreibzugriff auf das entsprechende Verzeichnis hat.

Die interne Datenbank wird im Produktivbetrieb nicht riesig (nur einige Kilobyte bis wenige Megabyte). In ihr werden die aktuellen Ressourcenzustände (states) gespeichert (welche Ressource ist z.B. gerade durch einen Nutzer `INUSE`) und welche Nutzer mit Rollen, Passwörtern und Cardkeys zugreifen dürfen. Diese interne Datenbank kann exportiert werden (Benutzerdaten können als `*.toml` Datei ausgegeben werden oder die ganze Datenbank inklusive Zuständen kann exportiert werden). Wir empfehlen die Ablage im Standardverzeichnis `/var/lib/bffh/`.

**Beispiel für db\_path:**

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

<div class="document" id="bkmrk--5" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section" id="bkmrk--6"><div class="section" id="bkmrk--7"></div></div></div></div></div><p class="callout warning">**BFFH Datenbank "Werkszustand" wiederherstellen:**  
  
Die Datenbankdatei und die Lock-Datei können prinizipiell nach Beenden des Dienstes gelöscht werden, jedoch gehen dabei die gespeicherten aktuellen Ressourcenzustände und Nutzer verloren. Die Nutzer können über eine **aktuelle** `<a href="https://docs.fab-access.org/books/fabaccess-konfiguration/page/benutzerkonfiguration-userstoml" title="Benutzerkonfiguration - users.toml">users.toml</a>` wieder importiert werden. Dass die Ressourcenzustände verloren gehen, ist je nach Werkstattsetup u.U. einfach zu verkraften. Entsprechend kann damit ein Reset durchgeführt werden. Ggf. sollten alte Audit-Einträge (siehe `auditlog_path`) ebenso bereinigt werden.  
</p>

<p class="callout warning">**Vor jedem Update / Recompile unbedingt beachten:** Die Datenbank speichert ihre Daten nicht im Klartext. Die als Datei erzeugte Datenbank `bffh.db` ist abhängig vom System, wo sie angelegt wurde. Jeder Recompile oder Umzug auf einen anderen Host bzw. Architektur macht die Datenbank unbrauchbar. Deshalb muss auch vor jedem Update eine Sicherung erstellt werden, die jedoch re-importiert werden kann.  
  
**Bei Nichtbeachtung droht Verlust der angelegten Benutzerdatenbank und den gespeicherten Ressourcenzuständen.**</p>

<p class="callout info">Die automatisch beim Start erstellte Lock-Datei `/var/lib/bffh/bffh.db-lock` wird nach dem Beenden von BFFH nicht automatisch gelöscht, kann aber manuell immer dann entfernt werden, so lange BFFH nicht läuft. Während des Betriebs sollte die Lock-Datei nicht gelöscht werden, da es sonst zu Korruption in Lese- und Schreibprozessen kommen kann.</p>

#### `auditlog_path`::String


Pfadangabe zur Auditdatei, die BFFH im laufenden Prozess schreibt. Die Ausgabe erfolgt im [JSON](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-json-%28javascript-obj)-Format. Siehe [Audit Log (Revisionsprotokoll)](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/audit-log-revisionsprotokoll "Audit Log (Revisionsprotokoll)") für Details zum Audit. Die Datei kann zum Beispiel mit Tools wie [Grafana Loki](https://docs.fab-access.org/books/schnittstellen-und-apis/page/monitoring-prometheus-loki-und-grafana "Monitoring: Prometheus, Loki und Grafana") 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/`.

**Beispiel für auditlog\_path:**

```ini
auditlog_path = "/var/log/bffh/audit.json"
```

<p class="callout warning">Falls BFFH wegen einem Log-Berechtigungsfehler nicht startet, kann folgender Trick angewendet werden.  
</p>

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

Lösung: die fehlende Datei neu und leer erzeugen und manuell die passende Berechtigungen setzen:

```bash
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](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-acl-%28access-control-) angelegt werden. Hierzu gibt es ein nettes Tool unter [https://techgeeks.io/unix/acl-generator](https://techgeeks.io/unix/acl-generator).

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

### Konfiguration von Rollen, Berechtigungen und Ressourcen  


FabAccess verwendet eine Role-Based Access Control (RBAC)-Struktur zur Verwaltung von Berechtigungen. Dabei werden Berechtigungen Ressourcenrollen zugewiesen, und diese Rollen werden dann den Benutzern zugewiesen. Auf diese Weise lässt sich ein komplexes Berechtigungssystem einfach, ufassend und flexibel abbilden. Details finden sich unter [RBAC (Benutzerrollen und Berechtigungen)](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/rbac-benutzerrollen-und-berechtigungen "RBAC (Benutzerrollen und Berechtigungen)").

#### `<span class="pre">roles</span>`::Record  


Enthält die Einträge der definierten Rollen. Rollen haben eine Liste von Berechtigungen und können vererbt werden. Die Berechtigung kann ein Platzhalter in der Berechtigungsliste sein. Jede Rolle **muss** eine `Role-ID` haben. Unterhalb des Eintrags `roles` schachteln sich Listen vom Typ `parents`::List (optional) und vom Typ `permissions`::List (notwendig).

#### Standardberechtigungen

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

- `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)

**Beispiel für eine Admin-Rolle mit Standardberechtigungen:**

```ini
roles = {
    Admin = {
       permissions =  [
               "bffh.users.manage",
               "bffh.users.info",
               "bffh.users.admin"
             ]
        }
    }
```

#### Modellieren von Berechtigungen für Ressourcen  


**Allgemeines Schema**  
`space.type.category.permission.model`

**Administrator**  
`space.machines.printers.*`

**Offene Berechtigung**  
`space.machines.printers.read.*`

**Allgemeine Erläuterungen zum Pfadformat und Platzhaltern (`*`, `+`)** 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 Ressourcen, 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](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-wildcard) verhalten sich ähnlich wie Regex- oder Bash-Wildcards:

<div class="document" id="bkmrk-%2A-grants-all-permiss" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section">- `*` gewährt alle Berechtigungen in diesem Teilbaum. So wird `perms.read.*` für jedes von passen:  
    
    - `<span class="pre">perms.read</span>`
    - `<span class="pre">perms.read.machineA</span>`
    - `<span class="pre">perms.read.machineB</span>`
    - `<span class="pre">perms.read.machineC.manage</span>`
- `+` gewährt alle Berechtigungen unter des Wertes. So wird `perms.read.+` für jedes von passen:  
    
    - `<span class="pre">perms.read.machineA</span>`
    - `<span class="pre">perms.read.machineB</span>`
    - `<span class="pre">perms.read.machineC.manage</span>`
    - **aber nicht** `<span class="pre">perms.read</span>`

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

<div class="document" id="bkmrk-write-permissions-ma" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section">1. Write (schreiben) Berechtigungen  
    
    - `<span class="pre">machines.printers.write.prusa.sl1</span>`
    - `<span class="pre">machines.printers.write.prusa.i3</span>`
    - `<span class="pre">machines.printers.write.anycubic</span>`
    - `<span class="pre">machines.bandsaws.write.bandsaw1</span>`
2. Manage (verwalten) Berechtigungen 
    - `<span class="pre">machines.printers.manage.prusa.sl1</span>`
    - `<span class="pre">machines.printers.manage.prusa.i3</span>`
    - `<span class="pre">machines.printers.manage.anycubic</span>`
    - `<span class="pre">machines.bandsaws.manage.bandsaw1</span>`
3. Admin Berechtigungen  
    
    - `<span class="pre">machines.printers</span>`
        - Für alle Drucker
    - `<span class="pre">machines.bandsaws</span>`
        - Für alle Bandsägen

</div></div></div></div>Dann erteilen wir den Rollen die entsprechenden Rechte:

<div class="document" id="bkmrk-use-any-3d-printer%3A-" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section">- Nutze beliebige 3D-Drucker: 
    - `<span class="pre">machines.printers.write.+</span>`
- Erlaube nur die Nutzung "billiger" Drucker: 
    - `<span class="pre">machines.printers.write.anycubic.*</span>`
    - `<span class="pre">machines.printers.write.prusa.i3</span>`
- Erlaube das Verwalten der Drucker: 
    - `<span class="pre">machines.printers.+</span>`
- Erlaubte das Administrieren aller Drucker: 
    - `<span class="pre">machines.printers.*</span>`

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

<div class="document" id="bkmrk-machines.printers.wr" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section">- `<span class="pre">machines.printers.write.anycubic.i3</span>`
- `<span class="pre">machines.printers.write.anycubic.megax</span>`

</div></div></div></div>**Beispiel für verschiedene Rollen mit Berechtigungen:**

- **Wildcard-Rolle `testrole`**
- **"normale" Rolle `anotherrole`**
- **Rolle `somerole` mit vererbter Rolle `testparent`:**

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

<div class="document" id="bkmrk--14" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section" id="bkmrk--15"><div class="section" id="bkmrk--16"></div></div></div></div></div>#### `<span class="pre">machines</span>`::Record

<p class="callout info">**Info:** Der Record `machines` wird in Zukunft `resources` heißen!</p>


Enthält die Einträge der definierten Ressourcen. Jede Ressource **muss** eine `Resource-ID` haben, um in anderen Teilen dieser Konfiguration oder über die API auf die Ressource verweisen zu können. Und jede Ressource **muss** einen Namen haben. In FabAccess ist die Ressource ein wichtiger Schlüsselbegriff und steht für Dinge wie Türen, Schließfächer, Geräte oder Maschine. Kurzum: Dinge, die sich ein- und ausschalten lassen sollen.

##### Allgemeine Ressourceninformationen

Um weitere Informationen über die Ressource bereitzustellen, können Namen, Kategorien, Beschreibungen oder ein externer Wiki-Link eingefügt werden:

- `name`::String (notwendig) - ein Klarname, zum Beispiel ein Spitzname für eine Maschine oder eine Modellbezeichnung (Unicode-Support)
- `description`::String (optional) - eine allgemeine, kurze Beschreibung der Ressource (Unicode-Support)
- `wiki`::String (optional) - Link zu einer Wiki- bzw. Dokumentationsseite
- `category`::String (optional) - eine beliebige Kategorie, z.B. "Holzwerkstatt" oder "Handgerät, elektrisch"(Unicode-Support)
- [`prodable`](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/zustande-traits "Zustände (Traits)")::Boolean (optional) - definiert, ob die Ressource ein Türschloss ist - zum Beispiel an einem Werkzeugschrank, Schließfach ode Eingangstür (ab `v0.4.4`). Dieses Attribut schaltet im [Borepin Client](https://docs.fab-access.org/books/fabaccess-konfiguration/page/client-benutzen-und-typische-konfigurationsfehler-bei-server-und-clients#bkmrk-eine-ressource-bedie) zwei Buttons frei, die sonst nicht sichtbar sind:  
    
    - `UNLOCK` öffnet bzw. entsperrt die Ressource, ohne sie für andere zu blockieren und ohne sie zurückgeben zu müssen
    - `IDENTIFY` löst eine einfache Aktion aus wie z.B. das Aufleuchten einer LED an der Ressource, um das Fach bzw. die Ressource optisch leichter zu identifizieren

**Was heißt Unicode-Support?** Durch diese Eigenschaft können wir neben einfachen Bezeichnungen auch beliebige Sonderzeichen einfügen. Das erlaubt unter anderem die Verwendung von Emojis, die innerhalb der Client-Anwendung visuell unterstützen können. Dafür kann ein beliebiger Emoji-Picker genutzt werden, zum Beispiel [https://www.freetool.dev/emoji-picker.](https://www.freetool.dev/emoji-picker) D.h. wir können z.B.  
`description = "Saug dir Energie ;-) 🪫",` konfigurieren. Das sieht dann in Borepin so aus:

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2025-01/scaled-1680-/6OU48ttMqxzUuJ9s-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2025-01/6OU48ttMqxzUuJ9s-grafik.png)

##### Zugewiesene Berechtigungen für Ressourcen  


Die Ressourcen haben verschiedene Berechtigungsstufen, mit denen interagiert werden kann und welche sich in den Records in `roles` wiederspiegeln:

<div class="document" id="bkmrk-disclose%3A%3Astring-%28no" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section"><div class="section">- `disclose`::String (notwendig) - Offenlegen: Der Benutzer kann die Ressource in der Übersichtsliste sehen (entdecken), jedoch keine Details zu ihr aufrufen (Detailansicht erfordert `read` Berechtigung)
- `read`::String (notwendig) - Lesen: Der Benutzer kann allgemeine Informationen über die Ressource und ihren Zustand lesen, sowie z.B. den Wiki Link anklicken
- `write`::String (notwendig) - Schreiben: Der Benutzer kann die Ressource bedienen und dabei folgende [Zustände](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/zustande-traits "Zustände (Traits)") verändern: 
    - Benutzen (`InUse`) bzw. Zurückgeben (`Free`, auch als GIVEBACK in der Client App bezeichnet)
    - Reservieren (`Reserve`)
- `manage`::String (notwendig) - Verwalten: Der Benutzer kann als Manager sehen, wer die Maschine aktuell benutzt und wer sie zuletzt benutzt hat. Außerdem können Mananger mit dem System interagieren und dabei folgende [Zustände](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/zustande-traits "Zustände (Traits)") verändern: 
    - Freigeben erzwingen (force `Free`)
    - Blockieren (`Blocked`)
    - Deaktivieren (`Disabled`)
    - Überprüfen (`ToCheck`)
    - Transferieren (`totakeover`)

</div></div></div></div></div><p class="callout info">**Tipp**: Wenn einer Ressource die `read` Berechtigung, aber nicht `disclose` Berechtigung gegeben wird, dann taucht die Ressource nicht in der Übersichtsliste auf. Sie kann auf diese Weise nur noch direkt angesprochen werden, indem ein QR-Code oder ein NFC-Tag gescannt wird. So lässt sich erzwingen, dass ein Nutzer zur Maschine lokal hingehen muss und die Ressource nicht von weiter weg bedienen kann (es sei denn der Benutzer klont den QR-Code oder NFC-Tag und nimmt ihn mit).</p>

**Bitte beachten:**

- Jeder Ressource **müssen alle** Berechtigungsstufen zugewiesen sein.
- Berechtigungsstufen sind nicht additiv, d.h. ein Benutzer mit der Berechtigung `manage` erhält nicht automatisch `read` oder `write` Berechtigung.
- `disclose` ist im Moment **nicht** vollständig implementiert. Benutzer, die keine `disclose` Berechtigung haben, werden in keiner Weise über diese Ressource informiert und sie wird vor ihnen im Client verborgen. Es ist deshalb am sinnvollsten, wenn `read` und `disclose` die gleiche Berechtigung zugewiesen bekommen, zum Beispiel `disclose = "lab.test.read"`.
- Benutzern ohne `read` wird eine Resource mit Namen, Beschreibung, Kategorie und Wiki angezeigt, aber nicht ihr aktueller Zustand. Der aktuelle Benutzer der Ressource wird nicht offengelegt.

**Beispiel einer Ressource mit allen Parametern:**

```ini
machines =  { 
    machine_123 =  { 
        name = "Testmachine",
        description = Some "A test machine",
        wiki = "https://someurl",
        category = "Testzone",
        prodable = True,
        disclose = "lab.some.read",
        read = "lab.some.read",
        write = "lab.some.write",
        manage = "lab.some.admin"
        }
    }
```

Im Beispiel heißt unser `Resource-ID` `machine_123`.

<div class="document" id="bkmrk--10" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div class="section"><div class="section" id="bkmrk--11"><div class="section" id="bkmrk--12"><div class="section" id="bkmrk--13"></div></div></div></div></div></div>
### Konfiguration von [Aktoren](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/aktoren-actors-und-initiatoren-initiators "Aktoren (Actors) und Initiatoren (Initiators)") (actors)  


#### `<span class="pre">actors</span>`::Record

Enthält eine Liste von Aktoren. Aktoren werden durch ein Modul `module`::String (notwendig) und einen oder mehrere Parameter `params`::Record definiert. Aktuell **von Haus aus unterstützte Aktoren** (ohne zusätzliche Plugins) sind [Dummy Actor](#bkmrk-dummy-initiator), [Shelly Actor](#bkmrk-shelly-actor-1) und [Process Actor](#bkmrk-process-actor).

Konkret nutzbare Aktoren-Beispiele finden sich [hier](https://docs.fab-access.org/books/plugins-aktoren-initiatoren).

Aktoren werden durch ein Modul und einen oder mehrere Parameter definiert. Die Liste kann bzw. **muss** leer sein, wenn keine Aktoren verwendet werden:

**Beispiel für leere actors:**

```ini
actors = {=}
```

Sonst kann die Liste einen oder mehrere Aktoren mit ihrem Name (`Actor-ID`) enthalten:

##### 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.

**Beispiel für Dummy Actor:**

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


Im Beispiel heißt unser `Actor-ID` `Dummy_123`.

##### Shelly Actor

Dieser Aktor verbindet BFFH über einen MQTT-Server mit einem Shelly Gerät (dafür wird intern die MQTT-Bibliothek [rumqttc](https://docs.rs/rumqttc/latest/rumqttc/) verwendet). Der Parameter `topic`::String (optional) des Shelly kann auf das Shelly-spezifische MQTT-Topic gesetzt werden. Wird kein `topic` definiert, dann verwendet der Actor automatisch die definierte `Actor-ID`.

[Anleitung zum Auffinden des Shelly Topic](https://shelly-api-docs.shelly.cloud/gen1/#shelly-plug-plugs-overview)

**Beispiel für Shelly Actor:**

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

Im Beispiel heißt unser `Actor-ID` `Shelly_123`.

##### Process Actor

Dieser Aktor ermöglicht es, eigene Prozesse (z.B. Python, Bash, Perl, Java ...) mit dem BFFH Server zu verbinden.

`<span class="pre">cmd</span>`::String (notwendig) - Pfad der ausführbaren Datei  
`<span class="pre">args</span>`::String (optional) - Argumente der ausführbaren Datei. Hier übergebene Argumente werden durch Leerzeichen getrennt, so dass in unserem Beispiel 5 separate Argumente übergeben werden.

**Beispiel für Process Actor:**

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

Im Beispiel heißt unser `Actor-ID` `Bash_123`.

<p class="callout info">**Wichtige Info:** BFFH baut aus den Argumenten `cmd` und `args` einen vollständigen Befehl, der ausgeführt wird und fügt automatisch zwei weitere Argumente am Ende hinzu - nämlich den zu erzielenden Status `state` (z.B. `INUSE` oder `GIVEBACK`) und den Benutzername `userid`. Der vollständige Befehl, der als Prozess ausgeführt wird, lautet folglich `$ ${cmd} ${args} ${state} ${userid}`.  
</p>

#### `<span class="pre">actor_connections</span>`::List  


Verknüpfung (Mapping) von Ressourcen mit Aktoren. Einer Ressource können mehrere Aktoren zugewiesen werden, aber ein Aktor kann nur einer Ressource zugewiesen werden.

Das Mapping `<span class="pre">actor_connections</span>` wird als Liste von Einträgen gespeichert. Die Parameter:

- `machine`::String (notwendig) - die `Resource-ID`
- `actor`::String (notwendig) - die `Actor-ID`

<p class="callout warning">**Achtung:** Die Reihenfolge ist wichtig. An erster Stelle kommt `machine`, dann `actor`!  
  
Außerdem darf die Definition `actor_connections`::List in `bffh.dhall` erst **nach** `actors`::Record und **nach** `machines`::Record kommen!</p>

**Beispiel für leere actor\_connections:**

```ini
actor_connections = [] : List { machine : Text, initiator : Text },
```

**Beispiel für init\_connections mit drei Mappings:**

```ini
actor_connections = [
    { machine = "Testmachine", actor = "Shelly_123" },
    { machine = "Another", actor = "Bash_123" },
    { machine = "Yetmore", actor = "Bash_234" } ]
```

### Konfiguration von [Initiatoren](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/aktoren-actors-und-initiatoren-initiators "Aktoren (Actors) und Initiatoren (Initiators)") (initiators)

Initiatoren werden fast genauso konfiguriert wie Aktoren. Es gibt aktuell die beiden Initiatorentypen Dummy Initiator und Process Initiator. Konkret nutzbare Initiatoren-Beispiele finden sich [hier](https://docs.fab-access.org/books/plugins-aktoren-initiatoren).

#### `initiators`::Record


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:

**Beispiel für leere initiators:**

```ini
initiators = {=}
```

Sonst kann die Liste einen oder mehrere Initiatoren mit ihrem Name (`Initiator-ID`) 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.

Die Initiator-Parameter:

`<span class="pre">uid</span>`::String (notwendig) - Nutzername

**Beispiel für Dummy Initiator:**

```ini
initiators = { 
   Initiator_123 = { 
          module = "Dummy", 
          params = { 
              uid = "Testuser" 
              } 
          } 
        }
```

##### Process Initiator

Dieser Initiator vom `module`::String Typ **Process** ermöglicht es, eigene Prozesse (z.B. Python, Bash, Perl, Java ...) mit dem BFFH Server zu verbinden.

Das Mapping `<span class="pre">initiator_connections</span>` wird als Liste von Einträgen gespeichert. Die Parameter:

`<span class="pre">cmd</span>`::String (notwendig) - Pfad der ausführbaren Datei  
`<span class="pre">args</span>`::String (optional) - Argumente der ausführbaren Datei. Hier übergebene Argumente werden durch Leerzeichen getrennt, so dass in unserem Beispiel 5 separate Argumente übergeben werden.

**Beispiel für Process Initiator:**

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


Im Beispiel heißt unser `Initiator-ID` `Bash_567`.

<p class="callout info">**Wichtige Info:** BFFH baut aus den Argumenten `cmd` und `args` einen vollständigen Befehl, der ausgeführt wird und fügt automatisch zwei weitere Argumente am Ende hinzu - nämlich den zu erzielenden Status `state` (z.B. `INUSE` oder `GIVEBACK`) und den Benutzername `userid`. Der vollständige Befehl, der als Prozess ausgeführt wird, lautet folglich `$ ${cmd} ${args} ${state} ${userid}`.</p>

#### `init_connections`::List

Verknüpfung (Mapping) von Ressourcen mit Initiatoren. Einer Ressource können mehrere Initiatoren zugewiesen werden, aber ein Initiator kann nur einer Ressource zugewiesen werden.

Das Mapping `<span class="pre">init_connections</span>` wird als Liste von Einträgen gespeichert. Die Parameter:

- `initiator`::String (notwendig) - die `Initiator-ID`
- `actor`::String (notwendig) - die `Actor-ID`

<p class="callout warning">**Achtung:** Die Reihenfolge ist wichtig. An erster Stelle kommt `machine`, dann `initiator`!  
  
Außerdem darf die Definition `init_connections`::List in `bffh.dhall` erst **nach** `initiators`::Record und **nach** `machines`::Record kommen!  
</p>

**Beispiel für leere init\_connections:**

```ini
init_connections = [] : List { machine : Text, initiator : Text }
```

**Beispiel für init\_connections mit einzelnen Mapping:**

```ini
init_connections = [
    { machine = "Testmachine", initiator = "Initiator_123" }
    ]
```

### [FabFire](https://docs.fab-access.org/books/fabfire-und-nfc-tags "NFC Key Cards und Transponder (FabCard / FabFire)") und Föderation

#### `spacename`::String

Der Name des Spaces (die offene Werkstatt, das FabLab, der HackerSpace, etc.) wird im [URN](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/url-und-urn "URL und 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](https://docs.fab-access.org/books/beschriftungen-und-sticker/page/fabaccess-sticker-aufkleber "FabAccess Sticker (Aufkleber)") von Ressourcen oder für DESFire Karten zur Nutzung von [FabFire](https://docs.fab-access.org/books/fabfire-und-nfc-tags "NFC Key Cards und Transponder (FabCard / FabFire)").

**Beispiel für spacename:**

```ini
spacename = "FabAccess DemoSpace"
```

#### `instanceurl`::String

Wird für eine allgemeine Space Info genutzt und als [URN](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/url-und-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! Es wird empfohlen die URL mit dem Protokoll vollständig anzugeben, also mit `http://` oder `https://`.

**Beispiel für instanceurl:**

```ini
instanceurl = "https://demo.fab-access.org"
```

### Hinweise zu unkonfigurierbaren Variablen

Folgende Variablen werden intern von BFFH gesetzt und können aktuell nicht über die bffh.dhall verändert werden. Wir führen sie nur zur Vollständigkeit auf:

- `verbosity`
- `filter`
- `format`

### Hinweise zur Vergabe von IDs

Wir verwenden in der Konfiguration IDs wie `Resource-ID`, `Actor-ID`, `Initiator-ID` oder `Role-ID`.

Diese IDs müssen folgendes Namensschema einhalten:

- ASCII Format
- Alphanumerisch
- muss mit einem Buchstaben beginnen

<p class="callout info">Für die Vergabe von IDs ist die Groß- und Kleinschreibung zu beachten! Wir empfehlen generell die Kleinschreibung. So ist zum Beispiel eine Rolle namens `RoleAdmin` ungleich `roleadmin`.  
Wir empfehlens außerdem ein konsisentes, leicht nachvollziehbares (logisches) Namenskonzept über alle ID-Typen, wo immer es möglich ist.  
</p>

<p class="callout danger">IDs müssen eindeutig bzw. einzigartig sein. IDs dürfen also nicht doppelt vergeben werden!</p>

### Minimal funktionierende Standardkonfiguration

Wie bereits eingangs erwähnt, werden [alle obigen Einstellungen](#bkmrk-%C3%9Cbersicht-%C3%BCber-alle-) für die Konfiguration benötigt. Für die absolut minimale Startfähigkeit eines BFFH Servers kann folgendes Konfigurationssample verwendet werden.

Vorraussetzungen für die Lauffähigkeit von BFFH:

- Dienst muss sich auf Interface:Port binden lassen
- MQTT-Server muss laufen und erreichbar sein (siehe [mqtt\_url::String](#bkmrk-mqtt_url)).
- Zertifikat und Keyfile müssen existieren
- Datenbankdatei und Auditlog müssen schreibbar sein

```ini
{
spacename = "fabaccess.sample.space",
instanceurl = "https://fabaccess.sample.space", 
listens = [{address = "127.0.0.1"}],
certfile = "/etc/bffh/certs/bffh.crt",
keyfile = "/etc/bffh/certs/bffh.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 }
}
```

Ein ebenso funktionales und umfangreicheres Standardsample kann auch per `bffhd --print-default` ausgegeben werden (siehe [Cheat Sheet](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht "Cheat Sheet - Wichtigste Befehle (Übersicht)")).

### Konfiguration per Config Generator

Ein Großteil der Konfiguration (Ressourcen, Berechtigungen, Aktoren, Aktorenverbindungen, etc.) kann mit einem generischen Arbeitswerkzeug effizient und übersichtlich erzeugt und verwaltet werden. Siehe [Einfache Konfiguration mit dem FabAccess Config Generator](https://docs.fab-access.org/books/fabaccess-konfiguration/page/einfache-konfiguration-mit-dem-fabaccess-config-generator "Einfache Konfiguration mit dem FabAccess Config Generator").

### Konfiguration von BFFH auf Synax und Inhalt prüfen

Konfiguration erstellt, aber unsicher, ob sie vollständig und syntaktisch korrekt ist?

Wir können das per `/usr/bin/bffhd --check --config /etc/bffh/bffh.dhall` überprüfen. Siehe [hier](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht#bkmrk-konfigurationsdatei-). 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). Etwaige Fehlkonfigurationen werden auch erkannt, da geprüft wird, ob wichtige Konfigurationschlüssel fehlen, unvollständig oder falsch sind.

<p class="callout info">Siehe auch [Dhall-Dateien parsen, prüfen und formatieren](#bkmrk-dhall-dateien-parsen) für die reine Überprüfung des Syntax und das automatische Umformatieren (Prettyprint).</p>

Ein letzter Blick in die von BFFH geladene Config kann auch über unsere systemd Journaleinträge gemacht werden, falls wir [BFFH als Service installiert](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren#bkmrk-systemd-service-anle) haben, oder aber über über die Log-Umgebungsvariable `BFFH_LOG`. Wir sehen damit, wie BFFH die Dhall nach dem Parsen vollständig auflöst und dass Log-Variablen beispielsweise auch gesetzt werden, welche jedoch nicht über `bffh.dhall` konfigurierbar sind, sondern über [Umgebungsvariablen](https://docs.fab-access.org/books/fabaccess-konfiguration/page/server-logs-konfigurieren-und-debugging "Server Logs konfigurieren").

```bash
sudo systemctl restart bffh.service; sudo journalctl -n2000 -e -u bffh.service
```

```bash
Jan 02 22:31:07 fabaccess systemd[1]: Starting bffh.service - FabAccess BFFH Service...
Jan 02 22:31:07 fabaccess bffhd[34844]: Config {
Jan 02 22:31:07 fabaccess bffhd[34844]:     listens: [
Jan 02 22:31:07 fabaccess bffhd[34844]:         Listen {
Jan 02 22:31:07 fabaccess bffhd[34844]:             address: "0.0.0.0",
Jan 02 22:31:07 fabaccess bffhd[34844]:             port: Some(
Jan 02 22:31:07 fabaccess bffhd[34844]:                 59661,
Jan 02 22:31:07 fabaccess bffhd[34844]:             ),
Jan 02 22:31:07 fabaccess bffhd[34844]:         },
Jan 02 22:31:07 fabaccess bffhd[34844]:     ],
Jan 02 22:31:07 fabaccess bffhd[34844]:     machines: {},
Jan 02 22:31:07 fabaccess bffhd[34844]:     actors: {},
Jan 02 22:31:07 fabaccess bffhd[34844]:     initiators: {},
Jan 02 22:31:07 fabaccess bffhd[34844]:     mqtt_url: "mqtt://fabinfra101:fablocal@0.0.0.0:1883",
Jan 02 22:31:07 fabaccess bffhd[34844]:     actor_connections: [],
Jan 02 22:31:07 fabaccess bffhd[34844]:     init_connections: [],
Jan 02 22:31:07 fabaccess bffhd[34844]:     db_path: "/var/lib/bffh/bffh.db",
Jan 02 22:31:07 fabaccess bffhd[34844]:     auditlog_path: "/var/log/bffh/audit.json",
Jan 02 22:31:07 fabaccess bffhd[34844]:     roles: {},
Jan 02 22:31:07 fabaccess bffhd[34844]:     tlsconfig: TlsListen {
Jan 02 22:31:07 fabaccess bffhd[34844]:         certfile: "/etc/bffh/certs/bffh.crt",
Jan 02 22:31:07 fabaccess bffhd[34844]:         keyfile: "/etc/bffh/certs/bffh.key",
Jan 02 22:31:07 fabaccess bffhd[34844]:         ciphers: None,
Jan 02 22:31:07 fabaccess bffhd[34844]:         tls_min_version: None,
Jan 02 22:31:07 fabaccess bffhd[34844]:         protocols: [],
Jan 02 22:31:07 fabaccess bffhd[34844]:     },
Jan 02 22:31:07 fabaccess bffhd[34844]:     tlskeylog: None,
Jan 02 22:31:07 fabaccess bffhd[34844]:     verbosity: 0,
Jan 02 22:31:07 fabaccess bffhd[34844]:     logging: LogConfig {
Jan 02 22:31:07 fabaccess bffhd[34844]:         filter: None,
Jan 02 22:31:07 fabaccess bffhd[34844]:         format: "full",
Jan 02 22:31:07 fabaccess bffhd[34844]:     },
Jan 02 22:31:07 fabaccess bffhd[34844]:     spacename: "FabAccess Demo Setup",
Jan 02 22:31:07 fabaccess bffhd[34844]:     instanceurl: "https://docs.fab-access.org",
Jan 02 22:31:07 fabaccess bffhd[34844]: }
Jan 02 22:31:07 fabaccess systemd[1]: Started bffh.service - FabAccess BFFH Service.
```

# Einfache Konfiguration mit dem FabAccess Config Generator

Wolfram vom [MakerSpace Leipzig](https://docs.fab-access.org/books/makerspace-leipzig "MakerSpace Leipzig") hat 2024 ein umfangreiches Tool - den [FabAccess Config Generator](https://github.com/elem74/fabaccess-config-generator) - entwickelt, um eine Konfiguration (`bffh.dhall`) anhand einer Maschinenliste im CSV-Format zu erzeugen.

## Funktionsumfang[<svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>](https://github.com/elem74/fabaccess-config-generator#funktionsumfang)

- Generierung von Ressourcen (Maschinen)
- Generierung von [Rollen](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/rbac-benutzerrollen-und-berechtigungen "RBAC (Benutzerrollen und Berechtigungen)")
- Generierung von [Aktoren](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/aktoren-actors-und-initiatoren-initiators "Aktoren (Actors) und Initiatoren (Initiators)")
- Generierung von einfachen Aktoren-Verbindungen
- Export einer gesonderten Rollenliste (interne ID &amp; Anzeigename der Rolle)
- Abbildung der Struktur mittels [Mermaid](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-mermaid)-Diagramm
- Direkte Aktualisierung einer bestehenden `bffh.dhall`
- [Modulare dhall-Erzeugung](https://docs.fab-access.org/Siehe%20https:/github.com/elem74/fabaccess-config-generator/issues/1) (`rolls.dhall`, `actors.dhall`, `actorconnections.dhall` und `machines.dhall`) lassen sich in der `bffh.dhall` referenzieren und verbessern die Übersicht.

## Funktionsweise[<svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>](https://github.com/elem74/fabaccess-config-generator#funktionsweise)

Die CSV-Datei enthält pro Zeile einen Eintrag für eine Maschine. Neben Angaben zur Maschine selbst (ID, Wiki-URL etc.) werden Angaben zum Bereich vermerkt, in dem sich die Maschine befindet. Aus den Angaben zu den Bereichen werden dann alle Rollen abgeleitet.

## Vollständige Dokumentation

Hier ist nur eine kurze Erklärung über das HowTo abgebildet. Eine umfangreiche und aktuelle Dokumentation findest du hier: [https://elem74.github.io/fabaccess-config-generator-docs](https://elem74.github.io/fabaccess-config-generator-docs)

## Installation

Das Tool kann direkt auf dem Server oder auf einem beliebigen Client installiert werden. Das ist Geschmackssache.

```bash
mkdir -p /opt/fabinfra/tools/
cd /opt/fabinfra/tools/
git clone https://github.com/elem74/fabaccess-config-generator.git
cd fabaccess-config-generator/
```

## Anpassungen

Alle Erklärungen zum Konzept und zu verschiedenen Einstellmöglichkeiten finden sich im Repository unter [https://github.com/elem74/fabaccess-config-generator/tree/main?tab=readme-ov-file#nutzung-des-python-skripts](https://github.com/elem74/fabaccess-config-generator/tree/main?tab=readme-ov-file#nutzung-des-python-skripts)

## Config Generator verwenden

Konfigurationsdatei maschinenliste.csv mit dem favorisierten Editor bearbeiten:

```bash
vim maschinenliste.csv
```

[http://docs.fab-access.org/data-fabaccess-config-generator/maschinenliste.csv](http://docs.fab-access.org/data-fabaccess-config-generator/maschinenliste.csv)

Danach kann der Generator ausgeführt werden:

```bash
python3 config-generator.py
```

Das Tool schreibt automatisch ins Unterverzeichnis `output/` die folgenden Dateien:

### bffh-dhall-data.txt  


Die Beispiel Dhall Daten sehen dabei wie folgt aus:

<details id="bkmrk-bffh-dhall-data.txt--1"><summary>bffh-dhall-data.txt</summary>

```
	roles = {
		Admin = {
			permissions =  [
				"bffh.users.manage",
				"bffh.users.info",
				"bffh.users.admin",
				"beispielw.*",
			]
		},
 
		_manager_schichtleitung = {
			permissions =  [
				"beispielw.*",
			]
		},
 
		beispielw_holz_manager = {
			permissions =  [
				"beispielw.holz.*",
			]
		},
 
		beispielw_holz_bandsaege_user = {
			permissions =  [
				"beispielw.holz.bandsaege.disclose.*",
				"beispielw.holz.bandsaege.read.*",
				"beispielw.holz.bandsaege.write.*",
			]
		},
 
		beispielw_holz_hobelmaschine_user = {
			permissions =  [
				"beispielw.holz.hobelmaschine.disclose.*",
				"beispielw.holz.hobelmaschine.read.*",
				"beispielw.holz.hobelmaschine.write.*",
			]
		},
 
		beispielw_holz_holzcnc_user = {
			permissions =  [
				"beispielw.holz.holzcnc.disclose.*",
				"beispielw.holz.holzcnc.read.*",
				"beispielw.holz.holzcnc.write.*",
			]
		},
 
		beispielw_holz_user = {
			permissions =  [
				"beispielw.holz.disclose.*",
				"beispielw.holz.read.*",
				"beispielw.holz.write.*",
			]
		},
 
		beispielw_textil_manager = {
			permissions =  [
				"beispielw.textil.*",
			]
		},
 
		beispielw_textil_user = {
			permissions =  [
				"beispielw.textil.disclose.*",
				"beispielw.textil.read.*",
				"beispielw.textil.write.*",
			]
		},
 
		beispielw_fablab_manager = {
			permissions =  [
				"beispielw.fablab.*",
			]
		},
 
		beispielw_fablab_3dprint_manager = {
			permissions =  [
				"beispielw.fablab.3dprint.*",
			]
		},
 
		beispielw_fablab_3dprint_user = {
			permissions =  [
				"beispielw.fablab.3dprint.disclose.*",
				"beispielw.fablab.3dprint.read.*",
				"beispielw.fablab.3dprint.write.*",
			]
		},
 
		beispielw_fablab_laser_manager = {
			permissions =  [
				"beispielw.fablab.laser.*",
			]
		},
 
		beispielw_fablab_laser_user = {
			permissions =  [
				"beispielw.fablab.laser.disclose.*",
				"beispielw.fablab.laser.read.*",
				"beispielw.fablab.laser.write.*",
			]
		},
 
		beispielw_siebdruck_manager = {
			permissions =  [
				"beispielw.siebdruck.*",
			]
		},
 
		beispielw_siebdruck_user = {
			permissions =  [
				"beispielw.siebdruck.disclose.*",
				"beispielw.siebdruck.read.*",
				"beispielw.siebdruck.write.*",
			]
		},
 
		beispielw_drucker = {
			permissions =  [
				"beispielw.siebdruck.disclose.a3drucker",
				"beispielw.siebdruck.read.a3drucker",
				"beispielw.siebdruck.write.a3drucker",
				"beispielw.buero.disclose.drucker",
				"beispielw.buero.read.drucker",
				"beispielw.buero.write.drucker",
			]
		},
 
		beispielw_buero_manager = {
			permissions =  [
				"beispielw.buero.*",
			]
		},
 
	},
	machines = {
		beispielw-holz-bandsaege-bandsaege = {
			name = "Bandsäge",
			description = "Bandsäge im Holzbereich",
			wiki = "",
			category = "Holzwerkstatt",
			disclose = "beispielw.holz.bandsaege.disclose.bandsaege",
			read = "beispielw.holz.bandsaege.read.bandsaege",
			write = "beispielw.holz.bandsaege.write.bandsaege",
			manage = "beispielw.holz.bandsaege.manage.bandsaege",
		},
 
		beispielw-holz-hobelmaschine-hobelmaschine = {
			name = "Hobelmaschine",
			description = "Hobelmaschine im Holzbereich",
			wiki = "",
			category = "Holzwerkstatt",
			disclose = "beispielw.holz.hobelmaschine.disclose.hobelmaschine",
			read = "beispielw.holz.hobelmaschine.read.hobelmaschine",
			write = "beispielw.holz.hobelmaschine.write.hobelmaschine",
			manage = "beispielw.holz.hobelmaschine.manage.hobelmaschine",
		},
 
		beispielw-holz-holzcnc-holzcncfraese = {
			name = "CNC-Fräse",
			description = "CNC Fräse im Holzbereich",
			wiki = "",
			category = "Holzwerkstatt",
			disclose = "beispielw.holz.holzcnc.disclose.holzcncfraese",
			read = "beispielw.holz.holzcnc.read.holzcncfraese",
			write = "beispielw.holz.holzcnc.write.holzcncfraese",
			manage = "beispielw.holz.holzcnc.manage.holzcncfraese",
		},
 
		beispielw-holz-kappsaege = {
			name = "Kappsäge",
			description = "Kappsäge mit allgemeiner Einweisung Holz",
			wiki = "",
			category = "Holzwerkstatt",
			disclose = "beispielw.holz.disclose.kappsaege",
			read = "beispielw.holz.read.kappsaege",
			write = "beispielw.holz.write.kappsaege",
			manage = "beispielw.holz.manage.kappsaege",
		},
 
		beispielw-holz-bandschleifer = {
			name = "Bandschleifer",
			description = "Bandschleifer mit allgemeiner Einweisung Holz",
			wiki = "",
			category = "Holzwerkstatt",
			disclose = "beispielw.holz.disclose.bandschleifer",
			read = "beispielw.holz.read.bandschleifer",
			write = "beispielw.holz.write.bandschleifer",
			manage = "beispielw.holz.manage.bandschleifer",
		},
 
		beispielw-holz-staenderbohrmaschine = {
			name = "Ständerbohrmaschine",
			description = "Ständerbohrmaschine mit allgemeiner Einweisung Holz",
			wiki = "",
			category = "Holzwerkstatt",
			disclose = "beispielw.holz.disclose.staenderbohrmaschine",
			read = "beispielw.holz.read.staenderbohrmaschine",
			write = "beispielw.holz.write.staenderbohrmaschine",
			manage = "beispielw.holz.manage.staenderbohrmaschine",
		},
 
		beispielw-textil-naehmaschine1 = {
			name = "Nähmaschine 1",
			description = "Nähmaschine Nummer 1",
			wiki = "",
			category = "Textilwerkstatt",
			disclose = "beispielw.textil.disclose.naehmaschine1",
			read = "beispielw.textil.read.naehmaschine1",
			write = "beispielw.textil.write.naehmaschine1",
			manage = "beispielw.textil.manage.naehmaschine1",
		},
 
		beispielw-textil-naehmaschine2 = {
			name = "Nähmaschine 2",
			description = "Nähmaschine Nummer 2",
			wiki = "",
			category = "Textilwerkstatt",
			disclose = "beispielw.textil.disclose.naehmaschine2",
			read = "beispielw.textil.read.naehmaschine2",
			write = "beispielw.textil.write.naehmaschine2",
			manage = "beispielw.textil.manage.naehmaschine2",
		},
 
		beispielw-fablab-3dprint-3ddrucker1 = {
			name = "3D-Drucker 1",
			description = "3D-Drucker Modell 111",
			wiki = "",
			category = "FabLab",
			disclose = "beispielw.fablab.3dprint.disclose.3ddrucker1",
			read = "beispielw.fablab.3dprint.read.3ddrucker1",
			write = "beispielw.fablab.3dprint.write.3ddrucker1",
			manage = "beispielw.fablab.3dprint.manage.3ddrucker1",
		},
 
		beispielw-fablab-3dprint-3ddrucker2 = {
			name = "3D-Drucker 2",
			description = "3D-Drucker Modell 222",
			wiki = "",
			category = "FabLab",
			disclose = "beispielw.fablab.3dprint.disclose.3ddrucker2",
			read = "beispielw.fablab.3dprint.read.3ddrucker2",
			write = "beispielw.fablab.3dprint.write.3ddrucker2",
			manage = "beispielw.fablab.3dprint.manage.3ddrucker2",
		},
 
		beispielw-fablab-laser-laser3000 = {
			name = "Lasercutter",
			description = "Modell Laser3000",
			wiki = "https://www.fiktivedoku.de",
			category = "FabLab",
			disclose = "beispielw.fablab.laser.disclose.laser3000",
			read = "beispielw.fablab.laser.read.laser3000",
			write = "beispielw.fablab.laser.write.laser3000",
			manage = "beispielw.fablab.laser.manage.laser3000",
		},
 
		beispielw-fablab-laser-kuehlung3000 = {
			name = "Kühlung",
			description = "Modell Kühlung3000",
			wiki = "",
			category = "FabLab",
			disclose = "beispielw.fablab.laser.disclose.kuehlung3000",
			read = "beispielw.fablab.laser.read.kuehlung3000",
			write = "beispielw.fablab.laser.write.kuehlung3000",
			manage = "beispielw.fablab.laser.manage.kuehlung3000",
		},
 
		beispielw-siebdruck-sdbelichter = {
			name = "SD-Belichter",
			description = "Belichter für die Siebe",
			wiki = "",
			category = "Siebdruck",
			disclose = "beispielw.siebdruck.disclose.sdbelichter",
			read = "beispielw.siebdruck.read.sdbelichter",
			write = "beispielw.siebdruck.write.sdbelichter",
			manage = "beispielw.siebdruck.manage.sdbelichter",
		},
 
		beispielw-siebdruck-a3drucker = {
			name = "A3-Drucker",
			description = "A3 Drucker im Siebdruck Bereich",
			wiki = "",
			category = "Siebdruck",
			disclose = "beispielw.siebdruck.disclose.a3drucker",
			read = "beispielw.siebdruck.read.a3drucker",
			write = "beispielw.siebdruck.write.a3drucker",
			manage = "beispielw.siebdruck.manage.a3drucker",
		},
 
		beispielw-buero-drucker = {
			name = "Drucker",
			description = "",
			wiki = "",
			category = "Büro",
			disclose = "beispielw.buero.disclose.drucker",
			read = "beispielw.buero.read.drucker",
			write = "beispielw.buero.write.drucker",
			manage = "beispielw.buero.manage.drucker",
		},
 
	},
	actors = {
        tasmota_1 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 1",
            }
        },
 
        tasmota_2 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 2",
            }
        },
 
        tasmota_6 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 6",
            }
        },
 
        tasmota_7 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 7",
            }
        },
 
        tasmota_8 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 8",
            }
        },
 
        tasmota_9 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 9",
            }
        },
 
        tasmota_10 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 10",
            }
        },
 
        tasmota_11 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 11",
            }
        },
 
        tasmota_12 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 12",
            }
        },
 
        tasmota_13 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 13",
            }
        },
 
        tasmota_14 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 14",
            }
        },
 
        tasmota_15 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 15",
            }
        },
 
        tasmota_16 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 16",
            }
        },
 
        tasmota_17 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 17",
            }
        },
 
        tasmota_18 =
        {
           module = "Process",
            params =
            {
                cmd = "/usr/local/lib/bffh/adapters/tasmota/main.py",
                args = "--host mqtt --tasmota 18",
            }
        },
 
	},    
	actor_connections = [
		{ machine = "beispielw-holz-bandsaege-bandsaege", actor = "tasmota_1" },
		{ machine = "beispielw-holz-hobelmaschine-hobelmaschine", actor = "tasmota_2" },
		{ machine = "beispielw-holz-holzcnc-holzcncfraese", actor = "tasmota_6" },
		{ machine = "beispielw-holz-kappsaege", actor = "tasmota_7" },
		{ machine = "beispielw-holz-bandschleifer", actor = "tasmota_8" },
		{ machine = "beispielw-holz-staenderbohrmaschine", actor = "tasmota_9" },
		{ machine = "beispielw-textil-naehmaschine1", actor = "tasmota_10" },
		{ machine = "beispielw-textil-naehmaschine2", actor = "tasmota_11" },
		{ machine = "beispielw-fablab-3dprint-3ddrucker1", actor = "tasmota_12" },
		{ machine = "beispielw-fablab-3dprint-3ddrucker2", actor = "tasmota_13" },
		{ machine = "beispielw-fablab-laser-laser3000", actor = "tasmota_14" },
		{ machine = "beispielw-fablab-laser-kuehlung3000", actor = "tasmota_15" },
		{ machine = "beispielw-siebdruck-sdbelichter", actor = "tasmota_16" },
		{ machine = "beispielw-siebdruck-a3drucker", actor = "tasmota_17" },
		{ machine = "beispielw-buero-drucker", actor = "tasmota_18" },
	],
```

</details>### mermaid-code.txt

Der Beispiel Mermaid Code sieht wie folgt aus und kann in [https://mermaid.live](https://mermaid.live) gerendet werden.

<details id="bkmrk-mermaid-%25%25%7Binit%3A-%7B%22f"><summary>mermaid-code.txt</summary>

```
%%{init: {"flowchart" : {"curve" : "linear"}}}%%
 
flowchart TD
 
 

subgraph legende["<b>Legende</b><p style="text-align:left;">👑 = Administrator
🛠️ = Manager
👷‍♂️ = Benutzer
👩‍🚀 = Benutzer (Alternativrolle)"]
end 
 

subgraph root["
    <p style="font-size: 2em">Infrastruktur</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>👑_Admin FabAccess<br>🛠️_Manager Schichtleitung</p></p>
    <p style="text-align: left; margin-top: 0px;"></p>
    <p style="opacity: 0;">.</p>
"]
end 

subgraph root_beispielw["
    <p style="font-size: 1.75em">Beispielwerkstatt </p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Beispielwerkstatt </p></p>
    <p style="text-align: left; margin-top: 0px;"></p>
    <p style="opacity: 0;">.</p>
"]
end 
root ------ root_beispielw
 
 
root_beispielw ---- root_beispielw_buero_wrapper
subgraph root_beispielw_buero_wrapper["<p style="opacity: 0;">.</p>"]
style root_beispielw_buero_wrapper stroke: none, fill: none
 

subgraph root_beispielw_buero["
    <p style="font-size: 1.5em">Büro</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Büro<br>👷‍♂️Benutzer Büro<br>👩‍🚀Benutzer Druckernutzung</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>Drucker (👩‍🚀Benutzer Druckernutzung)</p>
    <p style="opacity: 0;">.</p>
"]
end 
 

subgraph filler_root_beispielw_buero_1["
<p style="opacity: 0;">
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</p>"]
end
style filler_root_beispielw_buero_1 fill: none, stroke: none
root_beispielw_buero~~~~~~filler_root_beispielw_buero_1

subgraph filler_root_beispielw_buero_2["
<p style="opacity: 0;">
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</p>"]
end
style filler_root_beispielw_buero_2 fill: none, stroke: none
filler_root_beispielw_buero_1~~~~~filler_root_beispielw_buero_2
 
 
end
 
 
root_beispielw ---- root_beispielw_fablab_wrapper
subgraph root_beispielw_fablab_wrapper["<p style="opacity: 0;">.</p>"]
style root_beispielw_fablab_wrapper stroke: none, fill: none
 

subgraph root_beispielw_fablab["
    <p style="font-size: 1.5em">FabLab</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager FabLab<br>👷‍♂️Benutzer FabLab</p></p>
    <p style="text-align: left; margin-top: 0px;"></p>
    <p style="opacity: 0;">.</p>
"]
end 

subgraph root_beispielw_fablab_3dprint["
    <p style="font-size: 1.25em">3D-Druck</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager FabLab 3D-Druck<br>👷‍♂️Benutzer FabLab 3D-Druck</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>3D-Drucker 1<br>3D-Drucker 2</p>
    <p style="opacity: 0;">.</p>
"]
end 
root_beispielw_fablab ------ root_beispielw_fablab_3dprint
 

subgraph root_beispielw_fablab_laser["
    <p style="font-size: 1.25em">Laser</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager FabLab Laser<br>👷‍♂️Benutzer FabLab Laser</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>Lasercutter<br>Kühlung</p>
    <p style="opacity: 0;">.</p>
"]
end 
root_beispielw_fablab ------ root_beispielw_fablab_laser
 
 
end
 
 
root_beispielw ---- root_beispielw_holz_wrapper
subgraph root_beispielw_holz_wrapper["<p style="opacity: 0;">.</p>"]
style root_beispielw_holz_wrapper stroke: none, fill: none
 

subgraph root_beispielw_holz["
    <p style="font-size: 1.5em">Holzwerkstatt</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Holzwerkstatt<br>👷‍♂️Benutzer Holzwerkstatt</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>Kappsäge<br>Bandschleifer<br>Ständerbohrmaschine</p>
    <p style="opacity: 0;">.</p>
"]
end 

subgraph root_beispielw_holz_bandsaege["
    <p style="font-size: 1.25em">Bandsäge</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Holzwerkstatt Bandsäge<br>👷‍♂️Benutzer Holzwerkstatt Bandsäge</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>Bandsäge</p>
    <p style="opacity: 0;">.</p>
"]
end 
root_beispielw_holz ------ root_beispielw_holz_bandsaege
 

subgraph root_beispielw_holz_hobelmaschine["
    <p style="font-size: 1.25em">Hobelmaschine</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Holzwerkstatt Hobelmaschine<br>👷‍♂️Benutzer Holzwerkstatt Hobelmaschine</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>Hobelmaschine</p>
    <p style="opacity: 0;">.</p>
"]
end 
root_beispielw_holz ------ root_beispielw_holz_hobelmaschine
 

subgraph root_beispielw_holz_holzcnc["
    <p style="font-size: 1.25em">CNC-Fräse</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Holzwerkstatt CNC-Fräse<br>👷‍♂️Benutzer Holzwerkstatt CNC-Fräse</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>CNC-Fräse</p>
    <p style="opacity: 0;">.</p>
"]
end 
root_beispielw_holz ------ root_beispielw_holz_holzcnc
 
 
end
 
 
root_beispielw ---- root_beispielw_siebdruck_wrapper
subgraph root_beispielw_siebdruck_wrapper["<p style="opacity: 0;">.</p>"]
style root_beispielw_siebdruck_wrapper stroke: none, fill: none
 

subgraph root_beispielw_siebdruck["
    <p style="font-size: 1.5em">Siebdruck</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Siebdruck<br>👷‍♂️Benutzer Siebdruck<br>👩‍🚀Benutzer Druckernutzung</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>SD-Belichter<br>A3-Drucker (👩‍🚀Benutzer Druckernutzung)</p>
    <p style="opacity: 0;">.</p>
"]
end 
 

subgraph filler_root_beispielw_siebdruck_1["
<p style="opacity: 0;">
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</p>"]
end
style filler_root_beispielw_siebdruck_1 fill: none, stroke: none
root_beispielw_siebdruck~~~~~~filler_root_beispielw_siebdruck_1

subgraph filler_root_beispielw_siebdruck_2["
<p style="opacity: 0;">
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</p>"]
end
style filler_root_beispielw_siebdruck_2 fill: none, stroke: none
filler_root_beispielw_siebdruck_1~~~~~filler_root_beispielw_siebdruck_2
 
 
end
 
 
root_beispielw ---- root_beispielw_textil_wrapper
subgraph root_beispielw_textil_wrapper["<p style="opacity: 0;">.</p>"]
style root_beispielw_textil_wrapper stroke: none, fill: none
 

subgraph root_beispielw_textil["
    <p style="font-size: 1.5em">Textilwerkstatt</p><p style="text-align: left; margin-top: 20px;"><b><center>Rollen</center></b>🛠️Manager Textilwerkstatt<br>👷‍♂️Benutzer Textilwerkstatt</p></p>
    <p style="text-align: left; margin-top: 0px;"><b><center>Maschinen</center></b>Nähmaschine 1<br>Nähmaschine 2</p>
    <p style="opacity: 0;">.</p>
"]
end 
 

subgraph filler_root_beispielw_textil_1["
<p style="opacity: 0;">
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</p>"]
end
style filler_root_beispielw_textil_1 fill: none, stroke: none
root_beispielw_textil~~~~~~filler_root_beispielw_textil_1

subgraph filler_root_beispielw_textil_2["
<p style="opacity: 0;">
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</p>"]
end
style filler_root_beispielw_textil_2 fill: none, stroke: none
filler_root_beispielw_textil_1~~~~~filler_root_beispielw_textil_2
 
 
end
```

</details>#### [![mermaid-diagram-2024-11-06-014627.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/5UsWttkQXSxbRkh8-mermaid-diagram-2024-11-06-014627.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/5UsWttkQXSxbRkh8-mermaid-diagram-2024-11-06-014627.png)

### roles.csv  


[http://docs.fab-access.org/data-fabaccess-config-generator/roles.csv](http://docs.fab-access.org/data-fabaccess-config-generator/roles.csv)

## Tipps

Wer die maschinenliste.csv per CLI bearbeiten oder ansehen möchte, hier ein paar Tipps:

- CSV-Dateien in der Kommandozeile editieren: [https://www.visidata.org/install](https://www.visidata.org/install)
- CSV-Dateien in der Kommandozeile übersichtlich formatieren: `column -t -s ';' -o '|' < maschinenliste.csv`

In der Datei settings.ini kann `fa_update_dhall = True` gesetzt werden, sowie ein Dateifpad `fa_dhall_file` gesetzt werden. In unsere Konfigurationsdatei `bffh.dhall` muss dann nur noch das folgende Snippet (siehe [https://github.com/elem74/fabaccess-config-generator/blob/main/docs/bffh-vorlage.dhall)](https://github.com/elem74/fabaccess-config-generator/blob/main/docs/bffh-vorlage.dhall)) eingebunden werden:

```bash
---||| GENERATOR START
---||| GENERATOR END
```

# Server Logs konfigurieren und Debugging

Der Log Level und die Formatierung von BFFH Logzeilen können über folgende Wege konfiguriert werden.

## Log Level per Umgebungsvariable

Über die Umgebungsvariable `BFFH_LOG=debug` - zum Beispiel eingebunden per [systemd Service](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren#bkmrk-systemd-service-anle)

Unterstützte Level für `BFFH_LOG` sind:

<div id="bkmrk-trace-debug-warn-inf" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div>- info
- <span style="color: rgb(241, 196, 15);">warn</span>
- <span style="color: rgb(224, 62, 45);">error</span>
- <span style="color: rgb(53, 152, 219);">debug</span>
- <span style="color: rgb(132, 63, 161);">trace</span>

</div></div></div>## Log Parameter in bffhd

Über `bffhd` Parameter kann das Kommando beliebig angepasst werden (und ebenso im [systemd Service](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren#bkmrk-systemd-service-anle) verwendet werden):

### `--log-format`

Gültige Werte sind:

- `Full` (Standard)
- `Compact`
- `Pretty`

<p class="callout info">Die Groß- bzw. Kleinschreibung spielt hierbei keine Rolle!</p>

#### Full

```bash
2024-12-07T11:10:48.198579Z DEBUG bffh:tls: difluoroborane::tls: TLS secret logging is disabled. keylog=false
2024-12-07T11:10:48.198685Z DEBUG bffh:tls: difluoroborane::tls: reading certificates path=/etc/ssl/fablabchemnitz.de.cert.pem
2024-12-07T11:10:48.198959Z DEBUG bffh:tls: difluoroborane::tls: reading private key path=/etc/ssl/fablabchemnitz.de.privkey.pem
2024-12-07T11:10:48.199608Z DEBUG difluoroborane::actors::process: Process actor updating state name=actor-process-test cmd=/opt/fabinfra/actor-process-test.sh state=SendState(ArchivedState { inner: ArchivedMachineState { state: Free, previous: Some(ArchivedUserRef { id: "local_lab_admin" }) } })
2024-12-07T11:10:48.200451Z DEBUG difluoroborane::actors::process: Process actor updating state name=Tasmota_Mjolnir cmd=/opt/fabinfra/adapters/tasmota/main.py state=SendState(ArchivedState { inner: ArchivedMachineState { state: InUse(ArchivedUserRef { id: "local_lab_admin" }), previous: Some(ArchivedUserRef { id: "local_lab_admin" }) } })
2024-12-07T11:10:48.203817Z  INFO bffh:binding API listen sockets: difluoroborane::capnp: Opened listen socket on 127.0.0.1:5961
2024-12-07T11:10:48.204079Z  INFO bffh:binding API listen sockets: difluoroborane::capnp: Opened listen socket on 192.168.1.192:5961
```

#### Compact

<p class="callout info">Das kompakte Layout ist identisch zu Full</p>

```bash
2024-12-07T11:10:48.198579Z DEBUG bffh:tls: difluoroborane::tls: TLS secret logging is disabled. keylog=false
2024-12-07T11:10:48.198685Z DEBUG bffh:tls: difluoroborane::tls: reading certificates path=/etc/ssl/fablabchemnitz.de.cert.pem
2024-12-07T11:10:48.198959Z DEBUG bffh:tls: difluoroborane::tls: reading private key path=/etc/ssl/fablabchemnitz.de.privkey.pem
2024-12-07T11:10:48.199608Z DEBUG difluoroborane::actors::process: Process actor updating state name=actor-process-test cmd=/opt/fabinfra/actor-process-test.sh state=SendState(ArchivedState { inner: ArchivedMachineState { state: Free, previous: Some(ArchivedUserRef { id: "local_lab_admin" }) } })
2024-12-07T11:10:48.200451Z DEBUG difluoroborane::actors::process: Process actor updating state name=Tasmota_Mjolnir cmd=/opt/fabinfra/adapters/tasmota/main.py state=SendState(ArchivedState { inner: ArchivedMachineState { state: InUse(ArchivedUserRef { id: "local_lab_admin" }), previous: Some(ArchivedUserRef { id: "local_lab_admin" }) } })
2024-12-07T11:10:48.203817Z  INFO bffh:binding API listen sockets: difluoroborane::capnp: Opened listen socket on 127.0.0.1:5961
2024-12-07T11:10:48.204079Z  INFO bffh:binding API listen sockets: difluoroborane::capnp: Opened listen socket on 192.168.1.192:5961
```

#### Pretty

```bash
  2024-12-07T11:09:18.093419Z DEBUG difluoroborane::tls: reading certificates, path: /etc/ssl/fablabchemnitz.de.cert.pem
    at bffhd/tls.rs:113
    in difluoroborane::tls::tls
    in bffh::bffh

  2024-12-07T11:09:18.093835Z DEBUG difluoroborane::tls: reading private key, path: /etc/ssl/fablabchemnitz.de.privkey.pem
    at bffhd/tls.rs:123
    in difluoroborane::tls::tls
    in bffh::bffh

  2024-12-07T11:09:18.097839Z  INFO difluoroborane::capnp: Opened listen socket on 127.0.0.1:5961
    at bffhd/capnp/mod.rs:99
    in difluoroborane::capnp::binding API listen sockets
    in bffh::bffh

  2024-12-07T11:09:18.098140Z  INFO difluoroborane::capnp: Opened listen socket on 192.168.1.192:5961
    at bffhd/capnp/mod.rs:99
    in difluoroborane::capnp::binding API listen sockets
    in bffh::bffh
```

### `--log-level`  


Die Level sind die gleichen wie `BFFH_LOG`

Siehe auch [Cheat Sheet - Wichtigste Befehle (Übersicht)](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht "Cheat Sheet - Wichtigste Befehle (Übersicht)")

<p class="callout warning">Hinweis. Der `--log-level` Parameter funktioniert auf einer normalen Kommandozeilenausgabe derzeit nicht. Details finden sich in [https://gitlab.com/fabinfra/fabaccess/bffh/-/issues/83](https://gitlab.com/fabinfra/fabaccess/bffh/-/issues/83). Als Workaround empfehlen wir die Verwendung der Umgebungsvariable `BFFH_LOG`.</p>

### `--verbose` (`-v`)

Dieser Parameter kann bis zu drei mal als Argument angegeben werden und erhöht die Log-Ausgabe zusätzlich. Beispiel:

```bash
/usr/bin/bffhd --config /etc/bffh/bffh.dhall --verbose --verbose --verbose
```

oder

```bash
/usr/bin/bffhd --config /etc/bffh/bffh.dhall -vvv
```

### `--tls-key-log`

Dieser Parameter wird nur für Entwickler benötigt. Wenn für Debug Zwecke der Inhalt der verschlüsselten Verbindungen eingesehen werden soll, werden in der angegeben Datei `<PATH> `die Schlüssel für jede Verbindung gespeichert und können z.B. von [Wireshark](https://www.wireshark.org/about.html) geladen werden.

## Log File schreiben

<div id="bkmrk-wer-bffhd-nicht-%C3%BCber" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div>Wer `bffhd` nicht über systemd startet und deshalb auch keine Logs mit journalctl auslesen möchte, der kann den Output auch konventiell in eine Log-Datei schreiben - hier im Beispiel `bffh.log`. Wir fangen dabei die Output-Streams `stdout` und `stderr` gemeinsam in einem Ausgabestrom ab (`2>&1`). Den Parameter `--log-level` verwenden wir nicht, da er bei der normalen Systemausgabe ignoriert wird (siehe [Issue #83](https://gitlab.com/fabinfra/fabaccess/bffh/-/issues/83)) und führen stattdessen mit `BFFH_LOG` an:  
</div><div>  
</div></div></div>```bash
BFFH_LOG=debug /usr/bin/bffhd --config /etc/bffh/bffh.dhall --log-format Pretty > bffh.log 2>&1
```

## Erweitertes Logging

Für Entwickler gibt es verschiedene Möglichkeiten aus der Laufzeit ein detaillierteres Fehlerbild zu erzeugen, über die Umgebungsvariable `RUST_BACKTRACE` zum Beispiel:

```bash
RUST_BACKTRACE=1 /usr/bin/bffhd
```

... oder ...

```bash
RUST_BACKTRACE=full /usr/bin/bffhd
```

## Audit Log

Der Audit Log ist das Log File, was `bffhd` schreibt und in `bffh.dhall` konfiguriert wird. Es gibt Aufschluss über "Wer hat wann welche Ressource genutzt oder zurückgegeben?". Details finden sich in [Audit Log (Revisionsprotokoll)](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/audit-log-revisionsprotokoll "Audit Log (Revisionsprotokoll)"). Es ist also kein Programmlog im eigentliche Sinne. Jedoch zeigt es auch die eventuell letzten Lebenszeichen des Servers an.

## Debugging

Wer nicht nur Logs möchte, sondern debuggen will, der benötigt die Laufzeit inklusive Debug Symbolen. Im Falle der Installation mit [\*.deb oder \*.rpm](https://docs.fab-access.org/books/fabaccess-installation/page/server-installation-mit-distributionspaketen-deb-rpm "Server - Installation mit Distributionspaketen (*:deb, *.rpm)") Paketen gibt es dafür gesonderte Pakete. Alternativ kann BFFH auch mit `cargo` [kompiliert](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren "Server - Anleitung zum selber kompilieren") werden. Letzteres erlaubt das Beeinflussen des Builds mit entsprechenden Parametern. Siehe [BFFH testen / entwickeln](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren#bkmrk-bffh-testen).

### Hilfreiche Debugging Tools

- `strace` - ein in Linux-Distros standardmäßig eingebautes Kommando
- `gdb` - [GNU Debugger](https://wiki.ubuntuusers.de/GDB/)
- `<a href="https://wiki.ubuntuusers.de/Archiv/Valgrind/">valgrind</a>` - [https://valgrind.org](https://valgrind.org)

### Versionsinformationen anzeigen

Welche BFFH Version läuft, bzw. ist installiert? Un mit welchem Branch, Commit ID und Rustumgebung wurde `bffhd` gebaut? Siehe hier: [Version anzeigen](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht#bkmrk-version-anzeigen-%28-v)

### Bugs melden

Bugs können zum einen in den [Community Kanälen](https://docs.fab-access.org/books/mitmachen-unterstutzen-join-the-community/page/werde-teil-der-fabinfra-community#bkmrk-digitale-kontaktm%C3%B6gl "Community / Übersicht & Join us!") besprochen werden und/oder [auf GitLab gemeldet](https://docs.fab-access.org/books/mitmachen-unterstutzen-join-the-community/page/werde-teil-der-fabinfra-community#bkmrk-gitlab) werden.

<div id="bkmrk--1" itemscope="itemscope" itemtype="http://schema.org/Article" role="main"><div itemprop="articleBody"><div id="bkmrk-trace-debug-warn-inf-1"></div></div></div>

# Server Hardening

Wie sichern wir unseren Server (Host) ab, auf dem BFFH läuft und verhindern Angriffe? Auf dieser Seite finden sich Ansätze für ein stabiles, sicheres Setup in einer Linux-Umgebung.

## Offene Ports prüfen

```
netstat -an | grep LISTEN | grep -v LISTENING
```

## Unattended Upgrades  


Automatische Updates per apt

```bash
sudo apt update
sudo apt upgrade -y
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades
```

## Fail2ban

Basiert auf [https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-20-04](https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-20-04)

```bash
sudo apt install fail2ban
sudo systemctl enable fail2ban.service
```

```bash
cd /etc/fail2ban/
sudo cp jail.conf jail.local
```

```bash
sudo vim /etc/fail2ban/jail.local
```

```ini
[DEFAULT]
. . .
findtime = 10m
maxretry = 5
. . .
```

```bash
sudo vim /etc/fail2ban/jail.d/defaults-debian.conf
```

```ini
[DEFAULT]
banaction = nftables
banaction_allports = nftables[type=allports]
backend = systemd

[sshd]
enabled = true
```

```bash
sudo systemctl restart fail2ban.service
```

# Cheat Sheet - Wichtigste Befehle (Übersicht)

Auf dieser Seite finden sich wichtige Befehle im Zusammenhang mit der Benutzung von Difluoroborane.

## bffh Daemon (bffhd) Befehlsübersicht (`-h`, `--help`)  


```bash
/usr/bin/bffhd --help
```

```bash
difluoroborane 0.4.4


USAGE:
    bffhd [OPTIONS]

OPTIONS:
    -c, --config <config>            Path to the DHALL config file to use
        --check                      Check DHALL config for validity
        --dump-db <FILE>             Dump all internal databases (states and users) to the given
                                     file as TOML
        --dump-users <FILE>          Dump the users db to the given file as TOML
        --force                      Force owerwriting existing files
    -h, --help                       Print help information
        --load-db <FILE>             Load values from TOML into the internal databases
        --load-users <FILE>          Load users from TOML into the internal databases
        --log-format <log format>    Use an alternative log formatter. Available: Full, Compact,
                                     Pretty [possible values: Full, Compact, Pretty]
        --log-level <log level>      Set the desired log levels. [possible values: info, warn,
                                     error, debug, trace]
        --print-default              Print a default DHALL config to stdout instead of running
        --quiet                      Decrease logging verbosity
        --tls-key-log [<PATH>...]    Log TLS keys into PATH. If no path is specified the value of
                                     the envvar SSLKEYLOGFILE is used.
    -v, --verbose                    Increase logging verbosity. Stackable from -v up to -vvv
    -V, --version                    Print version information
```

### Logging-Konfiguration (`--log-level`, `--log-format`, `--quiet`, `-v`, `--verbose`, `--tls-key-log`)

Log-spezifische Parameter inklusive Audit Log sind zusammenfassend in [Server Logs konfigurieren](https://docs.fab-access.org/books/fabaccess-konfiguration/page/server-logs-konfigurieren-und-debugging "Server Logs konfigurieren") genauer erklärt.

### Alle internen Datenbanken exportieren (`--dump-db <bffh-db.toml file>`)

BFFH speichert zwei Datenbanken intern ab und trennt sie auf in Benutzer (users) und Zustände ([states](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/zustande-traits "Traits")).

<p class="callout warning">Die BFFH Datenbank muss bei jedem Versionsupgrade exportiert und sauber importiert werden.</p>

Dazu muss `--config <Pfad zu bffh.dhall>` angegeben werden, damit bffhd weiß, welche Datenbank angefragt werden soll.

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --dump-db <bffh-db.toml file>
```

Soll eine bereits existierende Datei überschrieben werden, nutzen wir zusätzlich den optionalen Parameter `--force`.

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --dump-db <bffh-db.toml file> --force
```

<p class="callout info">Wird kein Dateiname hinter `--dump-db` angegeben, wird die Datei automatisch `bffh-db.toml` benannt  
</p>

#### Datenbeispiel eines Dumps

<details id="bkmrk-dump-beispiel-anzeig"><summary>Dump-Beispiel anzeigen</summary>

 Ein Datenbank-Dump sieht wie folgt aus und enthält sowohl die Benutzer, als auch die States.

```ini
[users."Raum 1 Manager"]
roles = ["zam_raum1_manager"]
passwd = "$argon2i$v=19$m=4096,t=3,p=1$aE7DYpmOPy+ZAB305S26iQ$G+cx4wEQzaVsB4Vq05+mvvxBgqXlYnejbzpLcK24SPg"

[users.Werkstattleiter]
roles = ["_manager_schichtleitung"]
passwd = "$argon2i$v=19$m=4096,t=3,p=1$nqY/EsDGzlwLzRgtZQUBzA$a55mDPB20CxYixvafyYGRIZH/EsPBguzhTBm7O3D3QA"

[users.Admin]
roles = ["zam_raum1_ecke1_user", "zam_raum1_ecke2_user", "zam_raum1_ecke3_user", "zam_raum1_ecke4_user", "zam_raum1_ecke5_user", "zam_raum1_ecke6_user", "zam_raum1_ecke7_user", "zam_raum1_ecke8_user", "zam_raum1_ecke9_user", "_manager_schichtleitung", "Admin", "zam_raum1_manager"]
passwd = "$argon2i$v=19$m=4096,t=3,p=1$Ykyx7xGXwWKPMP7Q5FysBA$lbMnVRwZZheYt5u2kEZYuwkWW8DwaHF/JNgqH791WdQ"
[state.zam-raum1-ecke7-random3."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke2-arrow."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke5-random1."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke6-random2."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke9-shutdown."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke8-macgyver."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke10-restartbffh."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"

[state.zam-raum1-ecke10-restartbffh."1.3.6.1.4.1.48398.612.2.4".previous]
id = "Admin"
[state.zam-raum1-ecke3-fan."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke1-lamp."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
[state.zam-raum1-ecke4-mesh."1.3.6.1.4.1.48398.612.2.4"]
state = "Free"
```

</details>### Alle internen Datenbanken importieren (`--load-db <bffh-db.toml file>`)

Dieses Kommando lädt die angegebene `*.toml` Datei in die internen Datenbanken innerhalb `bffh.db` hinein.

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --load-db <db.toml file>
```



### Benutzerdatenbank exportieren (`--dump-users <users.toml file>`)

Einmal importiere Nutzerdaten können genauso wieder aus der bffh Datenbank exportiert werden. Dazu muss `--config <Pfad zu bffh.dhall>` (oder in kurz `-c`) angegeben werden, damit bffhd weiß, welche Datenbank (`db_path`) angefragt werden soll. Außerdem muss auch der Ausgabepfad für `--dump-users` angegeben werden.

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --dump-users /etc/bffh/users.toml
```


Soll eine bereits existierende Datei überschrieben werden, nutzen wir zusätzlich den optionalen Parameter `--force`.

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --dump-users /etc/bffh/users.toml --force
```

<p class="callout info">Wird kein Dateiname hinter `--dump-users` angegeben, wird die Datei automatisch `users.toml` benannt</p>

### Benutzerdatenbank importieren (`--load-users <users.toml file>`)

Dieses Kommando lädt die angegebene `*.toml` Datei in die Benutzerdatenbank innerhalb `bffh.db` hinein.

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --load-users <users.toml file>
```

Es kann auch ein Alias `--load` verwendet werden, der das gleiche bewirkt:

```
/usr/bin/bffhd -c /etc/bffh/bffh.dhall --load <users.toml file>
```

<p class="callout info">Praxistipps zum smarten Umgang mit der `users.toml` findest du unter [Nutzerdatenbank laden / hashen / prüfen](https://docs.fab-access.org/books/fabaccess-konfiguration/page/nutzerdatenbank-laden-hashen-prufen "Nutzerdatenbank laden / hashen / prüfen").</p>

<p class="callout danger">Das Laden einer `users.toml` Datei tauscht alle Nutzer in der internen BFFH-Datenbank aus. Etwaige, nicht gesicherte Nutzer, die per App angelegt wurden, werden somit unwiederbringlich verworfen! Hier ist ein ggf. ein Diff zwischen der dann vorher zu sichernden `users.toml` (kann per `--dump-users` erzeugt werden) und der neuen `users.toml` lohnenswert. Ein geeignetes Diff-Werkzeug ist beispielweise [Meld](https://meldmerge.org/).</p>



### Konfigurationsdatei prüfen (`--check`)

Dieser Parameter prüft die angegebene Konfigurationsdatei auf fehlende oder fehlerhafte Angaben und überprüft generell, ob die \*.dhall Datei geparsed werden kann. Sobald eine z.B. eine eckige oder geschweifte Klammer, ein Hochkomma oder normales Komma fehlt, gibt es in der Regel Probleme. Für das Prüfen muss ebenso `--config <Pfad zu bffh.dhall>` angegeben werden.

Die Ausgabe ist granuliert (Fehler werden mit Zeile und Spalte angezeigt) und sieht zum Beispiel so aus:

```bash
failed to parse config:  --> 2:2
  |
2 | 	let mqtt_password = 
  | 	^---
  |
  = expected any_label_or_some or empty_record_literal
```

### Konfigurationsstandard ausgeben (`--print-default`)  


```bash
/usr/bin/bffhd --print-default
```

Dieser Befehl gibt eine minimale Beispielkonfiguration im [Dhall](https://docs.fab-access.org/books/awesome-fabinfra/page/glossar-begrifflichkeiten-und-abkurzungen#bkmrk-dhall)-Format aus, die wir direkt in eine `bffh.dhall` Datei pipen können. Die ausführliche Erläuterung der bffh-Konfiguration findest du [hier](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall "Hauptkonfiguration - bffh.dhall").

```yaml
{ actor_connections = [{ actor = "actor_123", machine = "resource_a" }], actors = { actor_123 = { module = "Shelly", params = {=} } }, auditlog_path = "/var/log/bffh/audit.json", certfile = "/etc/bffh/certs/bffh.crt", db_path = "/var/lib/bffh/bffh.db", init_connections = [{ initiator = "initiator_123", machine = "resource_a" }], initiators = { initiator_123 = { module = "Process", params = { args = "", cmd = "echo" } } }, instanceurl = "https://fabaccess.sample.space", keyfile = "/etc/bffh/certs/bffh.key", listens = [{ address = "127.0.0.1" }], machines = { resource_a = { category = Some "A category", description = Some "A description", disclose = "lab.some.disclose", manage = "lab.some.manage", name = "Resource A", prodable = True, read = "lab.some.read", wiki = Some "https://some.wiki.url", write = "lab.some.write" }, resource_b = { category = Some "A category", description = Some "A description", disclose = "lab.some.disclose", manage = "lab.some.manage", name = "Resource B", read = "lab.some.read", wiki = Some "https://some.wiki.url", write = "lab.some.write" } }, mqtt_url = "mqtt://127.0.0.1:1883", roles = { admin = { permissions = ["bffh.users.info", "bffh.users.manage", "bffh.users.admin"] }, member = { permissions = ["lab.some.disclose", "lab.some.read", "lab.some.write", "lab.some.manage"] } }, spacename = "fabaccess.sample.space" }
```

Sofern das Paket [`dhall`](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-dhall-dateien-parsen) installiert wurde, kann auch die Ausgabe [sauber formatiert](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-dhall-dateien-parsen) werden:

```bash
/usr/bin/bffhd --print-default | dhall format > bffh.dhall
```

```yaml
{ actor_connections = [ { actor = "actor_123", machine = "resource_a" } ]
, actors.actor_123 = { module = "Shelly", params = {=} }
, auditlog_path = "/var/log/bffh/audit.json"
, certfile = "/etc/bffh/certs/bffh.crt"
, db_path = "/var/lib/bffh/bffh.db"
, init_connections = [ { initiator = "initiator_123", machine = "resource_a" } ]
, initiators.initiator_123 =
  { module = "Process", params = { args = "", cmd = "echo" } }
, instanceurl = "https://fabaccess.sample.space"
, keyfile = "/etc/bffh/certs/bffh.key"
, listens = [ { address = "127.0.0.1" } ]
, machines =
  { resource_a =
    { category = Some "A category"
    , description = Some "A description"
    , disclose = "lab.some.disclose"
    , manage = "lab.some.manage"
    , name = "Resource A"
    , prodable = True
    , read = "lab.some.read"
    , wiki = Some "https://some.wiki.url"
    , write = "lab.some.write"
    }
  , resource_b =
    { category = Some "A category"
    , description = Some "A description"
    , disclose = "lab.some.disclose"
    , manage = "lab.some.manage"
    , name = "Resource B"
    , read = "lab.some.read"
    , wiki = Some "https://some.wiki.url"
    , write = "lab.some.write"
    }
  }
, mqtt_url = "mqtt://127.0.0.1:1883"
, roles =
  { admin.permissions =
    [ "bffh.users.info", "bffh.users.manage", "bffh.users.admin" ]
  , member.permissions =
    [ "lab.some.disclose"
    , "lab.some.read"
    , "lab.some.write"
    , "lab.some.manage"
    ]
  }
, spacename = "fabaccess.sample.space"
}
```

### Version anzeigen (`--version`, `-V`)  


Folgendes Kommando kann genutzt werden, um die von dir derzeitig verwendete Version von BFFH auszulesen:

```bash
/usr/bin/bffhd -V
```

```bash
diflouroborane 0.4.3
```

Wer BFFH als Debian-Paket installiert hat, kann es auch wie folgt herausfinden:

```bash
dpkg -l | grep fabaccess-bffh
```

```bash
ii  fabaccess-bffh    0.4.3    amd64    FabAccess Diflouroborane Server (bffh)
```

---

Wer den genauen Feature Branch auslesen will, von dem kompiliert wurde:

```bash
VERSION_DETAIL=$(BFFH_LOG=trace timeout 1s /usr/bin/bffhd | grep "branch:") && echo -e $VERSION_DETAIL | tail -n +2
```

```bash
pkg_version:0.4.4
branch:feature/cardloginwithtoken
commit_hash:2d127d5c
build_time:2025-03-11 01:30:34 +01:00
build_env:rustc 1.84.1 (e71f9a9a9 2025-01-27),stable-x86_64-unknown-linux-gnu"
```


## FabFire Provisioning Tool

Die Kommandos für dieses kleine Tools sind separat in [FabFire Tools](https://docs.fab-access.org/books/plugins-aktoren-initiatoren/page/fabfire-tools#bkmrk-benutzung-1) beschrieben.

## Helfer-Skripte

Diverse Helfer-Scripts, die verschiedene Optionen/Parameter automatisieren (z.B. Benutzerdankbank zuverlässig sichern) finden sich in der [Script-Sammlung](https://docs.fab-access.org/books/fabaccess-konfiguration/chapter/script-sammlung "Script-Sammlung").

## Ein paar Tipps für Git

Für BFFH nutzen wir diverse Git-Repositories, die teilweise externe Abhängigkeiten laden. Hier finden sich ein paar Tipps für den Alltag.

### Git Repository ausgecheckt, aber Untermodule vergessen auszuchecken?

Wer z.B. das Repo von BFFH auscheckt, muss die externen Module laden, um das Projekt erfolgreich zu kompilieren. Wenn das vergessen wurden, ist das kein Problem, das geht auch nachträglich:

```bash
git submodule update --init --recursive
```

Und reguläre Updates vom Projekt inklusive Untermodulen dann mit:

```bash
git pull --recurse-submodules
```

Untermodule updaten

```bash
git submodule update --remote --merge
```

# Client benutzen und typische Konfigurationsfehler bei Server und Clients

Nach [Download und Installation des Clients](https://docs.fab-access.org/books/fabaccess-installation/page/downloads-demo "Downloads / Demo") kann's endlich praktisch werden!

## Der FabAccess Client "Borepin"

### Anwendung starten

Der Client sieht beim ersten Öffnen so aus:

<p class="callout info">Borepin ist nun in Deutsch und Englisch verfügbar. Die Sprache wird automatisch anhand des erkannten Betriebssystems justiert.</p>

[![Screenshot_20241129-030717_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/jBxrQ1EJzLfKowS2-screenshot-20241129-030717-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/jBxrQ1EJzLfKowS2-screenshot-20241129-030717-fabaccess.png) [![Screenshot_20241129-025456_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/jqHqlol3m1ZHrH68-screenshot-20241129-025456-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/jqHqlol3m1ZHrH68-screenshot-20241129-025456-fabaccess.png)

### Mit Server(n) verbinden (Connect to Server)  


Das Verbinden mit einem FabAccess BFFH Server kann durch Eingabe des Hosts erfolgen. Entweder ist die Adresse bekannt und wird manuell angegeben oder ein QR-Code (`SCAN QR-CODE`) wird gescannt. Der QR-Code enthält den Host im Plaintext-Format (**keine** [URN-Syntax wie bei Ressourcen](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/url-und-urn "URL und URN") notwendig). Die Hostadresse erlaubt verschiedene, übliche Formate. Zum Beispiel:

- FQDN, ohne Port (z.B. `test.fab-access.org`)
- FQDN, mit Port (z.B. `test.fab-access.org:59661`)
- ein Hostname aus dem lokalen Netzwerk, ohne Port, z.B. `fabaccess.local`
- ein Hostname aus dem lokalen Netzwerk, inkl. Port, z.B. `fabaccess.local:59661`
- eine IPv4-Adresse, ohne Port, z.B. `192.168.188.31`
- eine IPv4-Adresse, mit Port, z.B. `192.168.188.31:59661`
- eine IPv6-Adresse, ohne Port, z.B. `[2003:f3:1736:db00:3e10:77ba:cb8b:6633]`
- eine IPv6-Adresse, mit Port, z.B. `[2003:f3:1736:db00:3e10:77ba:cb8b:6633]:59661`

<p class="callout info">Die vorausgefüllte Adresse [test.fab-access.org](https://docs.fab-access.org/books/fabaccess-installation/page/server-online-demo-server-testfab-accessorg) kann reell genutzt werden. Obwohl das Feld vorasugefüllt ist, muss diese URL jedoch exakt so von Hand eingetragen werden!</p>

Die stabilste Art und Weise der Verbindung kann dir dein FabAccess-Administrator des Vertrauens mitteilen. Wir verbinden uns mit dem Button `CONNECT TO SERVER`. Das Verbinden dauert in der Regel ein paar Sekunden - je nach Leistungsfähigkeit des Servers, des Clients, des Netzwerks und der Komplexität der Gesamtkonfiguration des Systems.

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/NOIkI6hLj2XFZGv5-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/NOIkI6hLj2XFZGv5-grafik.png) [![Screenshot_20241129-025716_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/CElaMdUnxzkZXsCv-screenshot-20241129-025716-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/CElaMdUnxzkZXsCv-screenshot-20241129-025716-fabaccess.png)

### Nach dem Verbinden: Einloggen

Nach erfolgreicher Verbindung zum Server folgt der nächste Screen, der nach Login-Daten fragt. Hier gibt es drei Interaktionsmöglichkeiten:

- Mit Passwort einloggen (`LOGIN WITH PASSWORD`)
- mit Karte einloggen (`LOGIN WITH CARD`) - aktuell nicht funktional ???
- Account anlegen, also neu registrieren (`REGISTER`) - aktuell nicht funktional ???

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/GN11ZljcnuYpQE8w-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/GN11ZljcnuYpQE8w-grafik.png)

###  Hauptmenü  


Ist der Nutzer mit dem Server verbunden, so hat er verschiedene Möglichkeiten der Interaktion. Zum einen kann die Ressourcenübersicht (`MACHINES`) aufgerufen werden. Ist der Nutzer Administrator, so können auch serverweit die Benutzer verwaltet werden (`USERS`). Außerdem kan das eigene Profil angepasst werden (`MY PROFILE`). Zudem gibt es eine Übersicht über alle auf dem Endgerät aktuell konfigurierten Serververbindungen (`SERVERS`: Borepin kann mit mehreren Servern bzw. auch je Server mit verschiedenen Benutzern verbunden werden).

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/6YieO4EBx29kP49y-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/6YieO4EBx29kP49y-grafik.png)

### Ressourcenübersicht

Die Ressourcenübersicht zeigt alle für den Nutzer sichtbaren Geräte bzw. Maschinen an (Berechtigung [`disclose`](https://docs.fab-access.org/books/was-ist-fabaccess-grundkonzepte/page/rbac-benutzerrollen-und-berechtigungen "RBAC (Benutzerrollen und Berechtigungen)")), getrennt nach Kategorie bzw. Raum, Zone oder Sektor. Die Übersicht zeigt an, welche Geräte vom Benutzer gerade in Benutzung sind (`In Use by Me`), sowie welche Ressourcen noch frei (`Free`) oder durch jemand anderen gerade in Benutzung sind (`In Use`). Die Ansicht aktualisiert sich nicht automatisch - dafür gibt es den Button `REFRESH`.

Ein praktischer Button ist der große ganz oben: es ist möglich eine Ressource direkt aufzurufen, indem wir einen [speziell formatierten QR-Code](https://docs.fab-access.org/books/beschriftungen-und-sticker/page/fabaccess-sticker-aufkleber "URL und URN") beispielsweise von einem gedruckten Sticker scannen (`SCAN QR-CODE`).

[![Screenshot_20241123-152802_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/Wuu4RQmF55TXS4Cx-screenshot-20241123-152802-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/Wuu4RQmF55TXS4Cx-screenshot-20241123-152802-fabaccess.png)

### Eine Ressource bedienen

Nach Auswahl einer Ressource aus der Übersicht gibt es verschiedene Handlungen, die ein Nutzer ausführen kann:

- Benutzen (`USE`) oder zurückgeben, falls gerade in der eigenen Benutzung (`GIVEBACK`)
- Wiki-Link öffnen (falls eine URL hinterlegt wurde) - hier öffnet sich ein Browser im Vollbildmodus durch Klick auf `OPEN WIKI`
- Falls die [**manage**](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-contains-list-of-mac "Hauptkonfiguration - bffh.dhall")-Berechtigung zur Verfügung steht: 
    - Ressource freigeben (erzwungene Rückgabe) (`FREE MACHINE`)
    - Ressource reservieren (`BLOCK MACHINE`). Über `FREE MACHINE` wird die Ressource wieder freigegeben!
    - Ressource deaktivieren (z.B. für eine anstehende Wartung oder weil ein Defekt eingetreten ist) (`DISABLE MACHINE`). Über `FREE MACHINE` wird die Ressource wieder aktiviert!
- Falls die Ressource als [Prodable](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-optional-information) definiert ist (z.B. für ein Spind oder eine Tür), dann gibt es zwei weitere Buttons: 
    - `UNLOCK` öffnet bzw. entsperrt die Ressource, ohne sie für andere zu blockieren und ohne sie zurückgeben zu müssen
    - `IDENTIFY` löst eine einfache Aktion aus wie z.B. das Aufleuchten einer LED an der Ressource, um das Fach bzw. die Ressource optisch leichter zu identifizieren

In der rechten oberen Ecke ist zudem der **aktuelle Zustand** der Ressource erkennbar, z.B. `Free`, `In Use`, `Blocked`, `Disabled`, <span style="color: rgb(224, 62, 45);">`ToCheck`</span>, <span style="color: rgb(224, 62, 45);">`Reserved`</span>

<p class="callout warning">Die Zustände `ToCheck` und `Reserved` sind nicht funktional im Client implementiert, können jedoch durch Initiatoren oder die FabAccess-API gesetzt werden. Geräte mit diesen Zuständen werden zumindest entsprechend im Client angezeigt.</p>

Eine hilfreiche Information ist außerdem auch die Anzeige, welcher Nutzer die Ressource zuletzt benutzt hat (`Last User`).

[![Screenshot_20250311-144734_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2025-03/scaled-1680-/02nO8j9cw1SfAZvI-screenshot-20250311-144734-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2025-03/02nO8j9cw1SfAZvI-screenshot-20250311-144734-fabaccess.png)

### Benutzer verwalten

Dieses Menü obliegt der Rolle des Administrators bzw. Managers und erlaubt das Filtern (`Search User`), sowie das Anpassen oder Löschen (`DELETE`) bestehender Nutzer (Rollen zuweisen, Passwort zurücksetzen per `FORCE PASSWORD RESET`) und das Anlegen neuer Nutzer (`ADD NEW USER`). Beim Anlegen eines neuen Nutzers können lediglich Benutzername und Passwort konfiguriert werden. Etwaige Keycard-Codes müssen durch den Administrator in der [`users.toml`](https://docs.fab-access.org/books/fabaccess-konfiguration/page/benutzerkonfiguration-userstoml "Benutzerkonfiguration - user.toml") Konfiguration hinterlegt werden. Das Löschen bestehender Nutzer erfolgt mit Bestätigungsdialog.

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/66sx5txqpE4ezkkQ-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/66sx5txqpE4ezkkQ-grafik.png) [![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/XSUa28KOOC0OLs5P-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/XSUa28KOOC0OLs5P-grafik.png) [![Screenshot_20241129-025834_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/yTmwN210g4rO5N2U-screenshot-20241129-025834-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/yTmwN210g4rO5N2U-screenshot-20241129-025834-fabaccess.png) [![Screenshot_20241129-025857_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/jML7chqBfRxfQwoo-screenshot-20241129-025857-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/jML7chqBfRxfQwoo-screenshot-20241129-025857-fabaccess.png)

### Servers - Verbindungen managen

In diesem Menü werden alle Verbindungen (Server + Login) angezeigt, die erfolgreich zum Borepin Client hinzugefügt wurden (Historie). Es wird auch die aktuell genutzte Verbindung angezeigt. Hier kann eine Verbindung ausgewählt und als Standardverbindung gesetzt werden (`SET AS DEFAULT CONNECTION`). Außerdem können wir uns hier auch vom aktuellen Server abmelden (`DISCONNECT`) oder eine andere Verbindung zum Verbinden wählen (`CONNECT`). Selbstverständlich können unbenötigte Verbindungen entfernt werden (`DELETE`). Das Löschen erfolgt mit Rückvergewisserungsdialog zur Bestätigung des Vorgangs (weiter mit `CONFIRM` oder abbrechen durch `CANCEL`).

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/86QYA2OTbuEuJ50o-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/86QYA2OTbuEuJ50o-grafik.png) [![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/K2fCfeChbnFBEbRy-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/K2fCfeChbnFBEbRy-grafik.png) [![Screenshot_20241129-151804_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/9sZ6xdYUJzGrmUgE-screenshot-20241129-151804-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/9sZ6xdYUJzGrmUgE-screenshot-20241129-151804-fabaccess.png) [![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/ifJUaSrgz4H13quT-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/ifJUaSrgz4H13quT-grafik.png) [![Screenshot_20241129-025437_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/43o4OUm0F9aPHRdK-screenshot-20241129-025437-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/43o4OUm0F9aPHRdK-screenshot-20241129-025437-fabaccess.png)

### Profil verwalten

Die eigene Profilverwaltung ist relativ übersichtlich. Hier kann lediglich das Passwort neu gesetzt werden. Das Ändern des Namens muss vom Administrator beauftragt werden. Der Benutzer kann seinen Account nicht selbst löschen.

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/DtuHwwEIeaFyzwuy-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/DtuHwwEIeaFyzwuy-grafik.png)

## Probleme mit dem Client

### "Authentication failed. Credentials are invalid."

Dieser Fehler taucht in der Regel auf, wenn Benutzername und/oder Passwort falsch sind.

[![Screenshot_20250211-184930_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2025-02/scaled-1680-/uuSS3PWQMNgp13hz-screenshot-20250211-184930-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2025-02/uuSS3PWQMNgp13hz-screenshot-20250211-184930-fabaccess.png)

### "Connection failed. Connection time exceeded."

Ein Typischer Fehler und dieser kann verschiedene Gründe haben. Prüfen:

- läuft der BFFH Server? Log bzw. Journal überprüfen!
- läuft der MQTT Server? Log bzw. Journal überprüfen!
- Laufen MQTT und BFFH auf den korrekten Netzwerk-Interfaces?
- ist die Client Version mit dem Server kompatibel?
- möglicherweiser gibt es ein Port-Problem auf dem Client (Firewall überprüfen!)
- Tipp: Verbindung mit **telnet** prüfen: `telnet fabaccess.local 59661`

[![Screenshot_20241120-012409_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/VlKq6mBHQBvSgtZu-screenshot-20241120-012409-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/VlKq6mBHQBvSgtZu-screenshot-20241120-012409-fabaccess.png)

### "Connection failed. Unable to connect to server."

Dieser Fehler drückt aus, dass der Server nicht per DNS auflösbar ist. Hier ist es sinnvoll verschiedene Verbindungskonstellationen durchzuprobieren, zum Beispiel:

- fabaccess
- fabaccess.local
- fabaccess:59661
- fabaccess.local:59661
- 192.168.188.1:59661
- sonstige FQDNs
- Tipp: Verbindung mit **telnet** prüfen: `telnet fabaccess.local 59661`

Unter Umständen wird der Fehler auch hervorgerufen, weil der Client mit einem VPN-Netwerk verbunden ist und es Konflikte in der DNS-Auflösung oder IP-Adressbereich gibt.

[![Screenshot_20241120-173332_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2024-11/scaled-1680-/LhtdUptABisnCXLZ-screenshot-20241120-173332-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2024-11/LhtdUptABisnCXLZ-screenshot-20241120-173332-fabaccess.png)

### "Connection failed. TLS certificate is invalid."

Möglicherweise wurde in der [Konfiguration bffh.dhall](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall#bkmrk-tls_min_version%3A%3Astr) die minimale Version von TLS auf 1.3 gesetzt. Borepin unterstützt aktuell nur TLS 1.2.

[![Screenshot_20250102-215952_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2025-01/scaled-1680-/qqUZBR19JwpgWdba-screenshot-20250102-215952-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2025-01/qqUZBR19JwpgWdba-screenshot-20250102-215952-fabaccess.png)

### "Connection failed. Unexpected Error."

Dieser Error wurde möglicherweise verursacht, weil das vom Benutzer eingegebene Passwort zu komplex ist. Der Administrator des Server sollte überprüfen, ob für den Nutzer ein zu komplexer Argon2-Hash erzeugt wurde, der die Rechenkapazität übersteigt. Siehe auch [Benutzerkonfiguration - users.toml](https://docs.fab-access.org/books/fabaccess-konfiguration/page/benutzerkonfiguration-userstoml "Benutzerkonfiguration - users.toml").

Eine andere Ursache kann auch der Absturz des Serverdienstes sein, z.B. durch eine unbehandelte Ausnahme (z.B. `SIGSERV` oder `SEGFAULT` Error).

[![Screenshot_20250103-170757_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2025-01/scaled-1680-/YzJG2ftkpGonSiLA-screenshot-20250103-170757-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2025-01/YzJG2ftkpGonSiLA-screenshot-20250103-170757-fabaccess.png)

### "No Connection to Server. Please select a Server."

Dieser Fehler wird u.U. angezeigt, wenn der Nutzer gerade in FabAccess aktiv eingeloggt ist, aber der Server neu gestartet wurde und sich dabei das Passwort für den Nutzer geändert hat. Am besten ausloggen und erneut versuchen bzw. Rücksprache mit dem Administrator halten.

[![Screenshot_20250103-172848_FabAccess.png](https://docs.fab-access.org/uploads/images/gallery/2025-01/scaled-1680-/lJNAeyy9Fnp4VPyp-screenshot-20250103-172848-fabaccess.png)](https://docs.fab-access.org/uploads/images/gallery/2025-01/lJNAeyy9Fnp4VPyp-screenshot-20250103-172848-fabaccess.png)

### Ich habe die users.toml Datei editiert, aber der oder die Nutzer können sich trotzdem nicht einloggen

Das Editieren ist nur ein Schritt in der Prozesskette! Hast du nach dem Editieren auch einen neuen [Import durchgeführt](https://docs.fab-access.org/books/fabaccess-konfiguration/page/cheat-sheet-wichtigste-befehle-ubersicht#bkmrk-benutzerdatenbank-im "Cheat Sheet - Wichtigste Befehle (Übersicht)")? Erst durch Importieren der `users.toml` in BFFH werden die Änderungen auch wirksam.

## Serverseitige Fehlermeldungen

Fehlkonfigurationen auf dem Server (z.B. wegen fehlenden Ordnern oder Berechtigungen darauf) oder ungeeigneten oder unvollständigen Einstellungen in `bffh.dhall` führen zu Fehlern beim Start von `bffhd`. Wir empfehlen die [Referenz zur Hauptkonfiguration](https://docs.fab-access.org/books/fabaccess-konfiguration/page/hauptkonfiguration-bffhdhall "Hauptkonfiguration - bffh.dhall") zu lesen.

### config::notfound

```rust
Error: config::notfound

  × The config file '/etc/diflouroborane.dhall' does not exist or is not readable
  help: Make sure the config file and the directory it's in are readable by the user running bffh
```

Dieser Fehler tritt dann auf, wenn bffhd ohne Konfigurationsdatei gestartet wird. Verwende den Parameter `--config`, um eine entsprechende dhall-Datei (z.B. `bffh.dhall`) anzugeben.

### Failed to open socket on 127.0.0.1:59661: Address already in use (os error 98)

```rust
ERROR diflouroborane::capnp: Failed to open socket on 127.0.0.1:59661: Address already in use (os error 98)
    at bffhd/capnp/mod.rs:103
```

Auf dem angegebenen Port und Interface läuft bereits eine (andere) Anwendung. Prüfen Sie, ob bffh bereits läuft oder ein anderer Dienst den gleichen Port für sich beansprucht oder ob Firewall-Einschränkungen dazu führen.

Ermitteln von Anwendungen, die auf dem Port lauschen:

```bash
sudo lsof -i tcp:59661
netstat -an | grep LISTEN | grep 59661
```

### failed to initialize state database

```rust
Error:
   × failed to initialize state database
   ├─▶ opening the state db environment failed
   ╰─▶ Permission denied
```

Dieser Fehler drückt aus, dass die Datenbankdatei `bffh` nicht zum Schreiben geöffnet werden kann. Möglicherweise existiert das Zielverzeichnis nicht oder es gehört dem falschem Benutzer. Prüfen Sie den Schlüssel `db_path`.

### audit log failed

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

Dieser Fehler drückt aus, dass die Datenbankdatei `bffh.audit` nicht zum Schreiben geöffnet werden kann. Möglicherweise existiert das Zielverzeichnis nicht oder es gehört dem falschem Benutzer. Prüfen Sie den Schlüssel `auditlog_path`.

### error in actor subsystem  


```rust
   × error in actor subsystem
   ├─▶ MQTT connection failed
   ├─▶ I/O: failed to lookup address information: Name or service not known
   ╰─▶ failed to lookup address information: Name or service not known
```

Die Verbindung zum MQTT Server konnte nicht hergestellt werden. Der Schlüssel `mqtt_url` sollte überprüft werden:

- Ist das Protokoll (z.B. `tcp://`, `mqtt://` oder `mqtts://`) korrekt vorrangestellt?
- Stimmen Benutzername, Passwort, Host und Port?
- Läuft der Mosquitto MQTT Server korrekt und ist er vom Server aus erreichbar? (Firewall prüfen!)

### failed to initialize TLS config

```rust
Error:
   × failed to initialize TLS config
   ├─▶ failed to open certificate file at path examples/cert.pem
   ╰─▶ No such file or directory (os error 2)
```

Das angegebene SSL Zertifikat konnte nicht gefunden werden. Überprüfen Sie den Schlüssel `certfile` in `bffh.dhall`. Existiert die Datei? Hat bffhd Zugriff darauf?

```rust
Error:
   × failed to initialize TLS config
   ├─▶ failed to open private key file at path examples/key.pem
   ╰─▶ No such file or directory (os error 2)
```

Der Private Key für das SSL Zertifikat konnte nicht gefunden werden. Überprüfen Sie den Schlüssel `keyfile` in `bffh.dhall`. Existiert die Datei? Hat bffhd Zugriff darauf?

# Script-Sammlung

Eine Sammlung verschiedener Scripts über das Basic-Setup hinaus, um dem Admin Dinge zu erleichtern

# Backup für die Benutzerdatenbank einrichten

<div class="gl-flex" id="bkmrk-"></div>Mit folgendem Backup-Script (bash) können wir die Benutzerdatenbank sichern. Diese können wir außerdem mit `systemd` per Service und Timer automatisieren. Alternativ kann das Bash-Script auch als Cronjob eingebunden werden. Folgendes Script sollte je nach Bedarf angepasst werden (Pfade).

Es ist generell sehr empfehlenswert die Benutzerdatenbank regelmäßig zu sichern. Immer dann, wenn ein Administrator bzw. Manager per Client App neue Accounts hinzufügt, Accounts löscht oder Accounts ändert (z.B. Passwortwechsel), dann werden diese Änderungen in einen transienten Zwischenspeicher (`bffh` Datenbankdatei) gelegt. Dieser sollte mit der lokalen `users.toml` zusammengeführt werden, um alle Nutzer im Fall der Fälle wiederherstellen zu können. Das erfolgt über den `dump-users` Parameter.

<p class="callout danger">Wird dieser zusammengeführte Dump nicht ausgeführt und crasht der Serverdienst, dann sind alle etwaigen Remote-Änderungen u.U. nicht wiederbringbar.  
</p>

## Backup Script anlegen und konfigurieren  


<p class="callout info">Wir können theoretisch auch die komplette Datenbank sichern (Benutzerdatenbank + Statusdatenbank). Im Script muss dann `DB_DUMP_CMD` der Parameter `--dump-users` gegen `--dump-db` getauscht werden.  
  
</p>

```bash
vim /opt/fabinfra/scripts/bffh-backup.sh
```

```bash
#!/bin/bash

# Database dump command
DB_DUMP_CMD="/usr/bin/bffhd -c /etc/bffh/bffh.dhall --dump-users"

# Backup directory
BACKUP_DIR="/etc/bffh/config_backup"

# Number of backups to keep
NUM_BACKUPS_TO_KEEP=5

# Dry run flag
DRY_RUN=false

# Parse command-line options
while getopts ":n:r" opt; do
	case $opt in
	n)
		NUM_BACKUPS_TO_KEEP="$OPTARG"
		;;
	r)
		DRY_RUN=true
		;;
	\?)
		echo "Invalid option: -$OPTARG" >&2
		exit 1
		;;
	:)
		echo "Option -$OPTARG requires an argument." >&2
		exit 1
		;;
	esac
done

# Current date and time
CURRENT_DATE=$(date +"%Y%m%d%H%M%S")

# Create a backup file name
BACKUP_FILE="$BACKUP_DIR/db_backup_$CURRENT_DATE.toml"

# Create backup dir, if not existent
if [ ! -d $BACKUP_DIR ]; then
  mkdir -p $BACKUP_DIR
fi

# Execute the database dump command
if [ "$DRY_RUN" = true ]; then
	echo "Dry run mode: Database backup command will not be executed."
	echo "Backup command: $DB_DUMP_CMD $BACKUP_FILE"

else
	$DB_DUMP_CMD $BACKUP_FILE
fi

# Check if the database dump was successful
if [ $? -eq 0 ]; then
	echo "Database backup completed successfully."

	# Sort backup files by modification time in ascending order
	sorted_backup_files=($(ls -rt "$BACKUP_DIR"))

	# Determine number of backups to delete
	num_backups_to_delete=$((${#sorted_backup_files[@]} - NUM_BACKUPS_TO_KEEP))


	cd $BACKUP_DIR
	# Delete oldest backups if necessary
	if [ $num_backups_to_delete -gt 0 ]; then
		for ((i = 0; i < $num_backups_to_delete; i++)); do
			if [ "$DRY_RUN" = true ]; then
				echo "Dry run mode: Would remove old backup: ${BACKUP_DIR}/${sorted_backup_files[$i]}"
			else
				rm "${sorted_backup_files[$i]}"
				echo "Removed old backup: ${BACKUP_DIR}/${sorted_backup_files[$i]}"
			fi
		done
	fi

else
	echo "Error: Database backup failed."
fi
```

```bash
chmod +x /opt/fabinfra/scripts/bffh-backup.sh
```

Das Script kann einzeln getestet werden. Es kann mit Parametern gestartet werden:

- n = Anzahl der aufzuhebenden Backups
- r = dry run

```bash
# Trockenlauf (dry run) - nur testen und nichts löschen
/opt/fabinfra/scripts/bffh-backup.sh -r -n 2

# Backup durchführen und nur die letzten 5 aufheben, alle anderen löschen
/opt/fabinfra/scripts/bffh-backup.sh -n 5
```

## Backup-Script mit systemd Timer

Das Script kann als timed Service eingebunden werden, um es so zu automatisieren. Unter Beachtung **obiger** **Parameter** in `ExecStart` kann folgendes eingebunden werden:

```bash
vim /opt/fabinfra/scripts/bffh-backup.service
```

```ini
[Unit]
Description=BFFH Backup Service

[Service]
Type=oneshot
ExecStart=/opt/fabinfra/scripts/bffh-backup.sh -n 10
```

Außerdem als Timer. Dieser muss den gleichen Name haben wie der Service (siehe [https://wiki.ubuntuusers.de/systemd/Timer\_Units](https://wiki.ubuntuusers.de/systemd/Timer_Units))

```bash
vim /opt/fabinfra/scripts/bffh-backup.timer
```

```ini
[Unit]
Description=BFFH Backup Timer

[Timer]
# Run every day at midnight
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
```

Wir aktivieren und starten das Backup schließlich einmal manuell und prüfen dessen Ausgabe.

Hinweis: Da wir einen Timer für den Service verwenden, müssen wir den Service nicht "enablen". Denn das macht der Timer selbst.

```bash
sudo ln -sf /opt/fabinfra/scripts/bffh-backup.service /etc/systemd/system/bffh-backup.service
sudo ln -sf /opt/fabinfra/scripts/bffh-backup.timer /etc/systemd/system/bffh-backup.timer
sudo systemctl daemon-reload
sudo systemctl start bffh-backup.service
journal -f -u bffh-backup.service
```

## Backup-Script mit cron

Wer lieber auf einen klassischen Cronjob setzten möchte, kann statt dem Service folgendes machen:

```bash
sudo vim /etc/cron.d/bffh-backup
```

```bash
#“At 00:00.”
0 0 * * 0 bffh /opt/fabinfra/scripts/bffh-backup.sh -n 10
```

Der Cronjob wird um 00:00 Uhr vom Benutzer `bffh` gestartet. Es gibt keinen Log Output. Dieser lässt sich jedoch leicht ergänzen.

# Nutzerdatenbank laden / hashen / prüfen

## Nutzerdatenbank laden / hashen

Für das Laden und ggf. Rehashen der Nutzerdatenbank (`users.toml`) kann folgendes Script genutzt werden. Es prüft zunächst, ob die Konfiguration `bffh.dhall` von BFFH valid ist. Wenn nicht, wird nichts unternommen. Im Anschluss werden die Nutzer neu geladen und anschließend in die gleiche Datei **optional** wieder exportiert, um etwaige unverschüsselte Passwörter automatisch zu hashen. BFFH prüft beim Laden die users.toml auf Syntaxfehler. Wem diese Checks nicht ausreichen, der kann sich dem [erweiteren Validator-Script](#bkmrk-nutzerdankenbank-pr%C3%BC) widmen,

<p class="callout info">Hinweis: Das Script basiert auf einem auf dem System aktiven **systemd** Service namens `bffh.service`  
</p>

```bash
mkdir -p /opt/fabinfra/scripts/
vim /opt/fabinfra/scripts/bffh-load-users.sh
```

```bash
#!/bin/bash
#check config
BFFHD="/usr/bin/bffhd"
DATA="/etc/bffh"
CFG="$DATA/bffh.dhall"
USERS="$DATA/users.toml"

while [[ $# -gt 0 ]]; do
	case $1 in
		-h|--help)
			echo "-h|--help   - show help"
			echo "-r|--rehash - overwrite users.toml with hashed passwords (ensure secure secrets)"
			exit 1
			;;
		-r|--rehash)
			REHASH="y"
			shift
			;;
		-*)
			;;
	esac
done

echo "use -h|--help to show additional script options!"

$BFFHD --check --config $CFG > /dev/null
if [ $? == 0 ]; then
	#pre-check bffh.dhall
	echo "Config is valid. Loading users ..."
	$BFFHD --verbose --config $CFG --load-users=$USERS

	if [ $? == 0 ]; then
		#then load users.toml
		$BFFHD --verbose --config $CFG --dump-users /tmp/users.toml --force
	else
		echo "Error: Newly given users.toml is invalid!"
		exit 1
	fi

	#if this was successful and service is running, restart it, elso do nothing
	if [ $? == 0 ]; then
		if [[ $REHASH == "y" ]]; then #overwrite users if --rehash option is given (not null)
			echo "Rehasing users.toml!"
			cat /tmp/users.toml > $USERS
			rm /tmp/users.toml
		fi
		FAS="bffh.service"
		STATUS="$(systemctl is-active $FAS)"
		if [ "${STATUS}" = "active" ]; then
			echo "restarting $FAS"
			systemctl restart $FAS
		else
			echo -e "\n\n$FAS not active/existing. Not restarting bffh service!"
		fi
	fi

else
	echo "Error: Currently loaded users.toml is invalid!"
	exit 1
fi
```

```bash
chmod +x /opt/fabinfra/scripts/bffh-load-users.sh
```

## Nutzerdankenbank prüfen (users.toml validator)  


Ein Python-Script erlaubt die Auswertung einer `users.toml` Datei mit folgenden Features:

- zählt die Nutzer und deren zugewiesene Rollen
- überprüft auf Duplikate bei Nutzernamen, Passwörtern und Cardkeys
- validiert Passwörter auf Verschlüsselung (Argon2)
- validiert eventuell vergebene Cardkeys (UUID)
- gibt Hinweise bei möglichen Datenbankfehler (z.B. falsche Datentypen)
- erzeugt eine Statistik über die Verwendung von Passwörtern, Cardkeys und Rollen

<p class="callout warning">Das Script benötigt mindestens Python 3.11. Erst ab dieser Version wird `tomllib` unterstützt!</p>

Tool installieren:

```bash
cd /opt/fabinfra/tools/
git clone https://github.com/vmario89/fabaccess-users-toml-validator.git
```

Benutzen:

```bash
python3 /opt/fabinfra/tools/fabaccess-users-toml-validator/validate.py
```

<p class="callout info">Über den optionalen Parameter `--db /opt/fabinfra/bffh-data/config/users.toml` kann auch eine andere Benutzerdatenbankdatei an Stelle der Standarddatei `/etc/bffh/users.toml` angegeben werden!</p>

### Fehlermeldungen und deren Bedeutung

**Invalid initial character for a key part (at line x, column y)**

Möglicher enthält der Benutzername Sonderzeichen wie Umlaute und ist nicht in Hochkommas geführt. In diesem Falle bricht das Script ab. `[Ö]` ist kein gültiger Benutzername, `["Ö"]` schon.

# FabAccess State Script (Email-Warnung bei Fehlerzustand)

Ein kleines Helferscript, was an ein beliebiges Mail-Postfach eine Email sendet, wenn unser systemd Service `bffh.service` nicht mehr korrekt läuft. Falls der Service nicht installiert wurde, dann wird das Script einen Fehler werfen.

Wir verwenden außerdem im Script das smarte smtp-cli Tool von mludvig: [https://github.com/mludvig/smtp-cli/tags](https://github.com/mludvig/smtp-cli/tags). Es kann jedoch auch jeder andere beliebige Mail-Client verwendet werden, um cli-basierte Nachrichten zu versenden. Kleiner Fix: [https://github.com/mludvig/smtp-cli/issues/28](https://github.com/mludvig/smtp-cli/issues/28)

```bash
# smtp-cli installieren
sudo apt install cpanminus
sudo cpanm Net::DNS
sudo apt install libio-socket-ssl-perl libdigest-hmac-perl libterm-readkey-perl libmime-lite-perl libfile-libmagic-perl libio-socket-inet6-perl
cd /opt
sudo wget https://github.com/mludvig/smtp-cli/archive/refs/tags/v3.10.zip
sudo unzip v3.10.zip
sudo rm v3.10.zip
sudo mv /opt/smtp-cli-3.10/smtp-cli /usr/bin/
sudo rm -rf /opt/smtp-cli-3.10/

mkdir -p /opt/fabinfra/scripts/
vim /opt/fabinfra/scripts/bffh-state.sh
```

```bash
#/!bin/bash
SMTP_SERVER="smtp.fablabchemnitz.de:587"
SMTP_MAILBOX="REDACTED"
SMTP_PW='REDACTED'
 
FROM="fabaccess.noreply@stadtfabrikanten.org"
TO="webmaster@stadtfabrikanten.org"
SUBJECT="FabAccess BFFH Service failed"
 
MAILFILE="/tmp/mail.txt"
 
systemctl status bffh.service 2>&1 > ${MAILFILE}
if [ $? != 0 ]; then #wenn exit code von systemd unit nicht 0
	cat ${MAILFILE}
    /usr/bin/smtp-cli --server ${SMTP_SERVER} --user ${SMTP_MAILBOX} --password ${SMTP_PW} --from "${FROM}" --to "${TO}" --subject "${SUBJECT}" --body-plain ${MAILFILE}
fi
```

```bash
chmod +x /opt/fabinfra/scripts/bffh-state.sh
```

Und dann testen wir das Script. Es gibt den Status auf der Kommandozeile aus und sollte parallel eine Email an das Zielpostfach mit gleichem Inhalt senden.

## Einbinden in systemd Service

Wir können dieses Script auch direkt in unseren [bffh.service](https://docs.fab-access.org/books/fabaccess-installation/page/server-anleitung-zum-selber-kompilieren#bkmrk-systemd-service-anle) einbinden.

Dazu fügen wir folgende Zeile in die Sektion `[Service]` ein:

```
vim /etc/systemd/system/bffh.service
```

```
ExecStopPost=/usr/bin/bash /opt/fabinfra/scripts/bffh-state.sh $EXIT_CODE
```

Danach muss der Service neu gestartet werden:

```
sudo systemctl daemon-reload
sudo systemctl restart bffh.service
```

# Echtzeituhr (RTC) Modul DS3231 für Raspberry Pi

Wer FabAccess auf einem Raspberry Pi betreiben möchte und eine Echtzeituhr für genaue und unabhängige Zeitstempel wünscht, kann ein RTC Modul installieren und konfigurieren:

Diese Anleitung basiert auf [https://learn.adafruit.com/adding-a-real-time-clock-to-raspberry-pi/set-rtc-time](https://learn.adafruit.com/adding-a-real-time-clock-to-raspberry-pi/set-rtc-time)

[![grafik.png](https://docs.fab-access.org/uploads/images/gallery/2024-12/scaled-1680-/kccCtTZ3neSqAWTE-grafik.png)](https://docs.fab-access.org/uploads/images/gallery/2024-12/kccCtTZ3neSqAWTE-grafik.png)

*Das von uns verbaute Uhrenmodell ist <span data-slate-fragment="JTVCJTdCJTIyaWQlMjIlM0ElMjJmNnVZaVZIWFJNZU5aRi1xcy1MTGFnJTIyJTJDJTIydHlwZSUyMiUzQSUyMmhlYWRlcjIlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMmlkJTIyJTNBJTIySkpQMlBwX0dTMmlza0xtdlZZVUxCdyUyMiUyQyUyMnRleHQlMjIlM0ElMjJEUzMyMzElMjIlN0QlNUQlN0QlNUQ=" style="white-space: pre;">[DS3231](https://funduinoshop.com/elektronische-module/schnittstellen-konverter/rtc/ds3231-uhrzeit-modul-fuer-raspberry-pi)</span>*

Wir fügen in der Boot-Konfiguration folgende Device Tree Overlays (`dtoverlay`) ein:

```bash
sudo vim /boot/config.txt 
```

```bash

# Additional overlays and parameters are documented /boot/overlays/README
dtoverlay=i2c-rtc,ds3231
```

```bash
sudo vim /etc/modules
```

```bash
i2c-bcm2708
i2c_dev
```

Danach starten wir den Pi neu

```bash
sudo reboot
```

Nach dem Restart prüfen wir, ob unsere Echtzeituhr verfügbar ist.

```bash
i2cdetect -y 1 #should be visible at 0x68
```

Wenn ja, dann können wir die standardmäßig installierte "Fake Hardware Clock" deinstallieren:

```bash
sudo apt remove fake-hwclock
sudo update-rc.d -f fake-hwclock remove
sudo systemctl disable fake-hwclock
```

Noch etwas nachstellen und dann die Uhr in Betrieb nehmen:

```bash
sudo vim /lib/udev/hwclock-set
```

```bash
#!/bin/sh
# Reset the System Clock to UTC if the hardware clock from which it was copied by the kernel was in localtime.
dev=$1
/sbin/hwclock --rtc=$dev --hctosys
```

```bash
sudo hwclock -r
sudo hwclock --verbose -r
sudo hwclock -w #write the time
sudo hwclock -r #read the time
```