Fix air-gapped deployment: pip wheel install, UFW ports, installer crash

- Fix pip/distutils incompatibility: install Python wheels directly via
  zipfile extraction instead of broken pip3 from Ubuntu 22.04 .debs
  (pip3 crashes on Python 3.12 with ModuleNotFoundError: distutils)
- Fix UFW port types: quote loop items so string comparison works
  correctly, giving ports 67/69 UDP rules instead of TCP
- Fix autoinstall crash: set refresh-installer to no (can't reach
  internet on air-gapped network, was crashing subiquity)
- Remove python3-pip and python3-venv from download-packages.sh
  (no longer needed with direct wheel extraction)
- Add ignore_errors to WinPE/iPXE copy tasks (files only present
  on real USB media, not test VM)
- Use system python3 instead of venv for webapp service

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-02-09 16:16:55 -05:00
parent 851225d062
commit cb442f971b
4 changed files with 73 additions and 69 deletions

View File

@@ -272,7 +272,7 @@
- "{{ image_types }}"
- "{{ deploy_subdirs }}"
- name: "Copy WinPE & boot files from USB"
- name: "Copy WinPE & boot files from USB (skipped if not present)"
copy:
src: "{{ usb_mount }}/{{ item.src }}"
dest: "{{ web_root }}/win11/{{ item.dest }}"
@@ -284,14 +284,16 @@
- { src: "bootx64.efi", dest: "EFI/Boot/bootx64.efi" }
- { src: "boot.sdi", dest: "Boot/boot.sdi" }
- { src: "boot.wim", dest: "sources/boot.wim" }
ignore_errors: yes
- name: "Copy iPXE binaries from USB"
- name: "Copy iPXE binaries from USB (skipped if not present)"
copy:
src: "{{ usb_mount }}/{{ item }}"
dest: "{{ tftp_dir }}/{{ item }}"
mode: '0755'
loop:
- ipxe.efi
ignore_errors: yes
- name: "Copy boot tool files from USB (Clonezilla, Blancco, Memtest)"
shell: >
@@ -329,12 +331,12 @@
port: "{{ item }}"
proto: "{{ 'udp' if item in ['67','69'] else 'tcp' }}"
loop:
- 67
- 69
- 80
- 4433
- 445
- 9009
- "67"
- "69"
- "80"
- "4433"
- "445"
- "9009"
- name: "Enable UFW firewall"
ufw:
@@ -362,18 +364,37 @@
args:
creates: /opt/pxe-webapp/app.py
- name: "Create Python virtual environment for webapp"
command: python3 -m venv /opt/pxe-webapp/venv
args:
creates: /opt/pxe-webapp/venv/bin/python
- name: "Install webapp Python dependencies (offline wheels)"
shell: >
/opt/pxe-webapp/venv/bin/pip install --no-index
--find-links="{{ usb_mount }}/../pip-wheels/"
--find-links="{{ usb_mount }}/pip-wheels/"
-r /opt/pxe-webapp/requirements.txt 2>/dev/null ||
/opt/pxe-webapp/venv/bin/pip install -r /opt/pxe-webapp/requirements.txt
args:
executable: /bin/bash
shell: |
# Find the pip-wheels directory on the CIDATA mount
export WHEEL_DIR=""
for d in "{{ usb_mount }}/../pip-wheels" "{{ usb_mount }}/pip-wheels"; do
if [ -d "$d" ] && compgen -G "$d/*.whl" > /dev/null; then
export WHEEL_DIR="$(cd "$d" && pwd)"
break
fi
done
if [ -n "$WHEEL_DIR" ]; then
# Install wheels directly using Python zipfile (bypasses pip entirely)
# This avoids the pip3/distutils incompatibility between Ubuntu 22.04 debs and Python 3.12
python3 << 'PYEOF'
import zipfile, sysconfig, glob, os
site = sysconfig.get_path('platlib')
wheel_dir = os.environ['WHEEL_DIR']
for whl in sorted(glob.glob(os.path.join(wheel_dir, '*.whl'))):
name = os.path.basename(whl)
print(f' Installing {name}')
with zipfile.ZipFile(whl) as z:
z.extractall(site)
print('All wheels installed to ' + site)
PYEOF
else
# Fallback: try system pip (works if system has internet and compatible pip)
python3 -m pip install --break-system-packages flask lxml 2>/dev/null ||
pip3 install --break-system-packages flask lxml
fi
- name: "Create systemd service for PXE webapp"
copy:
@@ -392,7 +413,7 @@
Environment=WEB_ROOT={{ web_root }}
Environment=BLANCCO_REPORTS=/srv/samba/blancco-reports
Environment=AUDIT_LOG=/var/log/pxe-webapp-audit.log
ExecStart=/opt/pxe-webapp/venv/bin/python app.py
ExecStart=/usr/bin/python3 app.py
Restart=always
RestartSec=5