<% 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[@]}" < 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 %>