Skip to content

Commit

Permalink
chore: encrypt with casts + masking and interpolation + fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
David Buday committed Nov 9, 2024
1 parent 02d29d9 commit ebf2072
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 21 deletions.
22 changes: 12 additions & 10 deletions app/Jobs/ApplicationDeploymentJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -2477,18 +2477,20 @@ public function failed(Throwable $exception): void

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,
]
);
}
}
5 changes: 3 additions & 2 deletions app/Livewire/Project/Application/General.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -154,6 +154,7 @@ public function mount()
$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;
Expand Down
8 changes: 4 additions & 4 deletions app/Livewire/Project/New/DockerImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'
];
Expand All @@ -38,8 +38,8 @@ public function submit()
{
$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
Expand Down
4 changes: 4 additions & 0 deletions app/Models/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ class Application extends BaseModel

protected $guarded = [];

protected $casts = [
'docker_registry_token' => 'encrypted',
];

protected $appends = ['server_status'];

protected static function booted()
Expand Down
39 changes: 36 additions & 3 deletions app/Traits/ExecuteRemoteCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public function execute_remote_command(...$commands)
$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');
Expand All @@ -44,10 +48,14 @@ public function execute_remote_command(...$commands)
}
}
$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),
Expand Down Expand Up @@ -93,4 +101,29 @@ public function execute_remote_command(...$commands)
}
});
}

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;
}
}
4 changes: 2 additions & 2 deletions resources/views/livewire/project/new/docker-image.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ebf2072

Please sign in to comment.