diff --git a/setup-relay.sh b/setup-relay.sh index fb5e32a..59a03f4 100644 --- a/setup-relay.sh +++ b/setup-relay.sh @@ -163,21 +163,91 @@ for (( i=1; i<=RELAY_COUNT; i++ )); do echo " TLS mode:" echo " 1) Let's Encrypt (automatic — server needs port 80/tcp open)" echo " 2) Existing certificates (wildcard / own CA)" - ask TLS_MODE " Choose [1/2]" "1" + echo " 3) Self-signed certificate (generated by this script)" + ask TLS_MODE " Choose [1/2/3]" "1" if [[ "$TLS_MODE" == "1" ]]; then ask LE_EMAIL " Let's Encrypt email" "" LE_DATA_DIR="/data/letsencrypt" - CERT_HOST_PATH=""; CERT_FILE=""; KEY_FILE="" - else + CERT_HOST_PATH=""; CERT_FILE=""; KEY_FILE=""; SELFSIGN_DAYS="" + elif [[ "$TLS_MODE" == "2" ]]; then ask CERT_HOST_PATH " Host path to certs directory" "/opt/1panel/www/sites/${DOMAIN}/ssl" ask CERT_FILE " Cert file path inside container" "/certs/fullchain.pem" ask KEY_FILE " Key file path inside container" "/certs/privkey.pem" + LE_EMAIL=""; LE_DATA_DIR=""; SELFSIGN_DAYS="" + else + echo "" + echo " ── Self-signed certificate details ──" + + # Subject fields + ask SS_CN " Common Name (CN)" "${DOMAIN}" + ask SS_O " Organization (O)" "" + ask SS_OU " Organizational Unit (OU)" "" + ask SS_C " Country (C, 2-letter ISO)" "" + ask SS_ST " State / Province (ST)" "" + ask SS_L " Locality / City (L)" "" + + # SANs — domain always included, allow adding more + SS_SANS="DNS:${DOMAIN}" + echo "" + echo " Subject Alternative Names (SANs):" + echo " DNS:${DOMAIN} is included automatically." + 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 + read -rp "$(echo -e "${BOLD} Extra SAN (or Enter to finish): ${RESET}")" _san + [[ -z "$_san" ]] && break + SS_SANS+=",${_san}" + done + + # Validity + ask SS_DAYS " Certificate validity (days)" "3650" + + # Key type + echo "" + echo " Key type:" + echo " 1) RSA" + echo " 2) ECC (ECDSA)" + ask SS_KEY_TYPE " Choose [1/2]" "1" + + if [[ "$SS_KEY_TYPE" == "1" ]]; then + echo " RSA key size:" + echo " 1) 2048 bit" + echo " 2) 4096 bit" + ask SS_KEY_SIZE " Choose [1/2]" "2" + [[ "$SS_KEY_SIZE" == "1" ]] && SS_NEWKEY="rsa:2048" || SS_NEWKEY="rsa:4096" + else + echo " ECC curve:" + echo " 1) P-256 (prime256v1)" + echo " 2) P-384 (secp384r1)" + echo " 3) P-521 (secp521r1)" + ask SS_CURVE " Choose [1/2/3]" "1" + case "$SS_CURVE" in + 1) SS_NEWKEY="ec" ; SS_PKEYOPT="ec_paramgen_curve:P-256" ;; + 2) SS_NEWKEY="ec" ; SS_PKEYOPT="ec_paramgen_curve:P-384" ;; + 3) SS_NEWKEY="ec" ; SS_PKEYOPT="ec_paramgen_curve:P-521" ;; + esac + fi + + # Build subject string (only include non-empty fields) + SS_SUBJ="" + [[ -n "$SS_C" ]] && SS_SUBJ+="/C=${SS_C}" + [[ -n "$SS_ST" ]] && SS_SUBJ+="/ST=${SS_ST}" + [[ -n "$SS_L" ]] && SS_SUBJ+="/L=${SS_L}" + [[ -n "$SS_O" ]] && SS_SUBJ+="/O=${SS_O}" + [[ -n "$SS_OU" ]] && SS_SUBJ+="/OU=${SS_OU}" + SS_SUBJ+="/CN=${SS_CN}" + + CERT_HOST_PATH="" # filled in after RELAY_DIR is known + CERT_FILE="/certs/fullchain.pem" + KEY_FILE="/certs/privkey.pem" LE_EMAIL=""; LE_DATA_DIR="" fi # ── Output directory ────────────────────────────────────────────────────── ask RELAY_DIR " Output directory for this relay's files" "/opt/netbird-relay" + # For self-signed mode, certs live inside the relay dir + [[ "$TLS_MODE" == "3" ]] && CERT_HOST_PATH="${RELAY_DIR}/certs" RELAY_DIRS+=("$RELAY_DIR") RELAY_LISTEN_PORTS+=("$LISTEN_PORT") RELAY_STUN_PORTS_LIST+=("$STUN_PORTS") @@ -207,7 +277,7 @@ NB_LETSENCRYPT_DATA_DIR=${LE_DATA_DIR} " else ENV_CONTENT+=" -# TLS — Existing certificates +# TLS — $([ "$TLS_MODE" == "3" ] && echo "Self-signed certificate" || echo "Existing certificates") NB_TLS_CERT_FILE=${CERT_FILE} NB_TLS_KEY_FILE=${KEY_FILE} " @@ -285,13 +355,72 @@ volumes: echo -e "${BOLD}▶ ${COMPOSE_FILE}${RESET}" echo "────────────────────────────────────────" echo "$COMPOSE_CONTENT" + if [[ "$TLS_MODE" == "3" ]]; then + echo "" + echo -e "${BOLD}▶ Self-signed cert (would run):${RESET}" + echo "────────────────────────────────────────" + echo "mkdir -p ${CERT_HOST_PATH}" + if [[ "$SS_KEY_TYPE" == "1" ]]; then + echo "openssl req -x509 -newkey ${SS_NEWKEY} -sha256 -days ${SS_DAYS} -nodes \\" + else + echo "openssl req -x509 -newkey ${SS_NEWKEY} -pkeyopt ${SS_PKEYOPT} -sha256 -days ${SS_DAYS} -nodes \\" + fi + echo " -keyout ${CERT_HOST_PATH}/privkey.pem \\" + echo " -out ${CERT_HOST_PATH}/fullchain.pem \\" + echo " -subj \"${SS_SUBJ}\" \\" + echo " -addext \"subjectAltName=${SS_SANS}\"" + fi else mkdir -p "$RELAY_DIR" - printf '%s' "$ENV_CONTENT" > "$ENV_FILE" + printf '%s' "$ENV_CONTENT" > "$ENV_FILE" printf '%s' "$COMPOSE_CONTENT" > "$COMPOSE_FILE" - chmod 600 "$ENV_FILE" # protect the secret + chmod 600 "$ENV_FILE" success " Written: ${ENV_FILE}" success " Written: ${COMPOSE_FILE}" + + if [[ "$TLS_MODE" == "3" ]]; then + if ! command -v openssl &>/dev/null; then + error " openssl not found — cannot generate self-signed certificate." + error " Install openssl and re-run, or run the following manually:" + if [[ "$SS_KEY_TYPE" == "1" ]]; then + error " openssl req -x509 -newkey ${SS_NEWKEY} -sha256 -days ${SS_DAYS} -nodes \\" + else + error " openssl req -x509 -newkey ${SS_NEWKEY} -pkeyopt ${SS_PKEYOPT} -sha256 -days ${SS_DAYS} -nodes \\" + fi + error " -keyout ${CERT_HOST_PATH}/privkey.pem \\" + error " -out ${CERT_HOST_PATH}/fullchain.pem \\" + error " -subj \"${SS_SUBJ}\" \\" + error " -addext \"subjectAltName=${SS_SANS}\"" + else + info " Generating self-signed certificate ..." + mkdir -p "${CERT_HOST_PATH}" + if [[ "$SS_KEY_TYPE" == "1" ]]; then + openssl req -x509 \ + -newkey "${SS_NEWKEY}" \ + -sha256 -days "${SS_DAYS}" -nodes \ + -keyout "${CERT_HOST_PATH}/privkey.pem" \ + -out "${CERT_HOST_PATH}/fullchain.pem" \ + -subj "${SS_SUBJ}" \ + -addext "subjectAltName=${SS_SANS}" \ + 2>/dev/null + else + openssl req -x509 \ + -newkey "${SS_NEWKEY}" -pkeyopt "${SS_PKEYOPT}" \ + -sha256 -days "${SS_DAYS}" -nodes \ + -keyout "${CERT_HOST_PATH}/privkey.pem" \ + -out "${CERT_HOST_PATH}/fullchain.pem" \ + -subj "${SS_SUBJ}" \ + -addext "subjectAltName=${SS_SANS}" \ + 2>/dev/null + fi + chmod 600 "${CERT_HOST_PATH}/privkey.pem" + success " Certificate : ${CERT_HOST_PATH}/fullchain.pem" + success " Private key : ${CERT_HOST_PATH}/privkey.pem" + info " Subject : ${SS_SUBJ}" + info " SANs : ${SS_SANS}" + info " Valid for : ${SS_DAYS} days" + fi + fi fi done # end per-relay loop @@ -346,6 +475,9 @@ for i in "${!RELAY_DOMAINS[@]}"; do echo " 3. Trigger TLS cert (Let's Encrypt):" echo " curl -v https://${D}/" echo " Expect: 404 page not found + valid LE cert" + elif [[ "$TM" == "3" ]]; then + echo " Note: Self-signed cert is at ${DIR}/certs/" + echo " Clients must trust this CA or skip TLS verification." fi echo "" echo -e " ${BOLD}Firewall ports to open:${RESET}"