diff --git a/lxc-enroll.sh b/lxc-enroll.sh index 64b8eb5..bb9bf74 100644 --- a/lxc-enroll.sh +++ b/lxc-enroll.sh @@ -16,6 +16,7 @@ installed_label() { if [[ $1 == 1 ]]; then printf 'vorhanden'; else printf 'fehl LIST=0; DRY=0; MANAGER=''; VERSION=''; LOGIN_SERVER=''; SELECTION=''; VERSION_EXPLICIT=0 SSH_ENABLED=1; SSH_USER=''; SSH_SUDO=0; SSH_NOPASSWD=0; SAVE_SETTINGS=1 +TS_ACCEPT_DNS=0; DNS_SAVED=0 VARS_FILE=/etc/lxc-enroll/.vars while [[ $# -gt 0 ]]; do case "$1" in @@ -82,7 +83,8 @@ p=sys.argv[1]; s=os.lstat(p) if not stat.S_ISREG(s.st_mode) or s.st_uid != 0 or stat.S_IMODE(s.st_mode) & 0o077: sys.exit('Unsichere Rechte der .vars: regulaere Datei, root, Modus 600 erforderlich.') d=json.load(open(p)) -for k in ('manager','agent_version','login_server','ssh_enabled','ssh_user','ssh_sudo','ssh_nopasswd'): +for k in ('manager','agent_version','login_server','ssh_enabled','ssh_user','ssh_sudo','ssh_nopasswd','ts_accept_dns'): + if k not in d: continue v=d.get(k,'') if not isinstance(v,str) or any(c in v for c in '\n\r\t\0') or len(v)>16384: sys.exit('Ungueltige .vars.') @@ -98,6 +100,7 @@ PY ssh_user) SSH_USER=$value;; ssh_sudo) [[ $value =~ ^[01]$ ]] || fail 'Ungueltige Sudo-Einstellung.'; SSH_SUDO=$value;; ssh_nopasswd) [[ $value =~ ^[01]$ ]] || fail 'Ungueltige Sudo-Einstellung.'; SSH_NOPASSWD=$value;; + ts_accept_dns) [[ $value =~ ^[01]$ ]] || fail 'Ungueltige DNS-Einstellung.'; TS_ACCEPT_DNS=$value; DNS_SAVED=1;; esac done <<<"$fields" } @@ -105,10 +108,10 @@ PY save_settings() { [[ $SAVE_SETTINGS == 1 ]] || return 0 install -d -m 700 /etc/lxc-enroll - python3 - "$VARS_FILE" "$MANAGER" "$VERSION" "$LOGIN_SERVER" "$SSH_ENABLED" "$SSH_USER" "$SSH_SUDO" "$SSH_NOPASSWD" <<'PY' + python3 - "$VARS_FILE" "$MANAGER" "$VERSION" "$LOGIN_SERVER" "$SSH_ENABLED" "$SSH_USER" "$SSH_SUDO" "$SSH_NOPASSWD" "$TS_ACCEPT_DNS" <<'PY' import json, os, sys, tempfile p=sys.argv[1] -keys=('manager','agent_version','login_server','ssh_enabled','ssh_user','ssh_sudo','ssh_nopasswd') +keys=('manager','agent_version','login_server','ssh_enabled','ssh_user','ssh_sudo','ssh_nopasswd','ts_accept_dns') fd,tmp=tempfile.mkstemp(prefix='.vars.',dir=os.path.dirname(p)) try: os.fchmod(fd,0o600) @@ -191,6 +194,8 @@ set +x umask 077 DIR=$1; MANAGER=$2; AGENT_VERSION=$3; LOGIN=$4; NAME=$5; WANT_TS=$6; WANT_WZ=$7 SSH_ENABLED=$8; SSH_USER=$9; SSH_SUDO=${10}; SSH_NOPASSWD=${11} +TS_ACCEPT_DNS=${12} +case "$TS_ACCEPT_DNS" in 1) DNS_FLAG=true;; 0) DNS_FLAG=false;; *) exit 1;; esac trap 'rm -f -- "$DIR/headscale.key" "$DIR/enrollment.pass" "$DIR/sudo.pass"' EXIT export DEBIAN_FRONTEND=noninteractive present() { [[ $(dpkg-query -W -f='${Status}' "$1" 2>/dev/null || true) == 'install ok installed' ]]; } @@ -222,13 +227,17 @@ if [[ $WANT_TS == 1 ]]; then apt-get -o DPkg::Lock::Timeout=120 update apt-get -o DPkg::Lock::Timeout=120 install -y --no-install-recommends tailscale systemctl enable --now tailscaled - # File reference keeps the key out of command arguments/history. Keep local DNS. - timeout 90 tailscale up --login-server="$LOGIN" --auth-key="file:$DIR/headscale.key" --hostname="$NAME" --accept-dns=false --timeout=60s + # File reference keeps the key out of command arguments/history. + timeout 90 tailscale up --login-server="$LOGIN" --auth-key="file:$DIR/headscale.key" --hostname="$NAME" --accept-dns="$DNS_FLAG" --timeout=60s rm -f -- "$DIR/headscale.key" tailscale ip -4 echo 'Tailscale installiert und angemeldet.' fi +# Explicitly accepted DNS settings may also enable MagicDNS on an existing client. +# Do not disable or reauthenticate an existing client when this option is declined. +if [[ $TS_ACCEPT_DNS == 1 && $WANT_TS == 0 ]]; then tailscale set --accept-dns=true; fi + if [[ $WANT_WZ == 1 ]]; then # A Headscale manager IP only becomes reachable after the new client joins. for port in 1514 1515; do @@ -319,7 +328,7 @@ deploy_one() { if [[ -s $TMP/sudo.pass ]]; then pct push "$id" "$TMP/sudo.pass" "$dir/sudo.pass" --user 0 --group 0 --perms 600 || return 1; fi fi # Credentials are files, never pct arguments. bash receives only non-secret settings. - pct exec "$id" -- bash "$dir/worker.sh" "$dir" "$MANAGER" "$VERSION" "$LOGIN_SERVER" "${NAMES[$id]}" "$ts" "$wz" "$SSH_ENABLED" "$SSH_USER" "$SSH_SUDO" "$SSH_NOPASSWD" || return 1 + pct exec "$id" -- bash "$dir/worker.sh" "$dir" "$MANAGER" "$VERSION" "$LOGIN_SERVER" "${NAMES[$id]}" "$ts" "$wz" "$SSH_ENABLED" "$SSH_USER" "$SSH_SUDO" "$SSH_NOPASSWD" "$TS_ACCEPT_DNS" || return 1 if [[ $ts == 1 ]]; then pct exec "$id" -- tailscale status --json | python3 -c 'import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get("BackendState")=="Running" and d.get("Self",{}).get("Online") is True else 1)' || { say "LXC $id: Headscale-Verbindung nicht als online bestaetigt."; return 1; @@ -394,7 +403,16 @@ main() { if [[ $need_ts == 1 ]]; then if [[ -z $LOGIN_SERVER ]]; then ask 'Headscale-Login-Server (vollstaendige HTTPS-URL):'; LOGIN_SERVER=$REPLY; fi [[ $LOGIN_SERVER =~ ^https://[A-Za-z0-9][A-Za-z0-9.:-]*/?$ ]] || fail 'Bitte eine HTTPS-Server-URL ohne Zugangsdaten/Query eingeben.' - say 'Neue Tailscale-Clients verwenden Headscale; vorhandene lokale DNS-Einstellungen bleiben aktiv.' + say 'Neue Tailscale-Clients verwenden Headscale.' + fi + if [[ $need_wz == 1 && ! $MANAGER =~ ^[0-9.:]+$ && $DNS_SAVED == 0 ]]; then TS_ACCEPT_DNS=1; fi + if [[ $TS_ACCEPT_DNS == 1 ]]; then input='[J/n]'; else input='[j/N]'; fi + yes_no "Headscale-DNS/MagicDNS fuer die Auswahl uebernehmen? $input" "$TS_ACCEPT_DNS" + TS_ACCEPT_DNS=$REPLY + if [[ $TS_ACCEPT_DNS == 1 ]]; then + say 'Headscale-DNS wird fuer ausgewaehlte Clients aktiviert. Lokale Namensaufloesung muss zu den Headscale-DNS-Einstellungen passen.' + else + say 'Neue Clients behalten lokales DNS bei. Der Manager-Name muss ueber diesen Resolver aufloesbar sein.' fi ask 'Nur diese fehlenden Komponenten installieren? Bitte INSTALL eingeben:' [[ $REPLY == INSTALL ]] || { say 'Abgebrochen.'; return 0; }