Add IP and pasted certificate support

This commit is contained in:
i
2026-07-22 20:18:59 +08:00
parent f09231786f
commit d3bf52f884
2 changed files with 105 additions and 15 deletions
+97 -12
View File
@@ -101,6 +101,38 @@ validate_domain() {
[[ "$1" =~ ^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]
}
validate_ipv4() {
local IFS=.
local octet
local -a octets
read -ra octets <<< "$1"
[[ "${#octets[@]}" -eq 4 ]] || return 1
for octet in "${octets[@]}"; do
[[ "$octet" =~ ^[0-9]+$ ]] || return 1
(( octet >= 0 && octet <= 255 )) || return 1
done
}
validate_domain_or_ip() {
validate_domain "$1" || validate_ipv4 "$1"
}
read_multiline_block() {
# read_multiline_block <var_name> <prompt> [terminator]
local var="$1" prompt="$2" terminator="${3:-END}"
local line block=""
echo -e "${BOLD}${prompt}${RESET}"
echo " Paste the content below. Finish with a line containing only: ${terminator}"
while IFS= read -r line; do
[[ "$line" == "$terminator" ]] && break
block+="$line"$'\n'
done
printf -v "$var" '%s' "$block"
}
# ── Arg parsing ───────────────────────────────────────────────────────────────
DRY_RUN=false
OUTPUT_DIR="."
@@ -120,9 +152,9 @@ mkdir -p "$OUTPUT_DIR"
# ── Banner ────────────────────────────────────────────────────────────────────
echo -e "${BOLD}${CYAN}"
echo "╔══════════════════════════════════════════════════════════╗"
echo "║ NetBird External Relay Server Setup Wizard ║"
echo "╚══════════════════════════════════════════════════════════╝${RESET}"
echo "============================================================"
echo " NetBird External Relay Server Setup Wizard"
echo "============================================================${RESET}"
echo ""
echo "This script generates relay.env and docker-compose.yml"
echo "for one or more NetBird relay servers."
@@ -210,9 +242,9 @@ for (( i=1; i<=RELAY_COUNT; i++ )); do
# ── Domain ────────────────────────────────────────────────────────────────
while true; do
ask DOMAIN " Domain name (e.g. relay-us.example.com)" "${EX_DOMAIN}"
if validate_domain "$DOMAIN"; then break
else warn " That doesn't look like a valid domain. Try again."; fi
ask DOMAIN " Domain name or IP (e.g. relay-us.example.com / 203.0.113.10)" "${EX_DOMAIN}"
if validate_domain_or_ip "$DOMAIN"; then break
else warn " That doesn't look like a valid domain or IPv4 address. Try again."; fi
done
RELAY_DOMAINS+=("$DOMAIN")
@@ -237,7 +269,14 @@ for (( i=1; i<=RELAY_COUNT; i++ )); do
echo " 1) Let's Encrypt (automatic — server needs port 80/tcp open)"
echo " 2) Existing certificates (wildcard / own CA)"
echo " 3) Self-signed certificate (generated by this script)"
ask TLS_MODE " Choose [1/2/3]" "${EX_TLS_MODE:-1}"
echo " 4) Paste certificate and private key"
ask TLS_MODE " Choose [1/2/3/4]" "${EX_TLS_MODE:-1}"
if [[ "$TLS_MODE" == "1" ]] && validate_ipv4 "$DOMAIN"; then
warn " Let's Encrypt does not issue certificates for raw IP addresses."
warn " Choose mode 2, 3, or 4 for IP-based deployments."
ask TLS_MODE " Re-choose TLS mode [2/3/4]" "3"
fi
if [[ "$TLS_MODE" == "1" ]]; then
ask LE_EMAIL " Let's Encrypt email" "${EX_LE_EMAIL}"
@@ -255,6 +294,15 @@ for (( i=1; i<=RELAY_COUNT; i++ )); do
ask CERT_FILE " Cert file path inside container" "${EX_CERT_FILE:-/certs/fullchain.pem}"
ask KEY_FILE " Key file path inside container" "${EX_KEY_FILE:-/certs/privkey.pem}"
LE_EMAIL=""; LE_DATA_DIR=""
elif [[ "$TLS_MODE" == "4" ]]; then
echo ""
echo " ── Pasted certificate details ──"
CERT_HOST_PATH="${RELAY_DIR}/certs"
CERT_FILE="/certs/fullchain.pem"
KEY_FILE="/certs/privkey.pem"
LE_EMAIL=""; LE_DATA_DIR=""
read_multiline_block PASTED_CERT " Paste the certificate PEM" "END_CERT"
read_multiline_block PASTED_KEY " Paste the private key PEM" "END_KEY"
else
echo ""
echo " ── Self-signed certificate details ──"
@@ -266,10 +314,18 @@ for (( i=1; i<=RELAY_COUNT; i++ )); do
ask SS_ST " State / Province (ST)" ""
ask SS_L " Locality / City (L)" ""
SS_SANS="DNS:${DOMAIN}"
if validate_ipv4 "$DOMAIN"; then
SS_SANS="IP:${DOMAIN}"
else
SS_SANS="DNS:${DOMAIN}"
fi
echo ""
echo " Subject Alternative Names (SANs):"
echo " DNS:${DOMAIN} is included automatically."
if validate_ipv4 "$DOMAIN"; then
echo " IP:${DOMAIN} is included automatically."
else
echo " DNS:${DOMAIN} is included automatically."
fi
echo " Add extra SANs one by one (IP:x.x.x.x or DNS:other.example.com)."
echo " Press Enter on an empty line when done."
while true; do
@@ -318,14 +374,22 @@ for (( i=1; i<=RELAY_COUNT; i++ )); do
KEY_FILE="/certs/privkey.pem"
LE_EMAIL=""; LE_DATA_DIR=""
fi
# For self-signed mode, certs live inside the relay dir
[[ "$TLS_MODE" == "3" ]] && CERT_HOST_PATH="${RELAY_DIR}/certs"
# For generated or pasted certs, certs live inside the relay dir
[[ "$TLS_MODE" == "3" || "$TLS_MODE" == "4" ]] && CERT_HOST_PATH="${RELAY_DIR}/certs"
RELAY_DIRS+=("$RELAY_DIR")
RELAY_LISTEN_PORTS+=("$LISTEN_PORT")
RELAY_STUN_PORTS_LIST+=("$STUN_PORTS")
RELAY_TLS_MODES+=("$TLS_MODE")
RELAY_ENABLE_STUN+=("$ENABLE_STUN")
if [[ "$TLS_MODE" == "3" ]]; then
TLS_LABEL="Self-signed certificate"
elif [[ "$TLS_MODE" == "4" ]]; then
TLS_LABEL="Pasted certificate"
else
TLS_LABEL="Existing certificates"
fi
# ── Build relay.env ───────────────────────────────────────────────────────
ENV_FILE="${RELAY_DIR}/relay.env"
COMPOSE_FILE="${RELAY_DIR}/docker-compose.yml"
@@ -349,7 +413,7 @@ NB_LETSENCRYPT_DATA_DIR=${LE_DATA_DIR}
"
else
ENV_CONTENT+="
# TLS — $([ "$TLS_MODE" == "3" ] && echo "Self-signed certificate" || echo "Existing certificates")
# TLS — ${TLS_LABEL}
NB_TLS_CERT_FILE=${CERT_FILE}
NB_TLS_KEY_FILE=${KEY_FILE}
"
@@ -442,6 +506,18 @@ volumes:
echo " -subj \"${SS_SUBJ}\" \\"
echo " -addext \"subjectAltName=${SS_SANS}\""
fi
if [[ "$TLS_MODE" == "4" ]]; then
echo ""
echo -e "${BOLD}▶ Pasted certificate (would write):${RESET}"
echo "────────────────────────────────────────"
echo "mkdir -p ${CERT_HOST_PATH}"
echo "cat > ${CERT_HOST_PATH}/fullchain.pem <<'EOF_CERT'"
echo "$PASTED_CERT"
echo "EOF_CERT"
echo "cat > ${CERT_HOST_PATH}/privkey.pem <<'EOF_KEY'"
echo "$PASTED_KEY"
echo "EOF_KEY"
fi
else
mkdir -p "$RELAY_DIR"
printf '%s' "$ENV_CONTENT" > "$ENV_FILE"
@@ -492,6 +568,15 @@ volumes:
info " SANs : ${SS_SANS}"
info " Valid for : ${SS_DAYS} days"
fi
if [[ "$TLS_MODE" == "4" ]]; then
info " Writing pasted certificate ..."
mkdir -p "${CERT_HOST_PATH}"
printf '%s' "${PASTED_CERT}" > "${CERT_HOST_PATH}/fullchain.pem"
printf '%s' "${PASTED_KEY}" > "${CERT_HOST_PATH}/privkey.pem"
chmod 600 "${CERT_HOST_PATH}/privkey.pem"
success " Certificate : ${CERT_HOST_PATH}/fullchain.pem"
success " Private key : ${CERT_HOST_PATH}/privkey.pem"
fi
fi
fi