diff --git a/caddy/Caddyfile b/caddy/Caddyfile new file mode 100644 index 0000000..ebdc847 --- /dev/null +++ b/caddy/Caddyfile @@ -0,0 +1,47 @@ +{ + admin off + auto_https off + log { + format json + } +} + +:443 { + tls internal + + handle /auth/* { + reverse_proxy authentik-server:9000 { + header_up X-Forwarded-Proto https + header_up X-Forwarded-For {remote_host} + } + } + + handle /outpost.goauthentik.io/* { + reverse_proxy authentik-server:9000 { + header_up X-Forwarded-Proto https + } + } + + handle { + reverse_proxy zroc-ui:3001 { + header_up X-Forwarded-Proto https + header_up X-Forwarded-For {remote_host} + header_up X-Real-IP {remote_host} + health_uri /api/health + health_interval 15s + } + } + + header { + X-Frame-Options "SAMEORIGIN" + X-Content-Type-Options "nosniff" + X-XSS-Protection "1; mode=block" + Referrer-Policy "strict-origin-when-cross-origin" + Strict-Transport-Security "max-age=31536000; includeSubDomains" + -Server + } +} + +:80 { + redir https://{host}{uri} permanent +} diff --git a/docker-compose.yaml b/docker-compose.yaml index ac645c2..85ecd93 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,111 +1,289 @@ -version: '3.7' +--- +# zROC — Zerto Resiliency Observation Console +# Full stack: Caddy (TLS), zroc-ui (React dashboard + Node backend), Authentik (SSO), +# Prometheus, Grafana, Zerto Exporter, Watchtower (auto-updates). +# +# Configuration is driven entirely by /opt/zroc/.env — see zroc-setup wizard. + +version: '3.8' networks: front-tier: back-tier: + auth-tier: volumes: - prometheus_data: {} - grafana_data: {} + prometheus_data: {} + grafana_data: {} + zroc_ui_data: {} + authentik_postgres: {} + authentik_redis: {} + authentik_media: {} + caddy_data: {} services: - # Exporter for ZVM/vCenter site 1 - zertoexporter: - container_name: zvmexporter1 - hostname: zvmexporter1 # this hostname will need to be set in the prometheus.yaml file as well - image: recklessop/zerto-exporter:stable - command: python python-node-exporter.py + # ── Reverse proxy / TLS termination ─────────────────────────────────────── + caddy: + image: caddy:2-alpine + container_name: zroc-caddy + restart: unless-stopped ports: - - "9999:9999" + - "80:80" + - "443:443" volumes: - - ./zvmexporter/:/usr/src/app/logs/ + - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - ./certs:/certs:ro + - caddy_data:/data + networks: + - front-tier + depends_on: + - zroc-ui + - authentik-server + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:80"] + interval: 30s + timeout: 5s + retries: 3 + + # ── zROC React UI + Node backend ────────────────────────────────────────── + zroc-ui: + image: recklessop/zroc-ui:stable + container_name: zroc-ui + restart: unless-stopped environment: - # Site 1 configuration settings - - VERIFY_SSL=False - - ZVM_HOST=192.168.50.60 - - ZVM_PORT=443 - - SCRAPE_SPEED=20 #how often should the exporter scrape the Zerto API - - CLIENT_ID=api-script - - CLIENT_SECRET=js51tDM8oappYUGRJBhF7bcsedNoHA5j - - LOGLEVEL=DEBUG - - VCENTER_HOST=vcenter.local - - VCENTER_USER=administrator@vsphere.local - - VCENTER_PASSWORD=password - networks: - - back-tier - restart: always - - # This is used for a second ZVM / vCenter (maybe your DR site?) - #zertoexporter2: - # container_name: zvmexporter2 - # hostname: zvmexporter2 - # image: recklessop/zerto-exporter:stable - # command: python python-node-exporter.py - # ports: - # - "9998:9999" # if you add a third or more exporters change the port number before the : - # volumes: - # - ./zvmexporter/:/usr/src/app/logs/ - # environment: - # # Site 2 configuration settings - # - VERIFY_SSL=False - # - ZVM_HOST=192.168.50.30 - # - ZVM_PORT=443 - # - SCRAPE_SPEED=20 #how often should the exporter scrape the Zerto API - # - CLIENT_ID=api-script - # - CLIENT_SECRET=x2aokKGPyS1O6LCW2uNqm2tbko2PLUSn - # - LOGLEVEL=DEBUG - # - VCENTER_HOST=192.168.50.20 - # - VCENTER_USER=administrator@vsphere.local - # - VCENTER_PASSWORD=password - # networks: - # - back-tier - # restart: always - - prometheus: - image: prom/prometheus:v2.40.6 + NODE_ENV: production + PORT: "3001" + PROMETHEUS_URL: http://zroc-prometheus:9090 + AUTHENTIK_URL: http://authentik-server:9000 + AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID} + AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET} + AUTHENTIK_ADMIN_TOKEN: ${AUTHENTIK_ADMIN_TOKEN} + PUBLIC_URL: ${PUBLIC_URL} + SESSION_SECRET: ${SESSION_SECRET} + JWT_EXPIRY_HOURS: "24" + AUTHENTIK_ADMIN_GROUP: zroc-admins + AUTHENTIK_VIEWER_GROUP: zroc-viewers volumes: - - ./prometheus/:/etc/prometheus/ - - prometheus_data:/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.path=/prometheus' - - '--web.console.libraries=/usr/share/prometheus/console_libraries' - - '--web.console.templates=/usr/share/prometheus/consoles' - ports: - - 9090:9090 + - zroc_ui_data:/app/data + networks: + - front-tier + - back-tier + - auth-tier + depends_on: + - zroc-prometheus + - authentik-server + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:3001/api/health"] + interval: 15s + timeout: 5s + retries: 3 + start_period: 20s + + # ── SSO — Authentik ─────────────────────────────────────────────────────── + authentik-postgresql: + image: postgres:16-alpine + container_name: authentik-db + restart: unless-stopped + environment: + POSTGRES_DB: authentik + POSTGRES_USER: authentik + POSTGRES_PASSWORD: ${AUTHENTIK_PG_PASS} + volumes: + - authentik_postgres:/var/lib/postgresql/data + networks: + - auth-tier + healthcheck: + test: ["CMD-SHELL", "pg_isready -U authentik"] + interval: 10s + timeout: 5s + retries: 5 + + authentik-redis: + image: redis:7-alpine + container_name: authentik-redis + restart: unless-stopped + command: --save 60 1 --loglevel warning + volumes: + - authentik_redis:/data + networks: + - auth-tier + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + authentik-server: + image: ghcr.io/goauthentik/server:latest + container_name: authentik-server + restart: unless-stopped + command: server + environment: + AUTHENTIK_REDIS__HOST: authentik-redis + AUTHENTIK_POSTGRESQL__HOST: authentik-postgresql + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true" + AUTHENTIK_ERROR_REPORTING__ENABLED: "false" + ZROC_OIDC_CLIENT_ID: ${ZROC_OIDC_CLIENT_ID} + ZROC_OIDC_CLIENT_SECRET: ${ZROC_OIDC_CLIENT_SECRET} + ZROC_PUBLIC_URL: ${ZROC_PUBLIC_URL} + volumes: + - authentik_media:/media + - ./zroc-ui/authentik/blueprints:/blueprints/custom:ro + networks: + - auth-tier + - front-tier + depends_on: + authentik-postgresql: + condition: service_healthy + authentik-redis: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "ak healthcheck || exit 1"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + + authentik-worker: + image: ghcr.io/goauthentik/server:latest + container_name: authentik-worker + restart: unless-stopped + command: worker + environment: + AUTHENTIK_REDIS__HOST: authentik-redis + AUTHENTIK_POSTGRESQL__HOST: authentik-postgresql + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true" + volumes: + - authentik_media:/media + - /var/run/docker.sock:/var/run/docker.sock + networks: + - auth-tier + depends_on: + - authentik-server + user: root + + # ── Metrics — Zerto exporter ────────────────────────────────────────────── + zertoexporter: + image: recklessop/zerto-exporter:stable + container_name: zvmexporter1 + hostname: zvmexporter1 + restart: unless-stopped + volumes: + - ./zvmexporter:/usr/src/app/logs + environment: + VERIFY_SSL: "False" + ZVM_HOST: ${ZVM_HOST} + ZVM_PORT: "443" + ZVM_USERNAME: ${ZVM_USERNAME} + ZVM_PASSWORD: ${ZVM_PASSWORD} + SCRAPE_SPEED: "20" + CLIENT_ID: ${ZVM_CLIENT_ID:-api-script} + CLIENT_SECRET: ${ZVM_CLIENT_SECRET} + LOGLEVEL: INFO + VCENTER_HOST: ${VCENTER_HOST:-} + VCENTER_USER: ${VCENTER_USER:-administrator@vsphere.local} + VCENTER_PASSWORD: ${VCENTER_PASSWORD:-} + networks: + - back-tier + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:9999/metrics"] + interval: 30s + timeout: 10s + retries: 3 + + # Optional second ZVM/vCenter site — uncomment and set ZVM2_* env vars + # zertoexporter2: + # image: recklessop/zerto-exporter:stable + # container_name: zvmexporter2 + # hostname: zvmexporter2 + # restart: unless-stopped + # ports: + # - "9998:9999" + # volumes: + # - ./zvmexporter:/usr/src/app/logs + # environment: + # VERIFY_SSL: "False" + # ZVM_HOST: ${ZVM2_HOST} + # ZVM_PORT: "443" + # ZVM_USERNAME: ${ZVM2_USERNAME} + # ZVM_PASSWORD: ${ZVM2_PASSWORD} + # SCRAPE_SPEED: "20" + # LOGLEVEL: INFO + # networks: + # - back-tier + + # ── Metrics — Prometheus ────────────────────────────────────────────────── + zroc-prometheus: + image: prom/prometheus:v2.51.0 + container_name: zroc-prometheus + restart: unless-stopped + command: + - --config.file=/etc/prometheus/prometheus.yml + - --storage.tsdb.path=/prometheus + - --storage.tsdb.retention.time=30d + - --storage.tsdb.retention.size=20GB + - --web.listen-address=0.0.0.0:9090 + - --web.enable-lifecycle + volumes: + - ./prometheus:/etc/prometheus:ro + - prometheus_data:/prometheus networks: - back-tier - restart: always depends_on: - zertoexporter + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:9090/-/healthy"] + interval: 30s + timeout: 5s + retries: 3 + # ── Dashboards — Grafana ────────────────────────────────────────────────── grafana: - image: grafana/grafana + image: grafana/grafana:10.4.2 + container_name: zroc-grafana + restart: unless-stopped user: "472" - depends_on: - - prometheus ports: - - 3000:3000 + - "3000:3000" volumes: - grafana_data:/var/lib/grafana - - ./grafana/provisioning/:/etc/grafana/provisioning/ + - ./grafana/provisioning:/etc/grafana/provisioning:ro environment: - - GF_SECURITY_ADMIN_PASSWORD=zertodata - - GF_USERS_ALLOW_SIGN_UP=false - - data-source-url=http://prometheus:9090 - - name=Prometheus - - type=prometheus - - update-interval=10 + GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-zertodata} + GF_USERS_ALLOW_SIGN_UP: "false" + GF_SERVER_ROOT_URL: "%(protocol)s://%(domain)s:%(http_port)s/grafana/" + GF_AUTH_GENERIC_OAUTH_ENABLED: ${GRAFANA_OIDC_ENABLED:-false} + GF_AUTH_GENERIC_OAUTH_NAME: Authentik + GF_AUTH_GENERIC_OAUTH_CLIENT_ID: ${GRAFANA_CLIENT_ID:-} + GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET: ${GRAFANA_CLIENT_SECRET:-} + GF_AUTH_GENERIC_OAUTH_SCOPES: openid profile email + GF_AUTH_GENERIC_OAUTH_AUTH_URL: ${PUBLIC_URL:-}/auth/application/o/authorize/ + GF_AUTH_GENERIC_OAUTH_TOKEN_URL: http://authentik-server:9000/application/o/token/ + GF_AUTH_GENERIC_OAUTH_API_URL: http://authentik-server:9000/application/o/userinfo/ networks: - back-tier - front-tier - restart: always + depends_on: + - zroc-prometheus + # ── Auto-updates — Watchtower ───────────────────────────────────────────── watchtower: image: containrrr/watchtower + container_name: zroc-watchtower + restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - - WATCHTOWER_POLL_INTERVAL=360 # 1 hour - restart: always + WATCHTOWER_POLL_INTERVAL: "3600" + WATCHTOWER_CLEANUP: "true" + WATCHTOWER_INCLUDE_STOPPED: "false" + command: --label-enable diff --git a/zroc-ova/.github/workflows/build-ova.yml b/zroc-ova/.github/workflows/build-ova.yml index fc6a201..0bf3907 100644 --- a/zroc-ova/.github/workflows/build-ova.yml +++ b/zroc-ova/.github/workflows/build-ova.yml @@ -31,7 +31,7 @@ jobs: fi echo "version=$VERSION" >> $GITHUB_OUTPUT echo "tag=v$VERSION" >> $GITHUB_OUTPUT - echo "ova_name=zroc-appliance-${VERSION}-ubuntu-26.04-amd64.ova" >> $GITHUB_OUTPUT + echo "ova_name=zroc-appliance-${VERSION}-ubuntu-24.04-amd64.ova" >> $GITHUB_OUTPUT - name: Install Packer run: | @@ -42,7 +42,7 @@ jobs: - name: Packer init working-directory: packer - run: packer init ubuntu-2604.pkr.hcl + run: packer init ubuntu-2404.pkr.hcl - name: Validate working-directory: packer @@ -50,7 +50,7 @@ jobs: packer validate \ -var "vm_version=${{ steps.ver.outputs.version }}" \ -var-file=variables.pkrvars.hcl \ - ubuntu-2604.pkr.hcl + ubuntu-2404.pkr.hcl - name: Build OVA working-directory: packer @@ -62,7 +62,7 @@ jobs: -var "vm_version=${{ steps.ver.outputs.version }}" \ -var "headless=true" \ -var-file=variables.pkrvars.hcl \ - ubuntu-2604.pkr.hcl + ubuntu-2404.pkr.hcl - name: Locate OVA id: ova diff --git a/zroc-ova/README.md b/zroc-ova/README.md index 086263b..6074bb7 100644 --- a/zroc-ova/README.md +++ b/zroc-ova/README.md @@ -1,11 +1,11 @@ # zroc-ova — zROC Appliance Builder -Packer build definitions and provisioner scripts for the **zROC Ubuntu 26.04 LTS OVA appliance**. +Packer build definitions and provisioner scripts for the **zROC Ubuntu 24.04 LTS OVA appliance**. ## What you get A 100 GB thin-provisioned VMware OVA containing: -- Ubuntu Server 26.04 LTS +- Ubuntu Server 24.04 LTS - Docker Engine + Compose plugin - Full zROC stack (cloned from recklessop/zroc) - Interactive first-boot setup wizard (`zroc-setup`) diff --git a/zroc-ova/packer/ubuntu-2604.pkr.hcl b/zroc-ova/packer/ubuntu-2404.pkr.hcl similarity index 60% rename from zroc-ova/packer/ubuntu-2604.pkr.hcl rename to zroc-ova/packer/ubuntu-2404.pkr.hcl index 669a310..6b0b2f4 100644 --- a/zroc-ova/packer/ubuntu-2604.pkr.hcl +++ b/zroc-ova/packer/ubuntu-2404.pkr.hcl @@ -14,12 +14,12 @@ packer { variable "ubuntu_iso_url" { type = string - default = "https://releases.ubuntu.com/26.04/ubuntu-26.04-live-server-amd64.iso" + default = "https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso" } variable "ubuntu_iso_checksum" { type = string - default = "file:https://releases.ubuntu.com/26.04/SHA256SUMS" + default = "file:https://releases.ubuntu.com/24.04/SHA256SUMS" } variable "vm_name" { @@ -57,46 +57,46 @@ variable "headless" { default = true } -source "vmware-iso" "ubuntu2604" { - vm_name = "${var.vm_name}-${var.vm_version}" - guest_os_type = "ubuntu-64" - headless = var.headless - iso_url = var.ubuntu_iso_url - iso_checksum = var.ubuntu_iso_checksum - disk_size = var.disk_size_mb - disk_adapter_type = "pvscsi" - memory = var.memory_mb - cpus = var.cpus - network_adapter_type = "vmxnet3" - network = "nat" - disk_type_id = 0 - http_directory = "http" - http_port_min = 8100 - http_port_max = 8199 - boot_wait = "5s" +source "vmware-iso" "ubuntu2404" { + vm_name = "${var.vm_name}-${var.vm_version}" + guest_os_type = "ubuntu-64" + headless = var.headless + iso_url = var.ubuntu_iso_url + iso_checksum = var.ubuntu_iso_checksum + disk_size = var.disk_size_mb + disk_adapter_type = "pvscsi" + memory = var.memory_mb + cpus = var.cpus + network_adapter_type = "vmxnet3" + network = "nat" + disk_type_id = 0 + http_directory = "http" + http_port_min = 8100 + http_port_max = 8199 + boot_wait = "5s" boot_command = [ "e", "", " autoinstall ds=nocloud-net;seedfrom=http://{{.HTTPIP}}:{{.HTTPPort}}/", "", ] - ssh_username = "zroc" - ssh_password = "zroc-setup-temp" - ssh_timeout = "30m" - ssh_port = 22 - shutdown_command = "echo 'zroc-setup-temp' | sudo -S shutdown -P now" - output_directory = "${var.output_dir}/vmware" - skip_export = false - format = "ovf" + ssh_username = "zroc" + ssh_password = "zroc-setup-temp" + ssh_timeout = "30m" + ssh_port = 22 + shutdown_command = "echo 'zroc-setup-temp' | sudo -S shutdown -P now" + output_directory = "${var.output_dir}/vmware" + skip_export = false + format = "ovf" vmx_data = { - "virtualHW.version" = "19" - "tools.syncTime" = "TRUE" - "annotation" = "zROC Appliance v${var.vm_version}" - "guestOS" = "ubuntu-64" + "virtualHW.version" = "19" + "tools.syncTime" = "TRUE" + "annotation" = "zROC Appliance v${var.vm_version}" + "guestOS" = "ubuntu-64" } } -source "qemu" "ubuntu2604" { +source "qemu" "ubuntu2404" { vm_name = "${var.vm_name}-${var.vm_version}" iso_url = var.ubuntu_iso_url iso_checksum = var.ubuntu_iso_checksum @@ -109,7 +109,7 @@ source "qemu" "ubuntu2604" { http_directory = "http" http_port_min = 8100 http_port_max = 8199 - boot_wait = "5s" + boot_wait = "5s" boot_command = [ "e", "", @@ -125,12 +125,18 @@ source "qemu" "ubuntu2604" { } build { - name = "zroc-appliance" - sources = ["source.vmware-iso.ubuntu2604"] + name = "zroc-appliance" + sources = ["source.vmware-iso.ubuntu2404"] + + # Copy overlay files (setup wizard, etc.) into the VM before provisioning + provisioner "file" { + source = "../overlays/" + destination = "/tmp/overlays/" + } provisioner "shell" { - script = "../scripts/00-base.sh" - execute_command = "echo 'zroc-setup-temp' | sudo -S bash {{.Path}}" + script = "../scripts/00-base.sh" + execute_command = "echo 'zroc-setup-temp' | sudo -S bash {{.Path}}" expect_disconnect = true } @@ -161,12 +167,12 @@ build { } post-processor "shell-local" { - only = ["vmware-iso.ubuntu2604"] + only = ["vmware-iso.ubuntu2404"] inline = [ "cd ${var.output_dir}/vmware", - "ovftool --compress=9 *.ovf ../${var.vm_name}-${var.vm_version}-ubuntu-26.04-amd64.ova", + "ovftool --compress=9 *.ovf ../${var.vm_name}-${var.vm_version}-ubuntu-24.04-amd64.ova", "cd ..", - "sha256sum ${var.vm_name}-${var.vm_version}-ubuntu-26.04-amd64.ova > ${var.vm_name}-${var.vm_version}-ubuntu-26.04-amd64.ova.sha256", + "sha256sum ${var.vm_name}-${var.vm_version}-ubuntu-24.04-amd64.ova > ${var.vm_name}-${var.vm_version}-ubuntu-24.04-amd64.ova.sha256", "echo 'OVA packaged successfully'", ] } diff --git a/zroc-ova/packer/variables.pkrvars.hcl b/zroc-ova/packer/variables.pkrvars.hcl index 4c3330c..17fe9f8 100644 --- a/zroc-ova/packer/variables.pkrvars.hcl +++ b/zroc-ova/packer/variables.pkrvars.hcl @@ -1,8 +1,8 @@ # zroc-ova/packer/variables.pkrvars.hcl vm_version = "1.0.0" -ubuntu_iso_url = "https://releases.ubuntu.com/26.04/ubuntu-26.04-live-server-amd64.iso" -ubuntu_iso_checksum = "file:https://releases.ubuntu.com/26.04/SHA256SUMS" +ubuntu_iso_url = "https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso" +ubuntu_iso_checksum = "file:https://releases.ubuntu.com/24.04/SHA256SUMS" memory_mb = 8192 cpus = 4 diff --git a/zroc-ova/scripts/02-zroc.sh b/zroc-ova/scripts/02-zroc.sh index d2210c0..86e043e 100644 --- a/zroc-ova/scripts/02-zroc.sh +++ b/zroc-ova/scripts/02-zroc.sh @@ -8,15 +8,29 @@ ZROC_REPO="https://github.com/recklessop/zroc.git" git clone --depth=1 "$ZROC_REPO" "$INSTALL_DIR" +# Ensure expected directories exist mkdir -p \ - "$INSTALL_DIR/certs" \ - "$INSTALL_DIR/zvmexporter" \ + "$INSTALL_DIR/certs" \ + "$INSTALL_DIR/zvmexporter" \ "$INSTALL_DIR/data" cd "$INSTALL_DIR" -docker compose pull prometheus grafana authentik-server authentik-worker \ - || echo "[02-zroc] Some images not yet available — will pull on first start" +# Pre-pull all container images into the OVA image layer so first-boot is fast. +# Failures are non-fatal — any missing images will be pulled on first docker compose up. +echo "==> [02-zroc] Pre-pulling container images (this may take a while)…" +docker compose pull \ + caddy \ + zroc-ui \ + authentik-postgresql \ + authentik-redis \ + authentik-server \ + authentik-worker \ + zertoexporter \ + zroc-prometheus \ + grafana \ + watchtower \ + || echo "[02-zroc] Warning: some images could not be pre-pulled — they will pull on first start" chown -R zroc:zroc "$INSTALL_DIR" diff --git a/zroc-ova/scripts/03-setup-wizard.sh b/zroc-ova/scripts/03-setup-wizard.sh index f894e47..6cd0e36 100644 --- a/zroc-ova/scripts/03-setup-wizard.sh +++ b/zroc-ova/scripts/03-setup-wizard.sh @@ -3,7 +3,10 @@ set -euo pipefail echo "==> [03-setup-wizard] Installing setup wizard" -install -m 0755 /tmp/zroc-setup /usr/local/bin/zroc-setup +# The Packer file provisioner copies overlays/ to /tmp/overlays/ +# Mirror the full directory tree into place +cp -r /tmp/overlays/usr / +chmod 0755 /usr/local/bin/zroc-setup cat > /etc/systemd/system/zroc-firstboot.service << 'EOF' [Unit]