405 lines
12 KiB
Bash
405 lines
12 KiB
Bash
#!/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:-<empty>}" ;;
|
||
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 <<SQL
|
||
DO \$do\$
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${safe_user}') THEN
|
||
EXECUTE format('ALTER ROLE %I WITH ${role_attr} PASSWORD %L', '${safe_user}', '${safe_pass}');
|
||
ELSE
|
||
EXECUTE format('CREATE ROLE %I WITH ${role_attr} PASSWORD %L', '${safe_user}', '${safe_pass}');
|
||
END IF;
|
||
END
|
||
\$do\$;
|
||
SQL
|
||
}
|
||
|
||
configure_postgres_role_password_if_needed() {
|
||
[[ "$SET_POSTGRES_ROLE_PASSWORD" == "true" ]] || return 0
|
||
|
||
log "Устанавливаю пароль для роли postgres"
|
||
local safe_pass
|
||
safe_pass="$(sql_escape_literal "$POSTGRES_ROLE_PASSWORD")"
|
||
|
||
psql_postgres <<SQL
|
||
ALTER ROLE postgres WITH PASSWORD '${safe_pass}';
|
||
SQL
|
||
}
|
||
|
||
configure_database() {
|
||
[[ "$PG_CREATE_DB" == "true" ]] || return 0
|
||
|
||
log "Создаю базу ${PG_DB_NAME}, если она отсутствует"
|
||
|
||
local safe_db safe_user
|
||
safe_db="$(sql_escape_literal "$PG_DB_NAME")"
|
||
safe_user="$(sql_escape_literal "$PG_ADMIN_USER")"
|
||
|
||
psql_postgres <<SQL
|
||
SELECT 'CREATE DATABASE "' || replace('${safe_db}', '"', '""') || '" OWNER "' || replace('${safe_user}', '"', '""') || '"'
|
||
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = '${safe_db}')
|
||
\gexec
|
||
SQL
|
||
}
|
||
|
||
detect_hba_file() {
|
||
psql_postgres -Atqc "SHOW hba_file;"
|
||
}
|
||
|
||
backup_hba_if_needed() {
|
||
local hba_file="$1"
|
||
if [[ "$CREATE_HBA_BACKUP" == "true" ]]; then
|
||
local backup_file="${hba_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||
cp -a "$hba_file" "$backup_file"
|
||
log "Создан бэкап pg_hba.conf: $backup_file"
|
||
fi
|
||
}
|
||
|
||
strip_managed_hba_block() {
|
||
local hba_file="$1"
|
||
local tmp_file
|
||
tmp_file="$(mktemp)"
|
||
|
||
awk '
|
||
BEGIN { skip=0 }
|
||
/^# BEGIN MANAGED BY install_pg1c\.sh$/ { skip=1; next }
|
||
/^# END MANAGED BY install_pg1c\.sh$/ { skip=0; next }
|
||
skip == 0 { print }
|
||
' "$hba_file" > "$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 <<EOF
|
||
|
||
Готово.
|
||
|
||
Что сделано:
|
||
- добавлен репозиторий PostgreSQL for 1C 18;
|
||
- установлен пакет в режиме: ${INSTALL_MODE};
|
||
- сервис: ${SERVICE_NAME};
|
||
- роль БД: ${PG_ADMIN_USER};
|
||
- суперпользователь: ${PG_ADMIN_SUPERUSER};
|
||
- база создана: ${PG_CREATE_DB}${PG_DB_NAME:+ (${PG_DB_NAME})};
|
||
- точечные правила в pg_hba.conf: ${ENFORCE_PASSWORD_AUTH};
|
||
- сохранён local peer для postgres: ${PRESERVE_POSTGRES_PEER_LOCAL};
|
||
- пароль роли postgres задан: ${SET_POSTGRES_ROLE_PASSWORD}.
|
||
|
||
Проверки:
|
||
systemctl status ${SERVICE_NAME}
|
||
sudo -u postgres ${PSQL} -h ${POSTGRES_SOCKET_DIR} -d postgres -c "\\du"
|
||
sudo -u postgres ${PSQL} -h ${POSTGRES_SOCKET_DIR} -d postgres -c "\\l"
|
||
|
||
Подключение под новым пользователем:
|
||
${PSQL} -h 127.0.0.1 -U ${PG_ADMIN_USER} -d ${PG_DB_NAME:-postgres} -W
|
||
|
||
Повторный запуск скрипта безопасен:
|
||
- блок в pg_hba.conf обновляется адресно;
|
||
- доступ sudo -u postgres не теряется, если PRESERVE_POSTGRES_PEER_LOCAL=true.
|
||
EOF
|
||
}
|
||
|
||
main() {
|
||
require_root
|
||
need_cmd systemctl
|
||
need_cmd runuser
|
||
need_cmd apt-get
|
||
need_cmd wget
|
||
need_cmd awk
|
||
need_cmd sed
|
||
|
||
load_env
|
||
check_os
|
||
ensure_base_packages
|
||
ensure_repo
|
||
install_pgpro
|
||
enable_and_start_service
|
||
wait_for_postgres
|
||
configure_role
|
||
configure_postgres_role_password_if_needed
|
||
configure_database
|
||
configure_pg_hba
|
||
show_summary
|
||
}
|
||
|
||
main "$@"
|