192 lines
7.6 KiB
Python
192 lines
7.6 KiB
Python
# FastAPI backend for bash script generator
|
|
from fastapi import FastAPI, Request, Form
|
|
from fastapi.responses import HTMLResponse, Response
|
|
from fastapi.templating import Jinja2Templates
|
|
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
# Initialize FastAPI app
|
|
app = FastAPI(title="Bash Script Generator")
|
|
templates = Jinja2Templates(directory="templates")
|
|
|
|
# Jinja2 environment for script template rendering
|
|
jinja_env = Environment(
|
|
loader=FileSystemLoader("templates"),
|
|
undefined=StrictUndefined,
|
|
autoescape=False,
|
|
trim_blocks=True,
|
|
lstrip_blocks=True,
|
|
)
|
|
|
|
def _bool(v: str | None) -> bool:
|
|
# HTML checkbox sends value only when checked
|
|
return v is not None
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
def index(request: Request):
|
|
"""Serve the main HTML form page"""
|
|
return templates.TemplateResponse("index.html", {"request": request})
|
|
|
|
@app.post("/generate")
|
|
def generate(
|
|
# feature flags - System Setup
|
|
system_update: str | None = Form(default=None),
|
|
auto_security_updates: str | None = Form(default=None),
|
|
setup_timezone: str | None = Form(default=None),
|
|
setup_hostname: str | None = Form(default=None),
|
|
setup_ntp: str | None = Form(default=None),
|
|
setup_swap: str | None = Form(default=None),
|
|
|
|
# feature flags - Security
|
|
ssh_harden: str | None = Form(default=None),
|
|
install_fail2ban: str | None = Form(default=None),
|
|
prelogin_banner: str | None = Form(default=None),
|
|
banner_type: str = Form(default="default"), # "default" or "dod_cmmc"
|
|
postlogin_banner: str | None = Form(default=None),
|
|
ssh_2fa: str | None = Form(default=None),
|
|
|
|
# feature flags - Docker & Services
|
|
install_docker: str | None = Form(default=None),
|
|
docker_admin_user: str | None = Form(default=None),
|
|
open_ports: str | None = Form(default=None),
|
|
combine_lan: str | None = Form(default=None),
|
|
|
|
# feature flags - User Management
|
|
create_admin_user: str | None = Form(default=None),
|
|
|
|
# feature flags - Monitoring
|
|
install_monitoring_tools: str | None = Form(default=None),
|
|
install_build_tools: str | None = Form(default=None),
|
|
|
|
# params
|
|
admin_username: str = Form(default="datamng"),
|
|
ssh_port: int = Form(default=22),
|
|
hostname: str = Form(default=""),
|
|
timezone: str = Form(default="UTC"),
|
|
new_admin_username: str = Form(default="admin"),
|
|
swap_size_gb: int = Form(default=2),
|
|
docker_data_dir: str = Form(default="/opt/docker"),
|
|
|
|
# firewall ports (comma-separated)
|
|
ports_csv: str = Form(default="22,80,81,443"),
|
|
|
|
# netplan params
|
|
lan_interfaces_csv: str = Form(default="eth0,eth1"),
|
|
static_ip_cidr: str = Form(default="192.168.1.9/24"),
|
|
gateway_ip: str = Form(default="192.168.1.1"),
|
|
dns_csv: str = Form(default="1.1.1.1,8.8.8.8"),
|
|
|
|
# owner information
|
|
owner_name: str = Form(default="Scardus"),
|
|
owner_website: str = Form(default="https://scardustech.com"),
|
|
owner_email: str = Form(default="info@scardustech.com"),
|
|
|
|
# SSH keys (multiline)
|
|
ssh_public_keys: str = Form(default=""),
|
|
):
|
|
"""Generate bash script based on form inputs"""
|
|
# Load banner templates from markdown files
|
|
# Try multiple paths to support both local development and Docker container
|
|
app_path = Path(__file__).parent
|
|
base_path = app_path.parent
|
|
|
|
# Determine which pre-login banner to use based on banner_type
|
|
banner_filename = "loginbanner_dod_cmmc.md.template" if banner_type == "dod_cmmc" else "loginbanner.md.template"
|
|
|
|
# Priority order: 1) workingscope directory, 2) templates directory, 3) Docker workingscope
|
|
prelogin_banner_path = base_path / "workingscope" / "loginbanner.md"
|
|
postlogin_banner_path = base_path / "workingscope" / "postloginbanner.md"
|
|
|
|
# Fallback to templates directory (for repository templates)
|
|
if not prelogin_banner_path.exists():
|
|
prelogin_banner_path = app_path / "templates" / banner_filename
|
|
if not postlogin_banner_path.exists():
|
|
postlogin_banner_path = app_path / "templates" / "postloginbanner.md.template"
|
|
|
|
# Final fallback: Docker container workingscope directory
|
|
if not prelogin_banner_path.exists():
|
|
# Try DOD CMMC banner if selected, otherwise default
|
|
if banner_type == "dod_cmmc":
|
|
prelogin_banner_path = Path("workingscope") / "loginbanner_dod_cmmc.md"
|
|
else:
|
|
prelogin_banner_path = Path("workingscope") / "loginbanner.md"
|
|
if not postlogin_banner_path.exists():
|
|
postlogin_banner_path = Path("workingscope") / "postloginbanner.md"
|
|
|
|
# Read banner content from markdown files
|
|
prelogin_text = ""
|
|
postlogin_text = ""
|
|
|
|
if prelogin_banner_path.exists():
|
|
with open(prelogin_banner_path, "r", encoding="utf-8") as f:
|
|
prelogin_text = f.read()
|
|
|
|
if postlogin_banner_path.exists():
|
|
with open(postlogin_banner_path, "r", encoding="utf-8") as f:
|
|
postlogin_text = f.read()
|
|
|
|
# Build context for Jinja2 template
|
|
ctx = {
|
|
"meta": {
|
|
"generated_at": datetime.utcnow().isoformat() + "Z",
|
|
},
|
|
"flags": {
|
|
# System Setup
|
|
"system_update": _bool(system_update),
|
|
"auto_security_updates": _bool(auto_security_updates),
|
|
"setup_timezone": _bool(setup_timezone),
|
|
"setup_hostname": _bool(setup_hostname),
|
|
"setup_ntp": _bool(setup_ntp),
|
|
"setup_swap": _bool(setup_swap),
|
|
# Security
|
|
"ssh_harden": _bool(ssh_harden),
|
|
"install_fail2ban": _bool(install_fail2ban),
|
|
"prelogin_banner": _bool(prelogin_banner),
|
|
"postlogin_banner": _bool(postlogin_banner),
|
|
"ssh_2fa": _bool(ssh_2fa),
|
|
# Docker & Services
|
|
"install_docker": _bool(install_docker),
|
|
"docker_admin_user": _bool(docker_admin_user),
|
|
"open_ports": _bool(open_ports),
|
|
"combine_lan": _bool(combine_lan),
|
|
# User Management
|
|
"create_admin_user": _bool(create_admin_user),
|
|
# Monitoring
|
|
"install_monitoring_tools": _bool(install_monitoring_tools),
|
|
"install_build_tools": _bool(install_build_tools),
|
|
},
|
|
"params": {
|
|
"admin_username": admin_username.strip(),
|
|
"ssh_port": int(ssh_port),
|
|
"hostname": hostname.strip(),
|
|
"timezone": timezone.strip(),
|
|
"new_admin_username": new_admin_username.strip(),
|
|
"swap_size_gb": int(swap_size_gb),
|
|
"docker_data_dir": docker_data_dir.strip(),
|
|
"ports": [p.strip() for p in ports_csv.split(",") if p.strip()],
|
|
"lan_ifaces": [i.strip() for i in lan_interfaces_csv.split(",") if i.strip()],
|
|
"static_ip_cidr": static_ip_cidr.strip(),
|
|
"gateway_ip": gateway_ip.strip(),
|
|
"dns": [d.strip() for d in dns_csv.split(",") if d.strip()],
|
|
"owner_name": owner_name.strip(),
|
|
"owner_website": owner_website.strip(),
|
|
"owner_email": owner_email.strip(),
|
|
"ssh_public_keys": [k.strip() for k in ssh_public_keys.split("\n") if k.strip()],
|
|
"prelogin_text": prelogin_text,
|
|
"postlogin_text": postlogin_text,
|
|
},
|
|
}
|
|
|
|
# Render bash script from template
|
|
tpl = jinja_env.get_template("script.sh.j2")
|
|
script = tpl.render(**ctx)
|
|
|
|
# Return as downloadable file
|
|
filename = "setup-server.sh"
|
|
return Response(
|
|
content=script,
|
|
media_type="application/x-sh",
|
|
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
|
)
|