Skip to content

Commit 2b44d94

Browse files
committed
feat: #17 support secrets encryption
1 parent 7f2a705 commit 2b44d94

30 files changed

+727
-301
lines changed

Diff for: api-nodes/Http/Controllers/EventController.php

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function started(Node $node, AgentStartedEventData $data)
2424

2525
$swarm->data->joinTokens = $data->swarm->joinTokens;
2626
$swarm->data->managerNodes = $data->swarm->managerNodes;
27+
$swarm->data->encryptionKey = $data->swarm->encryptionKey;
2728

2829
$nodeAddresses = $swarm->nodes->pluck('data.address')->toArray();
2930
$dockerServices = collect($swarm->services)

Diff for: api-nodes/Models/AgentStartedEventData/SwarmData.php

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ public function __construct(
1414
#[DataCollectionOf(ManagerNode::class)]
1515
/* @var ManagerNode[] */
1616
public array $managerNodes,
17+
public string $encryptionKey,
1718
) {}
1819
}

Diff for: app/Actions/Nodes/InitCluster.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ private function createSwarm(int $teamId): Swarm
8282
'manager' => '-',
8383
],
8484
'managerNodes' => [],
85+
'encryptionKey' => '-',
8586
]),
8687
]);
8788
}
@@ -217,9 +218,7 @@ private function getCaddyProcessConfig(Node $node): array
217218
'value' => '0.0.0.0:2019',
218219
],
219220
],
220-
'secretVars' => [
221-
'vars' => [],
222-
],
221+
'secretVars' => [],
223222
'configFiles' => [
224223
[
225224
'path' => '/ptah/caddy/tls/.keep',

Diff for: app/Console/Commands/SelfHostPtah.php

+3-9
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,7 @@ public function handle()
135135
'value' => 'ptah_sh',
136136
],
137137
],
138-
'secretVars' => [
139-
'vars' => [],
140-
],
138+
'secretVars' => [],
141139
'configFiles' => [],
142140
'secretFiles' => [],
143141
'volumes' => [
@@ -203,9 +201,7 @@ public function handle()
203201
'value' => 'ptah_sh',
204202
],
205203
],
206-
'secretVars' => [
207-
'vars' => [],
208-
],
204+
'secretVars' => [],
209205
'configFiles' => [],
210206
'secretFiles' => [],
211207
'volumes' => [],
@@ -292,9 +288,7 @@ public function handle()
292288
'value' => 'false',
293289
],
294290
],
295-
'secretVars' => [
296-
'vars' => [],
297-
],
291+
'secretVars' => [],
298292
'configFiles' => [],
299293
'secretFiles' => [],
300294
'volumes' => [],

Diff for: app/Http/Controllers/NodeController.php

+6
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ public function show(Node $node)
7979

8080
$node->load('swarm');
8181

82+
$s3TaskGroup = $node->actualTaskGroup(NodeTaskGroupType::UpdateS3Storages);
83+
if ($s3TaskGroup?->is_completed) {
84+
$s3TaskGroup = null;
85+
}
86+
8287
$registryTaskGroup = $node->actualTaskGroup(NodeTaskGroupType::UpdateDockerRegistries);
8388
if ($registryTaskGroup?->is_completed) {
8489
$registryTaskGroup = null;
@@ -91,6 +96,7 @@ public function show(Node $node)
9196
'lastAgentVersion' => $lastAgentVersion,
9297
'agentUpgradeTaskGroup' => $taskGroup?->is_completed ? null : $taskGroup,
9398
'registryUpdateTaskGroup' => $registryTaskGroup?->is_completed ? null : $registryTaskGroup,
99+
's3SUpdateTaskGroup' => $s3TaskGroup?->is_completed ? null : $s3TaskGroup,
94100
]);
95101
}
96102

Diff for: app/Http/Controllers/ServiceController.php

+7
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ public function create()
5959
'dockerRegistries' => $dockerRegistries,
6060
's3Storages' => $s3Storages,
6161
'marketplaceUrl' => config('ptah.marketplace_url'),
62+
'node' => [
63+
'swarm' => $swarm,
64+
],
6265
]);
6366
}
6467

@@ -73,13 +76,17 @@ public function show(Service $service)
7376
$nodes = $service->swarm->nodes;
7477
$dockerRegistries = $service->swarm->data->registries;
7578
$s3Storages = $service->swarm->data->s3Storages;
79+
$swarm = $service->swarm;
7680

7781
return Inertia::render('Services/Show', [
7882
'service' => $service,
7983
'networks' => $networks,
8084
'nodes' => $nodes,
8185
'dockerRegistries' => $dockerRegistries,
8286
's3Storages' => $s3Storages,
87+
'node' => [
88+
'swarm' => $swarm,
89+
],
8390
]);
8491
}
8592

Diff for: app/Http/Controllers/SwarmController.php

+8-11
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,11 @@ public function updateDockerRegistries(Swarm $swarm, Request $request)
8989
$tasks = [];
9090

9191
foreach ($swarmData->registries as $registry) {
92-
$previous = $registry->dockerName ? $swarm->data->findRegistry($registry->dockerName) : null;
93-
if ($previous) {
94-
if ($registry->sameAs($previous)) {
95-
$registry->dockerName = $previous->dockerName;
92+
$previous = $swarm->data->findRegistry($registry->id);
93+
if ($registry->sameAs($previous)) {
94+
$registry->password = $previous->password;
9695

97-
continue;
98-
}
96+
continue;
9997
}
10098

10199
$registry->dockerName = dockerize_name('registry_r'.$swarmData->registriesRev.'_'.$registry->name);
@@ -108,7 +106,6 @@ public function updateDockerRegistries(Swarm $swarm, Request $request)
108106
'type' => NodeTaskType::CreateRegistryAuth,
109107
'meta' => CreateRegistryAuthMeta::validateAndCreate($taskMeta),
110108
'payload' => [
111-
'PrevConfigName' => $previous?->dockerName,
112109
'AuthConfigSpec' => [
113110
'ServerAddress' => $registry->serverAddress,
114111
'Username' => $registry->username,
@@ -163,10 +160,10 @@ public function updateS3Storages(Swarm $swarm, Request $request)
163160

164161
foreach ($swarmData->s3Storages as $s3Storage) {
165162
$previous = $swarm->data->findS3Storage($s3Storage->id);
166-
if ($previous) {
167-
if ($s3Storage->sameAs($previous)) {
168-
continue;
169-
}
163+
if ($s3Storage->sameAs($previous)) {
164+
$s3Storage->secretKey = $previous->secretKey;
165+
166+
continue;
170167
}
171168

172169
$s3Storage->dockerName = dockerize_name('s3_r'.$swarmData->s3StoragesRev.'_'.$s3Storage->name);

Diff for: app/Models/DeploymentData.php

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use App\Models\DeploymentData\LaunchMode;
77
use App\Models\DeploymentData\Process;
88
use App\Models\DeploymentData\ReleaseCommand;
9-
use App\Models\DeploymentData\SecretVars;
109
use App\Rules\UniqueInArray;
1110
use App\Util\Arrays;
1211
use Illuminate\Validation\ValidationException;
@@ -43,9 +42,7 @@ public static function make(array $attributes): static
4342
'workers' => [],
4443
'launchMode' => LaunchMode::Daemon->value,
4544
'envVars' => [],
46-
'secretVars' => SecretVars::from([
47-
'vars' => [],
48-
]),
45+
'secretVars' => [],
4946
'configFiles' => [],
5047
'secretFiles' => [],
5148
'volumes' => [],

Diff for: app/Models/DeploymentData/ConfigFile.php

+6-8
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,23 @@ class ConfigFile extends Data
88
{
99
public function __construct(
1010
public string $path,
11-
public ?string $content,
11+
public string $content,
1212
public ?string $dockerName,
1313
) {
1414
$this->content = $content ?? '';
1515
}
1616

17-
public function hash(): string
18-
{
19-
// TODO: use cache?
20-
return md5($this->content);
21-
}
22-
2317
public function base64(): string
2418
{
2519
return base64_encode($this->content);
2620
}
2721

2822
public function sameAs(?ConfigFile $older): bool
2923
{
30-
return $older !== null && $this->hash() === $older->hash();
24+
if ($older === null) {
25+
return false;
26+
}
27+
28+
return $this->content === $older->content;
3129
}
3230
}

Diff for: app/Models/DeploymentData/Process.php

+28-20
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ public function __construct(
4444
#[DataCollectionOf(EnvVar::class)]
4545
/* @var EnvVar[] */
4646
public array $envVars,
47-
public SecretVars $secretVars,
47+
#[DataCollectionOf(SecretVar::class)]
48+
/* @var SecretVar[] */
49+
public array $secretVars,
4850
#[DataCollectionOf(ConfigFile::class)]
4951
/* @var ConfigFile[] */
5052
public array $configFiles,
51-
#[DataCollectionOf(ConfigFile::class)]
52-
/* @var ConfigFile[] */
53+
#[DataCollectionOf(SecretFile::class)]
54+
/* @var SecretFile[] */
5355
public array $secretFiles,
5456
#[DataCollectionOf(Volume::class)]
5557
/* @var Volume[] */
@@ -87,9 +89,14 @@ public function findConfigFile(string $path): ?ConfigFile
8789
return collect($this->configFiles)->first(fn (ConfigFile $file) => $file->path === $path);
8890
}
8991

90-
public function findSecretFile(string $path): ?ConfigFile
92+
public function findSecretFile(string $path): ?SecretFile
93+
{
94+
return collect($this->secretFiles)->first(fn (SecretFile $file) => $file->path === $path);
95+
}
96+
97+
public function findSecretVar(string $name): ?SecretVar
9198
{
92-
return collect($this->secretFiles)->first(fn (ConfigFile $file) => $file->path === $path);
99+
return collect($this->secretVars)->first(fn (SecretVar $var) => $var->name === $name);
93100
}
94101

95102
/**
@@ -142,7 +149,6 @@ public function asNodeTasks(Deployment $deployment): array
142149
'deploymentId' => $deployment->id,
143150
'processName' => $this->dockerName,
144151
'path' => $configFile->path,
145-
'hash' => $configFile->hash(),
146152
]),
147153
'payload' => [
148154
'SwarmConfigSpec' => [
@@ -151,7 +157,6 @@ public function asNodeTasks(Deployment $deployment): array
151157
'Labels' => dockerize_labels([
152158
...$labels,
153159
'kind' => 'config',
154-
'content.hash' => $configFile->hash(),
155160
]),
156161
],
157162
],
@@ -160,29 +165,28 @@ public function asNodeTasks(Deployment $deployment): array
160165

161166
foreach ($this->secretFiles as $secretFile) {
162167
$previousSecret = $previous?->findSecretFile($secretFile->path);
163-
if ($previousSecret && ($secretFile->content === null || $secretFile->sameAs($previousSecret))) {
164-
$secretFile->dockerName = $previousSecret->dockerName;
168+
if ($secretFile->sameAs($previousSecret)) {
169+
$secretFile->content = $previousSecret->content;
165170

166171
continue;
167172
}
168173

169-
$secretFile->dockerName = $this->makeResourceName('dpl_'.$deployment->id.'_cfg_'.$secretFile->path);
174+
$secretFile->dockerName = $this->makeResourceName('dpl_'.$deployment->id.'_secret_'.$secretFile->path);
170175

171176
$tasks[] = [
172177
'type' => NodeTaskType::CreateSecret,
173178
'meta' => CreateSecretMeta::from([
174179
'deploymentId' => $deployment->id,
175180
'processName' => $this->dockerName,
176181
'path' => $secretFile->path,
177-
'hash' => $secretFile->hash(),
178182
]),
179183
'payload' => [
180184
'SwarmSecretSpec' => [
181185
'Name' => $secretFile->dockerName,
182186
'Data' => $secretFile->base64(),
183187
'Labels' => dockerize_labels([
184188
...$labels,
185-
'content.hash' => $secretFile->hash(),
189+
'kind' => 'secret',
186190
]),
187191
],
188192
],
@@ -289,7 +293,7 @@ public function asNodeTasks(Deployment $deployment): array
289293
'value' => $internalDomain,
290294
]);
291295

292-
$serviceSecretVars = $this->getSecretVars();
296+
$serviceSecretVars = $this->getSecretVars($previous);
293297

294298
$tasks[] = [
295299
'type' => NodeTaskType::LaunchService,
@@ -313,7 +317,7 @@ public function asNodeTasks(Deployment $deployment): array
313317
'Hosts' => [
314318
$internalDomain,
315319
],
316-
'Secrets' => collect($this->secretFiles)->map(fn (ConfigFile $secretFile) => [
320+
'Secrets' => collect($this->secretFiles)->map(fn (SecretFile $secretFile) => [
317321
'File' => [
318322
'Name' => $secretFile->path,
319323
// TODO: figure out better permissions settings (if any)
@@ -404,7 +408,7 @@ public function asNodeTasks(Deployment $deployment): array
404408
'Hosts' => [
405409
"{$worker->name}.{$internalDomain}",
406410
],
407-
'Secrets' => collect($this->secretFiles)->map(fn (ConfigFile $secretFile) => [
411+
'Secrets' => collect($this->secretFiles)->map(fn (SecretFile $secretFile) => [
408412
'File' => [
409413
'Name' => $secretFile->path,
410414
// TODO: figure out better permissions settings (if any)
@@ -454,12 +458,16 @@ public function asNodeTasks(Deployment $deployment): array
454458
return $tasks;
455459
}
456460

457-
protected function getSecretVars(): array
461+
protected function getSecretVars(?Process $previous): object
458462
{
459-
return [
460-
'Values' => (object) collect($this->secretVars->vars)
461-
->reduce(fn ($carry, EnvVar $var) => [...$carry, $var->name => $var->value ?? ''], []),
462-
];
463+
return (object) collect($this->secretVars)
464+
->reduce(function ($carry, SecretVar $var) use ($previous) {
465+
$prevVar = $previous?->findSecretVar($var->name);
466+
467+
$carry[$var->name] = ($var->sameAs($prevVar) ? $prevVar->value : $var->value) ?? '';
468+
469+
return $carry;
470+
}, []);
463471
}
464472

465473
public function makeResourceName(string $name): string

Diff for: app/Models/DeploymentData/SecretFile.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace App\Models\DeploymentData;
4+
5+
use Spatie\LaravelData\Attributes\Validation\RequiredWithout;
6+
use Spatie\LaravelData\Data;
7+
8+
class SecretFile extends Data
9+
{
10+
public function __construct(
11+
public string $path,
12+
#[RequiredWithout('dockerName')]
13+
public ?string $content,
14+
public ?string $dockerName,
15+
) {}
16+
17+
public function base64(): string
18+
{
19+
return base64_encode($this->content);
20+
}
21+
22+
public function sameAs(?SecretFile $older): bool
23+
{
24+
if ($older === null) {
25+
return false;
26+
}
27+
28+
return $this->path === $older->path && is_null($this->content);
29+
}
30+
}

Diff for: app/Models/DeploymentData/SecretVar.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace App\Models\DeploymentData;
4+
5+
use Spatie\LaravelData\Data;
6+
7+
class SecretVar extends Data
8+
{
9+
public function __construct(
10+
public string $name,
11+
public ?string $value
12+
) {}
13+
14+
public function sameAs(?SecretVar $other): bool
15+
{
16+
if ($other === null) {
17+
return false;
18+
}
19+
20+
return $this->name === $other->name && is_null($this->value);
21+
}
22+
}

0 commit comments

Comments
 (0)