feat(logging): strengthen secret redaction and add admin controls (#12151)

This commit is contained in:
Andras Bacsai
2026-10-13 20:43:19 +02:00
committed by GitHub
parent 9302395063
commit 448dc8ea0a
92 changed files with 4582 additions and 417 deletions
+2
View File
@@ -203,6 +203,7 @@ function sharedDataApplications(?Application $application = null): array
'inject_build_args_to_dockerfile' => 'boolean',
'include_source_commit_in_build' => 'boolean',
'is_env_sorting_enabled' => 'boolean',
'redact_all_env_values_in_logs' => 'boolean',
'is_pr_deployments_public_enabled' => 'boolean',
'is_gzip_enabled' => 'boolean',
'is_stripprefix_enabled' => 'boolean',
@@ -478,6 +479,7 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
$request->offsetUnset('disable_build_cache');
$request->offsetUnset('inject_build_args_to_dockerfile');
$request->offsetUnset('is_env_sorting_enabled');
$request->offsetUnset('redact_all_env_values_in_logs');
$request->offsetUnset('is_pr_deployments_public_enabled');
$request->offsetUnset('stop_grace_period');
$request->offsetUnset('docker_images_to_keep');
+2 -39
View File
@@ -9,6 +9,7 @@ use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\PrivateKey;
use App\Models\Server;
use App\Services\Security\SensitiveDataRedactor;
use App\Support\RemoteProcessCommand;
use Carbon\Carbon;
use Illuminate\Contracts\Process\ProcessResult;
@@ -371,45 +372,7 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
function remove_iip($text)
{
// Ensure the input is valid UTF-8 before processing
$text = sanitize_utf8_text($text);
// Git access tokens
$text = preg_replace('/x-access-token:.*?(?=@)/', 'x-access-token:'.REDACTED, $text);
$text = preg_replace('/oauth2:.*?(?=@)/', 'oauth2:'.REDACTED, $text);
// ANSI color codes
$text = preg_replace('/\x1b\[[0-9;]*m/', '', $text);
// Generic URLs with passwords (covers database URLs, ftp, amqp, ssh, git basic auth, etc.)
// (protocol://user:password@host → protocol://user:<REDACTED>@host)
$text = preg_replace('/((?:https?|postgres|mysql|mongodb|rediss?|mariadb|ftp|sftp|ssh|amqp|amqps|ldap|ldaps|s3):\/\/[^:]+:)[^@]+(@)/i', '$1'.REDACTED.'$2', $text);
// Email addresses
$text = preg_replace('/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/', REDACTED, $text);
// Bearer/JWT tokens
$text = preg_replace('/Bearer\s+[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+/i', 'Bearer '.REDACTED, $text);
// GitHub tokens (ghp_ = personal, gho_ = OAuth, ghu_ = user-to-server, ghs_ = server-to-server, ghr_ = refresh)
$text = preg_replace('/\bgh[pousr]_[A-Za-z0-9.\-_]{36,}(?![A-Za-z0-9.\-_])/', REDACTED, $text);
// GitLab tokens (glpat- = personal access token, glcbt- = CI build token, glrt- = runner token)
$text = preg_replace('/\b(gl(?:pat|cbt|rt)-[A-Za-z0-9\-_]{20,})\b/', REDACTED, $text);
// AWS credentials (Access Key ID starts with AKIA, ABIA, ACCA, ASIA)
$text = preg_replace('/\b(A(?:KIA|BIA|CCA|SIA)[A-Z0-9]{16})\b/', REDACTED, $text);
// AWS Secret Access Key (40 character base64-ish string, typically follows access key)
$text = preg_replace('/(aws_secret_access_key|AWS_SECRET_ACCESS_KEY)[=:]\s*[\'"]?([A-Za-z0-9\/+=]{40})[\'"]?/i', '$1='.REDACTED, $text);
// API keys (common patterns)
$text = preg_replace('/(api[_-]?key|apikey|api[_-]?secret|secret[_-]?key)[=:]\s*[\'"]?[A-Za-z0-9\-_]{16,}[\'"]?/i', '$1='.REDACTED, $text);
// Private key blocks
$text = preg_replace('/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/', REDACTED, $text);
return $text;
return app(SensitiveDataRedactor::class)->redactText((string) $text);
}
/**
+8 -3
View File
@@ -38,6 +38,7 @@ use App\Models\StandaloneSqlite;
use App\Models\SwarmDocker;
use App\Models\Team;
use App\Models\User;
use App\Services\Security\SensitiveDataRedactor;
use App\Support\DnsRecordHints;
use Carbon\CarbonImmutable;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
@@ -1716,10 +1717,14 @@ function removeAnsiColors($text)
return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text);
}
function sanitizeLogsForExport(string $text): string
/**
* Redact log text before it leaves Coolify (copy, download, API, MCP).
*
* @param array{known?: array<int, string>, locked?: array<int, string>} $secrets Resource values to hide, usually from EnvironmentVariable::logRedactionSecretsFor().
*/
function sanitizeLogsForExport(string $text, array $secrets = []): string
{
// All sanitization is now handled by remove_iip()
return remove_iip($text);
return app(SensitiveDataRedactor::class)->redactText($text, $secrets['known'] ?? [], $secrets['locked'] ?? []);
}
function getTopLevelNetworks(Service|Application $resource): Collection