Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update phpmyadmin to the latest commit #17398

Merged
merged 1 commit into from
Jan 13, 2025

Conversation

williamdes
Copy link
Contributor

  • Some changes for rootless
  • Support new ENVs for SSL

@williamdes williamdes requested a review from a team as a code owner August 20, 2024 18:42

This comment has been minimized.

@yosifkit
Copy link
Member

RUN chown ... is going to duplicate the file added by COPY, so it should use COPY --chown instead (https://docs.docker.com/reference/dockerfile/#copy---chown---chmod). It will require a Builder: buildkit in the library/phpmyadmin file (like rabbitmq) so that we use the correct docker builder (since the classic builder doesn't understand it).

buildkit will eventually be the default for Docker Official Images, but it is still an active migration.

@williamdes williamdes force-pushed the williamdes-phpmyadmin branch from 0f07766 to c09a8aa Compare January 11, 2025 22:57
@williamdes williamdes marked this pull request as ready for review January 11, 2025 22:57
Copy link

Diff for c09a8aa:
diff --git a/_bashbrew-arches b/_bashbrew-arches
index bedcccb..3616e93 100644
--- a/_bashbrew-arches
+++ b/_bashbrew-arches
@@ -6,4 +6,5 @@ arm64v8
 i386
 mips64le
 ppc64le
+riscv64
 s390x
diff --git a/_bashbrew-cat b/_bashbrew-cat
index 767f734..f90e90d 100644
--- a/_bashbrew-cat
+++ b/_bashbrew-cat
@@ -3,15 +3,15 @@ GitRepo: https://github.com/phpmyadmin/docker.git
 
 Tags: 5.2.1-apache, 5.2-apache, 5-apache, apache, 5.2.1, 5.2, 5, latest
 Architectures: amd64, arm32v5, arm32v7, arm64v8, i386, mips64le, ppc64le, s390x
-GitCommit: da4b8f273a0a81078185076683ed92a382814ef3
+GitCommit: ec0d6a5c3ae5d8df6e5f7d08570c91518cfc302e
 Directory: apache
 
 Tags: 5.2.1-fpm, 5.2-fpm, 5-fpm, fpm
 Architectures: amd64, arm32v5, arm32v7, arm64v8, i386, mips64le, ppc64le, s390x
-GitCommit: da4b8f273a0a81078185076683ed92a382814ef3
+GitCommit: ec0d6a5c3ae5d8df6e5f7d08570c91518cfc302e
 Directory: fpm
 
 Tags: 5.2.1-fpm-alpine, 5.2-fpm-alpine, 5-fpm-alpine, fpm-alpine
-Architectures: amd64, arm32v6, arm32v7, arm64v8, i386, ppc64le, s390x
-GitCommit: 8674356a6d0f67eb89d0200647832fc3853781fd
+Architectures: amd64, arm32v6, arm32v7, arm64v8, i386, ppc64le, riscv64, s390x
+GitCommit: ec0d6a5c3ae5d8df6e5f7d08570c91518cfc302e
 Directory: fpm-alpine
diff --git a/phpmyadmin_fpm-alpine/Dockerfile b/phpmyadmin_fpm-alpine/Dockerfile
index 60b095f..eefb733 100644
--- a/phpmyadmin_fpm-alpine/Dockerfile
+++ b/phpmyadmin_fpm-alpine/Dockerfile
@@ -40,6 +40,7 @@ RUN set -ex; \
 
 # set recommended PHP.ini settings
 # see https://secure.php.net/manual/en/opcache.installation.php
+ENV PMA_SSL_DIR /etc/phpmyadmin/ssl
 ENV MAX_EXECUTION_TIME 600
 ENV MEMORY_LIMIT 512M
 ENV UPLOAD_LIMIT 2048K
@@ -92,8 +93,11 @@ RUN set -ex; \
         gnupg \
     ; \
     mkdir $SESSION_SAVE_PATH; \
+    mkdir -p $PMA_SSL_DIR; \
     chmod 1777 $SESSION_SAVE_PATH; \
+    chmod 755 $PMA_SSL_DIR; \
     chown www-data:www-data $SESSION_SAVE_PATH; \
+    chown www-data:www-data $PMA_SSL_DIR; \
     \
     export GNUPGHOME="$(mktemp -d)"; \
     export GPGKEY="3D06A59ECE730EB71B511C17CE752F178259BD92"; \
@@ -115,10 +119,12 @@ RUN set -ex; \
     sed -i "s@'configFile' => .*@'configFile' => '/etc/phpmyadmin/config.inc.php',@" /var/www/html/libraries/vendor_config.php; \
     grep -q -F "'configFile' => '/etc/phpmyadmin/config.inc.php'," /var/www/html/libraries/vendor_config.php; \
     php -l /var/www/html/libraries/vendor_config.php; \
+    chown -R www-data:www-data -R /var/www/html/; \
     apk del --no-network .fetch-deps
 
 # Copy configuration
-COPY config.inc.php /etc/phpmyadmin/config.inc.php
+COPY --chown=www-data:www-data config.inc.php /etc/phpmyadmin/config.inc.php
+COPY --chown=www-data:www-data helpers.php /etc/phpmyadmin/helpers.php
 
 # Copy main script
 COPY docker-entrypoint.sh /docker-entrypoint.sh
diff --git a/phpmyadmin_fpm-alpine/config.inc.php b/phpmyadmin_fpm-alpine/config.inc.php
index 9a39134..b3a310e 100644
--- a/phpmyadmin_fpm-alpine/config.inc.php
+++ b/phpmyadmin_fpm-alpine/config.inc.php
@@ -1,6 +1,7 @@
 <?php
 
-require '/etc/phpmyadmin/config.secret.inc.php';
+require_once '/etc/phpmyadmin/config.secret.inc.php';
+require_once '/etc/phpmyadmin/helpers.php';
 
 /* Ensure we got the environment */
 $vars = [
@@ -27,6 +28,23 @@ $vars = [
     'MEMORY_LIMIT',
     'PMA_UPLOADDIR',
     'PMA_SAVEDIR',
+    'PMA_SSL',
+    'PMA_SSLS',
+    'PMA_SSL_DIR',
+    'PMA_SSL_VERIFY',
+    'PMA_SSL_VERIFIES',
+    'PMA_SSL_CA',
+    'PMA_SSL_CAS',
+    'PMA_SSL_CA_BASE64',
+    'PMA_SSL_CAS_BASE64',
+    'PMA_SSL_KEY',
+    'PMA_SSL_KEYS',
+    'PMA_SSL_KEY_BASE64',
+    'PMA_SSL_KEYS_BASE64',
+    'PMA_SSL_CERT',
+    'PMA_SSL_CERTS',
+    'PMA_SSL_CERT_BASE64',
+    'PMA_SSL_CERTS_BASE64',
 ];
 
 foreach ($vars as $var) {
@@ -35,6 +53,11 @@ foreach ($vars as $var) {
         $_ENV[$var] = $env;
     }
 }
+
+if (! defined('PMA_SSL_DIR')) {
+    define('PMA_SSL_DIR', $_ENV['PMA_SSL_DIR'] ?? '/etc/phpmyadmin/ssl');
+}
+
 if (isset($_ENV['PMA_QUERYHISTORYDB'])) {
     $cfg['QueryHistoryDB'] = (bool) $_ENV['PMA_QUERYHISTORYDB'];
 }
@@ -53,6 +76,35 @@ if (isset($_ENV['PMA_ABSOLUTE_URI'])) {
     $cfg['PmaAbsoluteUri'] = trim($_ENV['PMA_ABSOLUTE_URI']);
 }
 
+if (isset($_ENV['PMA_SSL_CA_BASE64'])) {
+    $_ENV['PMA_SSL_CA'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CA_BASE64'], 'phpmyadmin-ssl-CA', 'pem', PMA_SSL_DIR);
+}
+
+/* Decode and save the SSL key from base64 */
+if (isset($_ENV['PMA_SSL_KEY_BASE64'])) {
+    $_ENV['PMA_SSL_KEY'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_KEY_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save the SSL certificate from base64 */
+if (isset($_ENV['PMA_SSL_CERT_BASE64'])) {
+    $_ENV['PMA_SSL_CERT'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CERT_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL CA certificates from base64 */
+if (isset($_ENV['PMA_SSL_CAS_BASE64'])) {
+    $_ENV['PMA_SSL_CAS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CAS_BASE64'], 'phpmyadmin-ssl-CA', 'pem', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL keys from base64 */
+if (isset($_ENV['PMA_SSL_KEYS_BASE64'])) {
+    $_ENV['PMA_SSL_KEYS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_KEYS_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL certificates from base64 */
+if (isset($_ENV['PMA_SSL_CERTS_BASE64'])) {
+    $_ENV['PMA_SSL_CERTS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CERTS_BASE64'], 'phpmyadmin-ssl-KEY', 'key', PMA_SSL_DIR);
+}
+
 /* Figure out hosts */
 
 /* Fallback to default linked */
@@ -63,10 +115,20 @@ if (! empty($_ENV['PMA_HOST'])) {
     $hosts = [$_ENV['PMA_HOST']];
     $verbose = [$_ENV['PMA_VERBOSE']];
     $ports = [$_ENV['PMA_PORT']];
+    $ssls = [$_ENV['PMA_SSL']];
+    $ssl_verifies = [$_ENV['PMA_SSL_VERIFY']];
+    $ssl_cas = [$_ENV['PMA_SSL_CA']];
+    $ssl_keys = [$_ENV['PMA_SSL_KEY']];
+    $ssl_certs = [$_ENV['PMA_SSL_CERT']];
 } elseif (! empty($_ENV['PMA_HOSTS'])) {
     $hosts = array_map('trim', explode(',', $_ENV['PMA_HOSTS']));
     $verbose = array_map('trim', explode(',', $_ENV['PMA_VERBOSES']));
     $ports = array_map('trim', explode(',', $_ENV['PMA_PORTS']));
+    $ssls = array_map('trim', explode(',', $_ENV['PMA_SSLS']));
+    $ssl_verifies = array_map('trim', explode(',', $_ENV['PMA_SSL_VERIFIES']));
+    $ssl_cas = array_map('trim', explode(',', $_ENV['PMA_SSL_CAS']));
+    $ssl_keys = array_map('trim', explode(',', $_ENV['PMA_SSL_KEYS']));
+    $ssl_certs = array_map('trim', explode(',', $_ENV['PMA_SSL_CERTS']));
 }
 
 if (! empty($_ENV['PMA_SOCKET'])) {
@@ -77,6 +139,21 @@ if (! empty($_ENV['PMA_SOCKET'])) {
 
 /* Server settings */
 for ($i = 1; isset($hosts[$i - 1]); $i++) {
+    if (isset($ssls[$i - 1]) && $ssls[$i - 1] === '1') {
+        $cfg['Servers'][$i]['ssl'] = $ssls[$i - 1];
+    }
+    if (isset($ssl_verifies[$i - 1]) && $ssl_verifies[$i - 1] === '1') {
+        $cfg['Servers'][$i]['ssl_verify'] = $ssl_verifies[$i - 1];
+    }
+    if (isset($ssl_cas[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_ca'] = $ssl_cas[$i - 1];
+    }
+    if (isset($ssl_keys[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_key'] = $ssl_keys[$i - 1];
+    }
+    if (isset($ssl_certs[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_cert'] = $ssl_certs[$i - 1];
+    }
     $cfg['Servers'][$i]['host'] = $hosts[$i - 1];
     if (isset($verbose[$i - 1])) {
         $cfg['Servers'][$i]['verbose'] = $verbose[$i - 1];
@@ -128,9 +205,10 @@ for ($i = 1; isset($hosts[$i - 1]); $i++) {
     $cfg['Servers'][$i]['compress'] = false;
     $cfg['Servers'][$i]['AllowNoPassword'] = true;
 }
-for ($i = 1; isset($sockets[$i - 1]); $i++) {
-    $cfg['Servers'][$i]['socket'] = $sockets[$i - 1];
-    $cfg['Servers'][$i]['host'] = 'localhost';
+// Avoid overwriting the last server id $i, use another variable name
+for ($socketHostId = 1; isset($sockets[$socketHostId - 1]); $socketHostId++) {
+    $cfg['Servers'][$socketHostId]['socket'] = $sockets[$socketHostId - 1];
+    $cfg['Servers'][$socketHostId]['host'] = 'localhost';
 }
 /*
  * Revert back to last configured server to make
diff --git a/phpmyadmin_fpm-alpine/docker-entrypoint.sh b/phpmyadmin_fpm-alpine/docker-entrypoint.sh
index 24c45e8..0d98e27 100755
--- a/phpmyadmin_fpm-alpine/docker-entrypoint.sh
+++ b/phpmyadmin_fpm-alpine/docker-entrypoint.sh
@@ -7,6 +7,7 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then
 \$cfg['blowfish_secret'] = '$(tr -dc 'a-zA-Z0-9~!@#$%^&*_()+}{?></";.,[]=-' < /dev/urandom | fold -w 32 | head -n 1)';
 EOT
     fi
+    chgrp www-data /etc/phpmyadmin/config.secret.inc.php
 
     if [ ! -f /etc/phpmyadmin/config.user.inc.php ]; then
         touch /etc/phpmyadmin/config.user.inc.php
@@ -50,5 +51,7 @@ get_docker_secret PMA_HOST
 get_docker_secret PMA_CONTROLHOST
 get_docker_secret PMA_CONTROLUSER
 get_docker_secret PMA_CONTROLPASS
+get_docker_secret PMA_SSL
+get_docker_secret PMA_SSLS
 
 exec "$@"
diff --git a/phpmyadmin_fpm-alpine/helpers.php b/phpmyadmin_fpm-alpine/helpers.php
new file mode 100644
index 0000000..fd06bcd
--- /dev/null
+++ b/phpmyadmin_fpm-alpine/helpers.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * Helper function to decode and save multiple SSL files from base64.
+ *
+ * @param string $base64FilesContents The base64 encoded string containing multiple files separated by commas.
+ *                                    If no commas are present, the entire string is treated as a single file.
+ * @param string $prefix              The prefix to use for the generated file names.
+ * @param string $extension           The file extension to use for the generated files.
+ * @param string $storageFolder       The folder where to store the generated files.
+ *
+ * @return string A comma-separated list of paths to the generated files.
+ */
+function decodeBase64AndSaveFiles(string $base64FilesContents, string $prefix, string $extension, string $storageFolder): string
+{
+    // Ensure the output directory exists
+    if (! is_dir($storageFolder)) {
+        mkdir($storageFolder, 0755, true);
+    }
+
+    // Split the base64 string into an array of files
+    $base64FilesContents = explode(',', trim($base64FilesContents));
+    $counter = 1;
+    $outputFiles = [];
+
+    // Process each file
+    foreach ($base64FilesContents as $base64FileContent) {
+        $outputFile = $storageFolder . '/' . $prefix . '-' . $counter . '.' . $extension;
+
+        $fileContent = base64_decode($base64FileContent, true);
+        if ($fileContent === false) {
+            echo 'Failed to decode: ' . $base64FileContent;
+            exit(1);
+        }
+
+        // Write the decoded file to the output directory
+        if (file_put_contents($outputFile, $fileContent) === false) {
+            echo 'Failed to write to ' . $outputFile;
+            exit(1);
+        }
+
+        // Add the output file path to the list
+        $outputFiles[] = $outputFile;
+        $counter++;
+    }
+
+    // Return a comma-separated list of the generated file paths
+    return implode(',', $outputFiles);
+}
diff --git a/phpmyadmin_fpm/Dockerfile b/phpmyadmin_fpm/Dockerfile
index 34e24d1..f9fe9e1 100644
--- a/phpmyadmin_fpm/Dockerfile
+++ b/phpmyadmin_fpm/Dockerfile
@@ -38,6 +38,7 @@ RUN set -ex; \
         | sort -u \
         | xargs -rt apt-mark manual; \
     \
+    \
     apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
     rm -rf /var/lib/apt/lists/*; \
     ldd "$extdir"/*.so | grep -qzv "=> not found" || (echo "Sanity check failed: missing libraries:"; ldd "$extdir"/*.so | grep " => not found"; exit 1); \
@@ -47,6 +48,7 @@ RUN set -ex; \
 
 # set recommended PHP.ini settings
 # see https://secure.php.net/manual/en/opcache.installation.php
+ENV PMA_SSL_DIR /etc/phpmyadmin/ssl
 ENV MAX_EXECUTION_TIME 600
 ENV MEMORY_LIMIT 512M
 ENV UPLOAD_LIMIT 2048K
@@ -104,8 +106,11 @@ RUN set -ex; \
         dirmngr \
     ; \
     mkdir $SESSION_SAVE_PATH; \
+    mkdir -p $PMA_SSL_DIR; \
     chmod 1777 $SESSION_SAVE_PATH; \
+    chmod 755 $PMA_SSL_DIR; \
     chown www-data:www-data $SESSION_SAVE_PATH; \
+    chown www-data:www-data $PMA_SSL_DIR; \
     \
     export GNUPGHOME="$(mktemp -d)"; \
     export GPGKEY="3D06A59ECE730EB71B511C17CE752F178259BD92"; \
@@ -127,6 +132,7 @@ RUN set -ex; \
     sed -i "s@'configFile' => .*@'configFile' => '/etc/phpmyadmin/config.inc.php',@" /var/www/html/libraries/vendor_config.php; \
     grep -q -F "'configFile' => '/etc/phpmyadmin/config.inc.php'," /var/www/html/libraries/vendor_config.php; \
     php -l /var/www/html/libraries/vendor_config.php; \
+    chown -R www-data:www-data -R /var/www/html/; \
     \
     apt-mark auto '.*' > /dev/null; \
     apt-mark manual $savedAptMark; \
@@ -134,7 +140,8 @@ RUN set -ex; \
     rm -rf /var/lib/apt/lists/*
 
 # Copy configuration
-COPY config.inc.php /etc/phpmyadmin/config.inc.php
+COPY --chown=www-data:www-data config.inc.php /etc/phpmyadmin/config.inc.php
+COPY --chown=www-data:www-data helpers.php /etc/phpmyadmin/helpers.php
 
 # Copy main script
 COPY docker-entrypoint.sh /docker-entrypoint.sh
diff --git a/phpmyadmin_fpm/config.inc.php b/phpmyadmin_fpm/config.inc.php
index 9a39134..b3a310e 100644
--- a/phpmyadmin_fpm/config.inc.php
+++ b/phpmyadmin_fpm/config.inc.php
@@ -1,6 +1,7 @@
 <?php
 
-require '/etc/phpmyadmin/config.secret.inc.php';
+require_once '/etc/phpmyadmin/config.secret.inc.php';
+require_once '/etc/phpmyadmin/helpers.php';
 
 /* Ensure we got the environment */
 $vars = [
@@ -27,6 +28,23 @@ $vars = [
     'MEMORY_LIMIT',
     'PMA_UPLOADDIR',
     'PMA_SAVEDIR',
+    'PMA_SSL',
+    'PMA_SSLS',
+    'PMA_SSL_DIR',
+    'PMA_SSL_VERIFY',
+    'PMA_SSL_VERIFIES',
+    'PMA_SSL_CA',
+    'PMA_SSL_CAS',
+    'PMA_SSL_CA_BASE64',
+    'PMA_SSL_CAS_BASE64',
+    'PMA_SSL_KEY',
+    'PMA_SSL_KEYS',
+    'PMA_SSL_KEY_BASE64',
+    'PMA_SSL_KEYS_BASE64',
+    'PMA_SSL_CERT',
+    'PMA_SSL_CERTS',
+    'PMA_SSL_CERT_BASE64',
+    'PMA_SSL_CERTS_BASE64',
 ];
 
 foreach ($vars as $var) {
@@ -35,6 +53,11 @@ foreach ($vars as $var) {
         $_ENV[$var] = $env;
     }
 }
+
+if (! defined('PMA_SSL_DIR')) {
+    define('PMA_SSL_DIR', $_ENV['PMA_SSL_DIR'] ?? '/etc/phpmyadmin/ssl');
+}
+
 if (isset($_ENV['PMA_QUERYHISTORYDB'])) {
     $cfg['QueryHistoryDB'] = (bool) $_ENV['PMA_QUERYHISTORYDB'];
 }
@@ -53,6 +76,35 @@ if (isset($_ENV['PMA_ABSOLUTE_URI'])) {
     $cfg['PmaAbsoluteUri'] = trim($_ENV['PMA_ABSOLUTE_URI']);
 }
 
+if (isset($_ENV['PMA_SSL_CA_BASE64'])) {
+    $_ENV['PMA_SSL_CA'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CA_BASE64'], 'phpmyadmin-ssl-CA', 'pem', PMA_SSL_DIR);
+}
+
+/* Decode and save the SSL key from base64 */
+if (isset($_ENV['PMA_SSL_KEY_BASE64'])) {
+    $_ENV['PMA_SSL_KEY'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_KEY_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save the SSL certificate from base64 */
+if (isset($_ENV['PMA_SSL_CERT_BASE64'])) {
+    $_ENV['PMA_SSL_CERT'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CERT_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL CA certificates from base64 */
+if (isset($_ENV['PMA_SSL_CAS_BASE64'])) {
+    $_ENV['PMA_SSL_CAS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CAS_BASE64'], 'phpmyadmin-ssl-CA', 'pem', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL keys from base64 */
+if (isset($_ENV['PMA_SSL_KEYS_BASE64'])) {
+    $_ENV['PMA_SSL_KEYS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_KEYS_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL certificates from base64 */
+if (isset($_ENV['PMA_SSL_CERTS_BASE64'])) {
+    $_ENV['PMA_SSL_CERTS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CERTS_BASE64'], 'phpmyadmin-ssl-KEY', 'key', PMA_SSL_DIR);
+}
+
 /* Figure out hosts */
 
 /* Fallback to default linked */
@@ -63,10 +115,20 @@ if (! empty($_ENV['PMA_HOST'])) {
     $hosts = [$_ENV['PMA_HOST']];
     $verbose = [$_ENV['PMA_VERBOSE']];
     $ports = [$_ENV['PMA_PORT']];
+    $ssls = [$_ENV['PMA_SSL']];
+    $ssl_verifies = [$_ENV['PMA_SSL_VERIFY']];
+    $ssl_cas = [$_ENV['PMA_SSL_CA']];
+    $ssl_keys = [$_ENV['PMA_SSL_KEY']];
+    $ssl_certs = [$_ENV['PMA_SSL_CERT']];
 } elseif (! empty($_ENV['PMA_HOSTS'])) {
     $hosts = array_map('trim', explode(',', $_ENV['PMA_HOSTS']));
     $verbose = array_map('trim', explode(',', $_ENV['PMA_VERBOSES']));
     $ports = array_map('trim', explode(',', $_ENV['PMA_PORTS']));
+    $ssls = array_map('trim', explode(',', $_ENV['PMA_SSLS']));
+    $ssl_verifies = array_map('trim', explode(',', $_ENV['PMA_SSL_VERIFIES']));
+    $ssl_cas = array_map('trim', explode(',', $_ENV['PMA_SSL_CAS']));
+    $ssl_keys = array_map('trim', explode(',', $_ENV['PMA_SSL_KEYS']));
+    $ssl_certs = array_map('trim', explode(',', $_ENV['PMA_SSL_CERTS']));
 }
 
 if (! empty($_ENV['PMA_SOCKET'])) {
@@ -77,6 +139,21 @@ if (! empty($_ENV['PMA_SOCKET'])) {
 
 /* Server settings */
 for ($i = 1; isset($hosts[$i - 1]); $i++) {
+    if (isset($ssls[$i - 1]) && $ssls[$i - 1] === '1') {
+        $cfg['Servers'][$i]['ssl'] = $ssls[$i - 1];
+    }
+    if (isset($ssl_verifies[$i - 1]) && $ssl_verifies[$i - 1] === '1') {
+        $cfg['Servers'][$i]['ssl_verify'] = $ssl_verifies[$i - 1];
+    }
+    if (isset($ssl_cas[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_ca'] = $ssl_cas[$i - 1];
+    }
+    if (isset($ssl_keys[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_key'] = $ssl_keys[$i - 1];
+    }
+    if (isset($ssl_certs[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_cert'] = $ssl_certs[$i - 1];
+    }
     $cfg['Servers'][$i]['host'] = $hosts[$i - 1];
     if (isset($verbose[$i - 1])) {
         $cfg['Servers'][$i]['verbose'] = $verbose[$i - 1];
@@ -128,9 +205,10 @@ for ($i = 1; isset($hosts[$i - 1]); $i++) {
     $cfg['Servers'][$i]['compress'] = false;
     $cfg['Servers'][$i]['AllowNoPassword'] = true;
 }
-for ($i = 1; isset($sockets[$i - 1]); $i++) {
-    $cfg['Servers'][$i]['socket'] = $sockets[$i - 1];
-    $cfg['Servers'][$i]['host'] = 'localhost';
+// Avoid overwriting the last server id $i, use another variable name
+for ($socketHostId = 1; isset($sockets[$socketHostId - 1]); $socketHostId++) {
+    $cfg['Servers'][$socketHostId]['socket'] = $sockets[$socketHostId - 1];
+    $cfg['Servers'][$socketHostId]['host'] = 'localhost';
 }
 /*
  * Revert back to last configured server to make
diff --git a/phpmyadmin_fpm/docker-entrypoint.sh b/phpmyadmin_fpm/docker-entrypoint.sh
index 24c45e8..0d98e27 100755
--- a/phpmyadmin_fpm/docker-entrypoint.sh
+++ b/phpmyadmin_fpm/docker-entrypoint.sh
@@ -7,6 +7,7 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then
 \$cfg['blowfish_secret'] = '$(tr -dc 'a-zA-Z0-9~!@#$%^&*_()+}{?></";.,[]=-' < /dev/urandom | fold -w 32 | head -n 1)';
 EOT
     fi
+    chgrp www-data /etc/phpmyadmin/config.secret.inc.php
 
     if [ ! -f /etc/phpmyadmin/config.user.inc.php ]; then
         touch /etc/phpmyadmin/config.user.inc.php
@@ -50,5 +51,7 @@ get_docker_secret PMA_HOST
 get_docker_secret PMA_CONTROLHOST
 get_docker_secret PMA_CONTROLUSER
 get_docker_secret PMA_CONTROLPASS
+get_docker_secret PMA_SSL
+get_docker_secret PMA_SSLS
 
 exec "$@"
diff --git a/phpmyadmin_fpm/helpers.php b/phpmyadmin_fpm/helpers.php
new file mode 100644
index 0000000..fd06bcd
--- /dev/null
+++ b/phpmyadmin_fpm/helpers.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * Helper function to decode and save multiple SSL files from base64.
+ *
+ * @param string $base64FilesContents The base64 encoded string containing multiple files separated by commas.
+ *                                    If no commas are present, the entire string is treated as a single file.
+ * @param string $prefix              The prefix to use for the generated file names.
+ * @param string $extension           The file extension to use for the generated files.
+ * @param string $storageFolder       The folder where to store the generated files.
+ *
+ * @return string A comma-separated list of paths to the generated files.
+ */
+function decodeBase64AndSaveFiles(string $base64FilesContents, string $prefix, string $extension, string $storageFolder): string
+{
+    // Ensure the output directory exists
+    if (! is_dir($storageFolder)) {
+        mkdir($storageFolder, 0755, true);
+    }
+
+    // Split the base64 string into an array of files
+    $base64FilesContents = explode(',', trim($base64FilesContents));
+    $counter = 1;
+    $outputFiles = [];
+
+    // Process each file
+    foreach ($base64FilesContents as $base64FileContent) {
+        $outputFile = $storageFolder . '/' . $prefix . '-' . $counter . '.' . $extension;
+
+        $fileContent = base64_decode($base64FileContent, true);
+        if ($fileContent === false) {
+            echo 'Failed to decode: ' . $base64FileContent;
+            exit(1);
+        }
+
+        // Write the decoded file to the output directory
+        if (file_put_contents($outputFile, $fileContent) === false) {
+            echo 'Failed to write to ' . $outputFile;
+            exit(1);
+        }
+
+        // Add the output file path to the list
+        $outputFiles[] = $outputFile;
+        $counter++;
+    }
+
+    // Return a comma-separated list of the generated file paths
+    return implode(',', $outputFiles);
+}
diff --git a/phpmyadmin_latest/Dockerfile b/phpmyadmin_latest/Dockerfile
index 66a9262..dff2005 100644
--- a/phpmyadmin_latest/Dockerfile
+++ b/phpmyadmin_latest/Dockerfile
@@ -38,6 +38,10 @@ RUN set -ex; \
         | sort -u \
         | xargs -rt apt-mark manual; \
     \
+    # start: Apache specific build
+    a2enmod remoteip; \
+    # end: Apache specific build
+    \
     apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
     rm -rf /var/lib/apt/lists/*; \
     ldd "$extdir"/*.so | grep -qzv "=> not found" || (echo "Sanity check failed: missing libraries:"; ldd "$extdir"/*.so | grep " => not found"; exit 1); \
@@ -47,6 +51,7 @@ RUN set -ex; \
 
 # set recommended PHP.ini settings
 # see https://secure.php.net/manual/en/opcache.installation.php
+ENV PMA_SSL_DIR /etc/phpmyadmin/ssl
 ENV MAX_EXECUTION_TIME 600
 ENV MEMORY_LIMIT 512M
 ENV UPLOAD_LIMIT 2048K
@@ -104,8 +109,11 @@ RUN set -ex; \
         dirmngr \
     ; \
     mkdir $SESSION_SAVE_PATH; \
+    mkdir -p $PMA_SSL_DIR; \
     chmod 1777 $SESSION_SAVE_PATH; \
+    chmod 755 $PMA_SSL_DIR; \
     chown www-data:www-data $SESSION_SAVE_PATH; \
+    chown www-data:www-data $PMA_SSL_DIR; \
     \
     export GNUPGHOME="$(mktemp -d)"; \
     export GPGKEY="3D06A59ECE730EB71B511C17CE752F178259BD92"; \
@@ -127,6 +135,7 @@ RUN set -ex; \
     sed -i "s@'configFile' => .*@'configFile' => '/etc/phpmyadmin/config.inc.php',@" /var/www/html/libraries/vendor_config.php; \
     grep -q -F "'configFile' => '/etc/phpmyadmin/config.inc.php'," /var/www/html/libraries/vendor_config.php; \
     php -l /var/www/html/libraries/vendor_config.php; \
+    chown -R www-data:www-data -R /var/www/html/; \
     \
     apt-mark auto '.*' > /dev/null; \
     apt-mark manual $savedAptMark; \
@@ -134,7 +143,8 @@ RUN set -ex; \
     rm -rf /var/lib/apt/lists/*
 
 # Copy configuration
-COPY config.inc.php /etc/phpmyadmin/config.inc.php
+COPY --chown=www-data:www-data config.inc.php /etc/phpmyadmin/config.inc.php
+COPY --chown=www-data:www-data helpers.php /etc/phpmyadmin/helpers.php
 
 # Copy main script
 COPY docker-entrypoint.sh /docker-entrypoint.sh
diff --git a/phpmyadmin_latest/config.inc.php b/phpmyadmin_latest/config.inc.php
index 9a39134..b3a310e 100644
--- a/phpmyadmin_latest/config.inc.php
+++ b/phpmyadmin_latest/config.inc.php
@@ -1,6 +1,7 @@
 <?php
 
-require '/etc/phpmyadmin/config.secret.inc.php';
+require_once '/etc/phpmyadmin/config.secret.inc.php';
+require_once '/etc/phpmyadmin/helpers.php';
 
 /* Ensure we got the environment */
 $vars = [
@@ -27,6 +28,23 @@ $vars = [
     'MEMORY_LIMIT',
     'PMA_UPLOADDIR',
     'PMA_SAVEDIR',
+    'PMA_SSL',
+    'PMA_SSLS',
+    'PMA_SSL_DIR',
+    'PMA_SSL_VERIFY',
+    'PMA_SSL_VERIFIES',
+    'PMA_SSL_CA',
+    'PMA_SSL_CAS',
+    'PMA_SSL_CA_BASE64',
+    'PMA_SSL_CAS_BASE64',
+    'PMA_SSL_KEY',
+    'PMA_SSL_KEYS',
+    'PMA_SSL_KEY_BASE64',
+    'PMA_SSL_KEYS_BASE64',
+    'PMA_SSL_CERT',
+    'PMA_SSL_CERTS',
+    'PMA_SSL_CERT_BASE64',
+    'PMA_SSL_CERTS_BASE64',
 ];
 
 foreach ($vars as $var) {
@@ -35,6 +53,11 @@ foreach ($vars as $var) {
         $_ENV[$var] = $env;
     }
 }
+
+if (! defined('PMA_SSL_DIR')) {
+    define('PMA_SSL_DIR', $_ENV['PMA_SSL_DIR'] ?? '/etc/phpmyadmin/ssl');
+}
+
 if (isset($_ENV['PMA_QUERYHISTORYDB'])) {
     $cfg['QueryHistoryDB'] = (bool) $_ENV['PMA_QUERYHISTORYDB'];
 }
@@ -53,6 +76,35 @@ if (isset($_ENV['PMA_ABSOLUTE_URI'])) {
     $cfg['PmaAbsoluteUri'] = trim($_ENV['PMA_ABSOLUTE_URI']);
 }
 
+if (isset($_ENV['PMA_SSL_CA_BASE64'])) {
+    $_ENV['PMA_SSL_CA'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CA_BASE64'], 'phpmyadmin-ssl-CA', 'pem', PMA_SSL_DIR);
+}
+
+/* Decode and save the SSL key from base64 */
+if (isset($_ENV['PMA_SSL_KEY_BASE64'])) {
+    $_ENV['PMA_SSL_KEY'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_KEY_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save the SSL certificate from base64 */
+if (isset($_ENV['PMA_SSL_CERT_BASE64'])) {
+    $_ENV['PMA_SSL_CERT'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CERT_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL CA certificates from base64 */
+if (isset($_ENV['PMA_SSL_CAS_BASE64'])) {
+    $_ENV['PMA_SSL_CAS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CAS_BASE64'], 'phpmyadmin-ssl-CA', 'pem', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL keys from base64 */
+if (isset($_ENV['PMA_SSL_KEYS_BASE64'])) {
+    $_ENV['PMA_SSL_KEYS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_KEYS_BASE64'], 'phpmyadmin-ssl-CERT', 'cert', PMA_SSL_DIR);
+}
+
+/* Decode and save multiple SSL certificates from base64 */
+if (isset($_ENV['PMA_SSL_CERTS_BASE64'])) {
+    $_ENV['PMA_SSL_CERTS'] = decodeBase64AndSaveFiles($_ENV['PMA_SSL_CERTS_BASE64'], 'phpmyadmin-ssl-KEY', 'key', PMA_SSL_DIR);
+}
+
 /* Figure out hosts */
 
 /* Fallback to default linked */
@@ -63,10 +115,20 @@ if (! empty($_ENV['PMA_HOST'])) {
     $hosts = [$_ENV['PMA_HOST']];
     $verbose = [$_ENV['PMA_VERBOSE']];
     $ports = [$_ENV['PMA_PORT']];
+    $ssls = [$_ENV['PMA_SSL']];
+    $ssl_verifies = [$_ENV['PMA_SSL_VERIFY']];
+    $ssl_cas = [$_ENV['PMA_SSL_CA']];
+    $ssl_keys = [$_ENV['PMA_SSL_KEY']];
+    $ssl_certs = [$_ENV['PMA_SSL_CERT']];
 } elseif (! empty($_ENV['PMA_HOSTS'])) {
     $hosts = array_map('trim', explode(',', $_ENV['PMA_HOSTS']));
     $verbose = array_map('trim', explode(',', $_ENV['PMA_VERBOSES']));
     $ports = array_map('trim', explode(',', $_ENV['PMA_PORTS']));
+    $ssls = array_map('trim', explode(',', $_ENV['PMA_SSLS']));
+    $ssl_verifies = array_map('trim', explode(',', $_ENV['PMA_SSL_VERIFIES']));
+    $ssl_cas = array_map('trim', explode(',', $_ENV['PMA_SSL_CAS']));
+    $ssl_keys = array_map('trim', explode(',', $_ENV['PMA_SSL_KEYS']));
+    $ssl_certs = array_map('trim', explode(',', $_ENV['PMA_SSL_CERTS']));
 }
 
 if (! empty($_ENV['PMA_SOCKET'])) {
@@ -77,6 +139,21 @@ if (! empty($_ENV['PMA_SOCKET'])) {
 
 /* Server settings */
 for ($i = 1; isset($hosts[$i - 1]); $i++) {
+    if (isset($ssls[$i - 1]) && $ssls[$i - 1] === '1') {
+        $cfg['Servers'][$i]['ssl'] = $ssls[$i - 1];
+    }
+    if (isset($ssl_verifies[$i - 1]) && $ssl_verifies[$i - 1] === '1') {
+        $cfg['Servers'][$i]['ssl_verify'] = $ssl_verifies[$i - 1];
+    }
+    if (isset($ssl_cas[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_ca'] = $ssl_cas[$i - 1];
+    }
+    if (isset($ssl_keys[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_key'] = $ssl_keys[$i - 1];
+    }
+    if (isset($ssl_certs[$i - 1])) {
+        $cfg['Servers'][$i]['ssl_cert'] = $ssl_certs[$i - 1];
+    }
     $cfg['Servers'][$i]['host'] = $hosts[$i - 1];
     if (isset($verbose[$i - 1])) {
         $cfg['Servers'][$i]['verbose'] = $verbose[$i - 1];
@@ -128,9 +205,10 @@ for ($i = 1; isset($hosts[$i - 1]); $i++) {
     $cfg['Servers'][$i]['compress'] = false;
     $cfg['Servers'][$i]['AllowNoPassword'] = true;
 }
-for ($i = 1; isset($sockets[$i - 1]); $i++) {
-    $cfg['Servers'][$i]['socket'] = $sockets[$i - 1];
-    $cfg['Servers'][$i]['host'] = 'localhost';
+// Avoid overwriting the last server id $i, use another variable name
+for ($socketHostId = 1; isset($sockets[$socketHostId - 1]); $socketHostId++) {
+    $cfg['Servers'][$socketHostId]['socket'] = $sockets[$socketHostId - 1];
+    $cfg['Servers'][$socketHostId]['host'] = 'localhost';
 }
 /*
  * Revert back to last configured server to make
diff --git a/phpmyadmin_latest/docker-entrypoint.sh b/phpmyadmin_latest/docker-entrypoint.sh
index 5d74854..5c2e85a 100755
--- a/phpmyadmin_latest/docker-entrypoint.sh
+++ b/phpmyadmin_latest/docker-entrypoint.sh
@@ -7,6 +7,7 @@ if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then
 \$cfg['blowfish_secret'] = '$(tr -dc 'a-zA-Z0-9~!@#$%^&*_()+}{?></";.,[]=-' < /dev/urandom | fold -w 32 | head -n 1)';
 EOT
     fi
+    chgrp www-data /etc/phpmyadmin/config.secret.inc.php
 
     if [ ! -f /etc/phpmyadmin/config.user.inc.php ]; then
         touch /etc/phpmyadmin/config.user.inc.php
@@ -58,5 +59,7 @@ get_docker_secret PMA_HOST
 get_docker_secret PMA_CONTROLHOST
 get_docker_secret PMA_CONTROLUSER
 get_docker_secret PMA_CONTROLPASS
+get_docker_secret PMA_SSL
+get_docker_secret PMA_SSLS
 
 exec "$@"
diff --git a/phpmyadmin_latest/helpers.php b/phpmyadmin_latest/helpers.php
new file mode 100644
index 0000000..fd06bcd
--- /dev/null
+++ b/phpmyadmin_latest/helpers.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * Helper function to decode and save multiple SSL files from base64.
+ *
+ * @param string $base64FilesContents The base64 encoded string containing multiple files separated by commas.
+ *                                    If no commas are present, the entire string is treated as a single file.
+ * @param string $prefix              The prefix to use for the generated file names.
+ * @param string $extension           The file extension to use for the generated files.
+ * @param string $storageFolder       The folder where to store the generated files.
+ *
+ * @return string A comma-separated list of paths to the generated files.
+ */
+function decodeBase64AndSaveFiles(string $base64FilesContents, string $prefix, string $extension, string $storageFolder): string
+{
+    // Ensure the output directory exists
+    if (! is_dir($storageFolder)) {
+        mkdir($storageFolder, 0755, true);
+    }
+
+    // Split the base64 string into an array of files
+    $base64FilesContents = explode(',', trim($base64FilesContents));
+    $counter = 1;
+    $outputFiles = [];
+
+    // Process each file
+    foreach ($base64FilesContents as $base64FileContent) {
+        $outputFile = $storageFolder . '/' . $prefix . '-' . $counter . '.' . $extension;
+
+        $fileContent = base64_decode($base64FileContent, true);
+        if ($fileContent === false) {
+            echo 'Failed to decode: ' . $base64FileContent;
+            exit(1);
+        }
+
+        // Write the decoded file to the output directory
+        if (file_put_contents($outputFile, $fileContent) === false) {
+            echo 'Failed to write to ' . $outputFile;
+            exit(1);
+        }
+
+        // Add the output file path to the list
+        $outputFiles[] = $outputFile;
+        $counter++;
+    }
+
+    // Return a comma-separated list of the generated file paths
+    return implode(',', $outputFiles);
+}

Relevant Maintainers:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants