Syncthing, eigentlich gut…
Ich verwende nun schon seit bald zwei Jahren Syncthing, um meine Daten vom Handy zum Laptop und von dort auf TrueNAS und auch meinen Mietserver zu übertragen. Grundsätzlich funktioniert das sehr gut, außer man erstellt törichterweise eine Art Mesh-Netzwerk, wo sich dann Synchronisierungsschleifen bilden, die ins reine Chaos führen. Aber grundsätzlich bin ich sehr zufrieden mit dem kleinen Open Source Programm und werde es auch weiterhin verwenden. Allerdings ist mir vor kurzem aufgefallen, dass nicht alle Dinge im Lot sind.
Out-Of-Sync feiert fröhliche Urständ
Ohne es großartig im Alltag zu bemerken hat sich eine stattliche Anzahl an Dateien angesammelt, die im Zustand “out-of-sync” verweilen. Die grafische Oberfläche für Linux Rechner ist überhaupt nicht hilfreich…
… denn es steht nur “Syncing (95%)“. Aha, erst die Weboberfläche ist etwas auskunftsfreudiger (und widerspricht gleich einmal der Desktopvariante):
27.355 Items, oder 56.2 GiB, oder 92 (!) Prozent, die nicht synchron sein sollen. Eine Katastrophe!
Aber was ist hier wirklich los? Und wo befindet sich nun der “gute Datensatz”? Ich vermute auf dem Laptop, denn dorthin werden üblicherweise alle neuen Daten erstellt, oder draufkopiert, aber kann ich mir hier sicher sein? Denn dann könnte ich die TrueNAS-Seite temporär, oder vielleicht sogar permanent, auf Receive-Only setzen. Mit dieser Einstellung sollten alle Unterschiede auf der TrueNAS-Seite ignoriert werden und einfach mit der Version vom Laptop überschrieben werden.
Ich möchte also herausfinden, ob es auf irgendeiner Seite an Dateien fehlt, und wenn es nicht fehlende Dateien sind, die diese Out-of-Sync-Meldungen hervorbringen, dann möchte ich auch wissen, welche Metadaten (also Dinge wie Datumsstempel und möglicherweise Berechtigungen) die “besseren” sind, sprich von wo ich wohin überschreiben soll.
Das WebGUI ist leider recht unbrauchbar für diesen Zweck, man kann zwar auf die Items klicken, aber dann bekommt man nur eine unverdaubare Liste an Dateien, die halt unsynchron sind, aber man auch nicht erfährt was, wieso und weshalb:
OK, der Laptop hat die JPEGs 2020 modifiziert, ja eh, und? Die Oberfläche gibt keine weiteren Angaben preis und man kann sich, wenn der Tag lang ist, durch mehr als zweieinhalb Tausend Seiten klicken, die einem nicht weiterhelfen:
Für mich ist daher der erste Schritt den tatsächlichen Stand der Dinge zu erfassen. Vielleicht formt mich hier meine Arbeit, wo ich tagtäglich mit dem Byte-genauen Erhalt von digitalen Daten zu tun habe.
Rsync, bitte trocken!
Mein erster Versuch wird sein, einfach mithilfe des sehr hilfreichen Werkzeuges rsync, die betroffenen Ordner auf beiden Seiten vergleichen lassen. Rsync steht für “remote sync”, also das abgleichen von Datensätzen über das Netzwerk und noch besser man kann das Werkzeug so konfigurieren, dass es statt des tatsächlichen Kopierens einen Testlauf macht. Das nennt sich “dry run” bei rsync. Ich habe daher den Chatbot angeworfen und nach einem Beispielkommando für diesen Zweck befragt. Das ist der Vorschlag:
rsync -avnc --itemize-changes -e ssh /path/to/local/photos/ your_username@truenas_ip:/path/to/remote/photos/
Schaut schon ganz gut aus, die Flags (also Buchstaben nach dem ersten Minus) “avnc” stehen für:
- a - “archival” - die Berechtigungen und Zeitstempel bleiben erhalten
- v - “verbose” - rsync wirft detailliertere Informationen aus
- n - “dry run” - der Buchstabe passt hier zwar nicht, ist aber genau die richtige Option für unseren Testlauf
- c “checksum” - Der tatsächliche Inhalt der Dateien wird mittels Prüfsummen verglichen und Bytefehler können erkannt werden. Normal werden nur Zeitstempel und Co. für den Vergleich herangezogen. Wir wollen aber hier genau sein.
Weitere Optionen:
--itemize-changes
- gibt detaillierte Infos pro Datei aus. Ich bin mir nicht sicher, inwiefern sich das von der “verbose” Angabe unterscheidet, aber ich lasse es einmal drinnen.-e ssh
- aktiviert die Verbindung mittels SSH-Tunnel.
Also versuchen wir es mit einem Unterordner, der als unsynchron angezeigt wird…
rsync -avnc --itemize-changes -e ssh /home/Username/Documents/betroffener\ Ordner/ admin@192.168.x.x:/mnt/pool/dataset/Documents/betroffener\ Ordner/
… und bemerken, dass da nix gescheites zurückkommt:
sending incremental file list
sent 121,948 bytes received 30 bytes 10,606.78 bytes/sec
total size is 2,433,250,935 speedup is 19,948.28 (DRY RUN)
Der Chatbot meint mit der Zusatzoption --stats
bekommt man genauere Infos und das Ganze soll man auch in eine Textdatei mit Zeitstempel speichern. Der Befehl schaut so aus:
rsync -avnc --itemize-changes -e ssh /home/Username/Documents/betroffener\ Ordner/ admin@192.168.x.x:/mnt/pool/dataset/Documents/betroffener\ Ordner/ > "rsync_output_$(date +"%Y-%m-%d_%H-%M-%S").txt"
Das Ergebnis ist eine relativ lange Liste mit Dateipfaden…
sending incremental file list
.d..t...... ./
<fc........ DOC-20230224-WA0000..pdf
<fc........ HP Workstation.pdf
{...}
… und am Ende noch eine Zusammenfassung:
Number of files: 65,799 (reg: 65,137, dir: 662)
Number of created files: 0
Number of deleted files: 0
Number of regular files transferred: 3,179
Total file size: 138,417,566,696 bytes
Total transferred file size: 572,271,630 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 262,102
File list generation time: 0.917 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 2,965,349
Total bytes received: 10,801
sent 2,965,349 bytes received 10,801 bytes 5,807.12 bytes/sec
total size is 138,417,566,696 speedup is 46,508.93 (DRY RUN)
Die Zusammenfassung findet keine Unterschiede in den Dateien selbst, das Problem muss also bei den Metadaten (Zeitstempel, Berechtigungen etc.) liegen. Der Chatbot meint zu den Präfixen (.d..t......
und <fc........
– nur diese beiden Codes kommen in der Liste vor):
Es hat bei den betroffenen Ordnern also eine Differenz beim Änderungsdatum ergeben und bei den Dateien…
… unterscheiden sich die Prüfsummen und/oder Metadaten, dafür aber kein verändertes Änderungsdatum. Das ist eigenartig. Wie können es die Prüfsummen sein, wenn wir ja einen Prüfsummencheck (siehe rsync flag oben) machen?
The checksum, the merrier
Dann gehen wir einmal ganz tief in die Datenmaterie hinein und überprüfen eine Beispieldatei so auf Herz und Nieren mit md5sum
, ls -la
und stat
.
Prüfsummencheck:
md5sum HP\ Workstation.pdf
8411cfa8229bbe13d3bf96799b029239 Documents/HP Workstation.pdf
Berechtigungen und Besitzer:
ls -la HP\ Workstation.pdf
-rw-------. 1 usr usr 69141 Dec 23 2021 'HP Workstation.pdf'
Detaillierte Angaben mit dem Befehl stat
:
stat HP\ Workstation.pdf
File: HP Workstation.pdf
Size: 69141 Blocks: 136 IO Block: 4096 regular file
Device: 0,50 Inode: 687759 Links: 1
Access: (0600/-rw-------) Uid: ( 1000/ mf) Gid: ( 1000/ mf)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2025-03-19 19:15:11.931287703 +0100
Modify: 2021-12-23 16:36:12.758854000 +0100
Change: 2024-12-30 18:25:54.214931524 +0100
Birth: 2024-12-30 18:25:54.203931588 +0100
Auf der TrueNAS-Seite folgende Werte:
md5sum HP\ Workstation.pdf
md5sum: 'HP Workstation.pdf': Permission denied
ls -la HP\ Workstation.pdf
-rw------- 1 www-data www-data 69141 Dec 23 2021 'HP Workstation.pdf'
stat HP\ Workstation.pdf
File: HP Workstation.pdf
Size: 69141 Blocks: 137 IO Block: 69632 regular file
Device: 0,54 Inode: 56668 Links: 1
Access: (0600/-rw-------) Uid: ( 33/www-data) Gid: ( 33/www-data)
Access: 2021-12-23 16:36:12.758854000 +0100
Modify: 2021-12-23 16:36:12.758854000 +0100
Change: 2024-07-22 21:39:39.565352834 +0200
Birth: 2024-03-04 00:51:36.626147430 +0100
Was kann man daraus lesen? Die Berechtigungen sind sehr strikt ausgelegt, 0600/-rw-------
bedeutet, dass nur der Besitzer Lese- und Schreibrechte hat. Deswegen schlägt der md5sum
Checksummenbefehl auf der TrueNAS-Seite fehl (gehört dem User www-data
– das ist der Standard-User für Web-Angelegenheiten und muss so gesetzt werden, ansonsten wird Nextcloud ganz unrund – was so in Ordnung ist, aber vom User admin
nicht gelesen werden kann).
Warum liegt hier -rw-------.
herum?
Folgende Fragen stelle ich mir hier: Wie kommt dieses dahergelaufene PDF eigentlich dazu so strenge Berechtigungen zu haben? Die 0600
-er Berechtigung wird üblicherweise für SSH-Schlüsseldateien empfohlen. Ein Blick in den Dokumentenordner zeigt ein Durcheinander verschiedener Berechtigungen bei den Dateien:
-rw-rw-rw-.
-rw-rw-r--.
-rw-------.
Also von Laissez-faire (jeder darf Lesen und Schreiben = -rw-rw-rw-.
) zu nur der Besitzer darf sich die Datei überhaupt auch nur anschauen und beschreiben (-rw-------.
) ist alles dabei. Vielleicht ist diese Krautundrübitis schuld an den Syncthing Schwierigkeiten?
Zumindest keine Datenunterschiede
Beruhigend ist zumindest einmal, das bei dieser Stichprobe keine tatsächlichen Datenunterschiede zu Tage getreten sind, denn die Dateigröße ist komplett identisch und sie wurde auch am selben Tag erstellt (oder verändert, bzw. runtergeladen – je länger ich mir die Bezeichnungen für die Zeitstempel ansehe, desto weniger ergeben sie Sinn, als ob dort ewiger Gegenteiltag herrschen würde).
Dennoch werde ich die Berechtigungen der Datei noch einmal manuell ändern und schauen was da los ist:
sudo chmod +r HP\ Workstation.pdf
[sudo] password for admin:
ls -la HP\ Workstation.pdf
-rw-r--r-- 1 www-data www-data 69141 Dec 23 2021 'HP Workstation.pdf'
md5sum HP\ Workstation.pdf
8411cfa8229bbe13d3bf96799b029239 HP Workstation.pdf
stat HP\ Workstation.pdf
File: HP Workstation.pdf
Size: 69141 Blocks: 137 IO Block: 69632 regular file
Device: 0,54 Inode: 56668 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 33/www-data) Gid: ( 33/www-data)
Access: 2021-12-23 16:36:12.758854000 +0100
Modify: 2021-12-23 16:36:12.758854000 +0100
Change: 2025-03-19 21:58:54.502506093 +0100
Birth: 2024-03-04 00:51:36.626147430 +0100
8411cfa8229bbe13d3bf96799b029239
vs.
8411cfa8229bbe13d3bf96799b029239
Die Datei ist wirklich identisch.
Und just in diesem Moment poppt die Syncthing-Meldung am Laptop auf, dass genau diese Datei synchronisiert wurde. Schauen wir noch einmal nach auf dem Laptop:
ls -la HP\ Workstation.pdf
-rw-r--r--. 1 mf mf 69141 Dec 23 2021 'HP Workstation.pdf'
stat HP\ Workstation.pdf
File: HP Workstation.pdf
Size: 69141 Blocks: 136 IO Block: 4096 regular file
Device: 0,50 Inode: 687759 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ mf) Gid: ( 1000/ mf)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2021-12-23 16:36:12.758854000 +0100
Modify: 2021-12-23 16:36:12.758854000 +0100
Change: 2025-03-19 21:59:04.533791183 +0100
Birth: 2024-12-30 18:25:54.203931588 +0100
Einzeiler Rabbit Hole
Jetzt wollte ich wissen, wie der Stand der Dinge auf meinem Laptop überhaupt ist. Nach einem längerem Hin und Her, zuerst mit ChatGPT und anschließend Deepseek, ist dieses Monstrum, pardon, dieser elegante Einzeiler rausgekommen:
f() { p="$(realpath -- "${1:-.}")"; start=$(date +%s); t="$(date +"%Y-%m-%d_%H-%M-%S")"; [ ! -d "$p" ] && echo "Error: '$p' invalid" >&2 && return 1 || o="perms_report_${t}_$(sed 's@/@_@g;s/ /-/g' <<<"$p").txt"; errlog="$(mktemp)"; (find "$p" -type d 2>&1 1>/dev/null; find "$p" -type f 2>&1 1>/dev/null) | grep -c 'Permission denied' >"$errlog"; dirs=$(find "$p" -type d 2>/dev/null | wc -l); files=$(find "$p" -type f 2>/dev/null | wc -l); inaccessible=$(<"$errlog"); rm -f "$errlog"; perm_dist=$(find "$p" -type f -exec stat -c '%a' {} + 2>/dev/null | sort | uniq -c | sort -rn | awk '{printf "%6d × %s\n", $1, $2}'); ownership_pat=$(find "$p" -type f -exec stat -c '%U:%G' {} + 2>/dev/null | sort | uniq -c | sort -rn | awk '{split($2,ug,":"); printf "%6d × %s:%s\n", $1, ug[1], ug[2]}'); sec_suid=$(find "$p" -type f \( -perm -4000 -o -perm -2000 \) -printf '%p\n' 2>/dev/null); sec_world=$(find "$p" -type f -perm -0002 -printf '%p\n' 2>/dev/null); duration=$(( $(date +%s) - start )); { echo "=== Permissions Analysis Report ==="; echo " Directory : '$p'"; echo " Generated : $(date +"%F %T")"; echo " Scan Time : ${duration} seconds"; echo "-----------------------------------"; echo " Folders : $((dirs - 1))"; echo " Files : $files"; echo " Denied : $inaccessible"; echo -e "\n[ Permission Distribution ]\n(number of files × octal permissions)"; echo "$perm_dist"; echo -e "\n[ Ownership Patterns ]\n(number of files × user:group)"; echo "$ownership_pat"; echo -e "\n[ Security Findings ]\n(SUID/SGID binaries)"; echo "$sec_suid"; echo -e "\n(world-writable files)"; echo "$sec_world"; } > "$o"; echo -e "\n══════════════════════════════════\n Permissions Report Generated\n File: ${o}\n ────────────────────────────\n Folders : $((dirs - 1))\n Files : $files\n Denied : $inaccessible\n Duration : ${duration}s\n══════════════════════════════════\n"; }; f
Am Ende des Befehls den zu untersuchenden Ordner (sei es ein absoluter /home/Username/Wunschordner
oder relativer Wunschordner
Pfad) hinzufügen und warten. Wie lange? Das sagt einem der Einzeiler auch nach Beendigung im Terminal:
══════════════════════════════════
Permissions Report Generated
File: perms_report_2025-03-19_23-35-06__home_Username_Wunschordner.txt
────────────────────────────
Folders : 662
Files : 65146
Denied : 0
Duration : 55s
══════════════════════════════════
Und das erstellte Textdokument enthält einen summarischen Bericht über das Vorkommen der verschiedenen Berechtigungen bzw. Besitzer. Zusätzlich werden am Ende noch die Dateipfade aller problematischen Dateien aufgelistet. Was sind problematische Dateien? Nun, solche die von jedem beliebigen Benutzer beschrieben (und damit auch gelöscht) werden können. Das ist potenziell gefährlich und aus Sicherheitssicht zu vermeiden.
So sieht der Bericht aus:
=== Permissions Analysis Report ===
Directory : '/home/Username/Wunschordner'
Generated : 2025-03-19 23:36:01
Scan Time : 55 seconds
-----------------------------------
Folders : 662
Files : 65146
Denied : 0
[ Permission Distribution ]
(number of files × octal permissions)
25306 × 775
21658 × 666
11770 × 644
2863 × 600
2539 × 664
570 × 766
316 × 740
102 × 777
21 × 755
1 × 660
[ Ownership Patterns ]
(number of files × user:group)
65146 × Username:Username
[ Security Findings ]
(SUID/SGID binaries)
(world-writable files)
/home/Username/Wunschordner/Schlimmer_Schlingel.txt
...
Und hier noch in komplett durchkommentierter Skriptform:
#!/usr/bin/env bash
##
## perm_analyzer - Comprehensive File Permission Auditor
## ======================================================
##
## Purpose:
## - Generates detailed reports about file/directory permissions
## - Identifies security risks (SUID/SGID, world-writable files)
## - Tracks permission distribution and ownership patterns
## - Helps audit compliance and discover misconfigurations
##
## Key Differences from One-Liner:
## 1. Readability: Structured with clear sections vs compressed one-liner
## 2. Error Handling: Explicit exit codes and validation
## 3. Portability: Uses POSIX-compliant substitutions
## 4. Maintenance: Easy to modify vs nested one-liner complexity
## 5. Temp Files: Safer cleanup with trap handling
##
# Initialization
# ---------------
# One-liner equivalent: p="$(realpath -- "${1:-.}")"
TARGET_DIR="${1:-.}" # Use first argument or current directory
REPORT_PREFIX="perms_report"
START_TIME=$(date +%s) # For duration calculation
# Validate target directory
# --------------------------
# [ ! -d "$p" ] && echo "Error..." (one-liner)
if [[ ! -d "$TARGET_DIR" ]]; then
echo >&2 "Error: Invalid directory '$TARGET_DIR'"
exit 1
fi
# Resolve absolute path and sanitize for filename
# -----------------------------------------------
# One-liner: p="$(realpath ... ); sed substitution
ABSOLUTE_PATH=$(realpath -- "$TARGET_DIR")
SANITIZED_PATH=$(sed 's@/@_@g; s/[^[:alnum:]_-]/-/g' <<< "$ABSOLUTE_PATH")
# Generate output filename
# -------------------------
# Original: o="perms_report_${t}_... (date handling)
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
REPORT_FILE="${REPORT_PREFIX}_${TIMESTAMP}_${SANITIZED_PATH}.txt"
# Error Tracking Setup
# ---------------------
# Original: errlog="/tmp/permcheck_$$" + find pipeline
ERR_LOG=$(mktemp) # Safer temp file creation
trap 'rm -f "$ERR_LOG"' EXIT # Auto-cleanup
# Count Permission Denied Errors
# -------------------------------
# One-liner core: (find ... ) | grep -c ...
(
find "$ABSOLUTE_PATH" -type d 2>&1 1>/dev/null
find "$ABSOLUTE_PATH" -type f 2>&1 1>/dev/null
) | grep -c 'Permission denied' > "$ERR_LOG"
# Collect Directory/File Statistics
# ----------------------------------
# Original: dirs=... files=... (find | wc -l)
DIR_COUNT=$(find "$ABSOLUTE_PATH" -type d 2>/dev/null | wc -l)
FILE_COUNT=$(find "$ABSOLUTE_PATH" -type f 2>/dev/null | wc -l)
DENIED_COUNT=$(< "$ERR_LOG")
# Permission Distribution Analysis
# ---------------------------------
# One-liner: perm_dist=$(find ... | stat ... | sort | uniq...)
PERM_STATS=$(find "$ABSOLUTE_PATH" -type f -exec stat -c '%a' {} + 2>/dev/null | \
sort | uniq -c | sort -rn | \
awk '{printf "%6d × %s\n", $1, $2}')
# Ownership Pattern Analysis
# ---------------------------
# Original: ownership_pat=... (stat %U:%G processing)
OWNERSHIP_STATS=$(find "$ABSOLUTE_PATH" -type f -exec stat -c '%U:%G' {} + 2>/dev/null | \
sort | uniq -c | sort -rn | \
awk -F: '{split($0,a,":"); printf "%6d × %s:%s\n", a[1], a[2], a[3]}')
# Security Checks
# ----------------
# SUID/SGID (Original: sec_suid=...)
SUID_FILES=$(find "$ABSOLUTE_PATH" -type f \( -perm -4000 -o -perm -2000 \) -printf '%p\n' 2>/dev/null)
# World-writable (Original: sec_world=...)
WORLD_WRITABLE=$(find "$ABSOLUTE_PATH" -type f -perm -0002 -printf '%p\n' 2>/dev/null)
# Calculate Duration
# -------------------
# One-liner: duration=$(( ... ))
END_TIME=$(date +%s)
DURATION=$(( END_TIME - START_TIME ))
# Generate Report
# ----------------
{
# Header Section
echo "=== Permissions Analysis Report ==="
echo " Directory : '$ABSOLUTE_PATH'"
echo " Generated : $(date +"%F %T")"
echo " Scan Time : ${DURATION} seconds"
echo "-----------------------------------"
# Core Statistics
echo " Folders : $((DIR_COUNT - 1))" # Subtract root directory
echo " Files : $FILE_COUNT"
echo " Denied : $DENIED_COUNT"
# Detailed Analyses
echo -e "\n[ Permission Distribution ]\n(number of files × octal permissions)"
echo "$PERM_STATS"
echo -e "\n[ Ownership Patterns ]\n(number of files × user:group)"
echo "$OWNERSHIP_STATS"
echo -e "\n[ Security Findings ]\n(SUID/SGID binaries)"
echo "$SUID_FILES"
echo -e "\n(world-writable files)"
echo "$WORLD_WRITABLE"
} > "$REPORT_FILE"
# Final Output
# -------------
# Original: echo -e "\n════════..." summary
cat << EOF
══════════════════════════════════
Permissions Report Generated
File: ${REPORT_FILE}
────────────────────────────
Folders : $((DIR_COUNT - 1))
Files : $FILE_COUNT
Denied : $DENIED_COUNT
Duration : ${DURATION}s
══════════════════════════════════
EOF
Fazit
Diesen Beitrag kann man wohl mit “that escalated quickly” beschreiben. Zuerst hätte es nur eine Überprüfung von Syncthing werden sollen und am Ende habe ich mich Dateisystem-Berechtigungen und Metadaten auf der einen Seite, und elaborierten “Einzeilern” und Skripten auf der anderen befasst. Es war sehr lehhreich, aber die Dringlichkeit hat wohl etwas nachgelassen, nachdem ich verifiziert habe, dass sich nur die Metadaten zwischen den Syncthing Instanzen unterscheiden. Aber ich sollte diesen nicht-Zustand dennoch demnächst einmal beheben. Ich wollte der geneigten Leserschaft jedoch nicht die nicht ganz unkreativen Ergebnisse dieser Reise von Syncthing-Wartung zu file security audit verwehren. Vielleicht erweist sich dieses Skript oder dieser Einzeiler für wen anderen auch als hilfreich, oder als Basis für etwas noch besseres. Kann man eigentlich einfach im Text die Lizenz angeben? Wenn ja und falls das als Chatbot-Geschriebenes nicht sowieso in eine andere Kategorie fällt, dann stelle ich hiermit sowohl den Einzeiler, als auch das Skript unter GPLv3. Wohl bekomm’s!