Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f64b12a77 | ||
|
|
aa3ad2f716 | ||
|
|
5f532fe10a |
@@ -1,7 +1,8 @@
|
||||
name: CI
|
||||
|
||||
# K-114 (nexus-hub): Mini-CI für proxmox-scripts — Syntax, build.func-Source-
|
||||
# Pflicht und Validierungs-Unit-Tests. Läuft auf dem nexus-Runner (Host-Mode).
|
||||
# Pflicht und Validierungs-Unit-Tests. Läuft auf dem instanzweiten Runner
|
||||
# (Label homelab, ohne Deploy-Rechte — PR #6 / ct/runner.sh).
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -11,7 +12,7 @@ on:
|
||||
jobs:
|
||||
lint:
|
||||
name: Shell-Lint (bash -n, source-check, Validierungs-Tests)
|
||||
runs-on: nexus
|
||||
runs-on: homelab
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -26,7 +26,7 @@ All defaults (`DEFAULT_HOSTNAME`, `DEFAULT_DISK`, …) are settable per-call via
|
||||
|
||||
## Contributing (verbindlich seit K-114)
|
||||
|
||||
**Alle Änderungen laufen als PR mit Cross-Review** — keine Direkt-Commits auf `main`. Hintergrund: Der „`build.func` nicht gesourct"-Bug hat es einmal bis in die Produktion geschafft (authentik-Anlage), während dieselbe Fehlerklasse im nexus-db-PR vom Review gefangen wurde. Die CI (`.gitea/workflows/ci.yml`, Runner-Label `nexus`) erzwingt zusätzlich: `bash -n` über alle Scripts, „jedes `ct/*.sh` sourct `build.func`" (`tests/check_ct_source.sh`) und die Validierungs-Unit-Tests (`tests/test_validation.sh`).
|
||||
**Alle Änderungen laufen als PR mit Cross-Review** — keine Direkt-Commits auf `main`. Hintergrund: Der „`build.func` nicht gesourct"-Bug hat es einmal bis in die Produktion geschafft (authentik-Anlage), während dieselbe Fehlerklasse im nexus-db-PR vom Review gefangen wurde. Die CI (`.gitea/workflows/ci.yml`, Runner-Label `homelab` — instanzweiter Runner aus `ct/runner.sh`) erzwingt zusätzlich: `bash -n` über alle Scripts, „jedes `ct/*.sh` sourct `build.func`" (`tests/check_ct_source.sh`) und die Validierungs-Unit-Tests (`tests/test_validation.sh`).
|
||||
|
||||
Eingaben in `prompt_lxc_config` sind validiert (Ziffern-Checks, IP/CIDR/Gateway-Format, Re-Prompt bei unsichtbaren Steuer-/Non-ASCII-Zeichen — Lesson vom 2026-06-11). Neue App-Prompts bitte über `prompt_validated`/`require_valid` aus `lib/build.func` bauen statt nacktem `read`.
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ CRED_FILE="/root/authentik.credentials"
|
||||
|
||||
# ── base packages + Docker ────────────────────────────────────────────────────
|
||||
setup_base_apt ca-certificates curl
|
||||
configure_ssh_root_login
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
msg_info "Installing Docker (get.docker.com)..."
|
||||
curl -fsSL https://get.docker.com | sh >/dev/null
|
||||
|
||||
@@ -19,6 +19,7 @@ DEVPI_PORT="3141"
|
||||
|
||||
# ── packages + user + dirs ───────────────────────────────────────────────────
|
||||
setup_base_apt python3 python3-venv python3-pip
|
||||
configure_ssh_root_login
|
||||
|
||||
create_system_user "$DEVPI_USER" "$DEVPI_HOME"
|
||||
mkdir -p "$DEVPI_DATA"
|
||||
|
||||
@@ -36,6 +36,7 @@ CRED_FILE="/root/nexus-db.credentials"
|
||||
|
||||
# ── packages: PGDG repo + PostgreSQL 16 + pgvector ────────────────────────────
|
||||
setup_base_apt curl ca-certificates gnupg lsb-release
|
||||
configure_ssh_root_login
|
||||
|
||||
if [[ ! -f /etc/apt/sources.list.d/pgdg.sources ]] && [[ ! -f /etc/apt/sources.list.d/pgdg.list ]]; then
|
||||
msg_info "Adding PGDG apt repo..."
|
||||
|
||||
@@ -46,6 +46,7 @@ run_user() { runuser -u "$APP_USER" -- env HOME="$APP_HOME" "$@"; }
|
||||
|
||||
# ── packages: git + rsync + sudo ; Node.js via NodeSource ─────────────────────
|
||||
setup_base_apt git rsync sudo
|
||||
configure_ssh_root_login
|
||||
|
||||
NODE_HAVE="$(command -v node >/dev/null 2>&1 && node -v | sed -E 's/^v([0-9]+).*/\1/' || echo 0)"
|
||||
if [[ "$NODE_HAVE" != "$NODE_MAJOR" ]]; then
|
||||
|
||||
@@ -44,6 +44,7 @@ run_user() { runuser -u "$APP_USER" -- env HOME="$APP_HOME" "$@"; }
|
||||
|
||||
# ── packages: git + rsync + sudo ; Node.js via NodeSource ────────────────────
|
||||
setup_base_apt git rsync sudo
|
||||
configure_ssh_root_login
|
||||
|
||||
NODE_HAVE="$(command -v node >/dev/null 2>&1 && node -v | sed -E 's/^v([0-9]+).*/\1/' || echo 0)"
|
||||
if [[ "$NODE_HAVE" != "$NODE_MAJOR" ]]; then
|
||||
|
||||
+24
-1
@@ -147,6 +147,16 @@ is_cidr() {
|
||||
|
||||
is_ipcfg() { [[ "$1" == "dhcp" ]] || is_cidr "$1"; }
|
||||
|
||||
# Ja/Nein-Antworten (Prompts wie "… erlauben? [Y/n]"). Akzeptiert
|
||||
# deutsch/englisch, normalize_yesno macht daraus kanonisch yes|no.
|
||||
is_yesno() { is_clean_ascii "$1" && [[ "${1,,}" =~ ^(y|yes|j|ja|n|no|nein)$ ]]; }
|
||||
normalize_yesno() {
|
||||
case "${1,,}" in
|
||||
y|yes|j|ja) printf 'yes' ;;
|
||||
*) printf 'no' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Space/comma-separated list of IPv4s (DNS prompt). Gesamtstring zuerst
|
||||
# prüfen — die Wort-Splittung würde eingebettete Newlines sonst verstecken.
|
||||
is_ipv4_list() {
|
||||
@@ -311,6 +321,17 @@ prompt_lxc_config() {
|
||||
else
|
||||
msg_warn "No network profile for VLAN ${VLAN_TAG:-none}; DHCP DNS will be inherited."
|
||||
fi
|
||||
|
||||
# SSH-Root-Login (Default: ja, Homelab-Komfort). Umgesetzt wird das im
|
||||
# Install-Pfad per sshd-Drop-in (configure_ssh_root_login, lib/install.func);
|
||||
# bootstrap_install_script reicht den normalisierten Wert in den Container.
|
||||
if [[ -z "${SSH_ROOT_LOGIN:-}" ]]; then
|
||||
prompt_validated SSH_ROOT_LOGIN "SSH-Root-Login erlauben? [Y/n]: " is_yesno "y"
|
||||
else
|
||||
require_valid SSH_ROOT_LOGIN is_yesno "SSH root login (y/n)"
|
||||
fi
|
||||
SSH_ROOT_LOGIN="$(normalize_yesno "$SSH_ROOT_LOGIN")"
|
||||
echo " → SSH root login: $SSH_ROOT_LOGIN"
|
||||
}
|
||||
|
||||
# ── template ─────────────────────────────────────────────────────────────────
|
||||
@@ -408,7 +429,9 @@ bootstrap_install_script() {
|
||||
pct exec "$CTID" -- bash -c "apt-get update -qq && apt-get install -y -qq curl ca-certificates >/dev/null"
|
||||
|
||||
msg_info "Running installer ($url)..."
|
||||
pct exec "$CTID" -- bash -c "curl -fsSL '$url' -o /root/${APP}-install.sh && bash /root/${APP}-install.sh"
|
||||
# SSH_ROOT_LOGIN ist durch normalize_yesno kanonisch yes|no — als Env in
|
||||
# den Container durchreichen (configure_ssh_root_login wertet es aus).
|
||||
pct exec "$CTID" -- bash -c "curl -fsSL '$url' -o /root/${APP}-install.sh && SSH_ROOT_LOGIN='${SSH_ROOT_LOGIN:-yes}' bash /root/${APP}-install.sh"
|
||||
}
|
||||
|
||||
# ── summary ──────────────────────────────────────────────────────────────────
|
||||
|
||||
+48
-2
@@ -15,10 +15,14 @@ msg_warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
msg_err() { echo -e "${RED}[✗]${NC} $*" >&2; }
|
||||
|
||||
# ── apt ──────────────────────────────────────────────────────────────────────
|
||||
# Always installs: ca-certificates curl openssl tzdata gnupg
|
||||
# Always installs: ca-certificates curl openssl tzdata gnupg locales
|
||||
# Additional packages can be passed as args.
|
||||
setup_base_apt() {
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
# C.UTF-8 ist in glibc eingebaut und damit schon VOR dem locales-Paket
|
||||
# verfügbar — deckt den ersten apt/dpkg-Lauf ab (keine perl-Warnungen
|
||||
# "Setting locale failed" mehr, LXC-Templates kommen ohne Locale).
|
||||
export LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
msg_info "Updating apt index..."
|
||||
apt-get update -qq
|
||||
if [[ $# -gt 0 ]]; then
|
||||
@@ -27,17 +31,59 @@ setup_base_apt() {
|
||||
msg_info "Installing base packages..."
|
||||
fi
|
||||
apt-get install -y -qq \
|
||||
ca-certificates curl openssl tzdata gnupg \
|
||||
ca-certificates curl openssl tzdata gnupg locales \
|
||||
"$@" \
|
||||
>/dev/null
|
||||
setup_locales
|
||||
msg_ok "apt setup complete"
|
||||
}
|
||||
|
||||
# en_US.UTF-8 generieren und systemweit als Default setzen; C.UTF-8 braucht
|
||||
# keine Generierung (glibc-built-in). Idempotent: sed greift nur auf die
|
||||
# auskommentierte Zeile, locale-gen/update-locale sind re-run-sicher.
|
||||
setup_locales() {
|
||||
msg_info "Generating locales (en_US.UTF-8; C.UTF-8 built-in)..."
|
||||
sed -i 's/^# *en_US\.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
|
||||
locale-gen >/dev/null
|
||||
update-locale LANG=en_US.UTF-8
|
||||
msg_ok "Default locale: en_US.UTF-8"
|
||||
}
|
||||
|
||||
apt_cleanup() {
|
||||
apt-get autoremove -y -qq >/dev/null || true
|
||||
apt-get autoclean -qq >/dev/null || true
|
||||
}
|
||||
|
||||
# ── ssh ──────────────────────────────────────────────────────────────────────
|
||||
# SSH-Root-Login gemäß Host-Prompt (prompt_lxc_config setzt SSH_ROOT_LOGIN,
|
||||
# bootstrap_install_script reicht es als Env durch; Default: yes).
|
||||
# yes → PermitRootLogin yes (Passwort-Login mit dem generierten Root-Passwort)
|
||||
# no → PermitRootLogin prohibit-password (Debian-Default, nur SSH-Key)
|
||||
# Umsetzung als Drop-in, damit Paket-Updates von sshd_config nicht kollidieren.
|
||||
configure_ssh_root_login() {
|
||||
local choice="${SSH_ROOT_LOGIN:-yes}" value
|
||||
case "$choice" in
|
||||
yes) value="yes" ;;
|
||||
no) value="prohibit-password" ;;
|
||||
*) msg_err "SSH_ROOT_LOGIN must be yes|no, got: '$choice'"; return 1 ;;
|
||||
esac
|
||||
|
||||
if [[ ! -d /etc/ssh/sshd_config.d ]]; then
|
||||
if [[ "$choice" == "no" ]]; then
|
||||
msg_warn "openssh-server not installed — nothing to configure (root login stays off)"
|
||||
return 0
|
||||
fi
|
||||
msg_info "Installing openssh-server..."
|
||||
apt-get install -y -qq openssh-server >/dev/null
|
||||
fi
|
||||
|
||||
msg_info "Configuring SSH root login: PermitRootLogin $value"
|
||||
printf 'PermitRootLogin %s\n' "$value" >/etc/ssh/sshd_config.d/zz-root-login.conf
|
||||
systemctl reload ssh 2>/dev/null || systemctl restart ssh 2>/dev/null \
|
||||
|| msg_warn "ssh.service not active yet — config applies on first start"
|
||||
msg_ok "SSH root login: $choice"
|
||||
}
|
||||
|
||||
# ── users / dirs ─────────────────────────────────────────────────────────────
|
||||
create_system_user() {
|
||||
local user="$1" home="$2"
|
||||
|
||||
@@ -77,6 +77,38 @@ assert_false "is_hostname lehnt 'a b' ab" is_hostname "a b"
|
||||
assert_true "is_token akzeptiert local-lvm" is_token "local-lvm"
|
||||
assert_false "is_token lehnt 'a;b' ab" is_token "a;b"
|
||||
|
||||
# ── Ja/Nein (SSH-Root-Login-Prompt) ──────────────────────────────────────────
|
||||
assert_true "is_yesno akzeptiert y" is_yesno "y"
|
||||
assert_true "is_yesno akzeptiert Ja" is_yesno "Ja"
|
||||
assert_true "is_yesno akzeptiert NO" is_yesno "NO"
|
||||
assert_true "is_yesno akzeptiert nein" is_yesno "nein"
|
||||
assert_false "is_yesno lehnt 'maybe' ab" is_yesno "maybe"
|
||||
assert_false "is_yesno lehnt leeren Wert ab" is_yesno ""
|
||||
[[ "$(normalize_yesno "J")" == "yes" && "$(normalize_yesno "nein")" == "no" ]] \
|
||||
&& ok "normalize_yesno kanonisiert J→yes, nein→no" \
|
||||
|| nok "normalize_yesno kanonisiert J→yes, nein→no"
|
||||
|
||||
# SSH-Root-Login-Prompt: leere Eingabe = Default Y → normalisiert yes;
|
||||
# explizites 'n' → no.
|
||||
ssh_default="$(
|
||||
printf '\n' | {
|
||||
SSH_ROOT_LOGIN=""
|
||||
prompt_validated SSH_ROOT_LOGIN "SSH-Root-Login erlauben? [Y/n]: " is_yesno "y" >/dev/null 2>&1
|
||||
normalize_yesno "$SSH_ROOT_LOGIN"
|
||||
}
|
||||
)"
|
||||
[[ "$ssh_default" == "yes" ]] && ok "SSH-Root-Login: leere Eingabe → Default yes" \
|
||||
|| nok "SSH-Root-Login Default (got: '$ssh_default')"
|
||||
ssh_no="$(
|
||||
printf 'n\n' | {
|
||||
SSH_ROOT_LOGIN=""
|
||||
prompt_validated SSH_ROOT_LOGIN "SSH-Root-Login erlauben? [Y/n]: " is_yesno "y" >/dev/null 2>&1
|
||||
normalize_yesno "$SSH_ROOT_LOGIN"
|
||||
}
|
||||
)"
|
||||
[[ "$ssh_no" == "no" ]] && ok "SSH-Root-Login: 'n' → no" \
|
||||
|| nok "SSH-Root-Login 'n' (got: '$ssh_no')"
|
||||
|
||||
# ── require_valid: env-Werte werden sanitisiert + geprüft ────────────────────
|
||||
CHECKVAL=$' 7\r'
|
||||
require_valid CHECKVAL is_uint "Testwert" && [[ "$CHECKVAL" == "7" ]] \
|
||||
@@ -145,12 +177,12 @@ ctid_out="$(
|
||||
smoke_out="$(
|
||||
env CTID=999 CT_HOSTNAME=smoke DISK_SIZE=8 CORES=2 RAM=1024 BRIDGE=vmbr0 \
|
||||
VLAN_TAG=20 TEMPLATE_STORAGE=local ROOTFS_STORAGE=local-lvm \
|
||||
IPCFG=10.11.20.99/24 GATEWAY=10.11.20.1 NAMESERVER="" \
|
||||
IPCFG=10.11.20.99/24 GATEWAY=10.11.20.1 NAMESERVER="" SSH_ROOT_LOGIN=J \
|
||||
NET_PROFILES_FILE="$REPO_ROOT/lib/networks.conf" \
|
||||
bash -c "source '$REPO_ROOT/lib/build.func' && prompt_lxc_config >/dev/null && echo SMOKE-OK"
|
||||
bash -c "source '$REPO_ROOT/lib/build.func' && prompt_lxc_config >/dev/null && echo SMOKE-OK:\$SSH_ROOT_LOGIN"
|
||||
)" || true
|
||||
[[ "$smoke_out" == *SMOKE-OK* ]] \
|
||||
&& ok "prompt_lxc_config Dry-Run mit validen env-Werten läuft durch" \
|
||||
[[ "$smoke_out" == *SMOKE-OK:yes* ]] \
|
||||
&& ok "prompt_lxc_config Dry-Run mit validen env-Werten läuft durch (SSH_ROOT_LOGIN J→yes)" \
|
||||
|| nok "prompt_lxc_config Dry-Run (got: '$smoke_out')"
|
||||
|
||||
smoke_bad="$(
|
||||
|
||||
Reference in New Issue
Block a user