mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-05 20:32:15 +00:00
fix(oci): generate the Nextcloud MariaDB administrator and keep its umask private
- The administrator password is generated at install and written to autoconfig.php, so Nextcloud installs itself on the first request and the setup page cannot be claimed by anyone else. The summary shows it. - The private umask applies to the bootstrap only, not to /init. - The bootstrap creates its files without following links and gives them to the application user with lchown. - The startup check leaves out the default port of its scheme, so an application that trusts the host of its first request, like Nextcloud, opens from the LAN address. Installed on a Proxmox host: Nextcloud 35.0.2 installs with the generated administrator, opens from its LAN address and accepts a WebDAV upload; the application runs with umask 0022.
This commit is contained in:
+1
-1
@@ -1644,7 +1644,7 @@
|
||||
"Create scheduled backup job": "Erstellen Sie einen geplanten Sicherungsauftrag",
|
||||
"Create share directory:": "Freigabeverzeichnis erstellen:",
|
||||
"Create shared directory:": "Freigegebenes Verzeichnis erstellen:",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Erstellen Sie das Administratorkonto im Web-Assistenten; MariaDB und /data sind bereits eingetragen.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux erzeugt das Administratorpasswort und zeigt es am Ende der Installation an. Ändern Sie es nach der ersten Anmeldung.",
|
||||
"Create this LXC in privileged mode?": "Erstellen Sie diese LXC im privilegierten Modus?",
|
||||
"Create with create_user in the container console": "Erstellen Sie den Benutzer mit create_user in der Container-Konsole",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Erstelle, versende und signiere Dokumente mit einer selbst gehosteten Web-App.",
|
||||
|
||||
+1
-1
@@ -1642,7 +1642,7 @@
|
||||
"Create scheduled backup job": "Crear trabajo de backup programado",
|
||||
"Create share directory:": "Crear directorio compartido:",
|
||||
"Create shared directory:": "Crear directorio compartido:",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Crea el administrador en el asistente web; MariaDB y /data ya están rellenados.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux genera la contraseña del administrador y la muestra al final de la instalación. Cámbiala después del primer inicio de sesión.",
|
||||
"Create this LXC in privileged mode?": "¿Crear este LXC en modo privilegiado?",
|
||||
"Create with create_user in the container console": "Crea el usuario con create_user desde la consola del contenedor",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Crea, envía y firma documentos con una aplicación web autoalojada.",
|
||||
|
||||
+1
-1
@@ -1644,7 +1644,7 @@
|
||||
"Create scheduled backup job": "Créer une tâche de sauvegarde planifiée",
|
||||
"Create share directory:": "Créer un répertoire de partage :",
|
||||
"Create shared directory:": "Créer un répertoire partagé :",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Créez le compte administrateur dans l’assistant web ; MariaDB et /data sont déjà renseignés.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux génère le mot de passe de l'administrateur et l'affiche à la fin de l'installation. Modifiez-le après la première connexion.",
|
||||
"Create this LXC in privileged mode?": "Créer ce LXC en mode privilégié ?",
|
||||
"Create with create_user in the container console": "Créez l’utilisateur avec create_user dans la console du conteneur",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Créez, envoyez et signez des documents avec une application web auto-hébergée.",
|
||||
|
||||
+1
-1
@@ -1644,7 +1644,7 @@
|
||||
"Create scheduled backup job": "Crea attività di backup pianificata",
|
||||
"Create share directory:": "Crea directory condivisa:",
|
||||
"Create shared directory:": "Crea directory condivisa:",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Crea l’amministratore nella procedura guidata web; MariaDB e /data sono già compilati.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux genera la password dell'amministratore e la mostra alla fine dell'installazione. Cambiala dopo il primo accesso.",
|
||||
"Create this LXC in privileged mode?": "Creare questo LXC in modalità privilegiata?",
|
||||
"Create with create_user in the container console": "Crea l’utente con create_user nella console del container",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Crea, invia e firma documenti con un'app web self-hosted.",
|
||||
|
||||
+1
-1
@@ -1644,7 +1644,7 @@
|
||||
"Create scheduled backup job": "Criar tarefa de backup agendada",
|
||||
"Create share directory:": "Crie um diretório de compartilhamento:",
|
||||
"Create shared directory:": "Crie um diretório compartilhado:",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Crie o administrador no assistente web; MariaDB e /data já estão preenchidos.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "O ProxMenux gera a senha do administrador e mostra-a no fim da instalação. Altere-a após o primeiro login.",
|
||||
"Create this LXC in privileged mode?": "Criar este LXC em modo privilegiado?",
|
||||
"Create with create_user in the container console": "Crie o utilizador com create_user na consola do contentor",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Crie, envie e assine documentos com uma aplicação web autoalojada.",
|
||||
|
||||
+1
-1
@@ -1644,7 +1644,7 @@
|
||||
"Create scheduled backup job": "Vytvoriť naplánovanú úlohu zálohy",
|
||||
"Create share directory:": "Vytvoriť zdieľaný priečinok:",
|
||||
"Create shared directory:": "Vytvoriť zdieľaný priečinok:",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Vytvorte správcu vo webovom sprievodcovi; nastavenia MariaDB a /data sú už vyplnené.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux vygeneruje heslo správcu a zobrazí ho na konci inštalácie. Po prvom prihlásení ho zmeňte.",
|
||||
"Create this LXC in privileged mode?": "Vytvoriť tento LXC v privilegovanom režime?",
|
||||
"Create with create_user in the container console": "Používateľa vytvorte príkazom create_user v konzole kontajnera",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Vytvárajte, posielajte a podpisujte dokumenty vo webovej aplikácii prevádzkovanej vo vlastnej réžii.",
|
||||
|
||||
+1
-1
@@ -1644,7 +1644,7 @@
|
||||
"Create scheduled backup job": "Skapa schemalagt säkerhetskopieringsjobb",
|
||||
"Create share directory:": "Skapa delningskatalog:",
|
||||
"Create shared directory:": "Skapa delad katalog:",
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.": "Skapa administratören i webbguiden; MariaDB och /data är redan ifyllda.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.": "ProxMenux genererar administratörslösenordet och visar det i slutet av installationen. Ändra det efter första inloggningen.",
|
||||
"Create this LXC in privileged mode?": "Skapa denna LXC i privilegierat läge?",
|
||||
"Create with create_user in the container console": "Skapa användaren med create_user i containerns konsol",
|
||||
"Create, send and sign documents with a self-hosted web app.": "Skapa, skicka och signera dokument med en webbapp som du driftar själv.",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"documentation": "https://docs.linuxserver.io/images/docker-nextcloud/",
|
||||
"repository": "https://github.com/linuxserver/docker-nextcloud",
|
||||
"tips": [
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.",
|
||||
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.",
|
||||
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.",
|
||||
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time."
|
||||
@@ -91,6 +91,13 @@
|
||||
"sensitive": false,
|
||||
"source": "docker-compose"
|
||||
},
|
||||
{
|
||||
"name": "NEXTCLOUD_ADMIN_PASSWORD",
|
||||
"example": "${GENERATED_ADMIN_PASSWORD}",
|
||||
"required": true,
|
||||
"sensitive": true,
|
||||
"source": "docker-compose"
|
||||
},
|
||||
{
|
||||
"name": "PGID",
|
||||
"example": "${PGID}",
|
||||
@@ -272,7 +279,7 @@
|
||||
"entrypoint": [
|
||||
"/bin/sh",
|
||||
"-ec",
|
||||
"umask 077; printf %s PD9waHAKLy8gUHJlcGFyZSBhIGZyZXNoIExpbnV4U2VydmVyIE5leHRjbG91ZCB3aXRob3V0IHJlcGxhY2luZyBwZXJzaXN0ZW50IHVzZXIgc2V0dGluZ3MuCiRkaXIgPSAnL2NvbmZpZy93d3cvbmV4dGNsb3VkL2NvbmZpZyc7CmlmICghaXNfZGlyKCRkaXIpICYmICFta2RpcigkZGlyLCAwNzcwLCB0cnVlKSkgeyB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbignQ2Fubm90IGNyZWF0ZSB0aGUgTmV4dGNsb3VkIGNvbmZpZyBkaXJlY3RvcnknKTsgfQppZiAoIWlzX2ZpbGUoJGRpciAuICcvY29uZmlnLnBocCcpICYmICFpc19maWxlKCRkaXIgLiAnL2F1dG9jb25maWcucGhwJykpIHsKICAgIGZvcmVhY2ggKFsnTVlTUUxfSE9TVCcsICdNWVNRTF9EQVRBQkFTRScsICdNWVNRTF9VU0VSJywgJ01ZU1FMX1BBU1NXT1JEJ10gYXMgJG5hbWUpIHsKICAgICAgICBpZiAoZ2V0ZW52KCRuYW1lKSA9PT0gZmFsc2UgfHwgZ2V0ZW52KCRuYW1lKSA9PT0gJycpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ01pc3NpbmcgTmV4dGNsb3VkIGRhdGFiYXNlIHNldHRpbmc6ICcgLiAkbmFtZSk7IH0KICAgIH0KICAgIC8vIEtlZXAgdGhlIGRlZmF1bHRzIHRoYXQgTGludXhTZXJ2ZXIgY29waWVzIG9ubHkgaW50byBhbiBlbXB0eSBjb25maWcgZGlyZWN0b3J5LgogICAgJGRlZmF1bHRzID0gJy9hcHAvd3d3L3NyYy9jb25maWcnOwogICAgaWYgKGlzX2RpcigkZGVmYXVsdHMpKSB7CiAgICAgICAgZm9yZWFjaCAobmV3IERpcmVjdG9yeUl0ZXJhdG9yKCRkZWZhdWx0cykgYXMgJGZpbGUpIHsKICAgICAgICAgICAgaWYgKCRmaWxlLT5pc0RvdCgpIHx8ICEkZmlsZS0+aXNGaWxlKCkpIHsgY29udGludWU7IH0KICAgICAgICAgICAgJHRhcmdldCA9ICRkaXIgLiAnLycgLiAkZmlsZS0+Z2V0RmlsZW5hbWUoKTsKICAgICAgICAgICAgaWYgKCFmaWxlX2V4aXN0cygkdGFyZ2V0KSAmJiAhY29weSgkZmlsZS0+Z2V0UGF0aG5hbWUoKSwgJHRhcmdldCkpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCBzZWVkIHRoZSBOZXh0Y2xvdWQgY29uZmlndXJhdGlvbicpOyB9CiAgICAgICAgfQogICAgfQogICAgJHNldHRpbmdzID0gWydkYnR5cGUnID0+ICdteXNxbCcsICdkYmhvc3QnID0+IGdldGVudignTVlTUUxfSE9TVCcpLCAnZGJuYW1lJyA9PiBnZXRlbnYoJ01ZU1FMX0RBVEFCQVNFJyksICdkYnVzZXInID0+IGdldGVudignTVlTUUxfVVNFUicpLCAnZGJwYXNzJyA9PiBnZXRlbnYoJ01ZU1FMX1BBU1NXT1JEJyksICdkaXJlY3RvcnknID0+ICcvZGF0YSddOwogICAgJHBhdGggPSAkZGlyIC4gJy9hdXRvY29uZmlnLnBocCc7CiAgICBpZiAoZmlsZV9wdXRfY29udGVudHMoJHBhdGgsICI8P3BocFxuXCRBVVRPQ09ORklHID0gIiAuIHZhcl9leHBvcnQoJHNldHRpbmdzLCB0cnVlKSAuICI7XG4iLCBMT0NLX0VYKSA9PT0gZmFsc2UpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCB3cml0ZSB0aGUgTmV4dGNsb3VkIGRhdGFiYXNlIGNvbmZpZ3VyYXRpb24nKTsgfQogICAgY2htb2QoJHBhdGgsIDA2MDApOwogICAgLy8gRXhpc3RpbmcgaW5zdGFsbGF0aW9ucyBza2lwIExpbnV4U2VydmVyJ3MgZmlyc3QtcnVuIG93bmVyc2hpcCBzZXR1cC4KICAgIGlmICghY2hvd24oJHBhdGgsIGZpbGVvd25lcigkZGlyKSkgfHwgIWNoZ3JwKCRwYXRoLCBmaWxlZ3JvdXAoJGRpcikpKSB7IHRocm93IG5ldyBSdW50aW1lRXhjZXB0aW9uKCdDYW5ub3Qgc2V0IE5leHRjbG91ZCBjb25maWd1cmF0aW9uIG93bmVyc2hpcCcpOyB9Cn0KJHBhdGggPSAkZGlyIC4gJy9wcm94bWVudXgtcmVkaXMuY29uZmlnLnBocCc7Ci8vIEluc3RhbGxlZCBpbnN0YW5jZXMga2VlcCB0aGVpciBleGlzdGluZyBjYWNoZSBjb25maWd1cmF0aW9uIHVuY2hhbmdlZC4KaWYgKCFpc19maWxlKCRwYXRoKSAmJiAhaXNfZmlsZSgkZGlyIC4gJy9jb25maWcucGhwJykpIHsKICAgICRzZXR0aW5ncyA9IFsnZmlsZWxvY2tpbmcuZW5hYmxlZCcgPT4gdHJ1ZSwgJ21lbWNhY2hlLmxvY2FsJyA9PiAnXE9DXE1lbWNhY2hlXEFQQ3UnLCAnbWVtY2FjaGUubG9ja2luZycgPT4gJ1xPQ1xNZW1jYWNoZVxSZWRpcycsICdyZWRpcycgPT4gWydob3N0JyA9PiAncmVkaXMnLCAncG9ydCcgPT4gNjM3OSwgJ3RpbWVvdXQnID0+IDAuMF1dOwogICAgaWYgKGZpbGVfcHV0X2NvbnRlbnRzKCRwYXRoLCAiPD9waHBcblwkQ09ORklHID0gIiAuIHZhcl9leHBvcnQoJHNldHRpbmdzLCB0cnVlKSAuICI7XG4iLCBMT0NLX0VYKSA9PT0gZmFsc2UpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCB3cml0ZSB0aGUgTmV4dGNsb3VkIFJlZGlzIGNvbmZpZ3VyYXRpb24nKTsgfQogICAgY2htb2QoJHBhdGgsIDA2MDApOwogICAgLy8gRXhpc3RpbmcgaW5zdGFsbGF0aW9ucyBza2lwIExpbnV4U2VydmVyJ3MgZmlyc3QtcnVuIG93bmVyc2hpcCBzZXR1cC4KICAgIGlmICghY2hvd24oJHBhdGgsIGZpbGVvd25lcigkZGlyKSkgfHwgIWNoZ3JwKCRwYXRoLCBmaWxlZ3JvdXAoJGRpcikpKSB7IHRocm93IG5ldyBSdW50aW1lRXhjZXB0aW9uKCdDYW5ub3Qgc2V0IE5leHRjbG91ZCBjb25maWd1cmF0aW9uIG93bmVyc2hpcCcpOyB9Cn0K | base64 -d | php; exec /init"
|
||||
"(umask 077; printf %s PD9waHAKLy8gUHJlcGFyZSBhIGZyZXNoIExpbnV4U2VydmVyIE5leHRjbG91ZCB3aXRob3V0IHJlcGxhY2luZyBwZXJzaXN0ZW50IHVzZXIgc2V0dGluZ3MuCiRkaXIgPSAnL2NvbmZpZy93d3cvbmV4dGNsb3VkL2NvbmZpZyc7CnVtYXNrKDAwNzcpOwppZiAoIWlzX2RpcigkZGlyKSAmJiAhbWtkaXIoJGRpciwgMDc3MCwgdHJ1ZSkpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCBjcmVhdGUgdGhlIE5leHRjbG91ZCBjb25maWcgZGlyZWN0b3J5Jyk7IH0KLy8gVGhlIGRpcmVjdG9yeSBiZWxvbmdzIHRvIHRoZSBhcHBsaWNhdGlvbiB1c2VyLiBDcmVhdGUgZWFjaCBmaWxlIHdpdGhvdXQKLy8gZm9sbG93aW5nIGEgbGluayBwbGFjZWQgdGhlcmUsIGFuZCBnaXZlIGl0IHRvIHRoYXQgdXNlciB3aXRob3V0IGZvbGxvd2luZyBvbmUuCmZ1bmN0aW9uIGNyZWF0ZV9wcml2YXRlKCRwYXRoLCAkY29udGVudCwgJG93bmVyKSB7CiAgICAkaGFuZGxlID0gQGZvcGVuKCRwYXRoLCAneCcpOwogICAgaWYgKCRoYW5kbGUgPT09IGZhbHNlIHx8IGZ3cml0ZSgkaGFuZGxlLCAkY29udGVudCkgIT09IHN0cmxlbigkY29udGVudCkgfHwgIWZjbG9zZSgkaGFuZGxlKSkgeyB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbignQ2Fubm90IHdyaXRlICcgLiBiYXNlbmFtZSgkcGF0aCkpOyB9CiAgICBpZiAoJG93bmVyICYmICghbGNob3duKCRwYXRoLCBmaWxlb3duZXIoJG93bmVyKSkgfHwgIWxjaGdycCgkcGF0aCwgZmlsZWdyb3VwKCRvd25lcikpKSkgeyB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbignQ2Fubm90IHNldCBOZXh0Y2xvdWQgY29uZmlndXJhdGlvbiBvd25lcnNoaXAnKTsgfQp9CmlmICghaXNfZmlsZSgkZGlyIC4gJy9jb25maWcucGhwJykgJiYgIWlzX2ZpbGUoJGRpciAuICcvYXV0b2NvbmZpZy5waHAnKSkgewogICAgZm9yZWFjaCAoWydNWVNRTF9IT1NUJywgJ01ZU1FMX0RBVEFCQVNFJywgJ01ZU1FMX1VTRVInLCAnTVlTUUxfUEFTU1dPUkQnLCAnTkVYVENMT1VEX0FETUlOX1BBU1NXT1JEJ10gYXMgJG5hbWUpIHsKICAgICAgICBpZiAoZ2V0ZW52KCRuYW1lKSA9PT0gZmFsc2UgfHwgZ2V0ZW52KCRuYW1lKSA9PT0gJycpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ01pc3NpbmcgTmV4dGNsb3VkIHNldHRpbmc6ICcgLiAkbmFtZSk7IH0KICAgIH0KICAgIC8vIEtlZXAgdGhlIGRlZmF1bHRzIHRoYXQgTGludXhTZXJ2ZXIgY29waWVzIG9ubHkgaW50byBhbiBlbXB0eSBjb25maWcgZGlyZWN0b3J5LgogICAgJGRlZmF1bHRzID0gJy9hcHAvd3d3L3NyYy9jb25maWcnOwogICAgaWYgKGlzX2RpcigkZGVmYXVsdHMpKSB7CiAgICAgICAgZm9yZWFjaCAobmV3IERpcmVjdG9yeUl0ZXJhdG9yKCRkZWZhdWx0cykgYXMgJGZpbGUpIHsKICAgICAgICAgICAgaWYgKCRmaWxlLT5pc0RvdCgpIHx8ICEkZmlsZS0+aXNGaWxlKCkpIHsgY29udGludWU7IH0KICAgICAgICAgICAgJHRhcmdldCA9ICRkaXIgLiAnLycgLiAkZmlsZS0+Z2V0RmlsZW5hbWUoKTsKICAgICAgICAgICAgaWYgKCFmaWxlX2V4aXN0cygkdGFyZ2V0KSAmJiAhaXNfbGluaygkdGFyZ2V0KSkgeyBjcmVhdGVfcHJpdmF0ZSgkdGFyZ2V0LCBmaWxlX2dldF9jb250ZW50cygkZmlsZS0+Z2V0UGF0aG5hbWUoKSksIG51bGwpOyB9CiAgICAgICAgfQogICAgfQogICAgLy8gV2l0aCB0aGUgYWRtaW5pc3RyYXRvciBpbmNsdWRlZCwgTmV4dGNsb3VkIGluc3RhbGxzIGl0c2VsZiBvbiB0aGUgZmlyc3QKICAgIC8vIHJlcXVlc3QsIHNvIG5vYm9keSBlbHNlIG9uIHRoZSBuZXR3b3JrIGNhbiBjbGFpbSB0aGUgc2V0dXAgcGFnZS4KICAgICRzZXR0aW5ncyA9IFsnZGJ0eXBlJyA9PiAnbXlzcWwnLCAnZGJob3N0JyA9PiBnZXRlbnYoJ01ZU1FMX0hPU1QnKSwgJ2RibmFtZScgPT4gZ2V0ZW52KCdNWVNRTF9EQVRBQkFTRScpLCAnZGJ1c2VyJyA9PiBnZXRlbnYoJ01ZU1FMX1VTRVInKSwgJ2RicGFzcycgPT4gZ2V0ZW52KCdNWVNRTF9QQVNTV09SRCcpLCAnZGlyZWN0b3J5JyA9PiAnL2RhdGEnLCAnYWRtaW5sb2dpbicgPT4gJ2FkbWluJywgJ2FkbWlucGFzcycgPT4gZ2V0ZW52KCdORVhUQ0xPVURfQURNSU5fUEFTU1dPUkQnKV07CiAgICAvLyBFeGlzdGluZyBpbnN0YWxsYXRpb25zIHNraXAgTGludXhTZXJ2ZXIncyBmaXJzdC1ydW4gb3duZXJzaGlwIHNldHVwLgogICAgY3JlYXRlX3ByaXZhdGUoJGRpciAuICcvYXV0b2NvbmZpZy5waHAnLCAiPD9waHBcblwkQVVUT0NPTkZJRyA9ICIgLiB2YXJfZXhwb3J0KCRzZXR0aW5ncywgdHJ1ZSkgLiAiO1xuIiwgJGRpcik7Cn0KJHBhdGggPSAkZGlyIC4gJy9wcm94bWVudXgtcmVkaXMuY29uZmlnLnBocCc7Ci8vIEluc3RhbGxlZCBpbnN0YW5jZXMga2VlcCB0aGVpciBleGlzdGluZyBjYWNoZSBjb25maWd1cmF0aW9uIHVuY2hhbmdlZC4KaWYgKCFpc19maWxlKCRwYXRoKSAmJiAhaXNfbGluaygkcGF0aCkgJiYgIWlzX2ZpbGUoJGRpciAuICcvY29uZmlnLnBocCcpKSB7CiAgICAkc2V0dGluZ3MgPSBbJ2ZpbGVsb2NraW5nLmVuYWJsZWQnID0+IHRydWUsICdtZW1jYWNoZS5sb2NhbCcgPT4gJ1xPQ1xNZW1jYWNoZVxBUEN1JywgJ21lbWNhY2hlLmxvY2tpbmcnID0+ICdcT0NcTWVtY2FjaGVcUmVkaXMnLCAncmVkaXMnID0+IFsnaG9zdCcgPT4gJ3JlZGlzJywgJ3BvcnQnID0+IDYzNzksICd0aW1lb3V0JyA9PiAwLjBdXTsKICAgIGNyZWF0ZV9wcml2YXRlKCRwYXRoLCAiPD9waHBcblwkQ09ORklHID0gIiAuIHZhcl9leHBvcnQoJHNldHRpbmdzLCB0cnVlKSAuICI7XG4iLCAkZGlyKTsKfQo= | base64 -d | php); exec /init"
|
||||
],
|
||||
"environment": {
|
||||
"PUID": "${PUID}",
|
||||
@@ -281,7 +288,8 @@
|
||||
"MYSQL_HOST": "db",
|
||||
"MYSQL_DATABASE": "nextcloud",
|
||||
"MYSQL_USER": "nextcloud",
|
||||
"MYSQL_PASSWORD": "${GENERATED_DB_PASSWORD}"
|
||||
"MYSQL_PASSWORD": "${GENERATED_DB_PASSWORD}",
|
||||
"NEXTCLOUD_ADMIN_PASSWORD": "${GENERATED_ADMIN_PASSWORD}"
|
||||
},
|
||||
"image": "lscr.io/linuxserver/nextcloud:latest",
|
||||
"ports": [
|
||||
@@ -402,6 +410,16 @@
|
||||
"dependency_start_and_stop_order"
|
||||
],
|
||||
"generated_secrets": [
|
||||
{
|
||||
"id": "generated-admin-password",
|
||||
"strategy": "generate-cryptographically-random-at-install",
|
||||
"bindings": [
|
||||
{
|
||||
"service": "nextcloud",
|
||||
"environment_variable": "NEXTCLOUD_ADMIN_PASSWORD"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "generated-db-password",
|
||||
"strategy": "generate-cryptographically-random-at-install",
|
||||
@@ -439,7 +457,18 @@
|
||||
"source": "linuxserver-nextcloud-documentation"
|
||||
}
|
||||
],
|
||||
"credentials": []
|
||||
"credentials": [
|
||||
{
|
||||
"label": "Generated administrator login",
|
||||
"type": "runtime-generated",
|
||||
"username": "admin",
|
||||
"password": null,
|
||||
"password_environment": "NEXTCLOUD_ADMIN_PASSWORD",
|
||||
"change_required": true,
|
||||
"source": "proxmenux-installer-generated",
|
||||
"retrieval": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"proxmox": {
|
||||
"runtime": "native-oci-lxc",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"documentation": "https://docs.linuxserver.io/images/docker-nextcloud/",
|
||||
"repository": "https://github.com/linuxserver/docker-nextcloud",
|
||||
"tips": [
|
||||
"Create the administrator in the web setup; MariaDB and /data are prefilled.",
|
||||
"ProxMenux generates the administrator password and shows it at the end of the installation. Change it after the first login.",
|
||||
"Use managed storage for /config and MariaDB. A host/NAS directory can be selected for /data and needs a separate backup.",
|
||||
"Redis locking is configured automatically. Existing Nextcloud configuration files are preserved.",
|
||||
"Use a trusted HTTPS reverse proxy. Upgrade Nextcloud one major version at a time."
|
||||
@@ -91,6 +91,13 @@
|
||||
"sensitive": false,
|
||||
"source": "docker-compose"
|
||||
},
|
||||
{
|
||||
"name": "NEXTCLOUD_ADMIN_PASSWORD",
|
||||
"example": "${GENERATED_ADMIN_PASSWORD}",
|
||||
"required": true,
|
||||
"sensitive": true,
|
||||
"source": "docker-compose"
|
||||
},
|
||||
{
|
||||
"name": "PGID",
|
||||
"example": "${PGID}",
|
||||
@@ -272,7 +279,7 @@
|
||||
"entrypoint": [
|
||||
"/bin/sh",
|
||||
"-ec",
|
||||
"umask 077; printf %s PD9waHAKLy8gUHJlcGFyZSBhIGZyZXNoIExpbnV4U2VydmVyIE5leHRjbG91ZCB3aXRob3V0IHJlcGxhY2luZyBwZXJzaXN0ZW50IHVzZXIgc2V0dGluZ3MuCiRkaXIgPSAnL2NvbmZpZy93d3cvbmV4dGNsb3VkL2NvbmZpZyc7CmlmICghaXNfZGlyKCRkaXIpICYmICFta2RpcigkZGlyLCAwNzcwLCB0cnVlKSkgeyB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbignQ2Fubm90IGNyZWF0ZSB0aGUgTmV4dGNsb3VkIGNvbmZpZyBkaXJlY3RvcnknKTsgfQppZiAoIWlzX2ZpbGUoJGRpciAuICcvY29uZmlnLnBocCcpICYmICFpc19maWxlKCRkaXIgLiAnL2F1dG9jb25maWcucGhwJykpIHsKICAgIGZvcmVhY2ggKFsnTVlTUUxfSE9TVCcsICdNWVNRTF9EQVRBQkFTRScsICdNWVNRTF9VU0VSJywgJ01ZU1FMX1BBU1NXT1JEJ10gYXMgJG5hbWUpIHsKICAgICAgICBpZiAoZ2V0ZW52KCRuYW1lKSA9PT0gZmFsc2UgfHwgZ2V0ZW52KCRuYW1lKSA9PT0gJycpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ01pc3NpbmcgTmV4dGNsb3VkIGRhdGFiYXNlIHNldHRpbmc6ICcgLiAkbmFtZSk7IH0KICAgIH0KICAgIC8vIEtlZXAgdGhlIGRlZmF1bHRzIHRoYXQgTGludXhTZXJ2ZXIgY29waWVzIG9ubHkgaW50byBhbiBlbXB0eSBjb25maWcgZGlyZWN0b3J5LgogICAgJGRlZmF1bHRzID0gJy9hcHAvd3d3L3NyYy9jb25maWcnOwogICAgaWYgKGlzX2RpcigkZGVmYXVsdHMpKSB7CiAgICAgICAgZm9yZWFjaCAobmV3IERpcmVjdG9yeUl0ZXJhdG9yKCRkZWZhdWx0cykgYXMgJGZpbGUpIHsKICAgICAgICAgICAgaWYgKCRmaWxlLT5pc0RvdCgpIHx8ICEkZmlsZS0+aXNGaWxlKCkpIHsgY29udGludWU7IH0KICAgICAgICAgICAgJHRhcmdldCA9ICRkaXIgLiAnLycgLiAkZmlsZS0+Z2V0RmlsZW5hbWUoKTsKICAgICAgICAgICAgaWYgKCFmaWxlX2V4aXN0cygkdGFyZ2V0KSAmJiAhY29weSgkZmlsZS0+Z2V0UGF0aG5hbWUoKSwgJHRhcmdldCkpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCBzZWVkIHRoZSBOZXh0Y2xvdWQgY29uZmlndXJhdGlvbicpOyB9CiAgICAgICAgfQogICAgfQogICAgJHNldHRpbmdzID0gWydkYnR5cGUnID0+ICdteXNxbCcsICdkYmhvc3QnID0+IGdldGVudignTVlTUUxfSE9TVCcpLCAnZGJuYW1lJyA9PiBnZXRlbnYoJ01ZU1FMX0RBVEFCQVNFJyksICdkYnVzZXInID0+IGdldGVudignTVlTUUxfVVNFUicpLCAnZGJwYXNzJyA9PiBnZXRlbnYoJ01ZU1FMX1BBU1NXT1JEJyksICdkaXJlY3RvcnknID0+ICcvZGF0YSddOwogICAgJHBhdGggPSAkZGlyIC4gJy9hdXRvY29uZmlnLnBocCc7CiAgICBpZiAoZmlsZV9wdXRfY29udGVudHMoJHBhdGgsICI8P3BocFxuXCRBVVRPQ09ORklHID0gIiAuIHZhcl9leHBvcnQoJHNldHRpbmdzLCB0cnVlKSAuICI7XG4iLCBMT0NLX0VYKSA9PT0gZmFsc2UpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCB3cml0ZSB0aGUgTmV4dGNsb3VkIGRhdGFiYXNlIGNvbmZpZ3VyYXRpb24nKTsgfQogICAgY2htb2QoJHBhdGgsIDA2MDApOwogICAgLy8gRXhpc3RpbmcgaW5zdGFsbGF0aW9ucyBza2lwIExpbnV4U2VydmVyJ3MgZmlyc3QtcnVuIG93bmVyc2hpcCBzZXR1cC4KICAgIGlmICghY2hvd24oJHBhdGgsIGZpbGVvd25lcigkZGlyKSkgfHwgIWNoZ3JwKCRwYXRoLCBmaWxlZ3JvdXAoJGRpcikpKSB7IHRocm93IG5ldyBSdW50aW1lRXhjZXB0aW9uKCdDYW5ub3Qgc2V0IE5leHRjbG91ZCBjb25maWd1cmF0aW9uIG93bmVyc2hpcCcpOyB9Cn0KJHBhdGggPSAkZGlyIC4gJy9wcm94bWVudXgtcmVkaXMuY29uZmlnLnBocCc7Ci8vIEluc3RhbGxlZCBpbnN0YW5jZXMga2VlcCB0aGVpciBleGlzdGluZyBjYWNoZSBjb25maWd1cmF0aW9uIHVuY2hhbmdlZC4KaWYgKCFpc19maWxlKCRwYXRoKSAmJiAhaXNfZmlsZSgkZGlyIC4gJy9jb25maWcucGhwJykpIHsKICAgICRzZXR0aW5ncyA9IFsnZmlsZWxvY2tpbmcuZW5hYmxlZCcgPT4gdHJ1ZSwgJ21lbWNhY2hlLmxvY2FsJyA9PiAnXE9DXE1lbWNhY2hlXEFQQ3UnLCAnbWVtY2FjaGUubG9ja2luZycgPT4gJ1xPQ1xNZW1jYWNoZVxSZWRpcycsICdyZWRpcycgPT4gWydob3N0JyA9PiAncmVkaXMnLCAncG9ydCcgPT4gNjM3OSwgJ3RpbWVvdXQnID0+IDAuMF1dOwogICAgaWYgKGZpbGVfcHV0X2NvbnRlbnRzKCRwYXRoLCAiPD9waHBcblwkQ09ORklHID0gIiAuIHZhcl9leHBvcnQoJHNldHRpbmdzLCB0cnVlKSAuICI7XG4iLCBMT0NLX0VYKSA9PT0gZmFsc2UpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCB3cml0ZSB0aGUgTmV4dGNsb3VkIFJlZGlzIGNvbmZpZ3VyYXRpb24nKTsgfQogICAgY2htb2QoJHBhdGgsIDA2MDApOwogICAgLy8gRXhpc3RpbmcgaW5zdGFsbGF0aW9ucyBza2lwIExpbnV4U2VydmVyJ3MgZmlyc3QtcnVuIG93bmVyc2hpcCBzZXR1cC4KICAgIGlmICghY2hvd24oJHBhdGgsIGZpbGVvd25lcigkZGlyKSkgfHwgIWNoZ3JwKCRwYXRoLCBmaWxlZ3JvdXAoJGRpcikpKSB7IHRocm93IG5ldyBSdW50aW1lRXhjZXB0aW9uKCdDYW5ub3Qgc2V0IE5leHRjbG91ZCBjb25maWd1cmF0aW9uIG93bmVyc2hpcCcpOyB9Cn0K | base64 -d | php; exec /init"
|
||||
"(umask 077; printf %s PD9waHAKLy8gUHJlcGFyZSBhIGZyZXNoIExpbnV4U2VydmVyIE5leHRjbG91ZCB3aXRob3V0IHJlcGxhY2luZyBwZXJzaXN0ZW50IHVzZXIgc2V0dGluZ3MuCiRkaXIgPSAnL2NvbmZpZy93d3cvbmV4dGNsb3VkL2NvbmZpZyc7CnVtYXNrKDAwNzcpOwppZiAoIWlzX2RpcigkZGlyKSAmJiAhbWtkaXIoJGRpciwgMDc3MCwgdHJ1ZSkpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ0Nhbm5vdCBjcmVhdGUgdGhlIE5leHRjbG91ZCBjb25maWcgZGlyZWN0b3J5Jyk7IH0KLy8gVGhlIGRpcmVjdG9yeSBiZWxvbmdzIHRvIHRoZSBhcHBsaWNhdGlvbiB1c2VyLiBDcmVhdGUgZWFjaCBmaWxlIHdpdGhvdXQKLy8gZm9sbG93aW5nIGEgbGluayBwbGFjZWQgdGhlcmUsIGFuZCBnaXZlIGl0IHRvIHRoYXQgdXNlciB3aXRob3V0IGZvbGxvd2luZyBvbmUuCmZ1bmN0aW9uIGNyZWF0ZV9wcml2YXRlKCRwYXRoLCAkY29udGVudCwgJG93bmVyKSB7CiAgICAkaGFuZGxlID0gQGZvcGVuKCRwYXRoLCAneCcpOwogICAgaWYgKCRoYW5kbGUgPT09IGZhbHNlIHx8IGZ3cml0ZSgkaGFuZGxlLCAkY29udGVudCkgIT09IHN0cmxlbigkY29udGVudCkgfHwgIWZjbG9zZSgkaGFuZGxlKSkgeyB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbignQ2Fubm90IHdyaXRlICcgLiBiYXNlbmFtZSgkcGF0aCkpOyB9CiAgICBpZiAoJG93bmVyICYmICghbGNob3duKCRwYXRoLCBmaWxlb3duZXIoJG93bmVyKSkgfHwgIWxjaGdycCgkcGF0aCwgZmlsZWdyb3VwKCRvd25lcikpKSkgeyB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbignQ2Fubm90IHNldCBOZXh0Y2xvdWQgY29uZmlndXJhdGlvbiBvd25lcnNoaXAnKTsgfQp9CmlmICghaXNfZmlsZSgkZGlyIC4gJy9jb25maWcucGhwJykgJiYgIWlzX2ZpbGUoJGRpciAuICcvYXV0b2NvbmZpZy5waHAnKSkgewogICAgZm9yZWFjaCAoWydNWVNRTF9IT1NUJywgJ01ZU1FMX0RBVEFCQVNFJywgJ01ZU1FMX1VTRVInLCAnTVlTUUxfUEFTU1dPUkQnLCAnTkVYVENMT1VEX0FETUlOX1BBU1NXT1JEJ10gYXMgJG5hbWUpIHsKICAgICAgICBpZiAoZ2V0ZW52KCRuYW1lKSA9PT0gZmFsc2UgfHwgZ2V0ZW52KCRuYW1lKSA9PT0gJycpIHsgdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oJ01pc3NpbmcgTmV4dGNsb3VkIHNldHRpbmc6ICcgLiAkbmFtZSk7IH0KICAgIH0KICAgIC8vIEtlZXAgdGhlIGRlZmF1bHRzIHRoYXQgTGludXhTZXJ2ZXIgY29waWVzIG9ubHkgaW50byBhbiBlbXB0eSBjb25maWcgZGlyZWN0b3J5LgogICAgJGRlZmF1bHRzID0gJy9hcHAvd3d3L3NyYy9jb25maWcnOwogICAgaWYgKGlzX2RpcigkZGVmYXVsdHMpKSB7CiAgICAgICAgZm9yZWFjaCAobmV3IERpcmVjdG9yeUl0ZXJhdG9yKCRkZWZhdWx0cykgYXMgJGZpbGUpIHsKICAgICAgICAgICAgaWYgKCRmaWxlLT5pc0RvdCgpIHx8ICEkZmlsZS0+aXNGaWxlKCkpIHsgY29udGludWU7IH0KICAgICAgICAgICAgJHRhcmdldCA9ICRkaXIgLiAnLycgLiAkZmlsZS0+Z2V0RmlsZW5hbWUoKTsKICAgICAgICAgICAgaWYgKCFmaWxlX2V4aXN0cygkdGFyZ2V0KSAmJiAhaXNfbGluaygkdGFyZ2V0KSkgeyBjcmVhdGVfcHJpdmF0ZSgkdGFyZ2V0LCBmaWxlX2dldF9jb250ZW50cygkZmlsZS0+Z2V0UGF0aG5hbWUoKSksIG51bGwpOyB9CiAgICAgICAgfQogICAgfQogICAgLy8gV2l0aCB0aGUgYWRtaW5pc3RyYXRvciBpbmNsdWRlZCwgTmV4dGNsb3VkIGluc3RhbGxzIGl0c2VsZiBvbiB0aGUgZmlyc3QKICAgIC8vIHJlcXVlc3QsIHNvIG5vYm9keSBlbHNlIG9uIHRoZSBuZXR3b3JrIGNhbiBjbGFpbSB0aGUgc2V0dXAgcGFnZS4KICAgICRzZXR0aW5ncyA9IFsnZGJ0eXBlJyA9PiAnbXlzcWwnLCAnZGJob3N0JyA9PiBnZXRlbnYoJ01ZU1FMX0hPU1QnKSwgJ2RibmFtZScgPT4gZ2V0ZW52KCdNWVNRTF9EQVRBQkFTRScpLCAnZGJ1c2VyJyA9PiBnZXRlbnYoJ01ZU1FMX1VTRVInKSwgJ2RicGFzcycgPT4gZ2V0ZW52KCdNWVNRTF9QQVNTV09SRCcpLCAnZGlyZWN0b3J5JyA9PiAnL2RhdGEnLCAnYWRtaW5sb2dpbicgPT4gJ2FkbWluJywgJ2FkbWlucGFzcycgPT4gZ2V0ZW52KCdORVhUQ0xPVURfQURNSU5fUEFTU1dPUkQnKV07CiAgICAvLyBFeGlzdGluZyBpbnN0YWxsYXRpb25zIHNraXAgTGludXhTZXJ2ZXIncyBmaXJzdC1ydW4gb3duZXJzaGlwIHNldHVwLgogICAgY3JlYXRlX3ByaXZhdGUoJGRpciAuICcvYXV0b2NvbmZpZy5waHAnLCAiPD9waHBcblwkQVVUT0NPTkZJRyA9ICIgLiB2YXJfZXhwb3J0KCRzZXR0aW5ncywgdHJ1ZSkgLiAiO1xuIiwgJGRpcik7Cn0KJHBhdGggPSAkZGlyIC4gJy9wcm94bWVudXgtcmVkaXMuY29uZmlnLnBocCc7Ci8vIEluc3RhbGxlZCBpbnN0YW5jZXMga2VlcCB0aGVpciBleGlzdGluZyBjYWNoZSBjb25maWd1cmF0aW9uIHVuY2hhbmdlZC4KaWYgKCFpc19maWxlKCRwYXRoKSAmJiAhaXNfbGluaygkcGF0aCkgJiYgIWlzX2ZpbGUoJGRpciAuICcvY29uZmlnLnBocCcpKSB7CiAgICAkc2V0dGluZ3MgPSBbJ2ZpbGVsb2NraW5nLmVuYWJsZWQnID0+IHRydWUsICdtZW1jYWNoZS5sb2NhbCcgPT4gJ1xPQ1xNZW1jYWNoZVxBUEN1JywgJ21lbWNhY2hlLmxvY2tpbmcnID0+ICdcT0NcTWVtY2FjaGVcUmVkaXMnLCAncmVkaXMnID0+IFsnaG9zdCcgPT4gJ3JlZGlzJywgJ3BvcnQnID0+IDYzNzksICd0aW1lb3V0JyA9PiAwLjBdXTsKICAgIGNyZWF0ZV9wcml2YXRlKCRwYXRoLCAiPD9waHBcblwkQ09ORklHID0gIiAuIHZhcl9leHBvcnQoJHNldHRpbmdzLCB0cnVlKSAuICI7XG4iLCAkZGlyKTsKfQo= | base64 -d | php); exec /init"
|
||||
],
|
||||
"environment": {
|
||||
"PUID": "${PUID}",
|
||||
@@ -281,7 +288,8 @@
|
||||
"MYSQL_HOST": "db",
|
||||
"MYSQL_DATABASE": "nextcloud",
|
||||
"MYSQL_USER": "nextcloud",
|
||||
"MYSQL_PASSWORD": "${GENERATED_DB_PASSWORD}"
|
||||
"MYSQL_PASSWORD": "${GENERATED_DB_PASSWORD}",
|
||||
"NEXTCLOUD_ADMIN_PASSWORD": "${GENERATED_ADMIN_PASSWORD}"
|
||||
},
|
||||
"image": "lscr.io/linuxserver/nextcloud:latest",
|
||||
"ports": [
|
||||
@@ -402,6 +410,16 @@
|
||||
"dependency_start_and_stop_order"
|
||||
],
|
||||
"generated_secrets": [
|
||||
{
|
||||
"id": "generated-admin-password",
|
||||
"strategy": "generate-cryptographically-random-at-install",
|
||||
"bindings": [
|
||||
{
|
||||
"service": "nextcloud",
|
||||
"environment_variable": "NEXTCLOUD_ADMIN_PASSWORD"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "generated-db-password",
|
||||
"strategy": "generate-cryptographically-random-at-install",
|
||||
@@ -439,7 +457,18 @@
|
||||
"source": "linuxserver-nextcloud-documentation"
|
||||
}
|
||||
],
|
||||
"credentials": []
|
||||
"credentials": [
|
||||
{
|
||||
"label": "Generated administrator login",
|
||||
"type": "runtime-generated",
|
||||
"username": "admin",
|
||||
"password": null,
|
||||
"password_environment": "NEXTCLOUD_ADMIN_PASSWORD",
|
||||
"change_required": true,
|
||||
"source": "proxmenux-installer-generated",
|
||||
"retrieval": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"proxmox": {
|
||||
"runtime": "native-oci-lxc",
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<?php
|
||||
// Prepare a fresh LinuxServer Nextcloud without replacing persistent user settings.
|
||||
$dir = '/config/www/nextcloud/config';
|
||||
umask(0077);
|
||||
if (!is_dir($dir) && !mkdir($dir, 0770, true)) { throw new RuntimeException('Cannot create the Nextcloud config directory'); }
|
||||
// The directory belongs to the application user. Create each file without
|
||||
// following a link placed there, and give it to that user without following one.
|
||||
function create_private($path, $content, $owner) {
|
||||
$handle = @fopen($path, 'x');
|
||||
if ($handle === false || fwrite($handle, $content) !== strlen($content) || !fclose($handle)) { throw new RuntimeException('Cannot write ' . basename($path)); }
|
||||
if ($owner && (!lchown($path, fileowner($owner)) || !lchgrp($path, filegroup($owner)))) { throw new RuntimeException('Cannot set Nextcloud configuration ownership'); }
|
||||
}
|
||||
if (!is_file($dir . '/config.php') && !is_file($dir . '/autoconfig.php')) {
|
||||
foreach (['MYSQL_HOST', 'MYSQL_DATABASE', 'MYSQL_USER', 'MYSQL_PASSWORD'] as $name) {
|
||||
if (getenv($name) === false || getenv($name) === '') { throw new RuntimeException('Missing Nextcloud database setting: ' . $name); }
|
||||
foreach (['MYSQL_HOST', 'MYSQL_DATABASE', 'MYSQL_USER', 'MYSQL_PASSWORD', 'NEXTCLOUD_ADMIN_PASSWORD'] as $name) {
|
||||
if (getenv($name) === false || getenv($name) === '') { throw new RuntimeException('Missing Nextcloud setting: ' . $name); }
|
||||
}
|
||||
// Keep the defaults that LinuxServer copies only into an empty config directory.
|
||||
$defaults = '/app/www/src/config';
|
||||
@@ -12,22 +20,18 @@ if (!is_file($dir . '/config.php') && !is_file($dir . '/autoconfig.php')) {
|
||||
foreach (new DirectoryIterator($defaults) as $file) {
|
||||
if ($file->isDot() || !$file->isFile()) { continue; }
|
||||
$target = $dir . '/' . $file->getFilename();
|
||||
if (!file_exists($target) && !copy($file->getPathname(), $target)) { throw new RuntimeException('Cannot seed the Nextcloud configuration'); }
|
||||
if (!file_exists($target) && !is_link($target)) { create_private($target, file_get_contents($file->getPathname()), null); }
|
||||
}
|
||||
}
|
||||
$settings = ['dbtype' => 'mysql', 'dbhost' => getenv('MYSQL_HOST'), 'dbname' => getenv('MYSQL_DATABASE'), 'dbuser' => getenv('MYSQL_USER'), 'dbpass' => getenv('MYSQL_PASSWORD'), 'directory' => '/data'];
|
||||
$path = $dir . '/autoconfig.php';
|
||||
if (file_put_contents($path, "<?php\n\$AUTOCONFIG = " . var_export($settings, true) . ";\n", LOCK_EX) === false) { throw new RuntimeException('Cannot write the Nextcloud database configuration'); }
|
||||
chmod($path, 0600);
|
||||
// With the administrator included, Nextcloud installs itself on the first
|
||||
// request, so nobody else on the network can claim the setup page.
|
||||
$settings = ['dbtype' => 'mysql', 'dbhost' => getenv('MYSQL_HOST'), 'dbname' => getenv('MYSQL_DATABASE'), 'dbuser' => getenv('MYSQL_USER'), 'dbpass' => getenv('MYSQL_PASSWORD'), 'directory' => '/data', 'adminlogin' => 'admin', 'adminpass' => getenv('NEXTCLOUD_ADMIN_PASSWORD')];
|
||||
// Existing installations skip LinuxServer's first-run ownership setup.
|
||||
if (!chown($path, fileowner($dir)) || !chgrp($path, filegroup($dir))) { throw new RuntimeException('Cannot set Nextcloud configuration ownership'); }
|
||||
create_private($dir . '/autoconfig.php', "<?php\n\$AUTOCONFIG = " . var_export($settings, true) . ";\n", $dir);
|
||||
}
|
||||
$path = $dir . '/proxmenux-redis.config.php';
|
||||
// Installed instances keep their existing cache configuration unchanged.
|
||||
if (!is_file($path) && !is_file($dir . '/config.php')) {
|
||||
if (!is_file($path) && !is_link($path) && !is_file($dir . '/config.php')) {
|
||||
$settings = ['filelocking.enabled' => true, 'memcache.local' => '\OC\Memcache\APCu', 'memcache.locking' => '\OC\Memcache\Redis', 'redis' => ['host' => 'redis', 'port' => 6379, 'timeout' => 0.0]];
|
||||
if (file_put_contents($path, "<?php\n\$CONFIG = " . var_export($settings, true) . ";\n", LOCK_EX) === false) { throw new RuntimeException('Cannot write the Nextcloud Redis configuration'); }
|
||||
chmod($path, 0600);
|
||||
// Existing installations skip LinuxServer's first-run ownership setup.
|
||||
if (!chown($path, fileowner($dir)) || !chgrp($path, filegroup($dir))) { throw new RuntimeException('Cannot set Nextcloud configuration ownership'); }
|
||||
create_private($path, "<?php\n\$CONFIG = " . var_export($settings, true) . ";\n", $dir);
|
||||
}
|
||||
|
||||
@@ -247,12 +247,19 @@ def relay_child_output(stream):
|
||||
return result
|
||||
|
||||
|
||||
def http_url(scheme, host, port, path):
|
||||
"""Without the default port of its scheme: an application that records its
|
||||
first request, like Nextcloud's trusted domain, sees the host a browser sends."""
|
||||
default = (scheme, int(port)) in (('https', 443), ('http', 80))
|
||||
return f"{scheme}://{host}{'' if default else f':{port}'}{path}"
|
||||
|
||||
|
||||
def assign_health_address(service):
|
||||
"""Bind a generated dependency probe to its allocated private address."""
|
||||
check = service['healthcheck']
|
||||
if check['type'] == 'http':
|
||||
endpoint = check['endpoint']
|
||||
check['url'] = f"{endpoint['scheme']}://{service['ip']}:{endpoint['port']}{endpoint['path']}"
|
||||
check['url'] = http_url(endpoint['scheme'], service['ip'], endpoint['port'], endpoint['path'])
|
||||
elif check['type'] == 'tcp':
|
||||
check['host'] = service['ip']
|
||||
|
||||
@@ -383,7 +390,7 @@ def lan_access_urls(services, subnet, strict=True):
|
||||
if not candidates:
|
||||
raise RuntimeError(f"{translate('No LAN address was obtained for the service:')} {service['name']}")
|
||||
urls.append({'label':service['name'],
|
||||
'url':f"{endpoint['scheme']}://{candidates[0]}:{endpoint['port']}{endpoint['path']}"})
|
||||
'url':http_url(endpoint['scheme'], candidates[0], endpoint['port'], endpoint['path'])})
|
||||
except Exception:
|
||||
if strict:
|
||||
raise
|
||||
|
||||
@@ -373,6 +373,18 @@ healthcheck_probe_url() {
|
||||
&& (( status == 1 || status == 8 || status == 52 || status == 56 ))
|
||||
}
|
||||
|
||||
# The address of the check, without the default port of its scheme: an
|
||||
# application that records its first request, like Nextcloud's trusted domain,
|
||||
# then sees the same host a browser sends.
|
||||
healthcheck_url() {
|
||||
local port=$1
|
||||
if [[ ( $HC_SCHEME == https && $port == 443 ) || ( $HC_SCHEME == http && $port == 80 ) ]]; then
|
||||
printf '%s://%s%s' "$HC_SCHEME" "$IP" "$HC_PATH"
|
||||
else
|
||||
printf '%s://%s:%s%s' "$HC_SCHEME" "$IP" "$port" "$HC_PATH"
|
||||
fi
|
||||
}
|
||||
|
||||
# Any of the addresses answering means the application is up; HC_URL keeps the
|
||||
# one that answered.
|
||||
healthcheck_probe() {
|
||||
@@ -2351,12 +2363,12 @@ if [[ $START_AFTER == 1 && $HAS_STARTUP_HEALTHCHECK == 1 ]]; then
|
||||
(( HC_STABILITY < HC_TIMEOUT )) \
|
||||
|| die "$(translate "The stability period must be shorter than the healthcheck timeout")"
|
||||
[[ -n $IP ]] || die "$(translate "The healthcheck cannot run without an IP address")"
|
||||
HC_URL="${HC_SCHEME}://${IP}:${HC_PORT}${HC_PATH}"
|
||||
HC_URL=$(healthcheck_url "$HC_PORT")
|
||||
HC_URLS=("$HC_URL")
|
||||
while IFS= read -r HC_ALT_PORT; do
|
||||
[[ $HC_ALT_PORT =~ ^[0-9]+$ && $HC_ALT_PORT -ge 1 && $HC_ALT_PORT -le 65535 ]] \
|
||||
|| die "$(translate "Invalid healthcheck port")"
|
||||
HC_URLS+=("${HC_SCHEME}://${IP}:${HC_ALT_PORT}${HC_PATH}")
|
||||
HC_URLS+=("$(healthcheck_url "$HC_ALT_PORT")")
|
||||
done < <(jq -r '.alternative_ports[]? | tostring' <<<"$STARTUP_HEALTHCHECK")
|
||||
HC_ANY_STATUS=$(jq -r '.accept_any_status // false' <<<"$STARTUP_HEALTHCHECK")
|
||||
HC_ACCEPT_NON_HTTP=$(jq -r '.accept_non_http // false' <<<"$STARTUP_HEALTHCHECK")
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""The startup check asks for the address a browser would use: without the
|
||||
default port of its scheme. Nextcloud trusts the host of its first request."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
SCRIPT = (Path(__file__).resolve().parents[1] / 'remote/install_oci.sh').read_text()
|
||||
|
||||
|
||||
def url(scheme, port, path='/'):
|
||||
function = re.search(r'^healthcheck_url\(\) \{\n.*?^\}\n', SCRIPT, re.M | re.S).group(0)
|
||||
done = subprocess.run(['bash', '-c', function + f'HC_SCHEME={scheme} IP=192.0.2.10 HC_PATH={path}; healthcheck_url {port}'],
|
||||
capture_output=True, text=True, check=True)
|
||||
return done.stdout
|
||||
|
||||
|
||||
class HealthcheckUrlTests(unittest.TestCase):
|
||||
def test_default_ports_are_left_out(self):
|
||||
self.assertEqual(url('https', 443), 'https://192.0.2.10/')
|
||||
self.assertEqual(url('http', 80, '/status'), 'http://192.0.2.10/status')
|
||||
|
||||
def test_other_ports_are_kept(self):
|
||||
self.assertEqual(url('http', 443), 'http://192.0.2.10:443/')
|
||||
self.assertEqual(url('https', 8443), 'https://192.0.2.10:8443/')
|
||||
self.assertEqual(url('http', 3000), 'http://192.0.2.10:3000/')
|
||||
|
||||
def test_every_probe_address_uses_it(self):
|
||||
self.assertIn('HC_URL=$(healthcheck_url "$HC_PORT")', SCRIPT)
|
||||
self.assertIn('HC_URLS+=("$(healthcheck_url "$HC_ALT_PORT")")', SCRIPT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -18,7 +18,8 @@ class MainFrontendReadiness(unittest.TestCase):
|
||||
with patch.object(installer,'run',return_value='10.77.6.30\n172.30.249.70\n'), \
|
||||
patch.object(installer,'wait_web') as web:
|
||||
url=installer.wait_main_web(service,ipaddress.ip_network('10.77.6.0/24'))
|
||||
self.assertEqual(url,'http://172.30.249.70:80/')
|
||||
# Without the default port: the host a browser sends.
|
||||
self.assertEqual(url,'http://172.30.249.70/')
|
||||
self.assertEqual(service['healthcheck']['url'],url)
|
||||
self.assertEqual(service['ip'],'10.77.6.30')
|
||||
web.assert_called_once_with(service,url)
|
||||
|
||||
@@ -36,6 +36,8 @@ class NextcloudMariaDBTests(unittest.TestCase):
|
||||
'db': 'mariadb:10.11', 'redis': 'redis:7-alpine'})
|
||||
self.assertEqual(t['first_run']['endpoints'][0]['scheme'], 'https')
|
||||
self.assertEqual(t['first_run']['endpoints'][0]['port'], 443)
|
||||
self.assertEqual(t['first_run']['credentials'][0]['password_environment'], 'NEXTCLOUD_ADMIN_PASSWORD')
|
||||
self.assertEqual(t['first_run']['credentials'][0]['username'], 'admin')
|
||||
self.assertTrue(t['catalog_ui']['icon'].startswith('https://'))
|
||||
self.assertEqual(set(services['nextcloud']['depends_on']), {'db', 'redis'})
|
||||
for name in ('db', 'redis'):
|
||||
@@ -64,6 +66,11 @@ class NextcloudMariaDBTests(unittest.TestCase):
|
||||
for name, s in services.items()}
|
||||
self.assertEqual(env['nextcloud']['MYSQL_PASSWORD'], env['db']['MYSQL_PASSWORD'])
|
||||
self.assertNotEqual(env['db']['MYSQL_PASSWORD'], env['db']['MYSQL_ROOT_PASSWORD'])
|
||||
# The administrator is created by ProxMenux, not by whoever opens the page first.
|
||||
self.assertEqual(len(env['nextcloud']['NEXTCLOUD_ADMIN_PASSWORD']), 64)
|
||||
self.assertNotIn(env['nextcloud']['NEXTCLOUD_ADMIN_PASSWORD'],
|
||||
(env['db']['MYSQL_PASSWORD'], env['db']['MYSQL_ROOT_PASSWORD']))
|
||||
self.assertNotIn('NEXTCLOUD_ADMIN_PASSWORD', env['db'])
|
||||
self.assertEqual(env['nextcloud']['MYSQL_HOST'], 'db')
|
||||
self.assertEqual([s['name'] for s in plan['services']], ['db', 'redis', 'nextcloud'])
|
||||
self.assertEqual([s['name'] for s in plan['services'] if s.get('frontend')], ['nextcloud'])
|
||||
@@ -78,6 +85,9 @@ class NextcloudMariaDBTests(unittest.TestCase):
|
||||
{'nextcloud': 2048, 'db': 1024, 'redis': 512})
|
||||
runtime = services['nextcloud']['template']['proxmox']['installer_profile']['runtime']
|
||||
self.assertIn('exec /init', runtime['compose_entrypoint'][2])
|
||||
# The private umask of the bootstrap does not reach the application.
|
||||
self.assertTrue(runtime['compose_entrypoint'][2].startswith('(umask 077; '))
|
||||
self.assertTrue(runtime['compose_entrypoint'][2].endswith('| php); exec /init'))
|
||||
self.assertNotIn('\n', runtime['compose_entrypoint'][2])
|
||||
self.assertNotIn('\r', runtime['compose_entrypoint'][2])
|
||||
|
||||
@@ -109,7 +119,8 @@ class NextcloudMariaDBTests(unittest.TestCase):
|
||||
test_source = source.replace('/config/www/nextcloud/config', str(folder)).replace('/app/www/src/config', str(image_config))
|
||||
script = entry[2].replace(payload, base64.b64encode(test_source.encode()).decode()).replace('exec /init', 'exit 0')
|
||||
password = "test-'quote\\backslash$not-a-real-secret\nline"
|
||||
env = dict(os.environ, MYSQL_HOST='db', MYSQL_DATABASE='nextcloud', MYSQL_USER='nextcloud', MYSQL_PASSWORD=password)
|
||||
env = dict(os.environ, MYSQL_HOST='db', MYSQL_DATABASE='nextcloud', MYSQL_USER='nextcloud', MYSQL_PASSWORD=password,
|
||||
NEXTCLOUD_ADMIN_PASSWORD='admin-' + password)
|
||||
subprocess.run(['/bin/sh', '-ec', script], env=env, check=True, capture_output=True)
|
||||
for name, content in defaults.items(): self.assertEqual((folder / name).read_text(), content)
|
||||
autoconfig = folder / 'autoconfig.php'
|
||||
@@ -122,7 +133,7 @@ class NextcloudMariaDBTests(unittest.TestCase):
|
||||
self.assertEqual(values['dbpass'], password)
|
||||
self.assertEqual(values['dbhost'], 'db')
|
||||
self.assertEqual(values['directory'], '/data')
|
||||
self.assertNotIn('adminpass', values)
|
||||
self.assertEqual((values['adminlogin'], values['adminpass']), ('admin', 'admin-' + password))
|
||||
cache = json.loads(subprocess.check_output(['php', '-r', 'require $argv[1]; echo json_encode($CONFIG);', str(redis)]))
|
||||
self.assertEqual(cache['redis']['host'], 'redis')
|
||||
self.assertEqual(cache['memcache.locking'], '\\OC\\Memcache\\Redis')
|
||||
@@ -137,6 +148,24 @@ class NextcloudMariaDBTests(unittest.TestCase):
|
||||
self.assertFalse(autoconfig.exists())
|
||||
self.assertFalse((folder / 'CAN_INSTALL').exists())
|
||||
|
||||
@unittest.skipUnless(shutil.which('php'), 'PHP CLI is needed to execute the bootstrap contract')
|
||||
def test_bootstrap_refuses_links_and_needs_the_administrator_password(self):
|
||||
source = (ROOT / 'examples/nextcloud-mariadb-bootstrap.php').read_text()
|
||||
base = dict(os.environ, MYSQL_HOST='db', MYSQL_DATABASE='nextcloud', MYSQL_USER='nextcloud', MYSQL_PASSWORD='x')
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
folder = Path(temp) / 'config'
|
||||
folder.mkdir()
|
||||
target = Path(temp) / 'outside'
|
||||
(folder / 'autoconfig.php').symlink_to(target)
|
||||
script = source.replace('/config/www/nextcloud/config', str(folder)).replace('/app/www/src/config', str(Path(temp) / 'none'))
|
||||
done = subprocess.run(['php'], input=script.encode(), env=dict(base, NEXTCLOUD_ADMIN_PASSWORD='y'), capture_output=True)
|
||||
self.assertNotEqual(done.returncode, 0)
|
||||
self.assertFalse(target.exists())
|
||||
(folder / 'autoconfig.php').unlink()
|
||||
done = subprocess.run(['php'], input=script.encode(), env=base, capture_output=True)
|
||||
self.assertNotEqual(done.returncode, 0)
|
||||
self.assertFalse((folder / 'autoconfig.php').exists())
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith('linux') and os.geteuid() == 0 and shutil.which('php'),
|
||||
'Linux root is needed to reproduce existing-volume ownership')
|
||||
def test_existing_config_volume_can_read_new_redis_configuration_as_its_app_user(self):
|
||||
|
||||
Reference in New Issue
Block a user