Files
appstore/apps/documenso/2.18.0/scripts/init.sh
T

153 lines
5.4 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
DATA_DIR="${ROOT_DIR}/data"
CERT_FILE="${DATA_DIR}/cert.p12"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
read_env_value() {
local key="$1"
local value=""
value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
case "$value" in
\"*\") value="${value#\"}"; value="${value%\"}" ;;
\'*\') value="${value#\'}"; value="${value%\'}" ;;
esac
printf '%s\n' "$value"
}
set_env_value() {
local key="$1"
local value="$2"
local temp_file
temp_file="$(mktemp "${ROOT_DIR}/.documenso-env.tmp.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { written = 0 }
$0 ~ "^" key "=" {
if (!written) {
print key "=" value
written = 1
}
next
}
{ print }
END { if (!written) print key "=" value }
' "$ENV_FILE" > "$temp_file"
chmod 600 "$temp_file"
mv -f -- "$temp_file" "$ENV_FILE"
}
generate_secret() {
local key="$1"
local value
value="$(read_env_value "$key")"
if [[ -z "$value" || "$value" == generate ]]; then
value="$(openssl rand -hex 32)"
set_env_value "$key" "$value"
fi
[[ ${#value} -ge 32 ]] || fail "$key must contain at least 32 characters"
}
urlencode() {
local LC_ALL=C
local input="$1"
local output=""
local character encoded
local index
for ((index = 0; index < ${#input}; index++)); do
character="${input:index:1}"
case "$character" in
[A-Za-z0-9.~_-]) output+="$character" ;;
*) printf -v encoded '%%%02X' "'$character"; output+="$encoded" ;;
esac
done
printf '%s' "$output"
}
secure_certificate_file() {
local current_uid owner_uid
current_uid="$(id -u)"
if [[ "$current_uid" == 0 ]]; then
chown 1001:65533 -- "$CERT_FILE"
elif [[ "$current_uid" == 1001 ]]; then
owner_uid="$(stat -c %u -- "$CERT_FILE")"
[[ "$owner_uid" == 1001 ]] || fail "the signing certificate must be owned by UID 1001"
[[ -r "$CERT_FILE" ]] || fail "the signing certificate is not readable by UID 1001"
else
fail "init.sh must run as root or UID 1001"
fi
chmod 0400 -- "$CERT_FILE"
}
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
command -v openssl >/dev/null 2>&1 || fail "openssl is required"
db_host="$(read_env_value PANEL_DB_HOST)"
db_port="$(read_env_value PANEL_DB_PORT)"
db_name="$(read_env_value PANEL_DB_NAME)"
db_user="$(read_env_value PANEL_DB_USER)"
db_password="$(read_env_value PANEL_DB_USER_PASSWORD)"
[[ "$db_host" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || fail "PANEL_DB_HOST is invalid"
if [[ ! "$db_port" =~ ^[0-9]+$ ]] || ((db_port < 1 || db_port > 65535)); then
fail "PANEL_DB_PORT is invalid"
fi
[[ "$db_name" =~ ^[A-Za-z0-9_][A-Za-z0-9_-]*$ ]] || fail "PANEL_DB_NAME is invalid"
[[ "$db_user" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]] || fail "PANEL_DB_USER is invalid"
[[ -n "$db_password" && "$db_password" != change-me ]] || fail "PANEL_DB_USER_PASSWORD must be configured"
public_url="$(read_env_value NEXT_PUBLIC_WEBAPP_URL)"
public_url_pattern='^https?://[A-Za-z0-9._~:/?&=%+-]+$'
[[ "$public_url" =~ $public_url_pattern ]] || fail "NEXT_PUBLIC_WEBAPP_URL must be a complete HTTP(S) URL"
smtp_host="$(read_env_value SMTP_HOST)"
smtp_port="$(read_env_value SMTP_PORT)"
smtp_secure="$(read_env_value SMTP_SECURE)"
smtp_from_address="$(read_env_value SMTP_FROM_ADDRESS)"
[[ "$smtp_host" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ && "$smtp_host" != smtp.example.com ]] || fail "SMTP_HOST must name a working SMTP service"
if [[ ! "$smtp_port" =~ ^[0-9]+$ ]] || ((smtp_port < 1 || smtp_port > 65535)); then
fail "SMTP_PORT is invalid"
fi
[[ "$smtp_secure" == true || "$smtp_secure" == false ]] || fail "SMTP_SECURE must be true or false"
[[ "$smtp_from_address" =~ ^[^[:space:]@]+@[^[:space:]@]+$ ]] || fail "SMTP_FROM_ADDRESS is invalid"
chmod 600 "$ENV_FILE"
generate_secret NEXTAUTH_SECRET
generate_secret NEXT_PRIVATE_ENCRYPTION_KEY
generate_secret NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY
encoded_user="$(urlencode "$db_user")"
encoded_password="$(urlencode "$db_password")"
set_env_value NEXT_PRIVATE_DATABASE_URL "postgresql://${encoded_user}:${encoded_password}@${db_host}:${db_port}/${db_name}"
install -d -m 0700 "$DATA_DIR"
passphrase="$(read_env_value SIGNING_PASSPHRASE)"
[[ ! -L "$CERT_FILE" ]] || fail "$CERT_FILE must not be a symbolic link"
if [[ -s "$CERT_FILE" ]]; then
[[ -n "$passphrase" && "$passphrase" != generate ]] || fail "SIGNING_PASSPHRASE is missing for the existing certificate"
CERT_PASS="$passphrase" openssl pkcs12 -in "$CERT_FILE" -passin env:CERT_PASS -noout >/dev/null 2>&1 || fail "the existing signing certificate or passphrase is invalid"
secure_certificate_file
else
if [[ -z "$passphrase" || "$passphrase" == generate ]]; then
passphrase="$(openssl rand -hex 32)"
set_env_value SIGNING_PASSPHRASE "$passphrase"
fi
temp_dir="$(mktemp -d "${ROOT_DIR}/.documenso-cert.tmp.XXXXXX")"
trap 'rm -rf -- "$temp_dir"' EXIT
openssl req -new -newkey rsa:3072 -x509 -sha256 -nodes -days 3650 \
-subj "/CN=Documenso 1Panel Signing" \
-keyout "${temp_dir}/private.key" -out "${temp_dir}/certificate.crt" >/dev/null 2>&1
CERT_PASS="$passphrase" openssl pkcs12 -export \
-inkey "${temp_dir}/private.key" -in "${temp_dir}/certificate.crt" \
-out "${temp_dir}/cert.p12" -passout env:CERT_PASS
install -m 0400 "${temp_dir}/cert.p12" "$CERT_FILE"
secure_certificate_file
rm -rf -- "$temp_dir"
trap - EXIT
fi