mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
chore: encrypt with casts + masking and interpolation + fixes
This commit is contained in:
parent
02d29d93b4
commit
ebf207296b
6 changed files with 61 additions and 21 deletions
|
|
@ -2477,18 +2477,20 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||
|
||||
private function handleRegistryAuth()
|
||||
{
|
||||
$username = escapeshellarg($this->application->docker_registry_username);
|
||||
$username = $this->application->docker_registry_username;
|
||||
$registry = $this->application->docker_registry_url ?: 'docker.io';
|
||||
$token = escapeshellarg($this->application->docker_registry_token);
|
||||
|
||||
$registry = escapeshellarg($this->application->docker_registry_url ?: 'docker.io'); // Default to docker.io
|
||||
|
||||
$this->application_deployment_queue->addLogEntry('Attempting to log into registry...');
|
||||
$command = "echo {{secrets.token}} | docker login {$registry} -u {$username} --password-stdin";
|
||||
|
||||
$command = "echo {$token} | docker login {$registry} -u {$username} --password-stdin";
|
||||
|
||||
$this->execute_remote_command([
|
||||
$command,
|
||||
'hidden' => true,
|
||||
]);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
'command' => $command,
|
||||
'secrets' => [
|
||||
'token' => $token,
|
||||
],
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ class General extends Component
|
|||
'application.docker_registry_image_tag' => 'nullable',
|
||||
'application.docker_use_custom_registry' => 'boolean',
|
||||
'application.docker_registry_url' => 'nullable',
|
||||
'application.docker_registry_username' => 'required_with:application.docker_use_custom_registry',
|
||||
'application.docker_registry_token' => 'required_with:application.docker_use_custom_registry',
|
||||
'application.docker_registry_username' => 'nullable|required_if:application.docker_use_custom_registry,true',
|
||||
'application.docker_registry_token' => 'nullable|required_if:application.docker_use_custom_registry,true',
|
||||
'application.dockerfile_location' => 'nullable',
|
||||
'application.docker_compose_location' => 'nullable',
|
||||
'application.docker_compose' => 'nullable',
|
||||
|
|
@ -154,6 +154,7 @@ class General extends Component
|
|||
$this->application->fqdn = null;
|
||||
$this->application->settings->save();
|
||||
}
|
||||
|
||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->is_preserve_repository_enabled = $this->application->settings->is_preserve_repository_enabled;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ class DockerImage extends Component
|
|||
|
||||
protected $rules = [
|
||||
'dockerImage' => 'required|string',
|
||||
'registryUsername' => 'required_with:useCustomRegistry|string',
|
||||
'registryToken' => 'required_with:useCustomRegistry|string',
|
||||
'registryUsername' => 'required_if:useCustomRegistry,true|string|nullable',
|
||||
'registryToken' => 'required_if:useCustomRegistry,true|string|nullable',
|
||||
'registryUrl' => 'nullable|string',
|
||||
'useCustomRegistry' => 'boolean'
|
||||
];
|
||||
|
|
@ -38,8 +38,8 @@ class DockerImage extends Component
|
|||
{
|
||||
$this->validate([
|
||||
'dockerImage' => 'required',
|
||||
'registryUsername' => 'required_with:useCustomRegistry',
|
||||
'registryToken' => 'required_with:useCustomRegistry',
|
||||
'registryUsername' => 'required_if:useCustomRegistry,true',
|
||||
'registryToken' => 'required_if:useCustomRegistry,true',
|
||||
]);
|
||||
|
||||
// Only save registry settings if useCustomRegistry is true
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ class Application extends BaseModel
|
|||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'docker_registry_token' => 'encrypted',
|
||||
];
|
||||
|
||||
protected $appends = ['server_status'];
|
||||
|
||||
protected static function booted()
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ trait ExecuteRemoteCommand
|
|||
$ignore_errors = data_get($single_command, 'ignore_errors', false);
|
||||
$append = data_get($single_command, 'append', true);
|
||||
$this->save = data_get($single_command, 'save');
|
||||
$secrets = data_get($single_command, 'secrets', []); // Secrets for interpolation and masking
|
||||
if (count($secrets) > 0) {
|
||||
$command = $this->interpolateCommand($command, $secrets);
|
||||
}
|
||||
if ($this->server->isNonRoot()) {
|
||||
if (str($command)->startsWith('docker exec')) {
|
||||
$command = str($command)->replace('docker exec', 'sudo docker exec');
|
||||
|
|
@ -44,10 +48,14 @@ trait ExecuteRemoteCommand
|
|||
}
|
||||
}
|
||||
$remote_command = SshMultiplexingHelper::generateSshCommand($this->server, $command);
|
||||
$process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden, $customType, $append) {
|
||||
$process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $secrets, $hidden, $customType, $append) {
|
||||
$output = str($output)->trim();
|
||||
if ($output->startsWith('╔')) {
|
||||
$output = "\n".$output;
|
||||
if (count($secrets) > 0) {
|
||||
$output = $this->maskSecrets($output, $secrets);
|
||||
$command = $this->maskSecrets($command, $secrets);
|
||||
}
|
||||
if (str($output)->startsWith('╔')) {
|
||||
$output = "\n" . $output;
|
||||
}
|
||||
$new_log_entry = [
|
||||
'command' => remove_iip($command),
|
||||
|
|
@ -93,4 +101,29 @@ trait ExecuteRemoteCommand
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function interpolateCommand(string $command, array $secrets): string
|
||||
{
|
||||
foreach ($secrets as $key => $value) {
|
||||
// Define the placeholder format
|
||||
$placeholder = "{{secrets.$key}}";
|
||||
// Replace placeholder with actual value
|
||||
$command = str_replace($placeholder, $value, $command);
|
||||
}
|
||||
return $command;
|
||||
}
|
||||
|
||||
private function maskSecrets(string $text, array $secrets): string
|
||||
{
|
||||
// Sort secrets by length descending to prevent partial masking
|
||||
usort($secrets, function ($a, $b) {
|
||||
return strlen($b) - strlen($a);
|
||||
});
|
||||
|
||||
foreach ($secrets as $value) {
|
||||
// Replace each secret value with '*****'
|
||||
$text = str_replace($value, '*****', $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@
|
|||
helper="Leave empty for Docker Hub" />
|
||||
|
||||
<x-forms.input id="registryUsername" label="Registry Username"
|
||||
required="required_with:useCustomRegistry" placeholder="Username for private registry"
|
||||
required="required_if:useCustomRegistry,true" placeholder="Username for private registry"
|
||||
helper="Leave empty for public images or server credentials" />
|
||||
|
||||
<x-forms.input type="password" id="registryToken" label="Registry Token/Password"
|
||||
required="required_with:useCustomRegistry" placeholder="Token or password for private registry"
|
||||
required="required_if:useCustomRegistry,true" placeholder="Token or password for private registry"
|
||||
helper="Leave empty for public images or server credentials" />
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue