fix: single-quote passwords in .env to handle special characters

Passwords with $, &, ;, [, etc. were written unquoted to .env and
cameleer.conf, causing Docker Compose to mangle them. Now all password
and secret fields are written as KEY='value' with embedded single
quotes escaped as '\''.

Also removes inline DB_URL from docker-compose.saas.yml — the Logto
entrypoint now builds it from PG_USER/PG_PASSWORD/PG_HOST using
node's encodeURIComponent for URL-safe encoding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-25 16:33:32 +02:00
parent a9aee77077
commit 4380aa790d
3 changed files with 54 additions and 32 deletions

View File

@@ -255,7 +255,14 @@ function Write-Utf8File {
$enc = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($Path, $Content, $enc)
}
# Single-quote a value for .env/.conf files so special characters ($, &, ;, etc.)
# are preserved. Embedded single quotes are escaped as '\''.
function Format-EnvVal([string]$Key, [string]$Val) {
$escaped = $Val -replace "'", "'\\''"
return "$Key='$escaped'"
}
# --- Config file ---
function Load-ConfigFile {
@@ -266,6 +273,12 @@ function Load-ConfigFile {
if ($line -match '^\s*([^=]+?)\s*=\s*(.*?)\s*$') {
$key = $Matches[1].Trim()
$val = $Matches[2].Trim()
# Strip surrounding quotes and unescape '\''
if ($val.Length -ge 2 -and $val[0] -eq "'" -and $val[-1] -eq "'") {
$val = $val.Substring(1, $val.Length - 2) -replace "'\\''" , "'"
} elseif ($val.Length -ge 2 -and $val[0] -eq '"' -and $val[-1] -eq '"') {
$val = $val.Substring(1, $val.Length - 2)
}
switch ($key) {
'install_dir' { if (-not $script:cfg.InstallDir) { $script:cfg.InstallDir = $val } }
'public_host' { if (-not $script:cfg.PublicHost) { $script:cfg.PublicHost = $val } }
@@ -669,15 +682,10 @@ HTTPS_PORT=$($c.HttpsPort)
# PostgreSQL
POSTGRES_USER=cameleer
POSTGRES_PASSWORD=$($c.PostgresPassword)
POSTGRES_DB=cameleer
# ClickHouse
CLICKHOUSE_PASSWORD=$($c.ClickhousePassword)
# Server admin
SERVER_ADMIN_USER=$($c.AdminUser)
SERVER_ADMIN_PASS=$($c.AdminPass)
# Bootstrap token
BOOTSTRAP_TOKEN=$bt
@@ -697,6 +705,10 @@ CLICKHOUSE_IMAGE=$($c.Registry)/cameleer-clickhouse
SERVER_IMAGE=$($c.Registry)/cameleer-server
SERVER_UI_IMAGE=$($c.Registry)/cameleer-server-ui
"@
# Passwords appended with single-quoting for special character safety
$content += "`n$(Format-EnvVal 'POSTGRES_PASSWORD' $c.PostgresPassword)"
$content += "`n$(Format-EnvVal 'CLICKHOUSE_PASSWORD' $c.ClickhousePassword)"
$content += "`n$(Format-EnvVal 'SERVER_ADMIN_PASS' $c.AdminPass)"
if ($c.TlsMode -eq 'custom') {
$content += "`nCERT_FILE=/user-certs/cert.pem"
$content += "`nKEY_FILE=/user-certs/key.pem"
@@ -728,16 +740,11 @@ LOGTO_CONSOLE_BIND=$consoleBind
# PostgreSQL
POSTGRES_USER=cameleer
POSTGRES_PASSWORD=$($c.PostgresPassword)
POSTGRES_DB=cameleer_saas
# ClickHouse
CLICKHOUSE_PASSWORD=$($c.ClickhousePassword)
# Admin user
SAAS_ADMIN_USER=$($c.AdminUser)
SAAS_ADMIN_PASS=$($c.AdminPass)
# TLS
NODE_TLS_REJECT=$($c.NodeTlsReject)
"@
@@ -772,10 +779,14 @@ CAMELEER_SERVER_SECURITY_JWTSECRET=$jwtSecret
SMTP_HOST=$($c.SmtpHost)
SMTP_PORT=$(if ($c.SmtpPort) { $c.SmtpPort } else { '587' })
SMTP_USER=$($c.SmtpUser)
SMTP_PASS=$($c.SmtpPass)
SMTP_FROM_EMAIL=$(if ($c.SmtpFromEmail) { $c.SmtpFromEmail } else { "noreply@$($c.PublicHost)" })
"@
$content += $provisioningBlock
# Passwords appended with single-quoting for special character safety
$content += "`n$(Format-EnvVal 'POSTGRES_PASSWORD' $c.PostgresPassword)"
$content += "`n$(Format-EnvVal 'CLICKHOUSE_PASSWORD' $c.ClickhousePassword)"
$content += "`n$(Format-EnvVal 'SAAS_ADMIN_PASS' $c.AdminPass)"
$content += "`n$(Format-EnvVal 'SMTP_PASS' $c.SmtpPass)"
$composeFile = 'docker-compose.yml;docker-compose.saas.yml'
if ($c.TlsMode -eq 'custom') { $composeFile += ';docker-compose.tls.yml' }
if ($c.MonitoringNetwork) { $composeFile += ';docker-compose.monitoring.yml' }
@@ -1017,12 +1028,13 @@ deployment_mode=$($c.DeploymentMode)
smtp_host=$($c.SmtpHost)
smtp_port=$($c.SmtpPort)
smtp_user=$($c.SmtpUser)
smtp_pass=$($c.SmtpPass)
smtp_from_email=$($c.SmtpFromEmail)
registry=$($c.Registry)
registry_user=$($c.RegistryUser)
registry_token=$($c.RegistryToken)
"@
# Passwords appended with single-quoting for special character safety
$txt += "`n$(Format-EnvVal 'smtp_pass' $c.SmtpPass)"
$txt += "`n$(Format-EnvVal 'registry_token' $c.RegistryToken)"
Write-Utf8File $f $txt
Log-Info 'Saved installer config to cameleer.conf'
}