add user/samba restore script + yireh and isaac smb shares
This commit is contained in:
+58
-7
@@ -75,7 +75,7 @@ git clone /mnt/backup/argento/docker/gitea/data/git/repositories/timothykim/arge
|
|||||||
|
|
||||||
Clone first -- ZFS datasets will mount into this directory tree in the next step.
|
Clone first -- ZFS datasets will mount into this directory tree in the next step.
|
||||||
|
|
||||||
## 7. ZFS pools + data restore
|
## 7. ZFS pools
|
||||||
|
|
||||||
**If ZFS disks are intact (OS drive failure only)** -- the common case:
|
**If ZFS disks are intact (OS drive failure only)** -- the common case:
|
||||||
|
|
||||||
@@ -88,15 +88,12 @@ zpool import threeteras
|
|||||||
# Verify mountpoints (update if the old /opt/docker/ paths are still set):
|
# Verify mountpoints (update if the old /opt/docker/ paths are still set):
|
||||||
zfs set mountpoint=/opt/argento/docker/nextcloud/data nextcloud/data
|
zfs set mountpoint=/opt/argento/docker/nextcloud/data nextcloud/data
|
||||||
zfs set mountpoint=/opt/argento/smb/media threeteras/media
|
zfs set mountpoint=/opt/argento/smb/media threeteras/media
|
||||||
|
zfs set mountpoint=/opt/argento/smb/yireh threeteras/yireh
|
||||||
|
zfs set mountpoint=/opt/argento/smb/isaac threeteras/isaac
|
||||||
zfs set mountpoint=/opt/argento/docker/garage/data threeteras/garage
|
zfs set mountpoint=/opt/argento/docker/garage/data threeteras/garage
|
||||||
zfs set mountpoint=/opt/argento/docker/immich/library threeteras/immich
|
zfs set mountpoint=/opt/argento/docker/immich/library threeteras/immich
|
||||||
|
|
||||||
zpool status # verify pools are ONLINE
|
zpool status # verify pools are ONLINE
|
||||||
|
|
||||||
# Restore non-ZFS app data (Gitea, Minecraft, Jellyfin config) from USB:
|
|
||||||
rsync -a --exclude='docker/nextcloud/data/' --exclude='docker/garage/data/' \
|
|
||||||
--exclude='docker/immich/library/' --exclude='smb/' \
|
|
||||||
/mnt/backup/argento/ /opt/argento/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**If ZFS disks are lost (full rebuild):**
|
**If ZFS disks are lost (full rebuild):**
|
||||||
@@ -116,12 +113,66 @@ zfs create -o mountpoint=/opt/argento/docker/nextcloud/data nextcloud/data
|
|||||||
zpool create threeteras raidz2 /dev/disk/by-id/<disk3> ... /dev/disk/by-id/<disk8>
|
zpool create threeteras raidz2 /dev/disk/by-id/<disk3> ... /dev/disk/by-id/<disk8>
|
||||||
zfs set compression=lz4 threeteras
|
zfs set compression=lz4 threeteras
|
||||||
zfs create -o mountpoint=/opt/argento/smb/media threeteras/media
|
zfs create -o mountpoint=/opt/argento/smb/media threeteras/media
|
||||||
|
zfs create -o mountpoint=/opt/argento/smb/yireh -o quota=1T threeteras/yireh
|
||||||
|
zfs create -o mountpoint=/opt/argento/smb/isaac -o quota=1T threeteras/isaac
|
||||||
zfs create -o mountpoint=/opt/argento/docker/garage/data threeteras/garage
|
zfs create -o mountpoint=/opt/argento/docker/garage/data threeteras/garage
|
||||||
zfs create -o mountpoint=/opt/argento/docker/immich/library threeteras/immich
|
zfs create -o mountpoint=/opt/argento/docker/immich/library threeteras/immich
|
||||||
|
|
||||||
zpool status # verify pools are ONLINE
|
zpool status # verify pools are ONLINE
|
||||||
|
|
||||||
# Restore everything from USB (ZFS mountpoints are set, data lands in the right place):
|
# Per-user SMB share dirs need ownership + setgid (rsync in step 7c will
|
||||||
|
# preserve these if a USB backup exists, but freshly-created datasets are
|
||||||
|
# root-owned and need this fixup).
|
||||||
|
for u in yireh isaac; do
|
||||||
|
chown "$u":sambagroup "/opt/argento/smb/$u"
|
||||||
|
chmod 2770 "/opt/argento/smb/$u"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7b. Restore users
|
||||||
|
|
||||||
|
System users and the samba passdb are not part of the OS install or the
|
||||||
|
`/opt/argento/` rsync. Recreate them now, BEFORE the data rsync in step 7c,
|
||||||
|
so file ownership lands on the correct UIDs.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/opt/argento/scripts/restore-users.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This recreates timothykim, yireh, isaac, and the sambagroup group with
|
||||||
|
their original UIDs/GIDs, then restores the samba password hashes from
|
||||||
|
`/mnt/backup/samba-private/` if present.
|
||||||
|
|
||||||
|
If the samba passdb backup is missing, set passwords manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for u in timothykim yireh isaac; do
|
||||||
|
smbpasswd -a "$u"
|
||||||
|
smbpasswd -e "$u"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Set timothykim's Linux login password (not in any backup):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
passwd timothykim
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7c. Restore data from USB
|
||||||
|
|
||||||
|
**If ZFS disks were intact:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Skip the big ZFS-backed dirs — that data is already on disk
|
||||||
|
rsync -a --exclude='docker/nextcloud/data/' --exclude='docker/garage/data/' \
|
||||||
|
--exclude='docker/immich/library/' --exclude='smb/' \
|
||||||
|
/mnt/backup/argento/ /opt/argento/
|
||||||
|
```
|
||||||
|
|
||||||
|
**If ZFS was rebuilt from scratch:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Restore everything (ZFS mountpoints are set, data lands in the right place)
|
||||||
rsync -a /mnt/backup/argento/ /opt/argento/
|
rsync -a /mnt/backup/argento/ /opt/argento/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ RSYNC_OPTS="-a --delete"
|
|||||||
rsync $RSYNC_OPTS /opt/argento/ /mnt/backup/argento/ \
|
rsync $RSYNC_OPTS /opt/argento/ /mnt/backup/argento/ \
|
||||||
|| FAILURES+=("rsync argento")
|
|| FAILURES+=("rsync argento")
|
||||||
|
|
||||||
|
# Back up samba passdb (binary — holds SMB password hashes, not in /opt/argento)
|
||||||
|
mkdir -p /mnt/backup/samba-private
|
||||||
|
rsync -a --delete /var/lib/samba/private/ /mnt/backup/samba-private/ \
|
||||||
|
|| FAILURES+=("rsync samba passdb")
|
||||||
|
|
||||||
if [ ${#FAILURES[@]} -gt 0 ]; then
|
if [ ${#FAILURES[@]} -gt 0 ]; then
|
||||||
echo "BACKUP FAILED: ${FAILURES[*]}" >&2
|
echo "BACKUP FAILED: ${FAILURES[*]}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Executable
+137
@@ -0,0 +1,137 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Recreate human users + groups with stable UIDs/GIDs, and restore the
|
||||||
|
# samba passdb from USB backup. Run during recovery BEFORE the data rsync
|
||||||
|
# so file ownership lands on the right UIDs.
|
||||||
|
#
|
||||||
|
# UIDs/GIDs MUST match what the original system used, or ZFS files end up
|
||||||
|
# owned by orphan numeric IDs. The script aborts loudly if it finds an
|
||||||
|
# existing user/group with a different UID/GID than expected — investigate
|
||||||
|
# and either rename the conflicting entry or update the constants below.
|
||||||
|
#
|
||||||
|
# Verify on a live system with:
|
||||||
|
# id timothykim # check uid + primary gid
|
||||||
|
# getent group sambagroup # check sambagroup gid
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
[ "$EUID" -eq 0 ] || { echo "must run as root" >&2; exit 1; }
|
||||||
|
|
||||||
|
SAMBAGROUP_GID=1001
|
||||||
|
|
||||||
|
# Format: username:uid:gid:shell:home_flag:supp_groups
|
||||||
|
# - gid: numeric gid for the user's primary group (group name = username)
|
||||||
|
# - home_flag: "home" creates /home/<user>, "nohome" skips it (samba-only)
|
||||||
|
# - supp_groups: comma-separated supplementary groups, or "-" for none
|
||||||
|
USERS=(
|
||||||
|
"timothykim:1000:1000:/bin/bash:home:wheel,video,docker,sambagroup"
|
||||||
|
"yireh:1100:1100:/sbin/nologin:nohome:sambagroup"
|
||||||
|
"isaac:1101:1101:/sbin/nologin:nohome:sambagroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Abort if an existing entity's numeric ID doesn't match what we expect
|
||||||
|
assert_match() {
|
||||||
|
local label=$1 expected=$2 actual=$3
|
||||||
|
if [ "$expected" != "$actual" ]; then
|
||||||
|
echo "[FATAL] $label mismatch: expected $expected, got $actual" >&2
|
||||||
|
echo " ZFS file ownership will break if you proceed." >&2
|
||||||
|
echo " Either rename the conflicting entry or update this script." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "=== sambagroup ==="
|
||||||
|
if getent group sambagroup &>/dev/null; then
|
||||||
|
actual=$(getent group sambagroup | cut -d: -f3)
|
||||||
|
assert_match "sambagroup gid" "$SAMBAGROUP_GID" "$actual"
|
||||||
|
echo "[ok] sambagroup exists (gid $actual)"
|
||||||
|
else
|
||||||
|
groupadd -g "$SAMBAGROUP_GID" sambagroup
|
||||||
|
echo "[+] created sambagroup (gid $SAMBAGROUP_GID)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Preflight: every supplementary group referenced must already exist.
|
||||||
|
# wheel/video are OS-default; docker is created by docker-ce install (RUNBOOK
|
||||||
|
# step 2); sambagroup was just handled above. If anything's missing, abort
|
||||||
|
# before touching users — half-applied group memberships are annoying to undo.
|
||||||
|
echo ""
|
||||||
|
echo "=== preflight: supplementary groups ==="
|
||||||
|
missing=()
|
||||||
|
for entry in "${USERS[@]}"; do
|
||||||
|
supp=$(echo "$entry" | cut -d: -f6)
|
||||||
|
[ "$supp" = "-" ] && continue
|
||||||
|
IFS=, read -ra groups <<< "$supp"
|
||||||
|
for g in "${groups[@]}"; do
|
||||||
|
if ! getent group "$g" &>/dev/null; then
|
||||||
|
missing+=("$g")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
if [ ${#missing[@]} -gt 0 ]; then
|
||||||
|
# de-dupe for a cleaner error message
|
||||||
|
uniq_missing=$(printf '%s\n' "${missing[@]}" | sort -u | tr '\n' ' ')
|
||||||
|
echo "[FATAL] required supplementary groups missing: $uniq_missing" >&2
|
||||||
|
echo " likely cause: a package that creates the group hasn't been" >&2
|
||||||
|
echo " installed yet (e.g., docker-ce creates the docker group)." >&2
|
||||||
|
echo " complete RUNBOOK step 2 before running this script." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[ok] all supplementary groups present"
|
||||||
|
|
||||||
|
for entry in "${USERS[@]}"; do
|
||||||
|
IFS=: read -r u uid gid shell home_flag supp <<< "$entry"
|
||||||
|
echo ""
|
||||||
|
echo "=== $u ==="
|
||||||
|
|
||||||
|
# Primary group (same name as user, pinned GID — pre-created so useradd
|
||||||
|
# doesn't auto-assign one via USERGROUPS_ENAB)
|
||||||
|
if getent group "$u" &>/dev/null; then
|
||||||
|
actual=$(getent group "$u" | cut -d: -f3)
|
||||||
|
assert_match "$u primary group gid" "$gid" "$actual"
|
||||||
|
echo "[ok] group $u exists (gid $actual)"
|
||||||
|
else
|
||||||
|
groupadd -g "$gid" "$u"
|
||||||
|
echo "[+] created group $u (gid $gid)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# User account
|
||||||
|
if id "$u" &>/dev/null; then
|
||||||
|
actual_uid=$(id -u "$u")
|
||||||
|
actual_gid=$(id -g "$u")
|
||||||
|
assert_match "$u uid" "$uid" "$actual_uid"
|
||||||
|
assert_match "$u primary gid" "$gid" "$actual_gid"
|
||||||
|
echo "[ok] $u exists (uid $actual_uid, gid $actual_gid)"
|
||||||
|
else
|
||||||
|
if [ "$home_flag" = "home" ]; then
|
||||||
|
useradd -u "$uid" -g "$gid" -s "$shell" -m "$u"
|
||||||
|
else
|
||||||
|
useradd -u "$uid" -g "$gid" -s "$shell" -M "$u"
|
||||||
|
fi
|
||||||
|
echo "[+] created $u (uid $uid, gid $gid)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Supplementary groups (idempotent — usermod -aG won't error on duplicates)
|
||||||
|
if [ "$supp" != "-" ]; then
|
||||||
|
usermod -aG "$supp" "$u"
|
||||||
|
echo "[+] $u in supplementary groups: $supp"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== samba passdb ==="
|
||||||
|
if ! mountpoint -q /mnt/backup; then
|
||||||
|
echo "[!] /mnt/backup is not mounted — skipping samba passdb restore"
|
||||||
|
echo " mount the USB backup drive (RUNBOOK step 5) and re-run if you want"
|
||||||
|
echo " to restore preserved SMB passwords; otherwise set them manually:"
|
||||||
|
echo " for u in timothykim yireh isaac; do smbpasswd -a \"\$u\"; done"
|
||||||
|
elif [ -f /mnt/backup/samba-private/passdb.tdb ]; then
|
||||||
|
install -d -m 700 /var/lib/samba/private
|
||||||
|
cp /mnt/backup/samba-private/*.tdb /var/lib/samba/private/
|
||||||
|
chmod 600 /var/lib/samba/private/*.tdb
|
||||||
|
echo "[+] restored samba passdb from /mnt/backup/samba-private/"
|
||||||
|
else
|
||||||
|
echo "[!] no samba passdb backup found at /mnt/backup/samba-private/"
|
||||||
|
echo " run: for u in timothykim yireh isaac; do smbpasswd -a \"\$u\"; done"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Done ==="
|
||||||
|
echo "Next: set timothykim's Linux login password if not already (passwd timothykim)"
|
||||||
Reference in New Issue
Block a user