Every server has an imaging NIC (static, the only one that serves DHCP/TFTP/HTTP boot/SMB) and a GE corporate NIC (DHCP client, SSH and the webapp on 9009). The playbook guessed the PXE NIC and the installer put 172.16.9.1/24 on every en* NIC, so a two-NIC box could answer on the GE network. - New per-site vars pxe_nic_mac / corp_nic_mac (required, from playbook/site-vars.yml or -e; template site-vars.example.yml), stable names pxe_nic_name / corp_nic_name (default pxe0 / corp0, overridable), pxe_server_ip, pxe_subnet, DHCP range. pxe_imaging_mac / pxe_ge_mac are derived from them. The play stops before touching anything if a MAC is unset, malformed, duplicated or not on the box (lists the box's NICs). - Heuristic NIC detection and the single-NIC netplan task replaced by a two-NIC netplan template (match macaddress + set-name), installed by pxe-netplan-apply.sh: validate in a scratch root, back up, generate, rename, apply, restore the previous config on any failure. cloud-init network config disabled so it cannot rewrite it. - dnsmasq: /etc/dnsmasq.d/pxe-nic-only.conf from the vars (interface=, except-interface=, no-dhcp-interface=); dnsmasq.conf IP/range from vars. - UFW per interface: imaging NIC 22, 67/udp, 69/udp, 80, 4433, 445, 139, 137/udp, 138/udp, 9009; GE NIC 22 and 9009 only. Old any-interface rules and the stale 9011 rule removed after the new ones are in. - sysctl drop-in: IPv4/IPv6 forwarding off; ip_nonlocal_bind so Apache can bind the imaging IP before the address is up. - Samba [global]: interfaces = 127.0.0.0/8 <imaging NIC>, bind interfaces only. Apache 80 and 4433 on the imaging IP only; 9009 stays on all (TODO: the webapp has no auth). - autoinstall: DHCP only on en*, no static 172.16.9.1; apt geoip off and offline-install fallback now that the corp NIC may have a lease. - Blancco UEFI/Secure Boot: blancco_kver (7.0.0-34-generic) drives the kernel, initramfs and modules builds instead of the running kernel; outputs vmlinuz-ubuntu-70, kexec-initrd-70.img, kmod-<kver>.tar.gz as the menu and blancco-init.sh expect; creates: guards keyed on the full kver. Ubuntu's shimx64.efi.signed published as blancco/shimx64.efi. download-packages.sh adds shim-signed, linux-generic-hwe-24.04 and the pinned linux-image/modules/modules-extra for BLANCCO_KVER. Rendered with the live MACs and names, netplan generate output is identical to the live config; pxe-nic-only.conf and the Samba binding match live directive for directive.
133 lines
4.1 KiB
Plaintext
133 lines
4.1 KiB
Plaintext
#cloud-config
|
|
autoinstall:
|
|
version: 1
|
|
|
|
# Locale, keyboard, timezone
|
|
locale: en_US.UTF-8
|
|
keyboard:
|
|
layout: us
|
|
variant: ""
|
|
timezone: America/New_York
|
|
|
|
# Network: DHCP only, on every wired NIC. On the standard two-NIC box the
|
|
# GE corporate NIC gets an address and the imaging NIC (no DHCP server on
|
|
# the imaging LAN) stays without one. No static 172.16.9.1 here: matched
|
|
# by name it would land on EVERY NIC, the GE one included, and the box
|
|
# would answer as 172.16.9.1 on the corporate network. First boot's
|
|
# playbook replaces this file with the two-NIC config pinned by MAC
|
|
# (pxe_nic_mac / corp_nic_mac in playbook/site-vars.yml). optional: the
|
|
# install and boot never wait for a lease (air-gapped is fine).
|
|
network:
|
|
version: 2
|
|
ethernets:
|
|
any-eth:
|
|
match:
|
|
name: "en*"
|
|
dhcp4: true
|
|
dhcp6: false
|
|
optional: true
|
|
|
|
# The corp NIC may now have a lease, but the install stays offline-capable:
|
|
# no geoip lookup, and if the Ubuntu mirror is unreachable (Zscaler,
|
|
# no route) install from the ISO instead of failing.
|
|
apt:
|
|
geoip: false
|
|
fallback: offline-install
|
|
|
|
# Storage configuration
|
|
storage:
|
|
layout:
|
|
name: lvm
|
|
match:
|
|
size: largest
|
|
swap:
|
|
size: 0
|
|
|
|
# User identity
|
|
identity:
|
|
hostname: pxeserver
|
|
username: pxe
|
|
password: '$6$MV3UwfSxKqxDYCwt$2IPUfSvqpzUdH6QVKHfPH8I9E0SPqNCh0lLvn.D9btxhZUYmceOGcB51Ryfg1Fojlroin.qguSKYFoIDGUIC20'
|
|
|
|
# Enable SSH
|
|
ssh:
|
|
install-server: true
|
|
allow-pw: true
|
|
|
|
# Installer-stage late commands
|
|
late-commands:
|
|
# Step 1: Install deb packages from CIDATA USB
|
|
- |
|
|
curtin in-target --target=/target -- bash -c '
|
|
mkdir -p /mnt/cidata
|
|
CIDATA_DEV=$(blkid -L CIDATA)
|
|
if [ -n "$CIDATA_DEV" ]; then
|
|
mount "$CIDATA_DEV" /mnt/cidata
|
|
if compgen -G "/mnt/cidata/packages/*.deb" > /dev/null; then
|
|
cp /mnt/cidata/packages/*.deb /tmp/
|
|
dpkg -i /tmp/*.deb 2>/dev/null || true
|
|
dpkg -i /tmp/*.deb 2>/dev/null || true
|
|
dpkg --configure -a 2>/dev/null || true
|
|
if command -v nmcli >/dev/null; then
|
|
systemctl enable NetworkManager
|
|
fi
|
|
fi
|
|
umount /mnt/cidata
|
|
fi
|
|
'
|
|
|
|
# Step 2: Disable networkd-wait-online (hangs on air-gapped network)
|
|
- ln -sf /dev/null /target/etc/systemd/system/systemd-networkd-wait-online.service
|
|
|
|
# Step 3: Create first-boot.sh
|
|
- |
|
|
curtin in-target --target=/target -- bash -c '
|
|
cat <<"EOF" > /opt/first-boot.sh
|
|
#!/bin/bash
|
|
# Expand root LV to full disk BEFORE playbook (playbook copies ~130GB of drivers+PPKGs)
|
|
ROOT_DEV=$(findmnt -n -o SOURCE /)
|
|
ROOT_DISK=$(lsblk -n -o PKNAME "$(readlink -f "$ROOT_DEV")" | tail -1)
|
|
PV_PART=$(pvs --noheadings -o pv_name 2>/dev/null | tr -d "[:space:]" | head -1)
|
|
if [ -n "$ROOT_DISK" ] && [ -n "$PV_PART" ]; then
|
|
PART_NUM=$(echo "$PV_PART" | grep -o "[0-9]*$")
|
|
growpart "/dev/${ROOT_DISK}" "${PART_NUM}" 2>&1 || true
|
|
pvresize "$PV_PART" 2>&1 || true
|
|
fi
|
|
lvextend -r -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv 2>&1 || true
|
|
CIDATA_DEV=$(blkid -L CIDATA)
|
|
if [ -n "$CIDATA_DEV" ]; then
|
|
mkdir -p /mnt/usb
|
|
mount "$CIDATA_DEV" /mnt/usb
|
|
if compgen -G "/mnt/usb/packages/*.deb" > /dev/null; then
|
|
dpkg -i /mnt/usb/packages/*.deb 2>/dev/null || true
|
|
dpkg -i /mnt/usb/packages/*.deb 2>/dev/null || true
|
|
dpkg --configure -a 2>/dev/null || true
|
|
fi
|
|
if [ -f /mnt/usb/playbook/pxe_server_setup.yml ]; then
|
|
cd /mnt/usb/playbook
|
|
ansible-playbook -i localhost, -c local pxe_server_setup.yml
|
|
fi
|
|
umount /mnt/usb
|
|
fi
|
|
sed -i "s|^/opt/first-boot.sh.*|# &|" /etc/rc.local
|
|
EOF
|
|
chmod +x /opt/first-boot.sh
|
|
'
|
|
|
|
# Step 4: Create rc.local
|
|
- |
|
|
curtin in-target --target=/target -- bash -c '
|
|
cat <<"EOF" > /etc/rc.local
|
|
#!/bin/bash
|
|
/opt/first-boot.sh > /var/log/first-boot.log 2>&1 &
|
|
exit 0
|
|
EOF
|
|
chmod +x /etc/rc.local
|
|
'
|
|
|
|
user-data:
|
|
disable_root: false
|
|
|
|
refresh-installer:
|
|
update: no
|