diff --git a/README.md b/README.md index c74e9cc..d30d449 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,24 @@ docker compose up -d ## Proxmox LXC Install -Run inside a Debian/Ubuntu LXC container: +Run this **on the Proxmox host** — it creates a Debian 12 LXC container and installs Homelable inside automatically: ```bash -bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh) +bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/install-proxmox.sh) ``` -This installs the backend as a systemd service and serves the frontend via nginx. +Default container settings: 2 cores, 1 GB RAM, 8 GB disk, DHCP on `vmbr0`. Override before running: + +```bash +CTID=150 RAM=2048 STORAGE=local-zfs bash <(curl -fsSL .../install-proxmox.sh) +``` + +The backend runs as a systemd service, the frontend is served via nginx on port 80. + +> To install manually inside an existing Debian/Ubuntu machine or LXC: +> ```bash +> bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh) +> ``` --- diff --git a/scripts/install-proxmox.sh b/scripts/install-proxmox.sh new file mode 100755 index 0000000..b3740aa --- /dev/null +++ b/scripts/install-proxmox.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Homelable — Proxmox VE LXC creator +# Run this on the Proxmox HOST (not inside a container): +# bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/install-proxmox.sh) + +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${GREEN}[homelable]${NC} $*"; } +warn() { echo -e "${YELLOW}[homelable]${NC} $*"; } +error() { echo -e "${RED}[homelable]${NC} $*"; exit 1; } +step() { echo -e "\n${CYAN}▶ $*${NC}"; } + +# ── Must run on a Proxmox VE host ───────────────────────────────────────────── +[[ $EUID -ne 0 ]] && error "Run as root on the Proxmox host" +command -v pct &>/dev/null || error "pct not found — run this on a Proxmox VE host, not inside a container" + +# ── Settings (override via env vars) ────────────────────────────────────────── +CTID="${CTID:-$(pvesh get /cluster/nextid 2>/dev/null || echo 200)}" +HOSTNAME="${HOSTNAME:-homelable}" +STORAGE="${STORAGE:-local-lvm}" +DISK_SIZE="${DISK_SIZE:-8}" # GB +RAM="${RAM:-1024}" # MB +CORES="${CORES:-2}" +BRIDGE="${BRIDGE:-vmbr0}" +RAW="https://raw.githubusercontent.com/Pouzor/homelable/main" + +step "Creating Homelable LXC (CTID=$CTID, hostname=$HOSTNAME)" + +# ── Download Debian 12 template if needed ───────────────────────────────────── +TEMPLATE_STORAGE=$(pvesm status --content vztmpl | awk 'NR>1 {print $1; exit}') +TEMPLATE=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep "debian-12" | tail -1 | awk '{print $1}') + +if [[ -z "$TEMPLATE" ]]; then + info "Downloading Debian 12 LXC template..." + pveam update + TEMPLATE_NAME=$(pveam available --section system | grep "debian-12" | tail -1 | awk '{print $2}') + [[ -z "$TEMPLATE_NAME" ]] && error "Could not find a Debian 12 template" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE_NAME" + TEMPLATE="$TEMPLATE_STORAGE:vztmpl/$TEMPLATE_NAME" +fi + +info "Using template: $TEMPLATE" + +# ── Create the container ─────────────────────────────────────────────────────── +pct create "$CTID" "$TEMPLATE" \ + --hostname "$HOSTNAME" \ + --storage "$STORAGE" \ + --rootfs "${STORAGE}:${DISK_SIZE}" \ + --memory "$RAM" \ + --cores "$CORES" \ + --net0 "name=eth0,bridge=${BRIDGE},ip=dhcp" \ + --ostype debian \ + --unprivileged 1 \ + --features "nesting=1" \ + --start 1 + +info "Container $CTID created and started" + +# ── Wait for network ─────────────────────────────────────────────────────────── +info "Waiting for container network..." +for i in $(seq 1 20); do + if pct exec "$CTID" -- ping -c1 -W1 8.8.8.8 &>/dev/null; then + break + fi + [[ $i -eq 20 ]] && error "Container has no network after 20s — check bridge $BRIDGE" + sleep 1 +done + +# ── Grant NET_RAW for nmap (ping-based checks) ───────────────────────────────── +# lxc.cap.keep must include net_raw for nmap SYN scan in unprivileged containers +pct set "$CTID" --mp0 "" 2>/dev/null || true +echo "lxc.cap.keep = net_raw net_bind_service" >> /etc/pve/lxc/${CTID}.conf 2>/dev/null || true + +# ── Run the installer inside the container ──────────────────────────────────── +step "Running Homelable installer inside container $CTID..." +pct exec "$CTID" -- bash -c "curl -fsSL ${RAW}/scripts/lxc-install.sh | bash" + +# ── Done ────────────────────────────────────────────────────────────────────── +IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "") +echo "" +echo -e " ${GREEN}✓ Homelable installed in LXC $CTID${NC}" +echo -e " ${GREEN}✓ Open http://${IP}${NC}" +echo -e " Default login: ${YELLOW}admin / admin${NC}" +echo -e " ${YELLOW}⚠ Change the password: edit /opt/homelable/backend/.env (AUTH_PASSWORD_HASH)${NC}" +echo "" diff --git a/scripts/lxc-install.sh b/scripts/lxc-install.sh index eb33a1e..3798939 100755 --- a/scripts/lxc-install.sh +++ b/scripts/lxc-install.sh @@ -1,13 +1,16 @@ #!/usr/bin/env bash -# Homelable — LXC/VM bootstrap installer -# Compatible with Proxmox VE (Debian/Ubuntu LXC containers) -# Usage: bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh) +# Homelable — in-container installer +# Runs INSIDE a Debian/Ubuntu LXC container (called automatically by install-proxmox.sh) +# Can also be run manually inside any Debian/Ubuntu machine: +# bash <(curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/lxc-install.sh) set -euo pipefail INSTALL_DIR=/opt/homelable DATA_DIR=/opt/homelable/data SERVICE_USER=homelable +REPO_URL="https://github.com/Pouzor/homelable.git" +RAW="https://raw.githubusercontent.com/Pouzor/homelable/main" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' info() { echo -e "${GREEN}[homelable]${NC} $*"; } @@ -16,45 +19,36 @@ error() { echo -e "${RED}[homelable]${NC} $*"; exit 1; } [[ $EUID -ne 0 ]] && error "Run as root (sudo bash ...)" -# ── Detect OS ──────────────────────────────────────────────────────────────── -if [[ -f /etc/os-release ]]; then - . /etc/os-release - OS=$ID -else - error "Cannot detect OS" -fi - +# ── Detect OS ───────────────────────────────────────────────────────────────── +[[ -f /etc/os-release ]] && . /etc/os-release || error "Cannot detect OS" info "Detected: $PRETTY_NAME" -[[ "$OS" =~ ^(debian|ubuntu)$ ]] || error "Requires Debian or Ubuntu" +[[ "$ID" =~ ^(debian|ubuntu)$ ]] || error "Requires Debian or Ubuntu" -# ── System deps ────────────────────────────────────────────────────────────── +# ── System deps ─────────────────────────────────────────────────────────────── info "Installing system dependencies..." apt-get update -qq -apt-get install -y -qq \ - python3 python3-pip python3-venv \ - nmap curl git nginx +apt-get install -y -qq python3 python3-pip python3-venv nmap curl git nginx -# ── Node.js (for frontend build) ───────────────────────────────────────────── +# ── Node.js 20 ──────────────────────────────────────────────────────────────── if ! command -v node &>/dev/null; then info "Installing Node.js 20..." curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y -qq nodejs fi -# ── Service user ───────────────────────────────────────────────────────────── +# ── Service user ────────────────────────────────────────────────────────────── if ! id "$SERVICE_USER" &>/dev/null; then useradd --system --shell /sbin/nologin "$SERVICE_USER" - info "Created user: $SERVICE_USER" + info "Created service user: $SERVICE_USER" fi # ── Clone / update repo ─────────────────────────────────────────────────────── -REPO_URL="https://github.com/Pouzor/homelable.git" if [[ -d "$INSTALL_DIR/.git" ]]; then info "Updating existing installation..." - git -C "$INSTALL_DIR" pull + git -C "$INSTALL_DIR" pull --quiet else info "Cloning repository..." - git clone "$REPO_URL" "$INSTALL_DIR" + git clone --quiet "$REPO_URL" "$INSTALL_DIR" fi mkdir -p "$DATA_DIR" @@ -65,38 +59,31 @@ cd "$INSTALL_DIR/backend" python3 -m venv .venv .venv/bin/pip install --quiet -r requirements.txt -# Generate config.yml if missing -if [[ ! -f config.yml ]]; then - cp config.yml.example config.yml 2>/dev/null || cat > config.yml <<'CONF' -auth: - username: admin - password_hash: "$2b$12$o/LWyvmBc978CNpSsHxcveXN0WqjAGW/gBR0.U.HURWbaYD3GCDqS" - -scanner: - ranges: - - "192.168.1.0/24" - -status_checker: - interval_seconds: 60 -CONF - warn "Created default config.yml — change admin password!" -fi - # Generate .env if missing if [[ ! -f .env ]]; then - SECRET=$(python3 -c "import secrets; print(secrets.token_hex(32))") + SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(32))") + # Default hash = bcrypt of "admin" (same as .env.example) cat > .env < /etc/systemd/system/homelable-backend.service < /etc/nginx/sites-available/homelable + ln -sf /etc/nginx/sites-available/homelable /etc/nginx/sites-enabled/homelable rm -f /etc/nginx/sites-enabled/default -nginx -t && systemctl reload nginx +nginx -t && systemctl reload nginx || systemctl start nginx # ── Enable & start ──────────────────────────────────────────────────────────── systemctl daemon-reload @@ -140,5 +129,5 @@ info "Done!" echo "" echo -e " ${GREEN}Homelable is running at http://$(hostname -I | awk '{print $1}')${NC}" echo -e " Default login: admin / admin" -echo -e " ${YELLOW}Change the password in $INSTALL_DIR/backend/config.yml${NC}" +echo -e " ${YELLOW}⚠ Change the password: edit $INSTALL_DIR/backend/.env (AUTH_PASSWORD_HASH)${NC}" echo ""