mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 16:13:15 +00:00
PR#2 of the AWS migration. deploy/ami/: Packer template (Ubuntu 24.04 arm64, uv+py3.12, nginx, awscli v2, CW agent; no swapfile), provisioning-only user-data (userDataCausesReplacement rationale), systemd unit + nginx + CW templates. Incorporates T5 /sh-security-review fixes: langgraph binds 127.0.0.1 (not 0.0.0.0); nginx is the sole ingress proxying only /dashboard/api/ + /webhooks/; ExecStartPre runs fetch-config as root (+) and passes the env arg; the app runs as the unprivileged openswe user reading an openswe-owned 0600 .env. packer validate clean.
105 lines
3.9 KiB
Bash
Executable file
105 lines
3.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Packer provisioner for the Open SWE base AMI (ARM64 Ubuntu 24.04).
|
|
#
|
|
# Bakes the runtime + boot-time templates ONLY. No secrets, no env-specific
|
|
# values. Everything env-specific is materialized at first boot by user-data.sh
|
|
# + deploy/seahaven/fetch-config.sh.
|
|
set -euo pipefail
|
|
|
|
PYTHON_VERSION="${PYTHON_VERSION:-3.12}"
|
|
NODE_MAJOR="${NODE_MAJOR:-24}"
|
|
UV_VERSION="${UV_VERSION:-0.11.24}"
|
|
CLOUDWATCH_AGENT_DEB_URL="${CLOUDWATCH_AGENT_DEB_URL:?}"
|
|
AWSCLI_ZIP_URL="${AWSCLI_ZIP_URL:?}"
|
|
|
|
# Layout (must match user-data.sh and the templates).
|
|
SERVICE_USER="openswe"
|
|
APP_DIR="/opt/open-swe/app"
|
|
SERVICE_HOME="/opt/open-swe"
|
|
WWW_ROOT="/var/www/open-swe"
|
|
TEMPLATE_DIR="/opt/open-swe/templates"
|
|
LOG_DIR="/var/log/open-swe"
|
|
UV_BIN="/usr/local/bin/uv"
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
|
echo "==> apt base packages"
|
|
apt-get update -y
|
|
apt-get upgrade -y
|
|
apt-get install -y --no-install-recommends \
|
|
nginx jq curl unzip ca-certificates gnupg lsb-release \
|
|
build-essential pkg-config git acl
|
|
|
|
echo "==> awscli v2 (aarch64)"
|
|
tmp="$(mktemp -d)"
|
|
curl -fsSL "$AWSCLI_ZIP_URL" -o "$tmp/awscliv2.zip"
|
|
unzip -q "$tmp/awscliv2.zip" -d "$tmp"
|
|
"$tmp/aws/install" --update
|
|
rm -rf "$tmp"
|
|
aws --version
|
|
|
|
echo "==> CloudWatch agent (arm64)"
|
|
tmp="$(mktemp -d)"
|
|
curl -fsSL "$CLOUDWATCH_AGENT_DEB_URL" -o "$tmp/amazon-cloudwatch-agent.deb"
|
|
dpkg -i -E "$tmp/amazon-cloudwatch-agent.deb"
|
|
rm -rf "$tmp"
|
|
# Do NOT enable/start the agent during the build; user-data fetches its config
|
|
# (with env-specific log-group names + 30-day retention) and starts it at boot.
|
|
systemctl disable amazon-cloudwatch-agent.service || true
|
|
|
|
echo "==> uv ${UV_VERSION} + Python ${PYTHON_VERSION} (system-wide)"
|
|
export UV_INSTALL_DIR=/usr/local/bin
|
|
curl -fsSL "https://astral.sh/uv/${UV_VERSION}/install.sh" | env UV_NO_MODIFY_PATH=1 sh
|
|
"$UV_BIN" --version
|
|
# Pre-install the interpreter so the box never reaches out at boot to build a venv.
|
|
UV_PYTHON_INSTALL_DIR=/opt/uv/python "$UV_BIN" python install "$PYTHON_VERSION"
|
|
|
|
echo "==> node ${NODE_MAJOR} + bun (build-time UI tooling only; the SPA is built in CI)"
|
|
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
|
|
apt-get install -y --no-install-recommends nodejs
|
|
node --version
|
|
# bun installed system-wide; used only if any UI tooling must run on-box. The
|
|
# production SPA build runs in GitHub Actions -> S3 (no on-box build, no swapfile).
|
|
export BUN_INSTALL=/usr/local
|
|
curl -fsSL https://bun.sh/install | bash
|
|
/usr/local/bin/bun --version || true
|
|
|
|
echo "==> non-login service user '${SERVICE_USER}'"
|
|
if ! id "$SERVICE_USER" >/dev/null 2>&1; then
|
|
useradd --system --create-home --home-dir "$SERVICE_HOME" \
|
|
--shell /usr/sbin/nologin "$SERVICE_USER"
|
|
fi
|
|
|
|
echo "==> directories"
|
|
install -d -o "$SERVICE_USER" -g "$SERVICE_USER" -m 0755 "$SERVICE_HOME" "$APP_DIR"
|
|
install -d -o "$SERVICE_USER" -g "$SERVICE_USER" -m 0755 "$WWW_ROOT"
|
|
install -d -o "$SERVICE_USER" -g "$SERVICE_USER" -m 0750 "$LOG_DIR"
|
|
install -d -o root -g root -m 0755 "$TEMPLATE_DIR"
|
|
|
|
echo "==> stage boot-time templates into the image"
|
|
cp /tmp/open-swe-templates/* "$TEMPLATE_DIR/"
|
|
chown root:root "$TEMPLATE_DIR"/*
|
|
chmod 0644 "$TEMPLATE_DIR"/*
|
|
rm -rf /tmp/open-swe-templates
|
|
|
|
echo "==> tmpfs for the runtime .env (root/owner-only, noexec/nosuid/nodev)"
|
|
# /run is already tmpfs on Ubuntu; this is an explicit, deliberately-small mount
|
|
# scoped to the service user so the materialized .env never touches disk.
|
|
if ! grep -q '/run/open-swe' /etc/fstab; then
|
|
cat >>/etc/fstab <<EOF
|
|
tmpfs /run/open-swe tmpfs rw,nosuid,nodev,noexec,mode=0700,uid=${SERVICE_USER},gid=${SERVICE_USER},size=8m 0 0
|
|
EOF
|
|
fi
|
|
|
|
echo "==> disable nginx default site (open-swe site is installed at boot)"
|
|
rm -f /etc/nginx/sites-enabled/default
|
|
systemctl enable nginx
|
|
|
|
echo "==> harden: no password auth, IMDSv2 already enforced by launch template"
|
|
# (sshd is not exposed publicly — instance is in a private subnet, SG inbound = ALB only.)
|
|
|
|
echo "==> clean apt caches"
|
|
apt-get clean
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
echo "==> provision complete"
|