mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-09-14 21:59:09 +02:00
fix(immich): URI-encode DB credentials for psql connection strings (#2980)
The addon builds every psql connection as a postgres:// URI with the raw username and password interpolated in. libpq percent-decodes the userinfo part of a URI, so a password containing '%' (or '@', '/', '?', '#') is decoded into different bytes before it reaches the server, and every connection fails with "password authentication failed for user". Encode the credentials with jq's @uri once and use the encoded copies in the URIs only; the raw password is still what gets handed to Immich via export_db_env and what is written by CREATE/ALTER USER. Those SQL statements now double single quotes so a password containing a single quote no longer breaks the statement either. This is the same approach already used by the postgres_15 and postgres_17 addons in this repo. Closes #1614 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,7 @@
|
|||||||
|
|
||||||
|
## 3.1.0.1 (2026-08-17)
|
||||||
|
- Fix `password authentication failed for user` when `DB_PASSWORD` contains special characters. Passwords are now URI-encoded before being used in the psql connection string, and SQL-escaped before being used in `CREATE`/`ALTER USER` statements
|
||||||
|
|
||||||
## 3.1.0 (2026-08-01)
|
## 3.1.0 (2026-08-01)
|
||||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,6 @@ slug: immich
|
|||||||
udev: true
|
udev: true
|
||||||
url: https://github.com/alexbelgium/hassio-addons
|
url: https://github.com/alexbelgium/hassio-addons
|
||||||
usb: true
|
usb: true
|
||||||
version: "3.1.0"
|
version: "3.1.0.1"
|
||||||
video: true
|
video: true
|
||||||
webui: http://[HOST]:[PORT:8080]
|
webui: http://[HOST]:[PORT:8080]
|
||||||
|
|||||||
@@ -98,10 +98,11 @@ setup_root_user() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the root user exists.
|
# Check if the root user exists.
|
||||||
if ! psql "postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}" -tAc "SELECT 1 FROM pg_roles WHERE rolname='root'" | grep -q 1; then
|
if ! psql "postgres://${DB_USERNAME_URI}:${DB_PASSWORD_URI}@${DB_HOSTNAME}:${DB_PORT}" -tAc "SELECT 1 FROM pg_roles WHERE rolname='root'" | grep -q 1; then
|
||||||
bashio::log.info "Root user does not exist. Creating root user with DB_ROOT_PASSWORD..."
|
bashio::log.info "Root user does not exist. Creating root user with DB_ROOT_PASSWORD..."
|
||||||
psql "postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
local root_password_sql="${DB_ROOT_PASSWORD//\'/\'\'}"
|
||||||
CREATE ROLE root WITH LOGIN SUPERUSER CREATEDB CREATEROLE PASSWORD '${DB_ROOT_PASSWORD}';
|
psql "postgres://${DB_USERNAME_URI}:${DB_PASSWORD_URI}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
||||||
|
CREATE ROLE root WITH LOGIN SUPERUSER CREATEDB CREATEROLE PASSWORD '${root_password_sql}';
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
bashio::log.info "Root user exists with a non-default password. No migration needed."
|
bashio::log.info "Root user exists with a non-default password. No migration needed."
|
||||||
@@ -113,10 +114,10 @@ setup_database() {
|
|||||||
bashio::log.info "Setting up external PostgreSQL database..."
|
bashio::log.info "Setting up external PostgreSQL database..."
|
||||||
|
|
||||||
# Create the database if it does not exist
|
# Create the database if it does not exist
|
||||||
if ! psql "postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}/postgres" -tAc \
|
if ! psql "postgres://${DB_USERNAME_URI}:${DB_PASSWORD_URI}@${DB_HOSTNAME}:${DB_PORT}/postgres" -tAc \
|
||||||
"SELECT 1 FROM pg_database WHERE datname='${DB_DATABASE_NAME}';" | grep -q 1; then
|
"SELECT 1 FROM pg_database WHERE datname='${DB_DATABASE_NAME}';" | grep -q 1; then
|
||||||
bashio::log.info "Database does not exist. Creating it now..."
|
bashio::log.info "Database does not exist. Creating it now..."
|
||||||
psql "postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
psql "postgres://${DB_USERNAME_URI}:${DB_PASSWORD_URI}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
||||||
CREATE DATABASE ${DB_DATABASE_NAME};
|
CREATE DATABASE ${DB_DATABASE_NAME};
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
@@ -124,20 +125,21 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure the user exists and update its password
|
# Ensure the user exists and update its password
|
||||||
psql "postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
local db_password_sql="${DB_PASSWORD//\'/\'\'}"
|
||||||
|
psql "postgres://${DB_USERNAME_URI}:${DB_PASSWORD_URI}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
||||||
DO \$\$
|
DO \$\$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${DB_USERNAME}') THEN
|
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${DB_USERNAME}') THEN
|
||||||
CREATE USER ${DB_USERNAME} WITH ENCRYPTED PASSWORD '${DB_PASSWORD}';
|
CREATE USER ${DB_USERNAME} WITH ENCRYPTED PASSWORD '${db_password_sql}';
|
||||||
ELSE
|
ELSE
|
||||||
ALTER USER ${DB_USERNAME} WITH ENCRYPTED PASSWORD '${DB_PASSWORD}';
|
ALTER USER ${DB_USERNAME} WITH ENCRYPTED PASSWORD '${db_password_sql}';
|
||||||
END IF;
|
END IF;
|
||||||
END
|
END
|
||||||
\$\$;
|
\$\$;
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Ensure the user has full privileges on the database
|
# Ensure the user has full privileges on the database
|
||||||
psql "postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
psql "postgres://${DB_USERNAME_URI}:${DB_PASSWORD_URI}@${DB_HOSTNAME}:${DB_PORT}" << EOF
|
||||||
GRANT ALL PRIVILEGES ON DATABASE ${DB_DATABASE_NAME} TO ${DB_USERNAME};
|
GRANT ALL PRIVILEGES ON DATABASE ${DB_DATABASE_NAME} TO ${DB_USERNAME};
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -147,7 +149,7 @@ EOF
|
|||||||
# Function to check if the vectors (pgvecto.rs) extension is available on the server
|
# Function to check if the vectors (pgvecto.rs) extension is available on the server
|
||||||
check_vector_extension() {
|
check_vector_extension() {
|
||||||
echo "Checking if 'vectors' extension is available for database '${DB_DATABASE_NAME}'..."
|
echo "Checking if 'vectors' extension is available for database '${DB_DATABASE_NAME}'..."
|
||||||
RESULT=$(psql "postgres://$DB_USERNAME:$DB_PASSWORD@$DB_HOSTNAME:$DB_PORT/${DB_DATABASE_NAME}" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = 'vectors';")
|
RESULT=$(psql "postgres://$DB_USERNAME_URI:$DB_PASSWORD_URI@$DB_HOSTNAME:$DB_PORT/${DB_DATABASE_NAME}" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = 'vectors';")
|
||||||
if [[ "$RESULT" == "1" ]]; then
|
if [[ "$RESULT" == "1" ]]; then
|
||||||
echo "✅ 'vectors' extension is available."
|
echo "✅ 'vectors' extension is available."
|
||||||
return 0
|
return 0
|
||||||
@@ -163,7 +165,7 @@ check_vector_extension() {
|
|||||||
# itself on first startup; checking pg_extension would false-warn on every fresh install.
|
# itself on first startup; checking pg_extension would false-warn on every fresh install.
|
||||||
check_vchord_extension() {
|
check_vchord_extension() {
|
||||||
echo "Checking if 'vchord' extension is available for database '${DB_DATABASE_NAME}'..."
|
echo "Checking if 'vchord' extension is available for database '${DB_DATABASE_NAME}'..."
|
||||||
RESULT=$(psql "postgres://$DB_USERNAME:$DB_PASSWORD@$DB_HOSTNAME:$DB_PORT/${DB_DATABASE_NAME}" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = 'vchord';")
|
RESULT=$(psql "postgres://$DB_USERNAME_URI:$DB_PASSWORD_URI@$DB_HOSTNAME:$DB_PORT/${DB_DATABASE_NAME}" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = 'vchord';")
|
||||||
if [[ "$RESULT" == "1" ]]; then
|
if [[ "$RESULT" == "1" ]]; then
|
||||||
echo "✅ 'vchord' extension is available."
|
echo "✅ 'vchord' extension is available."
|
||||||
return 0
|
return 0
|
||||||
@@ -187,6 +189,14 @@ export DB_PORT="$(bashio::config 'DB_PORT')"
|
|||||||
export JWT_SECRET="$(bashio::config 'JWT_SECRET')"
|
export JWT_SECRET="$(bashio::config 'JWT_SECRET')"
|
||||||
export DB_HOSTNAME="$(bashio::config 'DB_HOSTNAME')"
|
export DB_HOSTNAME="$(bashio::config 'DB_HOSTNAME')"
|
||||||
|
|
||||||
|
# libpq percent-decodes the userinfo part of a postgres:// URI, so credentials
|
||||||
|
# containing reserved characters (% @ / : ? #) are misread and every psql call
|
||||||
|
# below fails with "password authentication failed". Encode them once here and
|
||||||
|
# use the encoded copies in URIs only - the app itself still gets the raw value
|
||||||
|
# through export_db_env. Same approach as the postgres_15/postgres_17 addons.
|
||||||
|
export DB_USERNAME_URI="$(jq -rn --arg x "$DB_USERNAME" '$x|@uri')"
|
||||||
|
export DB_PASSWORD_URI="$(jq -rn --arg x "$DB_PASSWORD" '$x|@uri')"
|
||||||
|
|
||||||
if bashio::config.true 'VIPS_NOVECTOR'; then
|
if bashio::config.true 'VIPS_NOVECTOR'; then
|
||||||
export VIPS_NOVECTOR="1"
|
export VIPS_NOVECTOR="1"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user