Add Proxmox ISO builder, CSRF protection, boot-files integration

- Add build-proxmox-iso.sh: remaster Ubuntu ISO with autoinstall config,
  offline packages, playbook, webapp, and boot files for zero-touch
  Proxmox VM deployment
- Add boot-files/ directory for WinPE boot files (wimboot, boot.wim,
  BCD, ipxe.efi, etc.) sourced from WestJeff playbook
- Update build-usb.sh and test-vm.sh to bundle boot-files automatically
- Add usb_root variable to playbook, fix all file copy paths to use it
- Unify Apache VirtualHost config (merge default site + webapp proxy)
- Add CSRF token protection to all webapp POST forms and API endpoints
- Update README with Proxmox deployment instructions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-02-09 20:01:19 -05:00
parent cb442f971b
commit f3a384fa1a
14 changed files with 492 additions and 32 deletions

View File

@@ -3,6 +3,7 @@
import logging
import os
import secrets
import shutil
import subprocess
import tempfile
@@ -11,12 +12,14 @@ from pathlib import Path
from flask import (
Flask,
abort,
flash,
jsonify,
redirect,
render_template,
request,
send_file,
session,
url_for,
)
from lxml import etree
@@ -71,6 +74,32 @@ FRIENDLY_NAMES = {
"ge-shopfloor-mce": "GE Legacy Shop Floor MCE",
}
# ---------------------------------------------------------------------------
# CSRF protection
# ---------------------------------------------------------------------------
def generate_csrf_token():
"""Return the CSRF token for the current session, creating one if needed."""
if "_csrf_token" not in session:
session["_csrf_token"] = secrets.token_hex(32)
return session["_csrf_token"]
@app.context_processor
def inject_csrf_token():
"""Make csrf_token() available in all templates."""
return {"csrf_token": generate_csrf_token}
@app.before_request
def validate_csrf():
"""Reject POST requests with a missing or invalid CSRF token."""
if request.method != "POST":
return
token = request.form.get("_csrf_token") or request.headers.get("X-CSRF-Token")
if not token or token != generate_csrf_token():
abort(403)
NS = "urn:schemas-microsoft-com:unattend"
WCM = "http://schemas.microsoft.com/WMIConfig/2002/State"
NSMAP = {None: NS, "wcm": WCM}