79 lines
2.5 KiB
PHP
79 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Applications;
|
|
|
|
use App\Models\Application;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Process;
|
|
use RuntimeException;
|
|
use Throwable;
|
|
|
|
class GenerateDeployKey
|
|
{
|
|
/**
|
|
* @param array{public: string, private: string, fingerprint?: string}|null $keyPair
|
|
*/
|
|
public function execute(Application $application, ?array $keyPair = null): Application
|
|
{
|
|
$keyPair ??= $this->generateWithSshKeygen($application);
|
|
|
|
$application->forceFill([
|
|
'deploy_key_public' => $keyPair['public'],
|
|
'deploy_key_private' => $keyPair['private'],
|
|
'deploy_key_fingerprint' => $keyPair['fingerprint'] ?? $this->fingerprint($keyPair['public']),
|
|
'deploy_key_installed_at' => null,
|
|
])->save();
|
|
|
|
return $application->refresh();
|
|
}
|
|
|
|
/**
|
|
* @return array{public: string, private: string, fingerprint: string}
|
|
*/
|
|
private function generateWithSshKeygen(Application $application): array
|
|
{
|
|
$directory = storage_path('app/private/deploy-keys/'.str()->uuid()->toString());
|
|
$privateKeyPath = $directory.'/id_ed25519';
|
|
|
|
File::ensureDirectoryExists($directory, 0700);
|
|
|
|
try {
|
|
$result = Process::run([
|
|
'ssh-keygen',
|
|
'-t',
|
|
'ed25519',
|
|
'-C',
|
|
"keystone-application-{$application->id}",
|
|
'-N',
|
|
'',
|
|
'-f',
|
|
$privateKeyPath,
|
|
]);
|
|
|
|
if ($result->failed()) {
|
|
throw new RuntimeException('Unable to generate deploy key: '.$result->errorOutput());
|
|
}
|
|
|
|
return [
|
|
'public' => trim(File::get($privateKeyPath.'.pub')),
|
|
'private' => trim(File::get($privateKeyPath)),
|
|
'fingerprint' => $this->fingerprint(trim(File::get($privateKeyPath.'.pub'))),
|
|
];
|
|
} finally {
|
|
rescue(fn () => File::deleteDirectory($directory), report: false);
|
|
}
|
|
}
|
|
|
|
private function fingerprint(string $publicKey): string
|
|
{
|
|
try {
|
|
$parts = explode(' ', trim($publicKey));
|
|
$keyMaterial = $parts[1] ?? $publicKey;
|
|
|
|
return 'SHA256:'.rtrim(strtr(base64_encode(hash('sha256', base64_decode($keyMaterial, true) ?: $publicKey, true)), '+/', '-_'), '=');
|
|
} catch (Throwable) {
|
|
return 'SHA256:'.hash('sha256', $publicKey);
|
|
}
|
|
}
|
|
}
|