krustyplanet.org/terraform/cloud-init.yaml.tpl
Jezza Hehn 9f7aa97f4e Fix cloud-init: escape heredoc vars, add contact-api snippet, fix CORS
- Escape $uri/$host in heredocs so nginx sees them, not bash
- Rename heredoc markers (NGINXEOF, PROXYEOF, SVCEOF) to avoid conflicts
- Add contact-api nginx snippet WITHOUT proxy_set_header Origin (CORS fix)
- Fix contact-api clone URL to Forgejo
- Simplify .env template
2026-04-13 22:23:04 +00:00

162 lines
4.2 KiB
Smarty

#!/bin/bash
set -euxo pipefail
###############################################################################
# Cloud-Init for KrustyPlanet VPS
###############################################################################
# Update system
apt-get update -y
apt-get upgrade -y
# Install required packages
apt-get install -y nginx certbot python3-certbot-nginx curl git
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_${node_version}.x | bash -
apt-get install -y nodejs
# Create directories
mkdir -p /opt/contact-api
mkdir -p /var/www/${project_name}
mkdir -p /var/www/${project_name}/css
mkdir -p /var/www/${project_name}/js
# Set domain variable
DOMAIN=${domain}
# Set up nginx configuration
cat > /etc/nginx/sites-available/${project_name} << NGINXEOF
server {
root /var/www/${project_name};
index index.html;
server_name ${domain} www.${domain};
location ~* \.(js|css)$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
try_files \$uri \$uri/ =404;
}
location / {
try_files \$uri \$uri/ =404;
}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
listen [::]:443 ssl ipv6only=on;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 80;
listen [::]:80;
server_name ${domain} www.${domain};
return 301 https://\$host\$request_uri;
}
NGINXEOF
# Symlink nginx config
ln -sf /etc/nginx/sites-available/${project_name} /etc/nginx/sites-enabled/${project_name}
# Remove default nginx site
rm -f /etc/nginx/sites-enabled/default
# Set up nginx snippet for contact-api proxy (NO proxy_set_header Origin — breaks CORS)
cat > /etc/nginx/snippets/contact-api.conf << 'PROXYEOF'
location /api/contact {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
location /api/health {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host \$host;
}
PROXYEOF
# Include the snippet in the main server block
sed -i '/location \/ {/i\ include /etc/nginx/snippets/contact-api.conf;' /etc/nginx/sites-available/${project_name}
# Set up contact-api service
cat > /etc/systemd/system/contact-api.service << 'SVCEOF'
[Unit]
Description=Contact Form API - Email Backend
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/contact-api
ExecStart=/usr/bin/node src/index.js
Restart=on-failure
RestartSec=10
EnvironmentFile=/opt/contact-api/.env
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/contact-api
StandardOutput=journal
StandardError=journal
SyslogIdentifier=contact-api
[Install]
WantedBy=multi-user.target
SVCEOF
# Start nginx
systemctl restart nginx
# Enable contact-api service
systemctl daemon-reload
systemctl enable contact-api.service
# Download contact-api source from Forgejo
cd /opt/contact-api
git clone ssh://git@git.jezzahehn.com:2222/KrustyPlanet/contact-api.git .
chown -R www-data:www-data /opt/contact-api
# .env will be created manually or via secrets management
cat > /opt/contact-api/.env << 'ENVEOF'
CONTACT_API_PORT=3001
ENVEOF
# Install dependencies
npm install
# Set permissions
chown -R www-data:www-data /opt/contact-api
chown -R www-data:www-data /var/www/${project_name}
# Start contact-api
systemctl start contact-api.service
# Get SSL certificate
certbot --nginx --non-interactive --agree-tos --email noreply@krustyplanet.org -d ${domain} -d www.${domain}
# Enable certbot auto-renewal
systemctl enable certbot.timer
# Clean up
apt-get autoremove -y
apt-get clean
echo "KrustyPlanet VPS setup complete!"