From d6a0fb3838c1b865ef2638b434ed2687e09a6fd5 Mon Sep 17 00:00:00 2001 From: "Harry (hjbdev)" Date: Mon, 31 Mar 2025 17:18:56 +0000 Subject: [PATCH] Firewall rules wip, server show improved --- app/Enums/FirewallRuleStatus.php | 10 +++ app/Http/Controllers/ServerController.php | 4 +- app/Jobs/Services/RunStep.php | 4 +- app/Models/FirewallRule.php | 65 +++++++++++++++++++ app/Models/Server.php | 13 ++++ ..._31_170943_create_firewall_rules_table.php | 28 ++++++++ package-lock.json | 4 +- package.json | 2 +- resources/js/components/ui/badge/Badge.vue | 16 +++++ resources/js/components/ui/badge/index.ts | 27 ++++++++ resources/js/pages/servers/Show.vue | 32 ++++++++- 11 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 app/Enums/FirewallRuleStatus.php create mode 100644 app/Models/FirewallRule.php create mode 100644 database/migrations/2025_03_31_170943_create_firewall_rules_table.php create mode 100644 resources/js/components/ui/badge/Badge.vue create mode 100644 resources/js/components/ui/badge/index.ts diff --git a/app/Enums/FirewallRuleStatus.php b/app/Enums/FirewallRuleStatus.php new file mode 100644 index 0000000..aa316aa --- /dev/null +++ b/app/Enums/FirewallRuleStatus.php @@ -0,0 +1,10 @@ + $request->location, 'os' => $request->image, 'plan' => $request->server_type, - 'user' => '', + 'user' => 'keystone', ]); dispatch(new WaitForServerToConnect( @@ -103,7 +103,7 @@ class ServerController extends Controller $server = $organisation->servers()->findOrFail($request->route('server')); return inertia('servers/Show', [ - 'server' => $server->load('services'), + 'server' => $server->load('services.slices'), ]); } } diff --git a/app/Jobs/Services/RunStep.php b/app/Jobs/Services/RunStep.php index 52827ef..13fd47c 100644 --- a/app/Jobs/Services/RunStep.php +++ b/app/Jobs/Services/RunStep.php @@ -30,9 +30,7 @@ class RunStep implements ShouldQueue $server = $this->step->deployment->target->server; - $ssh = Ssh::create('root', $server->ipv4) - ->usePrivateKey(storage_path('app/private/ssh/id_ed25519')) - ->disableStrictHostKeyChecking() + $ssh = $server->sshClient() ->onOutput(function ($output) { $this->step->update([ 'logs' => $this->step->logs . "\n" . trim($output), diff --git a/app/Models/FirewallRule.php b/app/Models/FirewallRule.php new file mode 100644 index 0000000..2426d19 --- /dev/null +++ b/app/Models/FirewallRule.php @@ -0,0 +1,65 @@ +execute(); + }); + } + + protected function casts(): array + { + return [ + 'status' => FirewallRuleStatus::class, + ]; + } + + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } + + public function execute(): void + { + $ssh = $this->server->sshClient(); + + $command = "ufw"; + + if ($this->type === 'allow') { + $command .= " allow"; + } elseif ($this->type === 'deny') { + $command .= " deny"; + } + + if ($this->from) { + $command .= " from {$this->from}"; + $command .= " to any port"; + } + + $command .= " {$this->ports}"; + + $result = $ssh->execute($command); + + if (! $result->isSuccessful()) { + $this->update([ + 'status' => FirewallRuleStatus::FAILED, + ]); + return; + } + $this->update([ + 'status' => FirewallRuleStatus::APPLIED, + ]); + } +} diff --git a/app/Models/Server.php b/app/Models/Server.php index 90887e7..e6afdd5 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -7,6 +7,7 @@ use App\Enums\ServerStatus; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Spatie\Ssh\Ssh; class Server extends Model { @@ -29,4 +30,16 @@ class Server extends Model { return $this->hasMany(Service::class); } + + public function firewallRules(): HasMany + { + return $this->hasMany(FirewallRule::class); + } + + public function sshClient(string $user = 'root'): Ssh + { + return Ssh::create($user, $this->ipv4) + ->usePrivateKey(storage_path('app/private/ssh/id_ed25519')) + ->disableStrictHostKeyChecking(); + } } diff --git a/database/migrations/2025_03_31_170943_create_firewall_rules_table.php b/database/migrations/2025_03_31_170943_create_firewall_rules_table.php new file mode 100644 index 0000000..338358d --- /dev/null +++ b/database/migrations/2025_03_31_170943_create_firewall_rules_table.php @@ -0,0 +1,28 @@ +id(); + $table->string('status')->default(FirewallRuleStatus::NOT_APPLIED->value); + $table->foreignIdfor(Server::class); + $table->string('type'); + $table->string('ports'); + $table->string('from')->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('firewall_rules'); + } +}; diff --git a/package-lock.json b/package-lock.json index 524714a..81ae410 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "vue-starter-kit", + "name": "keystone", "lockfileVersion": 3, "requires": true, "packages": { @@ -16,7 +16,7 @@ "lucide": "^0.468.0", "lucide-vue-next": "^0.468.0", "radix-vue": "^1.9.11", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "^2.6.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "typescript": "^5.2.2", diff --git a/package.json b/package.json index b6b68f3..9e6c852 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "lucide": "^0.468.0", "lucide-vue-next": "^0.468.0", "radix-vue": "^1.9.11", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "^2.6.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "typescript": "^5.2.2", diff --git a/resources/js/components/ui/badge/Badge.vue b/resources/js/components/ui/badge/Badge.vue new file mode 100644 index 0000000..9ed8039 --- /dev/null +++ b/resources/js/components/ui/badge/Badge.vue @@ -0,0 +1,16 @@ + + + diff --git a/resources/js/components/ui/badge/index.ts b/resources/js/components/ui/badge/index.ts new file mode 100644 index 0000000..5834f30 --- /dev/null +++ b/resources/js/components/ui/badge/index.ts @@ -0,0 +1,27 @@ +import { cva, type VariantProps } from 'class-variance-authority' + +export { default as Badge } from './Badge.vue' + +export const badgeVariants = cva( + 'inline-flex items-center rounded-full border px-2 py-0.25 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + { + variants: { + variant: { + default: + 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', + secondary: + 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: + 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', + success: + 'border-transparent bg-green-200 text-green-800 hover:bg-green-200/80', + outline: 'text-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +export type BadgeVariants = VariantProps diff --git a/resources/js/pages/servers/Show.vue b/resources/js/pages/servers/Show.vue index 61f5be1..e5670e5 100644 --- a/resources/js/pages/servers/Show.vue +++ b/resources/js/pages/servers/Show.vue @@ -1,6 +1,9 @@