From b1844d805a8f190af00faf3f6e5bed7d997ecaae Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 19 Mar 2026 15:05:32 +0100 Subject: created sorter for sorting downloads with custom rules; created straper for recreation of server --- straper/install-base.sh | 342 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 straper/install-base.sh (limited to 'straper/install-base.sh') diff --git a/straper/install-base.sh b/straper/install-base.sh new file mode 100644 index 0000000..e2a2d42 --- /dev/null +++ b/straper/install-base.sh @@ -0,0 +1,342 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}/lib/common.sh" + +SCRIPT_NAME="install-base.sh" +USE_DB_APT_SOURCES=false +FROM_DB_MANUAL=false +HOSTNAME_WANTED="" +TIMEZONE_WANTED="Europe/Warsaw" +LOCALE_WANTED="en_US.UTF-8" + +usage() { + cat </dev/null 2>&1; then + report "${SCRIPT_NAME}" "dns" "bootstrap" "check" "ok" "resolver already working" "" + return 0 + fi + + if ! ping -c 1 -W 3 1.1.1.1 >/dev/null 2>&1; then + note_failure "${SCRIPT_NAME}" "dns" "bootstrap" "check" "no outbound IP connectivity" + die "bootstrap DNS check failed: no outbound IP connectivity" + fi + + if command -v dig >/dev/null 2>&1 && dig +time=2 +tries=1 +short @1.1.1.1 "${test_host}" >/dev/null 2>&1; then + [[ -e /etc/resolv.conf && ! -e "${backup}" ]] && cp -a /etc/resolv.conf "${backup}" || true + printf 'nameserver 1.1.1.1 +nameserver 9.9.9.9 +' > /etc/resolv.conf + chmod 644 /etc/resolv.conf || true + + if getent ahostsv4 "${test_host}" >/dev/null 2>&1; then + report "${SCRIPT_NAME}" "dns" "bootstrap" "fallback" "changed" "static resolv.conf applied" "${backup}" + return 0 + fi + fi + + note_failure "${SCRIPT_NAME}" "dns" "bootstrap" "check" "DNS still broken after fallback attempt" + die "bootstrap DNS check failed" +} + +install_pkg_group() { + local group_name="$1"; shift + local pkgs=("$@") + log "installing package group: ${group_name}" + if apt_install_if_missing "${pkgs[@]}"; then + report "${SCRIPT_NAME}" "packages" "${group_name}" "install" "ok" "group processed" "" + else + note_failure "${SCRIPT_NAME}" "packages" "${group_name}" "install" "apt install failed" + die "package group failed: ${group_name}" + fi +} + +apply_hostname_locale_timezone() { + if [[ -n ${HOSTNAME_WANTED} ]]; then + if [[ ${DRY_RUN} == true ]]; then + printf '[dry] hostnamectl set-hostname %s\n' "${HOSTNAME_WANTED}" + else + hostnamectl set-hostname "${HOSTNAME_WANTED}" + printf '%s\n' "${HOSTNAME_WANTED}" > /etc/hostname + fi + report "${SCRIPT_NAME}" "system" "hostname" "set" "changed" "${HOSTNAME_WANTED}" "" + fi + + if [[ -f /etc/locale.gen ]] && grep -Eq "^#?\s*${LOCALE_WANTED}\b" /etc/locale.gen 2>/dev/null; then + if [[ ${DRY_RUN} == true ]]; then + printf '[dry] enable locale %s in /etc/locale.gen\n' "${LOCALE_WANTED}" + else + if have_cmd locale-gen && have_cmd update-locale; then + sed -i "s/^# *\(${LOCALE_WANTED}\b.*\)/\1/" /etc/locale.gen + locale-gen "${LOCALE_WANTED}" >/dev/null + update-locale LANG="${LOCALE_WANTED}" >/dev/null + else + report "${SCRIPT_NAME}" "system" "locale" "manual" "manual" "locale tools missing; install locales first or rerun after base packages" "" + return 0 + fi + fi + report "${SCRIPT_NAME}" "system" "locale" "set" "changed" "${LOCALE_WANTED}" "" + else + report "${SCRIPT_NAME}" "system" "locale" "manual" "manual" "locale missing in /etc/locale.gen: ${LOCALE_WANTED}" "" + fi + + if [[ ${DRY_RUN} == true ]]; then + printf '[dry] set timezone %s\n' "${TIMEZONE_WANTED}" + else + ln -sf "/usr/share/zoneinfo/${TIMEZONE_WANTED}" /etc/localtime + dpkg-reconfigure -f noninteractive tzdata >/dev/null + fi + report "${SCRIPT_NAME}" "system" "timezone" "set" "changed" "${TIMEZONE_WANTED}" "" +} + +restore_apt_sources_from_db() { + local src="${PUBLIC_DIR}/packages/apt" + [[ -d ${src} ]] || { mark_manual "${SCRIPT_NAME}" "apt" "sources" "db/public/packages/apt missing"; return 0; } + + if ! prompt_yes_no "Restore APT sources from DB now?" no; then + report "${SCRIPT_NAME}" "apt" "sources" "restore" "skipped" "operator skipped" "" + return 0 + fi + + restore_path "${SCRIPT_NAME}" "apt" "sources.list" "${src}/sources.list" "/etc/apt/sources.list" + restore_path "${SCRIPT_NAME}" "apt" "sources.list.d" "${src}/sources.list.d" "/etc/apt/sources.list.d" + restore_path "${SCRIPT_NAME}" "apt" "preferences" "${src}/preferences" "/etc/apt/preferences" + restore_path "${SCRIPT_NAME}" "apt" "preferences.d" "${src}/preferences.d" "/etc/apt/preferences.d" + restore_path "${SCRIPT_NAME}" "apt" "apt.conf.d" "${src}/apt.conf.d" "/etc/apt/apt.conf.d" + + if [[ ${DRY_RUN} == true ]]; then + printf '[dry] apt-get update\n' + else + if apt-get update; then + report "${SCRIPT_NAME}" "apt" "update-after-restore" "update" "ok" "apt metadata refreshed" "" + else + note_failure "${SCRIPT_NAME}" "apt" "update-after-restore" "update" "apt-get update failed after restoring sources" + fi + fi +} + +package_available() { + local pkg="$1" + apt-cache show -- "${pkg}" >/dev/null 2>&1 +} + +install_from_db_manual() { + local f="${PUBLIC_DIR}/packages/apt-mark-manual.txt" pkg installed=0 skipped=0 failed=0 + [[ -f ${f} ]] || { mark_manual "${SCRIPT_NAME}" "packages" "db-manual" "apt-mark-manual.txt missing"; return 0; } + + while IFS= read -r pkg; do + [[ -n ${pkg} ]] || continue + [[ ${pkg} =~ ^# ]] && continue + if dpkg -s -- "${pkg}" >/dev/null 2>&1; then + ((installed+=1)) + continue + fi + if ! package_available "${pkg}"; then + ((skipped+=1)) + report "${SCRIPT_NAME}" "packages" "${pkg}" "install" "skipped" "package not available in current apt sources" "" + continue + fi + if [[ ${DRY_RUN} == true ]]; then + printf '[dry] apt-get install -y --no-install-recommends %s\n' "${pkg}" + ((installed+=1)) + continue + fi + if DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${pkg}"; then + ((installed+=1)) + report "${SCRIPT_NAME}" "packages" "${pkg}" "install" "changed" "installed from apt-mark-manual" "" + else + ((failed+=1)) + note_failure "${SCRIPT_NAME}" "packages" "${pkg}" "install" "apt install failed" + fi + done < <(list_from_file "${f}") + + report "${SCRIPT_NAME}" "packages" "db-manual-summary" "install" "ok" "installed=${installed} skipped=${skipped} failed=${failed}" "" +} +normalize_service_baseline() { + local keep_active=( + ssh.service + systemd-resolved.service + fail2ban.service + ) + + local stop_disable=( + dnsmasq.service + unbound.service + unbound-resolvconf.service + nginx.service + mariadb.service + postfix.service + prosody.service + prometheus.service + tor.service + i2pd.service + mumble-server.service + pygopherd.service + ) + + local unit + + for unit in "${stop_disable[@]}"; do + if systemctl list-unit-files --type=service --no-legend --no-pager 2>/dev/null | awk '{print $1}' | grep -qx -- "${unit}"; then + systemctl disable --now "${unit}" >/dev/null 2>&1 || true + fi + done + + systemctl reset-failed "${stop_disable[@]}" >/dev/null 2>&1 || true + + for unit in "${keep_active[@]}"; do + if systemctl list-unit-files --type=service --no-legend --no-pager 2>/dev/null | awk '{print $1}' | grep -qx -- "${unit}"; then + systemctl enable "${unit}" >/dev/null 2>&1 || true + systemctl restart "${unit}" >/dev/null 2>&1 || true + fi + done + + report "${SCRIPT_NAME}" "services" "baseline" "normalize" "changed" "safe services kept; app/conflicting services stopped" "" +} +start_safe_services() { + local svc + for svc in ssh systemd-resolved fail2ban; do + systemctl_safe_enable "${svc}" || true + systemctl_safe_start "${svc}" || true + done + + case "${DNS_MODE}" in + resolved) + systemctl_safe_enable systemd-resolved || true + systemctl_safe_start systemd-resolved || true + ;; + esac + report "${SCRIPT_NAME}" "services" "safe-start" "start" "ok" "safe services processed" "" +} + +if [[ ${DRY_RUN} == false ]]; then + bootstrap_dns_prepare + if apt-get update; then + report "${SCRIPT_NAME}" "apt" "update" "update" "ok" "initial apt update" "" + else + note_failure "${SCRIPT_NAME}" "apt" "update" "update" "initial apt-get update failed" + die "initial apt-get update failed" + fi +else + report "${SCRIPT_NAME}" "apt" "update" "update" "ok" "initial apt update (dry-run)" "" +fi + +install_pkg_group base "${BASE_PACKAGES[@]}" +case "${PROFILE}" in + minimal) ;; + core) install_pkg_group core "${CORE_PACKAGES[@]}" ;; + full) + install_pkg_group core "${CORE_PACKAGES[@]}" + install_pkg_group full-extra "${FULL_EXTRA_PACKAGES[@]}" + ;; +esac + +normalize_service_baseline +apply_hostname_locale_timezone + +if [[ ${USE_DB_APT_SOURCES} == true ]]; then + restore_apt_sources_from_db +fi + +if [[ ${FROM_DB_MANUAL} == true ]]; then + install_from_db_manual +fi + +install -d -m 0755 /srv /srv/www /srv/docs /etc/wireguard /etc/labunix /var/lib/labunix +report "${SCRIPT_NAME}" "filesystem" "base-dirs" "create" "ok" "/srv /etc/wireguard /etc/labunix prepared" "" + +if [[ ${START_SERVICES} == true ]]; then + start_safe_services +else + report "${SCRIPT_NAME}" "services" "safe-start" "start" "skipped" "--start-safe-services not requested" "" +fi + +set_state install_base done +report "${SCRIPT_NAME}" "run" "finish" "exit" "ok" "completed" "" +log "${SCRIPT_NAME}: done; report=${REPORT_FILE}" -- cgit v1.3