ova: fix swap, auto-launch setup wizard, add password change step

- Replace direct storage layout with explicit partitioning (no swap)
- Setup wizard now auto-launches on TTY1 via getty override instead
  of a separate systemd service that competed with console output
- Add step 1/7: prompt user to change default zroc password on first boot
- Update Makefile for QEMU-based build (was referencing old ovftool flow)
- Add backend package-lock.json for Docker build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Justin
2026-04-13 09:59:17 -04:00
parent 8b146bc340
commit 79c025430e
6 changed files with 2188 additions and 95 deletions
+44 -53
View File
@@ -1,64 +1,55 @@
# zroc-ova/Makefile # zroc-ova/Makefile — Build and manage zROC appliance images
VERSION ?= 1.0.0 VERSION ?= 1.0.0
PACKER_DIR = packer PACKER_DIR = packer
OUTPUT_DIR = output OUTPUT_DIR = output
OVA_NAME = zroc-appliance-$(VERSION)-ubuntu-26.04-amd64.ova VM_NAME = zroc-appliance
ARTIFACT_PFX = $(VM_NAME)-$(VERSION)-ubuntu-24.04-amd64
.PHONY: all init validate build build-qemu package checksum clean help .PHONY: all build validate clean init docker-build docker-push help
all: build package checksum all: build ## Build everything (default)
init: help: ## Show available targets
cd $(PACKER_DIR) && packer init ubuntu-2604.pkr.hcl
validate: init
cd $(PACKER_DIR) && packer validate \
-var "vm_version=$(VERSION)" \
-var-file=variables.pkrvars.hcl \
ubuntu-2604.pkr.hcl
@echo "✓ Template valid"
build: init
@echo "==> Building zROC OVA v$(VERSION) with VMware builder"
cd $(PACKER_DIR) && PACKER_LOG=1 packer build \
-var "vm_version=$(VERSION)" \
-var "headless=true" \
-var-file=variables.pkrvars.hcl \
ubuntu-2604.pkr.hcl
@echo "✓ Build complete"
build-qemu: init
@echo "==> Building zROC image v$(VERSION) with QEMU builder"
cd $(PACKER_DIR) && PACKER_LOG=1 packer build \
-only="qemu.ubuntu2604" \
-var "vm_version=$(VERSION)" \
-var-file=variables.pkrvars.hcl \
ubuntu-2604.pkr.hcl
package:
@echo "==> Packaging OVF to OVA"
@OVF=$$(find $(OUTPUT_DIR)/vmware -name "*.ovf" | head -1); \
if [ -z "$$OVF" ]; then echo "No OVF found in $(OUTPUT_DIR)/vmware"; exit 1; fi; \
ovftool --compress=9 "$$OVF" "$(OUTPUT_DIR)/$(OVA_NAME)"
@echo "✓ OVA: $(OUTPUT_DIR)/$(OVA_NAME)"
checksum:
@cd $(OUTPUT_DIR) && sha256sum $(OVA_NAME) > $(OVA_NAME).sha256
@echo "✓ Checksum: $(OUTPUT_DIR)/$(OVA_NAME).sha256"
@cat $(OUTPUT_DIR)/$(OVA_NAME).sha256
verify:
@cd $(OUTPUT_DIR) && sha256sum -c $(OVA_NAME).sha256
clean:
rm -rf $(OUTPUT_DIR)
@echo "✓ Output directory cleaned"
help:
@echo "" @echo ""
@echo " zroc-ova build targets" @echo " zroc-ova build targets"
@echo " ──────────────────────────────────────────" @echo " ────────────────────────────────────────"
@grep -E '^## ' Makefile | sed 's/## / make /' @grep -E '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " make %-15s %s\n", $$1, $$2}'
@echo "" @echo ""
@echo " VERSION=$(VERSION) (override: make build VERSION=1.1.0)" @echo " VERSION=$(VERSION) (override: make build VERSION=1.1.0)"
@echo "" @echo ""
init: ## Install required Packer plugins
cd $(PACKER_DIR) && packer init ubuntu-2404.pkr.hcl
validate: ## Validate Packer config
cd $(PACKER_DIR) && packer validate \
-var "vm_version=$(VERSION)" \
-var-file=variables.pkrvars.hcl \
ubuntu-2404.pkr.hcl
build: validate ## Build OVA + qcow2 appliance images
cd $(PACKER_DIR) && packer build \
-var "vm_version=$(VERSION)" \
-var-file=variables.pkrvars.hcl \
ubuntu-2404.pkr.hcl
@echo ""
@echo "Artifacts:"
@ls -lh $(OUTPUT_DIR)/$(ARTIFACT_PFX).ova $(OUTPUT_DIR)/$(ARTIFACT_PFX).qcow2 2>/dev/null
@echo ""
@cat $(OUTPUT_DIR)/$(ARTIFACT_PFX).ova.sha256 $(OUTPUT_DIR)/$(ARTIFACT_PFX).qcow2.sha256 2>/dev/null
clean: ## Remove all build artifacts
rm -rf $(OUTPUT_DIR)/qemu $(OUTPUT_DIR)/$(VM_NAME)-*
@echo "Build artifacts cleaned"
docker-build: ## Build zroc-ui Docker image
cd ../zroc-ui && docker build --network=host \
-t recklessop/zroc-ui:stable \
-t recklessop/zroc-ui:$(VERSION) \
-t recklessop/zroc-ui:latest \
.
docker-push: ## Push zroc-ui Docker image to Docker Hub
docker push recklessop/zroc-ui:stable
docker push recklessop/zroc-ui:$(VERSION)
docker push recklessop/zroc-ui:latest
+25 -8
View File
@@ -30,8 +30,23 @@ cat << 'BANNER'
BANNER BANNER
echo -e "${RESET}" echo -e "${RESET}"
# Step 1: Network # Step 0: Change default zroc password
step "1/6 Network Configuration" step "1/7 Change Appliance Password"
echo "The default 'zroc' user password must be changed."
while true; do
read -rsp "New password for 'zroc' (min 8 chars): " NEW_PW; echo
read -rsp "Confirm password: " NEW_PW2; echo
if [[ "$NEW_PW" != "$NEW_PW2" ]]; then err "Passwords do not match.";
elif [[ ${#NEW_PW} -lt 8 ]]; then err "Password must be at least 8 characters.";
else
echo "zroc:$NEW_PW" | chpasswd
ok "Appliance password changed"
break
fi
done
# Step 2: Network
step "2/7 Network Configuration"
CURRENT_IP=$(hostname -I | awk '{print $1}') CURRENT_IP=$(hostname -I | awk '{print $1}')
echo "Current IP: ${BOLD}$CURRENT_IP${RESET} (DHCP)" echo "Current IP: ${BOLD}$CURRENT_IP${RESET} (DHCP)"
read -rp "Keep DHCP? [Y/n]: " NET_CHOICE read -rp "Keep DHCP? [Y/n]: " NET_CHOICE
@@ -40,13 +55,13 @@ PUBLIC_URL="https://$CURRENT_IP"
ok "Using $CURRENT_IP" ok "Using $CURRENT_IP"
# Step 2: TLS # Step 2: TLS
step "2/6 HTTPS / TLS Certificate" step "3/7 HTTPS / TLS Certificate"
echo "Using self-signed certificate (default)" echo "Using self-signed certificate (default)"
TLS_MODE="internal" TLS_MODE="internal"
ok "Self-signed certificate will be generated by Caddy" ok "Self-signed certificate will be generated by Caddy"
# Step 3: Admin password # Step 3: Admin password
step "3/6 zROC Admin Account" step "4/7 zROC Admin Account"
while true; do while true; do
read -rsp "Admin password (min 12 chars): " ADMIN_PASS; echo read -rsp "Admin password (min 12 chars): " ADMIN_PASS; echo
read -rsp "Confirm password: " ADMIN_PASS2; echo read -rsp "Confirm password: " ADMIN_PASS2; echo
@@ -56,18 +71,18 @@ while true; do
done done
# Step 4: ZVM Site 1 # Step 4: ZVM Site 1
step "4/6 Zerto ZVM Configuration — Site 1" step "5/7 Zerto ZVM Configuration — Site 1"
read -rp "ZVM Hostname or IP: " ZVM_HOST read -rp "ZVM Hostname or IP: " ZVM_HOST
read -rp "ZVM Username [admin]: " ZVM_USER; ZVM_USER="${ZVM_USER:-admin}" read -rp "ZVM Username [admin]: " ZVM_USER; ZVM_USER="${ZVM_USER:-admin}"
read -rsp "ZVM Password: " ZVM_PASS; echo read -rsp "ZVM Password: " ZVM_PASS; echo
read -rp "vCenter Hostname (optional): " VCENTER_HOST read -rp "vCenter Hostname (optional): " VCENTER_HOST
# Step 5: Second site # Step 5: Second site
step "5/6 Second ZVM Site (optional)" step "6/7 Second ZVM Site (optional)"
read -rp "Monitor a second site? [y/N]: " SITE2; SITE2="${SITE2:-N}" read -rp "Monitor a second site? [y/N]: " SITE2; SITE2="${SITE2:-N}"
# Step 6: Enterprise IdP # Step 6: Enterprise IdP
step "6/6 Enterprise Identity Provider (optional)" step "7/7 Enterprise Identity Provider (optional)"
echo "Using local Authentik accounts (default)" echo "Using local Authentik accounts (default)"
# Generate secrets # Generate secrets
@@ -105,7 +120,9 @@ echo "Starting zROC services..."
cd "$INSTALL_DIR" cd "$INSTALL_DIR"
docker compose up -d 2>&1 | tail -20 docker compose up -d 2>&1 | tail -20
systemctl disable zroc-firstboot.service 2>/dev/null || true # Remove the getty override so normal login resumes after reboot
rm -f /etc/systemd/system/getty@tty1.service.d/zroc-firstboot.conf
systemctl daemon-reload
echo -e "${GREEN}${BOLD}" echo -e "${GREEN}${BOLD}"
echo " ✅ zROC is ready!" echo " ✅ zROC is ready!"
+5 -16
View File
@@ -10,29 +10,20 @@ autoinstall:
id: ubuntu-server-minimal id: ubuntu-server-minimal
storage: storage:
layout:
name: direct
config: config:
- type: disk - type: disk
id: disk0 id: disk0
match:
size: largest
ptable: gpt ptable: gpt
wipe: superblock-recursive wipe: superblock-recursive
preserve: false preserve: false
grub_device: true grub_device: true
- type: partition - type: partition
id: part-efi id: part-bios
device: disk0 device: disk0
size: 512M size: 1M
flag: boot flag: bios_grub
number: 1 number: 1
preserve: false preserve: false
- type: format
id: fmt-efi
volume: part-efi
fstype: fat32
preserve: false
- type: partition - type: partition
id: part-root id: part-root
device: disk0 device: disk0
@@ -48,10 +39,8 @@ autoinstall:
id: mnt-root id: mnt-root
device: fmt-root device: fmt-root
path: / path: /
- type: mount swap:
id: mnt-efi size: 0
device: fmt-efi
path: /boot/efi
identity: identity:
hostname: zroc-appliance hostname: zroc-appliance
+12 -18
View File
@@ -8,29 +8,23 @@ echo "==> [03-setup-wizard] Installing setup wizard"
cp -r /tmp/overlays/usr / cp -r /tmp/overlays/usr /
chmod 0755 /usr/local/bin/zroc-setup chmod 0755 /usr/local/bin/zroc-setup
cat > /etc/systemd/system/zroc-firstboot.service << 'EOF' # Override getty on tty1 to run the setup wizard on first boot.
[Unit] # Once .env exists the override is removed and normal getty resumes.
Description=zROC First-Boot Setup Wizard mkdir -p /etc/systemd/system/getty@tty1.service.d
After=network-online.target cat > /etc/systemd/system/getty@tty1.service.d/zroc-firstboot.conf << 'EOF'
Wants=network-online.target
ConditionPathExists=!/opt/zroc/.env
[Service] [Service]
Type=oneshot ExecStartPre=/bin/sh -c '[ ! -f /opt/zroc/.env ] || { rm -f /etc/systemd/system/getty@tty1.service.d/zroc-firstboot.conf && systemctl daemon-reload && exit 1; }'
RemainAfterExit=yes ExecStart=
ExecStart=/usr/local/bin/zroc-setup ExecStart=-/usr/local/bin/zroc-setup
StandardInput=tty StandardInput=tty
TTYPath=/dev/tty1 StandardOutput=tty
StandardOutput=journal+console StandardError=tty
StandardError=journal+console TTYVTDisallocate=yes
TimeoutStartSec=0 Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
EOF EOF
systemctl daemon-reload systemctl daemon-reload
systemctl enable zroc-firstboot.service
rm -f /etc/sudoers.d/zroc-packer rm -f /etc/sudoers.d/zroc-packer
cat > /etc/sudoers.d/zroc << 'EOF' cat > /etc/sudoers.d/zroc << 'EOF'
+1
View File
@@ -29,6 +29,7 @@ dd if=/dev/zero of=/ZERO bs=4M status=progress 2>/dev/null || true
rm -f /ZERO rm -f /ZERO
sync sync
# Zero swap if present (appliance is built without swap by default)
SWAP_DEV=$(swapon --show=NAME --noheadings 2>/dev/null | head -1) SWAP_DEV=$(swapon --show=NAME --noheadings 2>/dev/null | head -1)
if [[ -n "$SWAP_DEV" ]]; then if [[ -n "$SWAP_DEV" ]]; then
swapoff "$SWAP_DEV" swapoff "$SWAP_DEV"
+2101
View File
File diff suppressed because it is too large Load Diff