mirror of
git://f0xx.org/ac/ac-deploy
synced 2026-07-29 01:37:54 +03:00
cluster0: Gmail mail relay (msmtp + PHP SMTP), lab cron
Mirror alpine-be msmtp for CLI; compose uses Gmail SMTP for php-fpm. Safe secrets.lab.env parser; deploy-mailrelay, install-lab-cron, verify-mail-lab. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -62,6 +62,8 @@ FE_PROXY_TARGET=cast01.intra.raptor.org:80
|
||||
|
||||
# Lab DNS (2026-06): acl0.f0xx.org + c1–c3.acl0.f0xx.org → FE (cast01–03 cluster)
|
||||
ACL0_CNAME=acl0.f0xx.org
|
||||
MAIL_RELAY_ENABLE=1
|
||||
MAIL_GMAIL_DEST=bestcastr@gmail.com
|
||||
CAST01_PUBLIC=c1.acl0.f0xx.org
|
||||
CAST02_PUBLIC=c2.acl0.f0xx.org
|
||||
CAST03_PUBLIC=c3.acl0.f0xx.org
|
||||
|
||||
16
sim/cluster0/mail/msmtprc.template
Normal file
16
sim/cluster0/mail/msmtprc.template
Normal file
@@ -0,0 +1,16 @@
|
||||
# Android Cast — outbound relay (Gmail app password via msmtp.secret)
|
||||
# Installed by scripts/deploy-mailrelay.sh on cluster primary (cast01).
|
||||
defaults
|
||||
auth on
|
||||
tls on
|
||||
tls_trust_file /etc/ssl/certs/ca-certificates.crt
|
||||
logfile /var/log/msmtp.log
|
||||
|
||||
account gmail
|
||||
host smtp.gmail.com
|
||||
port 587
|
||||
from __MSMTP_FROM__
|
||||
user __MSMTP_USER__
|
||||
passwordeval cat /etc/androidcast/msmtp.secret
|
||||
|
||||
account default : gmail
|
||||
@@ -80,6 +80,10 @@ run_phase_app() {
|
||||
if [ "${GITEA_ENABLE:-0}" = "1" ] && is_primary_node; then
|
||||
run_script "$ROOT/scripts/deploy-gitea.sh" || journal_warn gitea "install failed (non-fatal)"
|
||||
fi
|
||||
if [ "${MAIL_RELAY_ENABLE:-0}" = "1" ] && is_primary_node; then
|
||||
run_script "$ROOT/scripts/deploy-mailrelay.sh" || journal_warn mailrelay "install failed (non-fatal)"
|
||||
run_script "$ROOT/scripts/install-lab-cron.sh" || journal_warn lab_cron "install failed (non-fatal)"
|
||||
fi
|
||||
run_script "$ROOT/scripts/configure-nginx.sh"
|
||||
rc-service php-fpm83 restart 2>/dev/null || true
|
||||
rc-service nginx reload 2>/dev/null || rc-service nginx restart 2>/dev/null || true
|
||||
|
||||
@@ -104,14 +104,24 @@ ISSUES_BASE="${APP_BASE_PATH:-/app/androidcast_project/issues}"
|
||||
PUBLIC_ORIGIN_CFG="${LAB_PUBLIC_ORIGIN:-https://acl0.f0xx.org}"
|
||||
load_secrets_lab
|
||||
AUTH_KEY="${AUTH_ENCRYPTION_KEY:-lab-cluster-dev-32-char-min-secret!!}"
|
||||
MAIL_TRANSPORT="${MAIL_TRANSPORT:-smtp}"
|
||||
MAIL_FROM="${MAIL_FROM:-Android Cast Lab <noreply@acl0.f0xx.org>}"
|
||||
MAIL_REPLY="${MAIL_REPLY_TO:-info@apps.f0xx.org}"
|
||||
_MSMTP_PASS="$(read_secret MSMTP_APP_PASSWORD 2>/dev/null || true)"
|
||||
[ -n "$_MSMTP_PASS" ] || _MSMTP_PASS="${MSMTP_APP_PASSWORD:-}"
|
||||
MAIL_TRANSPORT="${MAIL_TRANSPORT:-sendmail}"
|
||||
MAIL_FROM="${MAIL_FROM:-Android Cast Lab <bestcastr@gmail.com>}"
|
||||
MAIL_REPLY="${MAIL_REPLY_TO:-bestcastr@gmail.com}"
|
||||
SMTP_HOST="${SMTP_HOST:-127.0.0.1}"
|
||||
SMTP_PORT="${SMTP_PORT:-587}"
|
||||
SMTP_ENC="${SMTP_ENCRYPTION:-tls}"
|
||||
SMTP_USER="${SMTP_USER:-}"
|
||||
SMTP_USER="${SMTP_USER:-${MSMTP_USER:-}}"
|
||||
SMTP_PASS="${SMTP_PASS:-}"
|
||||
if [ -n "$_MSMTP_PASS" ]; then
|
||||
MAIL_TRANSPORT=smtp
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_USER="${MSMTP_USER:-bestcastr@gmail.com}"
|
||||
SMTP_PASS="$_MSMTP_PASS"
|
||||
MAIL_FROM="${MAIL_FROM:-Android Cast <bestcastr@gmail.com>}"
|
||||
fi
|
||||
unset _MSMTP_PASS
|
||||
mkdir -p "${COMPOSED}/config"
|
||||
log "write composed config.php base_path=${ISSUES_BASE}"
|
||||
cat > "${COMPOSED}/config/config.php" <<PHP
|
||||
|
||||
39
sim/cluster0/scripts/deploy-mailrelay.sh
Executable file
39
sim/cluster0/scripts/deploy-mailrelay.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
# Lab cluster outbound mail — msmtp + sendmail shim (same pattern as alpine-be).
|
||||
set -eu
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
. "$ROOT/scripts/lib/common.sh"
|
||||
load_cluster_env
|
||||
load_secrets_lab
|
||||
|
||||
FROM="${MSMTP_FROM:-bestcastr@gmail.com}"
|
||||
USER="${MSMTP_USER:-bestcastr@gmail.com}"
|
||||
PASS="$(read_secret MSMTP_APP_PASSWORD)"
|
||||
[ -n "$PASS" ] || PASS="${MSMTP_APP_PASSWORD:-}"
|
||||
|
||||
[ -n "$PASS" ] || die "MSMTP_APP_PASSWORD missing in ${CAST_CLUSTER_ROOT}/secrets.lab.env"
|
||||
|
||||
apk add --no-cache msmtp mailx >/dev/null 2>&1 || apk add --no-cache msmtp
|
||||
|
||||
mkdir -p /etc/androidcast
|
||||
umask 077
|
||||
printf '%s\n' "$PASS" > /etc/androidcast/msmtp.secret
|
||||
chmod 600 /etc/androidcast/msmtp.secret
|
||||
|
||||
sed -e "s|__MSMTP_FROM__|${FROM}|g" -e "s|__MSMTP_USER__|${USER}|g" \
|
||||
"$ROOT/mail/msmtprc.template" > /etc/msmtprc
|
||||
chmod 644 /etc/msmtprc
|
||||
|
||||
touch /var/log/msmtp.log
|
||||
chmod 666 /var/log/msmtp.log 2>/dev/null || true
|
||||
|
||||
rc-update add msmtp default 2>/dev/null || true
|
||||
rc-service msmtp restart 2>/dev/null || true
|
||||
|
||||
# sendmail → msmtp (php mail() / AuthMailer sendmail transport)
|
||||
if [ ! -e /usr/sbin/sendmail ] || [ -L /usr/sbin/sendmail ]; then
|
||||
ln -sf /usr/bin/msmtp /usr/sbin/sendmail 2>/dev/null || true
|
||||
fi
|
||||
|
||||
log "deploy-mailrelay_ok from=${FROM} user=${USER}"
|
||||
28
sim/cluster0/scripts/install-lab-cron.sh
Executable file
28
sim/cluster0/scripts/install-lab-cron.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Lab maintenance cron on primary (cast01) — mirrors alpine-be schedules where applicable.
|
||||
set -eu
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
. "$ROOT/scripts/lib/common.sh"
|
||||
load_cluster_env
|
||||
|
||||
is_primary_node || { log "install-lab-cron: skip non-primary"; exit 0; }
|
||||
|
||||
COMPOSED="${COMPOSE_BACKEND:-/var/www/ac/composed/backend}"
|
||||
SHORTENER="/var/www/url-shortener"
|
||||
CRON_FILE=/etc/crontabs/root
|
||||
MARK="# ac-cluster0-lab"
|
||||
|
||||
grep -v "$MARK" "$CRON_FILE" 2>/dev/null > /tmp/crontab.ac.new || : > /tmp/crontab.ac.new
|
||||
|
||||
if [ -f "${COMPOSED}/scripts/purge_remote_access.php" ]; then
|
||||
echo "0 * * * * cd ${COMPOSED} && php83 scripts/purge_remote_access.php --hours=24 ${MARK}" >> /tmp/crontab.ac.new
|
||||
fi
|
||||
if [ -f "${SHORTENER}/scripts/purge_url_shortener.php" ]; then
|
||||
echo "0 4 * * * cd ${SHORTENER} && php83 scripts/purge_url_shortener.php ${MARK}" >> /tmp/crontab.ac.new
|
||||
fi
|
||||
|
||||
mv /tmp/crontab.ac.new "$CRON_FILE"
|
||||
chmod 600 "$CRON_FILE"
|
||||
rc-service crond restart 2>/dev/null || rc-service crond start 2>/dev/null || true
|
||||
log "install-lab-cron_ok"
|
||||
@@ -189,10 +189,34 @@ read_cred() {
|
||||
load_secrets_lab() {
|
||||
_file="${CAST_CLUSTER_ROOT}/secrets.lab.env"
|
||||
[ -f "$_file" ] || return 0
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
. "$_file"
|
||||
set +a
|
||||
# Export only simple KEY=value lines (no shell metacharacters in values via source).
|
||||
while IFS= read -r _line || [ -n "$_line" ]; do
|
||||
case "$_line" in
|
||||
''|\#*) continue ;;
|
||||
*=*)
|
||||
_k="${_line%%=*}"
|
||||
_v="${_line#*=}"
|
||||
case "$_k" in
|
||||
MAIL_*|MSMTP_*|SMTP_*|AUTH_*|GITEA_*|WG_*|RSSH_*)
|
||||
export "$_k=$_v"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
done < "$_file"
|
||||
}
|
||||
|
||||
read_secret() {
|
||||
_key="$1"
|
||||
_file="${CAST_CLUSTER_ROOT}/secrets.lab.env"
|
||||
[ -f "$_file" ] || return 1
|
||||
awk -v k="$_key" '
|
||||
$0 ~ "^" k "=" {
|
||||
sub("^" k "=", "")
|
||||
print
|
||||
exit
|
||||
}
|
||||
' "$_file"
|
||||
}
|
||||
|
||||
ssh_node() {
|
||||
|
||||
@@ -14,7 +14,9 @@ COMPOSED="${COMPOSE_BACKEND:-/var/www/ac/composed/backend}"
|
||||
[ -f "${COMPOSED}/config/config.php" ] || die "missing composed backend — run compose-lab-backend.sh"
|
||||
|
||||
export MAIL_TEST_TO="$TO"
|
||||
_out="$(php -r '
|
||||
PHP="${PHP_BIN:-php83}"
|
||||
command -v "$PHP" >/dev/null 2>&1 || PHP=php
|
||||
_out="$("$PHP" -r '
|
||||
require "'"${COMPOSED}"'/src/bootstrap.php";
|
||||
$to = getenv("MAIL_TEST_TO") ?: "";
|
||||
$origin = rtrim((string) cfg("public_origin", "https://acl0.f0xx.org"), "/");
|
||||
|
||||
@@ -18,13 +18,11 @@ AUTH_ENCRYPTION_KEY=lab-cluster-dev-32-char-min-secret!!
|
||||
# RSSH_BASTION_HOST=cast01.intra.raptor.org
|
||||
# RSSH_BASTION_PORT=22
|
||||
|
||||
# SMTP (when lab mail is configured — prefer email recovery)
|
||||
# Fill on NFS /shared/cluster/secrets.lab.env (mode 600); re-run compose-lab-backend.sh
|
||||
MAIL_TRANSPORT=smtp
|
||||
MAIL_FROM=Android Cast Lab <noreply@acl0.f0xx.org>
|
||||
MAIL_REPLY_TO=info@apps.f0xx.org
|
||||
# SMTP_HOST=
|
||||
# SMTP_PORT=587
|
||||
# SMTP_ENCRYPTION=tls
|
||||
# SMTP_USER=
|
||||
# SMTP_PASS=
|
||||
# Outbound mail (msmtp → Gmail — copy MSMTP_APP_PASSWORD from alpine-be /etc/androidcast/msmtp.secret)
|
||||
# Use quotes if the app password contains spaces.
|
||||
MAIL_TRANSPORT=sendmail
|
||||
MAIL_FROM="Android Cast Lab <bestcastr@gmail.com>"
|
||||
MAIL_REPLY_TO=bestcastr@gmail.com
|
||||
MSMTP_FROM=bestcastr@gmail.com
|
||||
MSMTP_USER=bestcastr@gmail.com
|
||||
# MSMTP_APP_PASSWORD="xxxx xxxx xxxx xxxx"
|
||||
|
||||
Reference in New Issue
Block a user