feat(mail): Mailcow provision wrappers, zone override for mail DNS/NPM
Some checks failed
Deploy to Phoenix / validate (push) Failing after 1s
Deploy to Phoenix / deploy (push) Has been skipped
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Has been skipped
Deploy to Phoenix / cloudflare (push) Has been skipped

- .env.master.example: CLOUDFLARE_ZONE_ID_OVERRIDE, Mailcow vars
- provision-d-bis-mail-dns-and-npmplus: optional https upstream, override zone id
- scripts/mailcow: defi-oracle.io DNS/NPM wrapper + Mailcow mailbox API helper

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-05-12 19:25:21 -07:00
parent b388cd1fbc
commit 07f5d80855
4 changed files with 226 additions and 13 deletions

View File

@@ -6,7 +6,7 @@
# - .env: CLOUDFLARE_API_TOKEN (or EMAIL+API_KEY) with Zone:DNS:Edit; for Origin CA also
# SSL and Certificates (or use Global API Key for Origin CA — prefer scoped token with
# Zone + SSL per Cloudflare dashboard).
# - CLOUDFLARE_ZONE_ID_D_BIS_ORG
# - Zone id: CLOUDFLARE_ZONE_ID_D_BIS_ORG (d-bis.org) or CLOUDFLARE_ZONE_ID_OVERRIDE / CLOUDFLARE_ZONE_ID for other zones
# - NPM_URL, NPM_EMAIL, NPM_PASSWORD for NPMplus API
#
# Usage:
@@ -25,6 +25,7 @@
# PROVISION_MX=0 1 = set apex MX to MX_TARGET (default 0: do not change — many zones use Zoho/365)
# PROVISION_SPF=0 1 = upsert ONE apex TXT to SPF_TXT (default 0: do not clobber Zoho/legacy TXT)
# PROVISION_DMARC=0
# MAIL_UPSTREAM_SCHEME=http NPM upstream scheme
# IP_MAIL_UPSTREAM=192.168.11.32 PMG / webmail HTTP backend
# PORT_MAIL_UPSTREAM=8006
# MAIL_NPM_BLOCK_EXPLOITS=0 0 = ModSecurity off for finicky UIs (PMG admin)
@@ -50,7 +51,8 @@ fi
ZONE_NAME="${ZONE_NAME:-d-bis.org}"
MAIL_SUB="${MAIL_SUBDOMAIN:-mail}"
MAIL_FQDN="${MAIL_SUB}.${ZONE_NAME}"
ZONE_ID="${CLOUDFLARE_ZONE_ID_D_BIS_ORG:-${CLOUDFLARE_ZONE_ID:-}}"
# Prefer CLOUDFLARE_ZONE_ID_OVERRIDE when set (e.g. defi-oracle.io + CLOUDFLARE_ZONE_ID_DEFI_ORACLE_IO).
ZONE_ID="${CLOUDFLARE_ZONE_ID_OVERRIDE:-${CLOUDFLARE_ZONE_ID_D_BIS_ORG:-${CLOUDFLARE_ZONE_ID:-}}}"
PUBLIC_IP="${PUBLIC_IP:-76.53.10.36}"
MX_TARGET="${MX_TARGET:-$MAIL_FQDN}"
MX_PRI="${MX_PRIORITY:-10}"
@@ -62,6 +64,7 @@ PROVISION_DMARC="${PROVISION_DMARC:-0}"
MAIL_NPM_BLOCK_EXPLOITS="${MAIL_NPM_BLOCK_EXPLOITS:-0}"
IP_MAIL_UP="${IP_MAIL_UPSTREAM:-${IP_PMG:-192.168.11.32}}"
PORT_MAIL_UP="${PORT_MAIL_UPSTREAM:-8006}"
MAIL_UPSTREAM_SCHEME="${MAIL_UPSTREAM_SCHEME:-http}"
PROVISION_CF_ORIGIN_CERT="${PROVISION_CF_ORIGIN_CERT:-0}"
PROVISION_NPM="${PROVISION_NPM:-1}"
CERT_OUT_DIR="${CERT_OUT_DIR:-$PROJECT_ROOT/backups/certs}"
@@ -79,7 +82,7 @@ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_err() { echo -e "${RED}[ERR]${NC} $1"; }
if [ -z "$ZONE_ID" ]; then
log_err "CLOUDFLARE_ZONE_ID_D_BIS_ORG (or CLOUDFLARE_ZONE_ID) is required"
log_err "Set zone id: CLOUDFLARE_ZONE_ID_OVERRIDE, or CLOUDFLARE_ZONE_ID_D_BIS_ORG / CLOUDFLARE_ZONE_ID (see script header)."
exit 1
fi
@@ -213,7 +216,7 @@ if [ "$PROVISION_NPM" != 1 ]; then
exit 0
fi
if [ "$DRY" = 1 ]; then
log_info "[dry-run] NPM: would ensure proxy host $MAIL_FQDNhttp://${IP_MAIL_UP}:${PORT_MAIL_UP}"
log_info "[dry-run] NPM: would ensure proxy host $MAIL_FQDN${MAIL_UPSTREAM_SCHEME}://${IP_MAIL_UP}:${PORT_MAIL_UP}"
log_ok "Done."
exit 0
fi
@@ -258,13 +261,13 @@ fi
# Create or update proxy host
if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then
log_info "Creating proxy host $MAIL_FQDNhttp://${IP_MAIL_UP}:${PORT_MAIL_UP} ..."
log_info "Creating proxy host $MAIL_FQDN${MAIL_UPSTREAM_SCHEME}://${IP_MAIL_UP}:${PORT_MAIL_UP} ..."
if [ -n "$CERT_ID_TO_USE" ] && [ "$CERT_ID_TO_USE" != "null" ]; then
PH=$(jq -n --arg d "$MAIL_FQDN" --arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" --argjson cid "$CERT_ID_TO_USE" \
'{domain_names:[$d], forward_scheme:"http", forward_host:$h, forward_port:$p, allow_websocket_upgrade:true, block_exploits:$be, certificate_id:$cid, ssl_forced:true, http2_support:true, hsts_enabled:true, hsts_subdomains:false}')
PH=$(jq -n --arg d "$MAIL_FQDN" --arg s "$MAIL_UPSTREAM_SCHEME" --arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" --argjson cid "$CERT_ID_TO_USE" \
'{domain_names:[$d], forward_scheme:$s, forward_host:$h, forward_port:$p, allow_websocket_upgrade:true, block_exploits:$be, certificate_id:$cid, ssl_forced:true, http2_support:true, hsts_enabled:true, hsts_subdomains:false}')
else
PH=$(jq -n --arg d "$MAIL_FQDN" --arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" \
'{domain_names:[$d], forward_scheme:"http", forward_host:$h, forward_port:$p, allow_websocket_upgrade:true, block_exploits:$be, certificate_id:null, ssl_forced:false}')
PH=$(jq -n --arg d "$MAIL_FQDN" --arg s "$MAIL_UPSTREAM_SCHEME" --arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" \
'{domain_names:[$d], forward_scheme:$s, forward_host:$h, forward_port:$p, allow_websocket_upgrade:true, block_exploits:$be, certificate_id:null, ssl_forced:false}')
fi
PR=$(curl_npm -X POST "$NPM_URL/api/nginx/proxy-hosts" -H "Authorization: Bearer $TOK" -H "Content-Type: application/json" -d "$PH")
@@ -278,12 +281,12 @@ else
log_info "Updating proxy host id $HOST_ID"
if [ -n "$CERT_ID_TO_USE" ] && [ "$CERT_ID_TO_USE" != "null" ]; then
PAYLOAD=$(curl_npm -X GET "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" -H "Authorization: Bearer $TOK" | jq \
--arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" --argjson cid "$CERT_ID_TO_USE" \
'.forward_host=$h | .forward_port=$p | .forward_scheme="http" | .block_exploits=$be | .certificate_id=$cid | .ssl_forced=true | .http2_support=true')
--arg s "$MAIL_UPSTREAM_SCHEME" --arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" --argjson cid "$CERT_ID_TO_USE" \
'{domain_names, forward_scheme:$s, forward_host:$h, forward_port:$p, allow_websocket_upgrade:(.allow_websocket_upgrade // true), block_exploits:$be, certificate_id:$cid, ssl_forced:true, http2_support:true, hsts_enabled:(.hsts_enabled // true), hsts_subdomains:(.hsts_subdomains // false), advanced_config:(.advanced_config // ""), locations:(.locations // [])}')
else
PAYLOAD=$(curl_npm -X GET "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" -H "Authorization: Bearer $TOK" | jq \
--arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" \
'.forward_host=$h | .forward_port=$p | .forward_scheme="http" | .block_exploits=$be')
--arg s "$MAIL_UPSTREAM_SCHEME" --arg h "$IP_MAIL_UP" --argjson p "$PORT_MAIL_UP" --argjson be "$NPM_BLOCK_JSON" \
'{domain_names, forward_scheme:$s, forward_host:$h, forward_port:$p, allow_websocket_upgrade:(.allow_websocket_upgrade // true), block_exploits:$be, certificate_id:(.certificate_id // null), ssl_forced:(.ssl_forced // false), http2_support:(.http2_support // true), hsts_enabled:(.hsts_enabled // false), hsts_subdomains:(.hsts_subdomains // false), advanced_config:(.advanced_config // ""), locations:(.locations // [])}')
fi
PUR=$(curl_npm -X PUT "$NPM_URL/api/nginx/proxy-hosts/$HOST_ID" -H "Authorization: Bearer $TOK" -H "Content-Type: application/json" -d "$PAYLOAD")
if echo "$PUR" | jq -e '.id' >/dev/null 2>&1; then