All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 5m46s
Het linuxserver-image schrijft /config/data/system.properties alleen bij de allereerste start. De authSource en het wachtwoord die daarna in Compose veranderden kwamen er nooit in, dus UniFi bleef verbinden met de gegevens van de eerste installatie — MongoDB antwoordde met UserNotFound: unifi@unifi. - De reparatiecontainer zet db.mongo.uri, statdb.mongo.uri, db.mongo.local en unifi.db.name opnieuw uit de actuele omgeving; overige instellingen blijven. - Gebruikersnaam en wachtwoord worden url-gecodeerd in de URI gezet. - Het bestand wordt op zijn plaats overschreven, dus eigenaar en rechten blijven van UniFi zelf. - Het wachtwoord uit system.properties telt mee als inlogpoging, zodat een afgeweken .env de stack niet onherstelbaar maakt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H9Vcwu7owJZ3nj5dCf4VFT
181 lines
6.5 KiB
Bash
Executable file
181 lines
6.5 KiB
Bash
Executable file
<% raw %>
|
|
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
if which mongosh > /dev/null 2>&1; then
|
|
mongo_init_bin="mongosh"
|
|
else
|
|
mongo_init_bin="mongo"
|
|
fi
|
|
|
|
mongo_args=()
|
|
if [[ -n "${MONGO_HOST:-}" ]]; then
|
|
mongo_args=(
|
|
--host "${MONGO_HOST}"
|
|
--port "${MONGO_PORT:-27017}"
|
|
)
|
|
fi
|
|
|
|
props="${UNIFI_CONFIG_DIR:-/unifi-config}/data/system.properties"
|
|
|
|
# UniFi bewaart in system.properties het wachtwoord waarmee de database ooit is
|
|
# aangemaakt. Wijkt dat af van wat er nu in .env staat, dan is dít het enige
|
|
# wachtwoord waarmee we nog binnenkomen — en dus de enige weg om het weer
|
|
# gelijk te trekken in plaats van de gebruiker met een dode stack te laten.
|
|
oud_wachtwoord=""
|
|
if [[ -f "${props}" ]]; then
|
|
oude_uri="$(grep -m1 '^db\.mongo\.uri=' "${props}" | cut -d= -f2- || true)"
|
|
oude_inlog="${oude_uri#mongodb://}"
|
|
oude_inlog="${oude_inlog%%@*}"
|
|
# Zonder @ staat er geen inlog in de URI; dan valt er ook niets te lezen.
|
|
if [[ "${oude_uri}" == *@* && "${oude_inlog}" == *:* ]]; then
|
|
oud_wachtwoord="${oude_inlog#*:}"
|
|
# %40 en vrienden weer terug naar het echte teken.
|
|
oud_wachtwoord="$(printf '%b' "${oud_wachtwoord//%/\\x}")"
|
|
fi
|
|
fi
|
|
# Aanhalingstekens en backslashes zouden de JavaScript-tekenreeks hieronder
|
|
# openbreken.
|
|
oud_wachtwoord="${oud_wachtwoord//\\/\\\\}"
|
|
oud_wachtwoord="${oud_wachtwoord//\"/\\\"}"
|
|
|
|
"${mongo_init_bin}" "${mongo_args[@]}" <<EOF
|
|
const oudWachtwoord = "${oud_wachtwoord}";
|
|
const beheerNaam = "${MONGO_INITDB_ROOT_USERNAME}";
|
|
const beheerWachtwoord = "${MONGO_INITDB_ROOT_PASSWORD}";
|
|
const appNaam = "${MONGO_USER}";
|
|
const appWachtwoord = "${MONGO_PASS}";
|
|
const authBron = "${MONGO_AUTHSOURCE}" || "admin";
|
|
const appDatabase = "${MONGO_DBNAME}";
|
|
|
|
const adminDb = db.getSiblingDB("admin");
|
|
|
|
// Welke beheerder in dit volume zit, hangt af van wie het heeft aangemaakt:
|
|
// het eerste sjabloon maakte 'unifi' aan, een later sjabloon 'root'. Daarom
|
|
// proberen we ze allemaal in plaats van er één te veronderstellen — anders
|
|
// mislukt de reparatie juist op de installaties die haar nodig hebben.
|
|
const kandidaten = [beheerNaam, appNaam, "root", "unifi"]
|
|
.filter((naam, i, alle) => naam && alle.indexOf(naam) === i);
|
|
// Het wachtwoord uit .env, en dat waarmee UniFi de database ooit heeft
|
|
// aangemaakt. Werkt alleen dat laatste nog, dan trekken we het hieronder
|
|
// alsnog gelijk.
|
|
const wachtwoorden = [beheerWachtwoord, oudWachtwoord]
|
|
.filter((ww, i, alle) => ww && alle.indexOf(ww) === i);
|
|
|
|
function aanmelden() {
|
|
for (const naam of kandidaten) {
|
|
for (const wachtwoord of wachtwoorden) {
|
|
try {
|
|
if (adminDb.auth(naam, wachtwoord)) {
|
|
return naam;
|
|
}
|
|
} catch (e) {
|
|
// Onbekende gebruiker of verkeerd wachtwoord: volgende combinatie.
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
// Draait deze server nog zonder authenticatie, dan mogen we zo verder.
|
|
adminDb.getUsers();
|
|
return "(zonder aanmelden)";
|
|
} catch (e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
const beheerder = aanmelden();
|
|
if (!beheerder) {
|
|
print("");
|
|
print("FOUT: geen enkele MongoDB-beheerder kon inloggen op dit volume.");
|
|
print("Het wachtwoord in .env hoort niet bij de database die er al staat.");
|
|
print("Zet het oorspronkelijke wachtwoord terug in .env, of verwijder het");
|
|
print("volume om opnieuw te beginnen (dat wist wel je UniFi-gegevens).");
|
|
print("");
|
|
quit(18);
|
|
}
|
|
print("MongoDB-beheerder: " + beheerder);
|
|
|
|
const roles = [
|
|
{ db: "admin", role: "clusterMonitor" },
|
|
// Zonder deze twee kan Server Up geen mongodump/mongorestore draaien voor
|
|
// de backup van deze stack.
|
|
{ db: "admin", role: "backup" },
|
|
{ db: "admin", role: "restore" },
|
|
{ db: appDatabase, role: "dbOwner" },
|
|
{ db: appDatabase + "_stat", role: "dbOwner" },
|
|
{ db: appDatabase + "_audit", role: "dbOwner" },
|
|
{ db: appDatabase + "_restore", role: "dbOwner" }
|
|
];
|
|
|
|
function ensureUser(database) {
|
|
const userDb = db.getSiblingDB(database);
|
|
const bestaand = userDb.getUser(appNaam);
|
|
if (!bestaand) {
|
|
userDb.createUser({ user: appNaam, pwd: appWachtwoord, roles: roles });
|
|
print("Gebruiker " + appNaam + " aangemaakt in " + database + ".");
|
|
return;
|
|
}
|
|
// Alleen het wachtwoord gelijktrekken en ontbrekende rollen aanvullen. De
|
|
// rollen overschrijven zou de beheerder van een oudere installatie
|
|
// degraderen, waarna onder meer de backup geen dump meer kan maken.
|
|
userDb.updateUser(appNaam, { pwd: appWachtwoord });
|
|
const missend = roles.filter(
|
|
(r) => !(bestaand.roles || []).some((b) => b.role === r.role && b.db === r.db));
|
|
if (missend.length) {
|
|
userDb.grantRolesToUser(appNaam, missend);
|
|
}
|
|
print("Gebruiker " + appNaam + " bijgewerkt in " + database + ".");
|
|
}
|
|
|
|
ensureUser(authBron);
|
|
if (authBron !== appDatabase) {
|
|
ensureUser(appDatabase);
|
|
}
|
|
EOF
|
|
|
|
# ── UniFi's eigen kopie van de databasegegevens ──────────────────────────────
|
|
# Het image schrijft /config/data/system.properties alleen als dat bestand er
|
|
# nog niet is ("Only evaluated on first run"). Alles wat daarna in compose
|
|
# verandert — een authSource erbij, een ander wachtwoord — komt er dus nooit in
|
|
# en UniFi blijft met de oude gegevens verbinden. Daarom zetten we de vier
|
|
# regels die over de database gaan bij elke start opnieuw goed.
|
|
if [[ -f "${props}" ]]; then
|
|
# De waarden gaan in een URI; een teken als @ of / zou die uit elkaar trekken.
|
|
urlcodeer() {
|
|
local invoer="$1" uit="" teken
|
|
for ((i = 0; i < ${#invoer}; i++)); do
|
|
teken="${invoer:i:1}"
|
|
case "${teken}" in
|
|
[a-zA-Z0-9.~_-]) uit+="${teken}" ;;
|
|
*) uit+="$(printf '%%%02X' "'${teken}")" ;;
|
|
esac
|
|
done
|
|
printf '%s' "${uit}"
|
|
}
|
|
|
|
gebruiker="$(urlcodeer "${MONGO_USER}")"
|
|
wachtwoord="$(urlcodeer "${MONGO_PASS}")"
|
|
basis="mongodb://${gebruiker}:${wachtwoord}@${MONGO_HOST}:${MONGO_PORT:-27017}"
|
|
achtervoegsel="?tls=false"
|
|
if [[ -n "${MONGO_AUTHSOURCE:-}" ]]; then
|
|
achtervoegsel="${achtervoegsel}&authSource=${MONGO_AUTHSOURCE}"
|
|
fi
|
|
|
|
tijdelijk="$(mktemp)"
|
|
grep -v -E '^(db\.mongo\.local|db\.mongo\.uri|statdb\.mongo\.uri|unifi\.db\.name)=' \
|
|
"${props}" > "${tijdelijk}" || true
|
|
{
|
|
echo "db.mongo.local=false"
|
|
echo "db.mongo.uri=${basis}/${MONGO_DBNAME}${achtervoegsel}"
|
|
echo "statdb.mongo.uri=${basis}/${MONGO_DBNAME}_stat${achtervoegsel}"
|
|
echo "unifi.db.name=${MONGO_DBNAME}"
|
|
} >> "${tijdelijk}"
|
|
# Via cat en niet via mv of sed -i: zo houdt het bestand zijn inode, en dus
|
|
# zijn eigenaar en rechten. Anders is het ineens van root en kan UniFi zelf
|
|
# niets meer wegschrijven.
|
|
cat "${tijdelijk}" > "${props}"
|
|
rm -f "${tijdelijk}"
|
|
echo "system.properties bijgewerkt met de huidige databasegegevens."
|
|
fi
|
|
<% endraw %>
|