mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 17:44:36 +08:00
remote_process() stored the full shell command as plain text in activity_log and kept it for 60 days. Database starts store the compose file with the database passwords, imports store S3 keys, and the Cloudflare tunnel setup stores its token. - Store the command encrypted. The queue worker decrypts it right before the SSH call and still reads plain commands of tasks queued before the upgrade. - Remove the command when the task is finished: after a successful run, in failed() when no attempt is left, and for a stopped import. A failed attempt that the queue retries keeps it. - Do not write a command preview into the error log. - Migration: remove stored commands from activities that no longer run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
1.9 KiB
PHP
65 lines
1.9 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Spatie\Activitylog\Models\Activity;
|
|
|
|
/**
|
|
* The command of a remote_process() activity.
|
|
*
|
|
* The queue worker reads the command from the activity, so it must be stored until the
|
|
* task is finished. Commands can contain secrets (database passwords, S3 keys, tunnel
|
|
* tokens), so the command is stored encrypted and removed after the final attempt.
|
|
*/
|
|
class RemoteProcessCommand
|
|
{
|
|
public const PROPERTY = 'command';
|
|
|
|
public const ENCRYPTED_PROPERTY = 'command_encrypted';
|
|
|
|
/**
|
|
* @return array{command: string, command_encrypted: true}
|
|
*/
|
|
public static function properties(string $command): array
|
|
{
|
|
return [
|
|
self::PROPERTY => Crypt::encryptString($command),
|
|
self::ENCRYPTED_PROPERTY => true,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* The plain command, or null when it was already removed.
|
|
* Activities queued before encryption was added store the command as plain text.
|
|
*/
|
|
public static function read(Activity $activity): ?string
|
|
{
|
|
$command = $activity->getExtraProperty(self::PROPERTY);
|
|
if (! is_string($command)) {
|
|
return null;
|
|
}
|
|
|
|
return $activity->getExtraProperty(self::ENCRYPTED_PROPERTY) === true
|
|
? Crypt::decryptString($command)
|
|
: $command;
|
|
}
|
|
|
|
/**
|
|
* Remove the command after the final attempt of the task.
|
|
* Reads the stored row first, so a status that Coolify set meanwhile is kept.
|
|
*/
|
|
public static function forget(Activity $activity): void
|
|
{
|
|
$stored = Activity::query()->whereKey($activity->getKey())->first();
|
|
if ($stored === null) {
|
|
return;
|
|
}
|
|
|
|
$stored->properties = $stored->properties->except([self::PROPERTY, self::ENCRYPTED_PROPERTY]);
|
|
$stored->save();
|
|
|
|
$activity->properties = $stored->properties;
|
|
}
|
|
}
|