From 82d4c372945babaa0e76d5bdecb385441087bc75 Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Mon, 5 Jan 2026 22:30:59 +0100 Subject: [PATCH] fix: Handle quoted password hash and improve temp directory setup - Strip surrounding quotes from ADMIN_PASSWORD_HASH (Portainer adds them) - Rewrite temp directory initialization with better fallback logic - Ensure /app/temp directory exists with correct permissions in Docker --- Dockerfile | 4 +- backend/src/services/auth.service.js | 9 ++- backend/src/services/minio.service.js | 81 +++++++++++++++------------ 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2c14e5b..3b62be9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,8 +52,8 @@ RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 # Create necessary directories with proper permissions -RUN mkdir -p logs temp /tmp/minio-webui-temp /run/nginx && \ - chown -R nodejs:nodejs /app logs temp /tmp/minio-webui-temp && \ +RUN mkdir -p /app/logs /app/temp /tmp/minio-webui-temp /run/nginx && \ + chown -R nodejs:nodejs /app /tmp/minio-webui-temp && \ chown -R nodejs:nodejs /var/lib/nginx /var/log/nginx /run/nginx # Create startup script diff --git a/backend/src/services/auth.service.js b/backend/src/services/auth.service.js index 630c110..fce8583 100644 --- a/backend/src/services/auth.service.js +++ b/backend/src/services/auth.service.js @@ -26,7 +26,12 @@ class AuthService { async verifyPassword(password) { try { - const hash = config.auth.adminPasswordHash; + let hash = config.auth.adminPasswordHash; + + // Clean up hash - remove surrounding quotes that Portainer/Docker may add + if (hash) { + hash = hash.trim().replace(/^["']|["']$/g, ''); + } // Debug logging for troubleshooting logger.debug('Password verification attempt', { @@ -46,7 +51,7 @@ class AuthService { if (!hash.match(/^\$2[aby]\$\d{2}\$/)) { logger.error('Invalid bcrypt hash format - hash may be corrupted by environment variable interpolation', { hashPrefix: hash.substring(0, 20), - expectedFormat: '$2b$12$...' + expectedFormat: '$2b$12$... or $2y$10$...' }); return false; } diff --git a/backend/src/services/minio.service.js b/backend/src/services/minio.service.js index 55cc471..f106337 100644 --- a/backend/src/services/minio.service.js +++ b/backend/src/services/minio.service.js @@ -14,46 +14,53 @@ const execAsync = util.promisify(exec); class MinIOService { constructor(alias = config.minio.defaultAlias) { this.alias = alias; - // Use Docker-prepared temp directory first, then fallback options - this.tempDir = process.env.TEMP_DIR || '/tmp/minio-webui-temp'; - this.ensureTempDir(); + // Initialize temp directory asynchronously + this.tempDir = null; + this.tempDirReady = this.initTempDir(); + } + + async initTempDir() { + // Try directories in order of preference + const candidates = [ + process.env.TEMP_DIR, + '/app/temp', + path.join(process.cwd(), 'temp'), + '/tmp/minio-webui-temp', + path.join(os.tmpdir(), 'minio-webui-temp') + ].filter(Boolean); + + for (const dir of candidates) { + if (await this.tryTempDir(dir)) { + this.tempDir = dir; + logger.info(`Using temp directory: ${this.tempDir}`); + this.cleanupTempFiles(); + return; + } + } + + // Last resort - use current directory + this.tempDir = path.join(process.cwd(), '.temp'); + await fs.mkdir(this.tempDir, { recursive: true }).catch(() => {}); + logger.warn(`Fallback to temp directory: ${this.tempDir}`); + } + + async tryTempDir(dir) { + try { + await fs.mkdir(dir, { recursive: true, mode: 0o755 }); + const testFile = path.join(dir, `.write-test-${Date.now()}-${Math.random()}`); + await fs.writeFile(testFile, 'test', { mode: 0o644 }); + await fs.unlink(testFile); + return true; + } catch (error) { + logger.debug(`Temp directory ${dir} not usable: ${error.message}`); + return false; + } } async ensureTempDir() { - try { - await fs.mkdir(this.tempDir, { recursive: true, mode: 0o755 }); - // Test write permissions - const testFile = path.join(this.tempDir, `.write-test-${Date.now()}`); - await fs.writeFile(testFile, 'test'); - await fs.unlink(testFile); - logger.info(`Using temp directory: ${this.tempDir}`); - // Clean up old temp files (older than 1 hour) - this.cleanupTempFiles(); - } catch (error) { - logger.error(`Failed to create or write to temp directory ${this.tempDir}:`, error); - // Try alternative local temp directory - const alternativeTempDir = path.join(process.cwd(), 'temp'); - try { - await fs.mkdir(alternativeTempDir, { recursive: true, mode: 0o755 }); - const testFile = path.join(alternativeTempDir, `.write-test-${Date.now()}`); - await fs.writeFile(testFile, 'test'); - await fs.unlink(testFile); - this.tempDir = alternativeTempDir; - logger.info(`Using alternative temp directory: ${this.tempDir}`); - } catch (altError) { - logger.error('Failed to use alternative temp directory:', altError); - // Last resort: use OS temp directory - this.tempDir = path.join(os.tmpdir(), 'minio-webui-temp'); - try { - await fs.mkdir(this.tempDir, { recursive: true, mode: 0o755 }); - logger.warn(`Using OS temp directory (may have permission issues): ${this.tempDir}`); - } catch (fallbackError) { - logger.error('Failed to create OS temp directory:', fallbackError); - // Use current directory as last resort - this.tempDir = path.join(process.cwd(), '.temp'); - logger.error(`Using current directory for temp files: ${this.tempDir}`); - } - } + // Wait for initialization if not complete + if (!this.tempDir) { + await this.tempDirReady; } }