H1 corp_admin_cidrs (UFW 22/9009 on the corp NIC from these only; refuses
when it would lock out the current SSH client) and corp_ssh_password_auth
(sshd drop-in, passwords only from the imaging subnet). Defaults keep the
current behaviour and print a loud warning.
H2 pxe-netplan-apply.sh: pre-flight refusal (pxe-MAC NIC holds the default
route, a DHCP lease or the route to an SSH client), run from the USB
before anything changes; post-apply checks (names, imaging IPv4, corp
IPv4 back within 30 s, SSH route) and a 120 s systemd-run auto-revert.
H3 restore() puts the previous NIC names back (two-phase rename via
temporary names, so swaps work) before generate + apply.
H4 a failed rename is fatal (restore + exit 1); the play drops the old
any-interface UFW rules only after verifying both NICs and their
per-interface SSH rules.
H5 first-boot checks the playbook exit code, retries at next boot (max 5),
writes /var/lib/pxe-firstboot.failed and /etc/issue + /etc/motd banners.
M1 build-usb.sh verifies iPXE files against the playbook pins, every wheel
against its own package's hashes, drops the unpinned pip fallback,
requires --site-vars or --no-site-vars.
M2 webapp restarts when the code, wheels or unit changed.
M3 ignore-carrier on the imaging NIC; ip_nonlocal_bind kept for the
apache/networkd boot race.
M4 menu suffix must match blancco_kver; missing kernel with no earlier
build is fatal; a live menu with the staged Blancco test entry is not
overwritten.
M5 build-proxmox-iso.sh is unsupported (hard exit); use build-usb --image.
M6 deploy-unattend.sh: password via SSHPASS env and sudo stdin only,
%q-quoted remote args, sha256 check of the live file; lint-unattend.py
reads PXE_SERVER/PXE_USER/PXE_PASS and uses sshpass -e.
M7 the playbook lints the unattend files before the force: yes copies
(lint-unattend.py ships on CIDATA as scripts/); pre-commit ACMR.
M8 Blancco initramfs build: no ignore_errors, pipefail, trap cleanup.
Also: Send-ImagingStage.ps1 deploy, carrier hook install tasks
(pxe-nic-carrier, networkd-dispatcher), L1 imaging-active hold
(allow_disruptive) and no iPXE downgrade below an Updates-page install,
L2 iso-volid, L3 kver pin read from the playbook, L4 cp -rL, L5 refuse
>= 4 GiB WinPE files, L6 PIP_DIR wipe guard, L7 per-package wheel hashes,
no online fallback, L8 check-updates.py (apt_pkg/cached compare, all
components, exit 2 on no debs / bad answers).
11 KiB
Site Customization
The PXE server in this repo was built for the GE Aerospace West Jefferson site. To stand it up at another site, several values need to change. This document lists every site-specific value, where it lives today, and the recommended forward state.
Status
The repo has not yet been parameterized. As of 2026-04-14 the site-specific
values below are hardcoded in multiple files. The config/ directory at the
repo root is a placeholder for the future state where one YAML file drives
substitution at build time. Until that exists, customizing for a new site
means editing the source files directly.
If you are bringing up the first non-WJ site, expect to do this manually and
contribute a config/sites/<sitename>.yaml template back to the repo.
Values to change
Network
Every server has two NICs: the imaging NIC (isolated imaging LAN, static
172.16.9.1, the only NIC that serves DHCP/TFTP/HTTP boot/SMB) and the GE
corporate NIC (DHCP client; SSH and the webapp on 9009 only). Put both MACs
in playbook/site-vars.yml (copy playbook/site-vars.example.yml) before
building the USB: pxe_nic_mac, corp_nic_mac. The installer brings the
network up with DHCP only; the first-boot playbook stops before changing
anything if either MAC is unset or missing, then pins both NICs by MAC in
netplan (names pxe0 / corp0, overridable), dnsmasq, UFW and Samba.
| Value | Default | Where it lives |
|---|---|---|
| PXE server IP | 172.16.9.1 | playbook/pxe_server_setup.yml (dnsmasq config, iPXE script, samba conf, webapp env), playbook/startnet.cmd (mount paths), boot-tools/blancco/grub-blancco.cfg (TFTP/HTTP URLs) |
| PXE subnet | 172.16.9.0/24 | Same as above, plus playbook/pxe_server_setup.yml (UFW rules) |
| DHCP range | 172.16.9.10-100 | playbook/site-vars.yml (pxe_dhcp_start, pxe_dhcp_end) |
| NIC MACs / names | (required) / pxe0, corp0 | playbook/site-vars.yml (pxe_nic_mac, corp_nic_mac, pxe_nic_name, corp_nic_name) |
| Hostname | pxeserver | autoinstall/user-data (identity.hostname) |
Access from the GE corporate network
SSH (password auth, the shared pxe password) and the webapp on 9009 (no
login, runs as root) are reachable on the corp NIC. By default from ALL of
GE, as before, so a re-run never locks anyone out; the play prints a loud
warning on every run while that is so. Set these in playbook/site-vars.yml:
| Var | Default | Effect |
|---|---|---|
corp_admin_cidrs |
[] |
List of admin subnets/hosts, e.g. ["10.134.48.0/24", "3.12.34.56/32"]. When non-empty, UFW allows 22 and 9009 on corp_nic_name from these only and drops the any-source rules. The play refuses (nothing changed) if an entry is invalid or your own SSH session is not inside one of them. The imaging NIC keeps its rules. |
corp_ssh_password_auth |
true |
false adds /etc/ssh/sshd_config.d/10-pxe-corp-auth.conf: PasswordAuthentication no, re-allowed by a Match Address block for pxe_subnet and localhost. Keys only from GE. The play refuses while pxe has no ~/.ssh/authorized_keys, and removes the drop-in again if sshd -t rejects it. |
allow_disruptive |
false |
Re-runs while imaging is active (SMB sessions on 445, or DHCP leases granted in the last pxe_busy_window_min = 30 min) do not bounce the NICs or restart dnsmasq/Apache/Samba; a needed network change stops the play. -e allow_disruptive=true overrides. |
Recommended for a production box: corp_admin_cidrs set to the admin
desks' subnet, then (after ssh-copy-id) corp_ssh_password_auth: false.
Re-running the playbook over SSH (from the GE side)
The network step refuses before changing anything when the NIC that
pxe_nic_mac names holds the default route, a DHCP lease, or the route to
your SSH client (swapped MACs or cables). When it does apply, it arms a
120 s auto-revert timer (pxe-netplan-revert-*), checks both names, the
imaging address and that the corp NIC gets its IPv4 back within 30 s, and
restores the previous netplan config AND NIC names on any failure. Backups:
/var/backups/netplan-<timestamp>/.
Identity and credentials
| Value | Default | Where it lives |
|---|---|---|
| Linux user | pxe / pxe | autoinstall/user-data (identity.username, password hash) |
| Samba upload user | pxe-upload / pxe | playbook/pxe_server_setup.yml (Samba user creation) |
| Samba blancco user | blancco / blancco | playbook/pxe_server_setup.yml |
| Sudo password | pxe | autoinstall password hash; also referenced in scripts that ssh + sudo |
WiFi and BMC (Blancco licensing)
The Blancco custom config.img carries WiFi credentials and BMC cloud
credentials in plaintext. These are GE-Aerospace specific.
| Value | Default | Where it lives |
|---|---|---|
| WiFi SSID | INTERNETACCESS | boot-tools/blancco/config.img (inside the cpio archive: preferences.xml) |
| WiFi password | Acc3ssGr@nted22 | Same |
| BMC endpoint | classic.eu-west-1.blancco.cloud:443 | Same; encrypted credentials inside config.img |
To rebuild config.img for a different site, see BLANCCO.md ("Rebuilding
config.img").
Image types
The repo defines seven image types (see playbook/pxe_server_setup.yml,
image_types). Most sites likely only need a subset. The current set:
- gea-standard
- gea-engineer
- gea-shopfloor
- ge-standard
- ge-engineer
- ge-shopfloor-lockdown
- ge-shopfloor-mce
Trim or extend this list and the matching shopfloor_types filter as needed.
PC types (shopfloor sub-types)
playbook/shopfloor-setup/ contains per-PC-type install logic. The current
types reflect WJ shopfloor needs:
- CMM (PC-DMIS)
- WaxAndTrace
- Keyence
- Genspect
- Display (Lobby and Dashboard variants)
- Standard (Timeclock and Machine variants)
- Lab
A site without CMMs can drop the CMM type and its installers entirely.
GCCH bulk-enrollment PPKGs
PPKG filenames are baked into playbook/startnet.cmd and
startnet-template.cmd. They are tied to the GE Aerospace tenant. Other
sites/tenants will have their own PPKGs with different filenames.
| PPKG enrollment menu choice | Default filename |
|---|---|
| 1 NoOffice | GCCH_Prod_SFLD_NoOffice_US_Exp_20260430_v4.10.ppkg |
| 2 StdOffice x86 | GCCH_Prod_SFLD_StdOffice-x86_US_Exp_20260430_v4.10.ppkg |
| 3 StdOffice x64 | GCCH_Prod_SFLD_StdOffice-x64_US_Exp_20260430_v4.10.ppkg |
| 4 ProPlusOffice x86 | GCCH_Prod_SFLD_ProPlusOffice-x86_US_Exp_20260430_v4.10.ppkg |
| 5 ProPlusOffice x64 | GCCH_Prod_SFLD_ProPlusOffice-x64_US_Exp_20260430_v4.10.ppkg |
When you bump PPKG versions, update the filenames in startnet.cmd, drop the
new PPKGs in enrollment/ on the workstation, and rebuild the USB.
Site-specific shopfloor config
playbook/shopfloor-setup/site-config.json has site-specific values like
default machine number ranges, share paths for software, and more. Inspect
that file for site name strings, share UNC paths, and machine number ranges.
Branding
The Flask webapp uses GE Aerospace branding:
- Logo:
webapp/static/ge-aerospace-logo.svg - Favicon:
webapp/static/favicon.ico - Page titles and templates reference "GE Aerospace" in
webapp/templates/*.html - iPXE menu title: "GE Aerospace PXE Boot Menu" in
playbook/pxe_server_setup.yml
Drivers (Dell)
Driver content is hardware-specific, not strictly site-specific, but the set of driver families to bundle changes by what hardware the site deploys:
Dell_11/ProandDell_11/ProMax: Dell Pro Micro PCs (most common shopfloor)Dell_11/Optiplex: traditional desktopsDell_11/Precision: workstationsDell_11/Latitude: laptops (large, often excluded if site has none)
The scripts/download-drivers.py and scripts/Download-Drivers.ps1 scripts
populate drivers-staging/ from upstream sources. Edit these to target the
relevant hardware lists.
BIOS update manifest
playbook/shopfloor-setup/BIOS/models.txt maps Dell model strings to the
BIOS .exe to flash. Update this whenever new hardware arrives or Dell
publishes a new firmware release for a model already in the fleet.
Intune DSC categories
The post-enrollment Intune DSC delivery (per intune-dsc.md memory) pulls
per-category YAML from Azure Blob:
- main
- cmm
- displaypcs
- waxtrace
Different sites/tenants will need their own categories and their own Azure Blob storage account.
Image-upload paths on Windows
scripts/Upload-Image.ps1 defaults to:
\\172.16.9.1\image-uploadas the destinationC:\ProgramData\GEAerospace\MediaCreator\Cache\as the source
Update both for a different site.
Recommended forward state
A site config file should drive substitution at build time. Proposed schema:
# config/sites/<sitename>.yaml
site:
name: westjeff
pxe_server_ip: 172.16.9.1
pxe_subnet: 172.16.9.0/24
dhcp_range_start: 172.16.9.10
dhcp_range_end: 172.16.9.100
hostname: pxeserver
credentials:
pxe_user: pxe
pxe_password: pxe
pxe_upload_password: pxe
blancco_password: blancco
blancco:
wifi_ssid: INTERNETACCESS
wifi_password: Acc3ssGr@nted22
bmc_endpoint: classic.eu-west-1.blancco.cloud:443
ppkg_versions:
current: v4.10
expiry: 20260430
intune_dsc_categories: [main, cmm, displaypcs, waxtrace]
image_types: [gea-standard, gea-engineer, gea-shopfloor]
shopfloor_types: [gea-shopfloor]
scripts/build-usb.sh and the playbook would consume this and substitute
into the templated config files. Implementing this is a separate piece of
work; until then, edit source files directly.
Secrets handling
Some values above are sensitive (WiFi passwords, BMC credentials, Samba passwords). Today these sit in plain text in the repo. For a multi-site deployment, treat them as per-site secrets:
- Keep
config/sites/<sitename>.secrets.yamlout of git (gitignore). - Distribute via secure channel (1Password, Vault, etc.) when standing up a new site.
- The
eMxInfo.txtprecedent (seefeedback_emxinfo_secret.md) is a good template: never commit, document where the canonical copy lives.