#!/usr/bin/env bash set -Eeuo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="${ENV_FILE:-${SCRIPT_DIR}/.env}" log() { printf '[%s] %s\n' "$(date '+%F %T')" "$*" } fail() { printf 'ERROR: %s\n' "$*" >&2 exit 1 } need_cmd() { command -v "$1" >/dev/null 2>&1 || fail "Не найдена команда: $1" } bool_norm() { case "${1:-}" in 1|true|TRUE|yes|YES|on|ON) echo "true" ;; 0|false|FALSE|no|NO|off|OFF) echo "false" ;; *) fail "Неверное булево значение: ${1:-}" ;; esac } require_root() { [[ "$(id -u)" -eq 0 ]] || fail "Скрипт нужно запускать от root" } sql_escape_literal() { printf "%s" "$1" | sed "s/'/''/g" } load_env() { [[ -f "$ENV_FILE" ]] || fail "Файл окружения не найден: $ENV_FILE" set -a # shellcheck disable=SC1090 . "$ENV_FILE" set +a : "${REPO_ADD_URL:?В .env должна быть переменная REPO_ADD_URL}" : "${PGPRO_BIN_DIR:?В .env должна быть переменная PGPRO_BIN_DIR}" : "${SERVICE_NAME:?В .env должна быть переменная SERVICE_NAME}" : "${PACKAGE_STANDALONE:?В .env должна быть переменная PACKAGE_STANDALONE}" : "${PACKAGE_PARALLEL:?В .env должна быть переменная PACKAGE_PARALLEL}" : "${PG_ADMIN_USER:?В .env должна быть переменная PG_ADMIN_USER}" : "${PG_ADMIN_PASSWORD:?В .env должна быть переменная PG_ADMIN_PASSWORD}" INSTALL_MODE="${INSTALL_MODE:-standalone}" PG_ADMIN_SUPERUSER="$(bool_norm "${PG_ADMIN_SUPERUSER:-true}")" PG_CREATE_DB="$(bool_norm "${PG_CREATE_DB:-true}")" PG_SERVICE_ENABLE="$(bool_norm "${PG_SERVICE_ENABLE:-true}")" PG_SERVICE_START="$(bool_norm "${PG_SERVICE_START:-true}")" ENFORCE_PASSWORD_AUTH="$(bool_norm "${ENFORCE_PASSWORD_AUTH:-true}")" CREATE_HBA_BACKUP="$(bool_norm "${CREATE_HBA_BACKUP:-true}")" PRESERVE_POSTGRES_PEER_LOCAL="$(bool_norm "${PRESERVE_POSTGRES_PEER_LOCAL:-true}")" SET_POSTGRES_ROLE_PASSWORD="$(bool_norm "${SET_POSTGRES_ROLE_PASSWORD:-false}")" POSTGRES_ROLE_PASSWORD="${POSTGRES_ROLE_PASSWORD:-}" POSTGRES_SOCKET_DIR="${POSTGRES_SOCKET_DIR:-/var/run/postgresql}" PG_DB_NAME="${PG_DB_NAME:-}" REPO_ADD_TMP="${REPO_ADD_TMP:-/tmp/pgpro-repo-add.sh}" PG_CLUSTER_DIR="${PG_CLUSTER_DIR:-/var/lib/pgpro/1c-18/data}" APP_LOCAL_AUTH_METHOD="${APP_LOCAL_AUTH_METHOD:-scram-sha-256}" APP_HOST_AUTH_METHOD="${APP_HOST_AUTH_METHOD:-scram-sha-256}" APP_HOST_IPV4_CIDR="${APP_HOST_IPV4_CIDR:-127.0.0.1/32}" APP_HOST_IPV6_CIDR="${APP_HOST_IPV6_CIDR:-::1/128}" case "$INSTALL_MODE" in standalone|parallel) ;; *) fail "INSTALL_MODE должен быть standalone или parallel" ;; esac case "$APP_LOCAL_AUTH_METHOD" in scram-sha-256|md5|password|trust|peer) ;; *) fail "APP_LOCAL_AUTH_METHOD должен быть scram-sha-256, md5, password, trust или peer" ;; esac case "$APP_HOST_AUTH_METHOD" in scram-sha-256|md5|password|trust) ;; *) fail "APP_HOST_AUTH_METHOD должен быть scram-sha-256, md5, password или trust" ;; esac if [[ "$PG_CREATE_DB" == "true" && -z "$PG_DB_NAME" ]]; then fail "Если PG_CREATE_DB=true, нужно указать PG_DB_NAME" fi if [[ "$SET_POSTGRES_ROLE_PASSWORD" == "true" && -z "$POSTGRES_ROLE_PASSWORD" ]]; then fail "Если SET_POSTGRES_ROLE_PASSWORD=true, нужно указать POSTGRES_ROLE_PASSWORD" fi PSQL="${PGPRO_BIN_DIR}/psql" PGSETUP="${PGPRO_BIN_DIR}/pg-setup" PG_ISREADY="${PGPRO_BIN_DIR}/pg_isready" } check_os() { [[ -f /etc/os-release ]] || fail "Не найден /etc/os-release" # shellcheck disable=SC1091 . /etc/os-release [[ "${ID:-}" == "debian" ]] || fail "Скрипт рассчитан на Debian. Найдено: ${ID:-unknown}" [[ "${VERSION_ID:-}" == "13" ]] || fail "Скрипт рассчитан на Debian 13. Найдено: ${VERSION_ID:-unknown}" } ensure_base_packages() { export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y wget ca-certificates gnupg apt-transport-https sed grep coreutils gawk } ensure_repo() { if [[ -f /etc/apt/sources.list.d/postgresql-1c-18.list ]]; then log "Репозиторий PostgreSQL for 1C 18 уже добавлен" apt-get update return fi log "Скачиваю vendor-скрипт добавления репозитория" wget -O "$REPO_ADD_TMP" "$REPO_ADD_URL" chmod +x "$REPO_ADD_TMP" sh "$REPO_ADD_TMP" rm -f "$REPO_ADD_TMP" } install_pgpro() { export DEBIAN_FRONTEND=noninteractive if [[ "$INSTALL_MODE" == "standalone" ]]; then log "Устанавливаю пакет ${PACKAGE_STANDALONE}" apt-get install -y "$PACKAGE_STANDALONE" else log "Устанавливаю пакет ${PACKAGE_PARALLEL} для параллельной установки" apt-get install -y "$PACKAGE_PARALLEL" [[ -x "$PGSETUP" ]] || fail "Не найден $PGSETUP" if [[ ! -d "${PG_CLUSTER_DIR}/base" ]]; then log "Инициализирую новый кластер Postgres Pro 1C 18" "$PGSETUP" initdb else log "Кластер уже инициализирован, initdb пропускаю" fi fi } service_unit_exists() { systemctl list-unit-files --type=service --no-legend 2>/dev/null | awk '{print $1}' | grep -Fxq "${SERVICE_NAME}.service" } service_enable() { if [[ -x "$PGSETUP" ]]; then "$PGSETUP" service enable else systemctl enable "$SERVICE_NAME" fi } service_start() { if [[ -x "$PGSETUP" ]]; then "$PGSETUP" service start else systemctl start "$SERVICE_NAME" fi } service_restart() { if [[ -x "$PGSETUP" ]]; then if "$PGSETUP" service condrestart >/dev/null 2>&1; then return 0 fi fi systemctl restart "$SERVICE_NAME" } enable_and_start_service() { if ! service_unit_exists; then fail "Не найден unit-файл ${SERVICE_NAME}.service" fi if [[ "$PG_SERVICE_ENABLE" == "true" ]]; then log "Включаю автозапуск ${SERVICE_NAME}" service_enable fi if [[ "$PG_SERVICE_START" == "true" ]]; then log "Запускаю ${SERVICE_NAME}" service_start fi } wait_for_postgres() { [[ -x "$PG_ISREADY" ]] || fail "Не найден $PG_ISREADY" log "Жду готовности PostgreSQL" local i for i in {1..60}; do if runuser -u postgres -- "$PG_ISREADY" -h "$POSTGRES_SOCKET_DIR" -d postgres >/dev/null 2>&1; then return 0 fi sleep 1 done systemctl status "$SERVICE_NAME" --no-pager || true fail "PostgreSQL не поднялся за ожидаемое время" } psql_postgres() { runuser -u postgres -- "$PSQL" -v ON_ERROR_STOP=1 -h "$POSTGRES_SOCKET_DIR" -d postgres "$@" } configure_role() { [[ -x "$PSQL" ]] || fail "Не найден $PSQL" log "Создаю или обновляю роль ${PG_ADMIN_USER}" local role_attr="LOGIN" if [[ "$PG_ADMIN_SUPERUSER" == "true" ]]; then role_attr+=" SUPERUSER" else role_attr+=" NOSUPERUSER" fi local safe_user safe_pass safe_user="$(sql_escape_literal "$PG_ADMIN_USER")" safe_pass="$(sql_escape_literal "$PG_ADMIN_PASSWORD")" psql_postgres < "$tmp_file" cat "$tmp_file" > "$hba_file" rm -f "$tmp_file" } prepend_managed_hba_block() { local hba_file="$1" local tmp_file tmp_file="$(mktemp)" { echo "# BEGIN MANAGED BY install_pg1c.sh" if [[ "$PRESERVE_POSTGRES_PEER_LOCAL" == "true" ]]; then echo "local all postgres peer" fi printf 'local all %s %s\n' "$PG_ADMIN_USER" "$APP_LOCAL_AUTH_METHOD" printf 'host all %s %s %s\n' "$PG_ADMIN_USER" "$APP_HOST_IPV4_CIDR" "$APP_HOST_AUTH_METHOD" printf 'host all %s %s %s\n' "$PG_ADMIN_USER" "$APP_HOST_IPV6_CIDR" "$APP_HOST_AUTH_METHOD" echo "# END MANAGED BY install_pg1c.sh" echo cat "$hba_file" } > "$tmp_file" cat "$tmp_file" > "$hba_file" rm -f "$tmp_file" } configure_pg_hba() { [[ "$ENFORCE_PASSWORD_AUTH" == "true" ]] || { log "Правка pg_hba.conf отключена: ENFORCE_PASSWORD_AUTH=false" return 0 } local hba_file hba_file="$(detect_hba_file)" [[ -n "$hba_file" ]] || fail "Не удалось определить путь к pg_hba.conf" [[ -f "$hba_file" ]] || fail "Файл pg_hba.conf не найден: $hba_file" log "Настраиваю точечные правила парольной аутентификации в $hba_file" backup_hba_if_needed "$hba_file" strip_managed_hba_block "$hba_file" prepend_managed_hba_block "$hba_file" log "Перезапускаю ${SERVICE_NAME} после правки pg_hba.conf" service_restart wait_for_postgres } show_summary() { cat <