Issue dashboard SSL per tenant instead of full cert sync.
Per-row Issue SSL now runs ssl-issue-tenant.sh for business/customer hosts only, with nginx cert map support, so one broken tenant cannot block others. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
202ac7d4bd
commit
b7bce38131
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# Issue / renew Let's Encrypt for one tenant's dashboard hosts only:
|
||||
# business.{apex} + customer.{apex}
|
||||
# Updates nginx cert map and reloads. Does NOT touch other domains.
|
||||
set -euo pipefail
|
||||
|
||||
CONF=/etc/meshkee/ssl-sync.env
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONF"
|
||||
|
||||
APEX="${SSL_TENANT_APEX:-}"
|
||||
if [[ -z "$APEX" ]]; then
|
||||
echo "$(date -Is) ERROR: SSL_TENANT_APEX not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APEX="${APEX,,}"
|
||||
CERT_NAME="${SSL_TENANT_CERT_NAME:-meshkee-dash-${APEX//./-}}"
|
||||
EMAIL="${SSL_EMAIL:-info@meshkee.com}"
|
||||
WEBROOT="${SSL_WEBROOT:-/var/www/certbot}"
|
||||
MAP_FILE="${SSL_DASHBOARD_CERT_MAP:-/etc/nginx/conf.d/meshkee-dashboard-certs-map.conf}"
|
||||
|
||||
BUSINESS_HOST="business.${APEX}"
|
||||
CUSTOMER_HOST="customer.${APEX}"
|
||||
|
||||
mkdir -p "$WEBROOT"
|
||||
|
||||
echo "$(date -Is) Issuing tenant dashboard cert ${CERT_NAME} for ${BUSINESS_HOST} ${CUSTOMER_HOST}"
|
||||
|
||||
certbot certonly \
|
||||
--webroot -w "$WEBROOT" \
|
||||
--cert-name "$CERT_NAME" \
|
||||
--email "$EMAIL" \
|
||||
--agree-tos \
|
||||
--non-interactive \
|
||||
-d "$BUSINESS_HOST" \
|
||||
-d "$CUSTOMER_HOST"
|
||||
|
||||
CERT_DIR="/etc/letsencrypt/live/${CERT_NAME}"
|
||||
if [[ ! -f "${CERT_DIR}/fullchain.pem" ]]; then
|
||||
echo "$(date -Is) ERROR: cert not found at ${CERT_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python3 - "$MAP_FILE" "$APEX" "$CERT_NAME" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
map_path = pathlib.Path(sys.argv[1])
|
||||
apex = sys.argv[2].strip().lower()
|
||||
cert_name = sys.argv[3].strip()
|
||||
cert_dir = f"/etc/letsencrypt/live/{cert_name}"
|
||||
business = f"business.{apex}"
|
||||
customer = f"customer.{apex}"
|
||||
|
||||
default_cert = "/etc/letsencrypt/live/meshkee-dashboards/fullchain.pem"
|
||||
default_key = "/etc/letsencrypt/live/meshkee-dashboards/privkey.pem"
|
||||
tenant_cert = f"{cert_dir}/fullchain.pem"
|
||||
tenant_key = f"{cert_dir}/privkey.pem"
|
||||
|
||||
entries_cert: dict[str, str] = {"default": default_cert}
|
||||
entries_key: dict[str, str] = {"default": default_key}
|
||||
|
||||
if map_path.is_file():
|
||||
mode = None
|
||||
for raw in map_path.read_text().splitlines():
|
||||
line = raw.strip()
|
||||
if line.startswith("map $host $meshkee_dashboard_ssl_cert"):
|
||||
mode = "cert"
|
||||
continue
|
||||
if line.startswith("map $host $meshkee_dashboard_ssl_key"):
|
||||
mode = "key"
|
||||
continue
|
||||
if mode and line and not line.startswith("#") and " " in line:
|
||||
host, path = line.split(None, 1)
|
||||
host = host.rstrip(";")
|
||||
path = path.rstrip(";")
|
||||
if host != "default":
|
||||
if mode == "cert":
|
||||
entries_cert[host] = path
|
||||
else:
|
||||
entries_key[host] = path
|
||||
|
||||
entries_cert[business] = tenant_cert
|
||||
entries_cert[customer] = tenant_cert
|
||||
entries_key[business] = tenant_key
|
||||
entries_key[customer] = tenant_key
|
||||
|
||||
def render_map(name: str, entries: dict[str, str]) -> str:
|
||||
lines = [f"map $host ${name} {{"]
|
||||
lines.append(f" default {entries['default']};")
|
||||
for host in sorted(h for h in entries if h != "default"):
|
||||
lines.append(f" {host} {entries[host]};")
|
||||
lines.append("}")
|
||||
return "\n".join(lines)
|
||||
|
||||
content = (
|
||||
"# Managed by ssl-issue-tenant.sh — per-tenant dashboard TLS paths\n"
|
||||
+ render_map("meshkee_dashboard_ssl_cert", entries_cert)
|
||||
+ "\n\n"
|
||||
+ render_map("meshkee_dashboard_ssl_key", entries_key)
|
||||
+ "\n"
|
||||
)
|
||||
map_path.write_text(content)
|
||||
print(f"Updated {map_path} for {business} / {customer}")
|
||||
PY
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
echo "$(date -Is) Tenant cert ${CERT_NAME} installed and nginx reloaded"
|
||||
Reference in New Issue
Block a user