#!/bin/sh
#
# nginx-ech-publish - republish the advertised ECHConfigList in DNS.
#
# nginx-ech-keygen rotates the key and reloads nginx, but the ECHConfigList a
# client encrypts to comes from the HTTPS resource record, not from the
# server. Without this step the record goes stale the first time the timer
# fires: clients keep working (they get ECH: failed+retry-configs and
# self-heal from the returned retry-config) but no first visit ever gets ECH
# on the first handshake, which quietly defeats the point of running it.
#
# Runs as the second ExecStart of nginx-ech-rotate.service (Type=oneshot runs
# them in order), so it fires only after a successful rotation. Rotation
# retains ECH_RETAIN generations, so the window where DNS still advertises
# the previous key is covered by design.
#
# This script is provider-agnostic: it reads the advertised value from
# nginx-ech-keygen --print-dns (public ECHCONFIG only - safe to log), then
# hands off to a provider script named by ECH_PUBLISH_PROVIDER. Providers are
# drop-in executables, so new DNS APIs can be added without touching this
# dispatcher. With no provider configured it exits 0 without doing anything,
# so installing the package changes nothing until an admin opts in.

set -eu

CONFIG=${NGINX_ECH_PUBLISH_CONFIG:-/etc/default/nginx-ech-publish}

# Defaults, overridden by $CONFIG.
ECH_PUBLISH_PROVIDER=""
ECH_ZONE_ID=""
ECH_RECORD_NAME=""
ECH_ALPN="h3,h2"
ECH_TTL=300
ECH_PUBLISH_CREDENTIALS=/root/.cloudflare.ini
ECH_KEYGEN=${ECH_KEYGEN:-/usr/sbin/nginx-ech-keygen}

# shellcheck source=/dev/null
[ -r "$CONFIG" ] && . "$CONFIG"

PROG=${0##*/}

die() {
    echo "$PROG: $*" >&2
    exit 1
}

# Inert until configured, exactly like ECH_PUBLIC_NAME gates the keygen.
[ -n "$ECH_PUBLISH_PROVIDER" ] || exit 0

# Provider names are bare identifiers, never paths.
case "$ECH_PUBLISH_PROVIDER" in
    */* | . | ..) die "invalid provider name '$ECH_PUBLISH_PROVIDER'" ;;
esac

PROVIDER_DIR=${ECH_PUBLISH_PROVIDER_DIR:-/usr/libexec/nginx-ech-publish}
PROVIDER="$PROVIDER_DIR/$ECH_PUBLISH_PROVIDER"
[ -x "$PROVIDER" ] || die "unknown provider '$ECH_PUBLISH_PROVIDER': $PROVIDER is not an executable. Available: $(ls "$PROVIDER_DIR" 2>/dev/null | tr '\n' ' ')"

[ -n "$ECH_RECORD_NAME" ] || die "ECH_RECORD_NAME is not set in $CONFIG"

case "$ECH_TTL" in
    '' | *[!0-9]*) die "ECH_TTL must be a number, got '$ECH_TTL'" ;;
esac

# --print-dns emits ech="..." and only ever reads the public ECHCONFIG block,
# never the private key, so its output is safe to pass around and to log.
ECH_B64=$("$ECH_KEYGEN" --print-dns | sed -n 's/^ech="\(.*\)"$/\1/p')
[ -n "$ECH_B64" ] || die "could not read the current ECHConfigList from '$ECH_KEYGEN --print-dns'"

export ECH_B64 ECH_ZONE_ID ECH_RECORD_NAME ECH_ALPN ECH_TTL \
       ECH_PUBLISH_CREDENTIALS

exec "$PROVIDER"
