Skip to content

Commit

Permalink
refactor: Update PrivateKey model to improve code readability and mai…
Browse files Browse the repository at this point in the history
…ntainability
  • Loading branch information
andrasbacsai committed Sep 20, 2024
1 parent b81f911 commit 5b00b66
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 15 deletions.
40 changes: 26 additions & 14 deletions app/Models/PrivateKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace App\Models;

use OpenApi\Attributes as OA;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\ValidationException;
use OpenApi\Attributes as OA;
use phpseclib3\Crypt\PublicKeyLoader;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;

#[OA\Schema(
description: 'Private Key model',
Expand Down Expand Up @@ -44,8 +44,8 @@ protected static function booted()
{
static::saving(function ($key) {
$key->private_key = formatPrivateKey($key->private_key);
if (!self::validatePrivateKey($key->private_key)) {

if (! self::validatePrivateKey($key->private_key)) {
throw ValidationException::withMessages([
'private_key' => ['The private key is invalid.'],
]);
Expand Down Expand Up @@ -73,13 +73,15 @@ public function getPublicKey()
public static function ownedByCurrentTeam(array $select = ['*'])
{
$selectArray = collect($select)->concat(['id']);

return self::whereTeamId(currentTeam()->id)->select($selectArray->all());
}

public static function validatePrivateKey($privateKey)
{
try {
PublicKeyLoader::load($privateKey);

return true;
} catch (\Throwable $e) {
return false;
Expand All @@ -91,33 +93,35 @@ public static function createAndStore(array $data)
$privateKey = new self($data);
$privateKey->save();
$privateKey->storeInFileSystem();

return $privateKey;
}

public static function generateNewKeyPair($type = 'rsa')
{
try {
$instance = new self();
$instance = new self;
$instance->rateLimit(10);
$name = generate_random_name();
$description = 'Created by Coolify';
$keyPair = generateSSHKey($type === 'ed25519' ? 'ed25519' : 'rsa');

return [
'name' => $name,
'description' => $description,
'private_key' => $keyPair['private'],
'public_key' => $keyPair['public'],
];
} catch (\Throwable $e) {
throw new \Exception("Failed to generate new {$type} key: " . $e->getMessage());
throw new \Exception("Failed to generate new {$type} key: ".$e->getMessage());
}
}

public static function extractPublicKeyFromPrivate($privateKey)
{
try {
$key = PublicKeyLoader::load($privateKey);

return $key->getPublicKey()->toString('OpenSSH', ['comment' => '']);
} catch (\Throwable $e) {
return null;
Expand All @@ -128,7 +132,7 @@ public static function validateAndExtractPublicKey($privateKey)
{
$isValid = self::validatePrivateKey($privateKey);
$publicKey = $isValid ? self::extractPublicKeyFromPrivate($privateKey) : '';

return [
'isValid' => $isValid,
'publicKey' => $publicKey,
Expand All @@ -138,12 +142,16 @@ public static function validateAndExtractPublicKey($privateKey)
public function storeInFileSystem()
{
$filename = "ssh_key@{$this->uuid}";
echo 'Storing key: '.$filename."\n".'Private key: '.$this->private_key."\n";
Storage::disk('ssh-keys')->put($filename, $this->private_key);
$file = Storage::disk('ssh-keys')->get($filename);
echo 'File: '.$file."\n";

return "/var/www/html/storage/app/ssh/keys/{$filename}";
}

public static function deleteFromStorage(self $privateKey)
{
{
$filename = "ssh_key@{$privateKey->uuid}";
Storage::disk('ssh-keys')->delete($filename);
}
Expand All @@ -157,6 +165,7 @@ public function updatePrivateKey(array $data)
{
$this->update($data);
$this->storeInFileSystem();

return $this;
}

Expand All @@ -182,18 +191,20 @@ public function gitlabApps()

public function isInUse()
{
return $this->servers()->exists()
|| $this->applications()->exists()
|| $this->githubApps()->exists()
return $this->servers()->exists()
|| $this->applications()->exists()
|| $this->githubApps()->exists()
|| $this->gitlabApps()->exists();
}

public function safeDelete()
{
if (!$this->isInUse()) {
if (! $this->isInUse()) {
$this->delete();

return true;
}

return false;
}

Expand All @@ -202,6 +213,7 @@ public static function generateFingerprint($privateKey)
try {
$key = PublicKeyLoader::load($privateKey);
$publicKey = $key->getPublicKey();

return $publicKey->getFingerprint('sha256');
} catch (\Throwable $e) {
return null;
Expand All @@ -211,7 +223,7 @@ public static function generateFingerprint($privateKey)
private static function fingerprintExists($fingerprint, $excludeId = null)
{
$query = self::where('fingerprint', $fingerprint);

if ($excludeId) {
$query->where('id', '!=', $excludeId);
}
Expand Down
9 changes: 8 additions & 1 deletion database/seeders/PopulateSshKeysDirectorySeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@

namespace Database\Seeders;

use App\Models\PrivateKey;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Storage;
use App\Models\PrivateKey;

class PopulateSshKeysDirectorySeeder extends Seeder
{
public function run()
{
Storage::disk('ssh-keys')->deleteDirectory('');
Storage::disk('ssh-keys')->makeDirectory('');
Storage::disk('ssh-mux')->deleteDirectory('');
Storage::disk('ssh-mux')->makeDirectory('');

PrivateKey::chunk(100, function ($keys) {
foreach ($keys as $key) {
echo 'Storing key: '.$key->name."\n";
$key->storeInFileSystem();
}
});

Process::run('chown -R 9999:9999 '.storage_path('app/ssh/keys'));
Process::run('chown -R 9999:9999 '.storage_path('app/ssh/mux'));
}
}
1 change: 1 addition & 0 deletions database/seeders/ProductionSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public function run(): void
]);
$server->update(['user' => $user]);
echo "SSH key found for the Coolify host machine (localhost).\n";

} else {
PrivateKey::create(
[
Expand Down

0 comments on commit 5b00b66

Please sign in to comment.