Add vpn-data-mount: SMB mount from a NetBird-connected home box

Offered right after NetBird setup during required/base setup, matching
the requested flow (base packages -> NetBird -> data mount). Repeatable
by design rather than a one-shot step, since different services can have
data on different home boxes — asks for a home box IP every time and can
be run again for additional boxes/shares.

Flow: test for existing passwordless SSH first (covers "both boxes already
share a key via GitHub import, or any other means" for free — if it
already works, nothing else runs). If not, generate an SSH keypair and
offer ssh-copy-id or a manual/GitHub-import fallback (ssh-import-id, the
same mechanism base.sh's own SSH setup already uses) — needed because a
home box that took base.sh's "disable password login" option won't accept
ssh-copy-id at all. Once passwordless SSH works, use it to remotely
install and configure Samba on the home box for a chosen path, then mount
it locally over CIFS with a tagged /etc/fstab entry.

SMB over NFS/SSHFS per this session's direction: not a "huge" speed gap
for normal use, and SSHFS's own encryption is redundant overhead once the
VPN tunnel already encrypts everything. Guest-accessible (no separate
Samba credentials) since the VPN is the real access control — only
NetBird-connected peers can reach the home box's NetBird IP at all.

Also:
- cifs-utils added to base.sh's always-installed packages, same reasoning
  as Docker/Compose being unconditional there instead of installed lazily
  on first mount.
- is_installed()/install_count() in setup.sh gained a vpn-data-mount case
  (state lives in tagged /etc/fstab entries, not $DOCKER_DIR, since this
  isn't a Docker service) — mirrors wordpress's "count real instances"
  handling rather than a flat 0/1.
- Every SSH call in the new service explicitly runs as $ACTUAL_USER
  (sudo -u), not root — the script itself runs as root throughout, but the
  SSH key lives in $ACTUAL_HOME/.ssh, so a bare `ssh` call would silently
  use root's own ~/.ssh instead and never find it. Caught by review before
  this shipped, not after.
- UNATTENDED mode skips outright with a message instead of spinning
  forever on prompt_text's always-blank default under --unattended, since
  none of this flow's prompts (home box IP, remote path, ...) have a
  sane non-interactive default.
This commit is contained in:
Claude
2026-08-10 17:56:50 +00:00
parent 63c227f101
commit 0e42de1cda
4 changed files with 373 additions and 5 deletions
+8 -2
View File
@@ -105,6 +105,9 @@ is_installed() {
# $DOCKER_DIR/wordpress dir the default case below could match) —
# [installed] means "at least one site exists", not any specific one.
wordpress) compgen -G "$DOCKER_DIR/wordpress-*" >/dev/null 2>&1 ;;
# Not a Docker service — state lives in tagged /etc/fstab entries
# (services/vpn-data-mount.sh's own convention), not $DOCKER_DIR.
vpn-data-mount) grep -q '^# vpn-data-mount:' /etc/fstab 2>/dev/null ;;
*) [ -e "$DOCKER_DIR/$1" ] ;;
esac
}
@@ -114,14 +117,17 @@ is_installed() {
# CLAUDE.md (a base install plus any number of "<name>-<suffix>" siblings,
# e.g. mattermost + mattermost-team-b). Only the default case knows that
# naming convention; the specially-cased services above aren't part of the
# multi-instance pattern (wordpress is the one exception and already counts
# sites directly), so for those this just mirrors is_installed() as 0 or 1.
# multi-instance pattern (wordpress and vpn-data-mount are the exceptions
# and already count sites/mounts directly), so for those this just mirrors
# is_installed() as 0 or 1.
install_count() {
case "$1" in
base|glow|crowdsec|security-dashboard|kdeconnect|silent-send|sync-cc|sky-cam|sky-cam-frigate|asterisk|pstn-trunk|sms-inbound|ssh-config)
is_installed "$1" && echo 1 || echo 0 ;;
wordpress)
find "$DOCKER_DIR" -mindepth 1 -maxdepth 1 -name 'wordpress-*' -type d 2>/dev/null | wc -l ;;
vpn-data-mount)
grep -c '^# vpn-data-mount:' /etc/fstab 2>/dev/null || echo 0 ;;
*)
local c=0
[ -e "$DOCKER_DIR/$1" ] && c=1