fix(deploy): provision tenant API domains
Some checks failed
Architecture Governance / architecture (push) Failing after 6m16s
Some checks failed
Architecture Governance / architecture (push) Failing after 6m16s
Reconcile TLS, exact CORS, and backend proxying before release activation so every storefront uses its derived API host.
This commit is contained in:
@@ -115,6 +115,17 @@ certbot --nginx "${CERT_ARGS[@]}" \
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
echo "==> companion API domain(s)"
|
||||
CONFIGURE_API="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/configure-api-domain.sh"
|
||||
[[ -x "$CONFIGURE_API" ]] || {
|
||||
echo "ERROR: configure-api-domain.sh must be executable and next to add-domain.sh" >&2
|
||||
exit 1
|
||||
}
|
||||
"$CONFIGURE_API" --domain "$DOMAIN" --email "$EMAIL"
|
||||
if [[ $WITH_WWW -eq 1 ]]; then
|
||||
"$CONFIGURE_API" --domain "www.$DOMAIN" --email "$EMAIL"
|
||||
fi
|
||||
|
||||
echo "==> renewal timer"
|
||||
systemctl enable --now certbot.timer
|
||||
systemctl status certbot.timer --no-pager | head -3 || true
|
||||
|
||||
107
scripts/deploy/configure-api-domain.sh
Executable file
107
scripts/deploy/configure-api-domain.sh
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
# Configure api.<storefront-domain> as the TLS/CORS reverse proxy for one tenant.
|
||||
# Idempotent. Run as root after both storefront and API DNS records resolve here.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DOMAIN=""
|
||||
EMAIL=""
|
||||
UPSTREAM="https://127.0.0.1:445"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--domain) DOMAIN="$2"; shift 2 ;;
|
||||
--email) EMAIL="$2"; shift 2 ;;
|
||||
--upstream) UPSTREAM="$2"; shift 2 ;;
|
||||
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ $EUID -eq 0 ]] || { echo "must run as root" >&2; exit 1; }
|
||||
[[ "$DOMAIN" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$ ]] || {
|
||||
echo "--domain must be a valid lowercase hostname" >&2; exit 2;
|
||||
}
|
||||
[[ "$EMAIL" =~ ^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$ ]] || {
|
||||
echo "--email must be valid" >&2; exit 2;
|
||||
}
|
||||
[[ "$UPSTREAM" =~ ^https?://[a-zA-Z0-9.:-]+$ ]] || {
|
||||
echo "--upstream must be an http(s) origin without a path" >&2; exit 2;
|
||||
}
|
||||
|
||||
API_DOMAIN="api.$DOMAIN"
|
||||
CONF="/etc/nginx/sites-available/$API_DOMAIN"
|
||||
|
||||
echo "==> checking DNS for $API_DOMAIN"
|
||||
getent hosts "$API_DOMAIN" >/dev/null || {
|
||||
echo "ERROR: $API_DOMAIN does not resolve; create DNS before provisioning TLS" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cat > "$CONF" <<NGINX
|
||||
# Managed by marketplaces configure-api-domain.sh. Manual edits are overwritten.
|
||||
# Storefront $DOMAIN derives this API origin as https://$API_DOMAIN.
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name $API_DOMAIN;
|
||||
|
||||
access_log /var/log/nginx/$API_DOMAIN.access.log;
|
||||
error_log /var/log/nginx/$API_DOMAIN.error.log;
|
||||
|
||||
set \$cors_origin "";
|
||||
if (\$http_origin = "https://$DOMAIN") { set \$cors_origin \$http_origin; }
|
||||
|
||||
add_header Access-Control-Allow-Origin \$cors_origin always;
|
||||
add_header Access-Control-Allow-Credentials "true" always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Authorization, Content-Type, AdminWebSessionID, X-Requested-With" always;
|
||||
add_header Vary "Origin" always;
|
||||
|
||||
if (\$request_method = OPTIONS) { return 204; }
|
||||
|
||||
location / {
|
||||
proxy_pass $UPSTREAM;
|
||||
proxy_http_version 1.1;
|
||||
# Keep the existing backend compatible: it already serves this tenant
|
||||
# when the storefront Host reaches :445. The original public API host
|
||||
# remains available in the trusted forwarding headers below.
|
||||
proxy_set_header Host $DOMAIN;
|
||||
proxy_set_header X-Forwarded-Host $API_DOMAIN;
|
||||
proxy_set_header X-Storefront-Host $DOMAIN;
|
||||
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 https;
|
||||
proxy_read_timeout 60s;
|
||||
proxy_connect_timeout 10s;
|
||||
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name $DOMAIN;
|
||||
}
|
||||
}
|
||||
NGINX
|
||||
|
||||
ln -sfn "$CONF" "/etc/nginx/sites-enabled/$API_DOMAIN"
|
||||
nginx -t
|
||||
|
||||
certbot --nginx -d "$API_DOMAIN" \
|
||||
--non-interactive --agree-tos --email "$EMAIL" \
|
||||
--redirect --keep-until-expiring
|
||||
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
echo "==> verifying https://$API_DOMAIN/bootstrap"
|
||||
bootstrap_tmp="$(mktemp)"
|
||||
trap 'rm -f "$bootstrap_tmp"' EXIT
|
||||
content_type="$(curl --resolve "$API_DOMAIN:443:127.0.0.1" -fsS \
|
||||
-o "$bootstrap_tmp" -w '%{content_type}' \
|
||||
"https://$API_DOMAIN/bootstrap")"
|
||||
[[ "$content_type" == application/json* ]] || {
|
||||
echo "ERROR: $API_DOMAIN/bootstrap returned $content_type, expected application/json" >&2
|
||||
exit 1
|
||||
}
|
||||
jq -e 'type == "object"' "$bootstrap_tmp" >/dev/null
|
||||
rm -f "$bootstrap_tmp"
|
||||
trap - EXIT
|
||||
|
||||
echo "configured: $DOMAIN -> https://$API_DOMAIN -> $UPSTREAM"
|
||||
@@ -133,8 +133,17 @@ nginx -t
|
||||
systemctl enable --now nginx
|
||||
systemctl reload nginx
|
||||
|
||||
echo "==> dynamic domain reconciler"
|
||||
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
echo "==> tenant API-domain configurator"
|
||||
if [[ -f "$SRC_DIR/configure-api-domain.sh" ]]; then
|
||||
install -m 755 -o root -g root "$SRC_DIR/configure-api-domain.sh" \
|
||||
/usr/local/sbin/marketplaces-configure-api-domain
|
||||
else
|
||||
echo "configure-api-domain.sh not found next to server-setup.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> dynamic domain reconciler"
|
||||
install -d -m 755 "$BASE/bin" /etc/marketplaces "/var/lib/marketplaces"
|
||||
if [[ -f "$SRC_DIR/sync-domains.sh" ]]; then
|
||||
install -m 755 "$SRC_DIR/sync-domains.sh" "$BASE/bin/sync-domains.sh"
|
||||
@@ -172,9 +181,10 @@ else
|
||||
echo " sync-domains.sh not found next to this script - skipping"
|
||||
fi
|
||||
|
||||
echo "==> sudoers: let the deploy user reload nginx, nothing else"
|
||||
echo "==> sudoers: deployment reload plus validated tenant API provisioning"
|
||||
cat > /etc/sudoers.d/marketplaces-deploy <<SUDO
|
||||
$DEPLOY_USER ALL=(root) NOPASSWD: /bin/systemctl reload nginx
|
||||
Cmnd_Alias MARKETPLACES_DEPLOY = /bin/systemctl reload nginx, /usr/local/sbin/marketplaces-configure-api-domain *
|
||||
$DEPLOY_USER ALL=(root) NOPASSWD: MARKETPLACES_DEPLOY
|
||||
SUDO
|
||||
chmod 440 /etc/sudoers.d/marketplaces-deploy
|
||||
visudo -c -f /etc/sudoers.d/marketplaces-deploy
|
||||
|
||||
Reference in New Issue
Block a user