Add Servers and PrivateKeys

Add new testhost
Remove privatekey injection from Dockerfile
This commit is contained in:
Andras Bacsai 2023-03-24 22:15:36 +01:00
parent 9e326d15b9
commit f57684b024
14 changed files with 116 additions and 38 deletions

View File

@ -64,12 +64,14 @@ protected function getCommand(): string
{ {
$user = $this->activity->getExtraProperty('user'); $user = $this->activity->getExtraProperty('user');
$destination = $this->activity->getExtraProperty('destination'); $destination = $this->activity->getExtraProperty('destination');
$private_key_location = $this->activity->getExtraProperty('private_key_location');
$port = $this->activity->getExtraProperty('port'); $port = $this->activity->getExtraProperty('port');
$command = $this->activity->getExtraProperty('command'); $command = $this->activity->getExtraProperty('command');
$delimiter = 'EOF-COOLIFY-SSH'; $delimiter = 'EOF-COOLIFY-SSH';
return 'ssh ' $ssh_command = "ssh "
. "-i {$private_key_location} "
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' . '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
. '-o PasswordAuthentication=no ' . '-o PasswordAuthentication=no '
. '-o RequestTTY=no ' . '-o RequestTTY=no '
@ -78,6 +80,7 @@ protected function getCommand(): string
. " 'bash -se' << \\$delimiter" . PHP_EOL . " 'bash -se' << \\$delimiter" . PHP_EOL
. $command . PHP_EOL . $command . PHP_EOL
. $delimiter; . $delimiter;
return $ssh_command;
} }
protected function handleOutput(string $type, string $output) protected function handleOutput(string $type, string $output)

View File

@ -10,6 +10,7 @@ class RemoteProcessArgs extends Data
{ {
public function __construct( public function __construct(
public string $destination, public string $destination,
public string $private_key_location,
public string $command, public string $command,
public int $port, public int $port,
public string $user, public string $user,

View File

@ -2,6 +2,7 @@
namespace App\Http\Livewire; namespace App\Http\Livewire;
use App\Models\Server;
use Livewire\Component; use Livewire\Component;
class RunCommand extends Component class RunCommand extends Component
@ -16,6 +17,12 @@ class RunCommand extends Component
public $server = 'testing-host'; public $server = 'testing-host';
public $servers = [];
public function mount()
{
$this->servers = Server::all()->pluck('name')->toArray();
}
public function render() public function render()
{ {
return view('livewire.run-command'); return view('livewire.run-command');

View File

@ -31,5 +31,6 @@ public function handle(): void
]); ]);
$remoteProcess(); $remoteProcess();
// @TODO: Remove file at $this->activity->getExtraProperty('private_key_location') after process is finished
} }
} }

View File

@ -4,8 +4,13 @@
class PrivateKey extends BaseModel class PrivateKey extends BaseModel
{ {
public function private_key_morph() public function private_keyables()
{ {
return $this->morphTo(); return $this->hasMany(PrivateKeyable::class);
}
public function servers()
{
return $this->morphedByMany(Server::class, 'private_keyable');
} }
} }

View File

@ -4,8 +4,8 @@
class Server extends BaseModel class Server extends BaseModel
{ {
public function private_key() public function privateKeys()
{ {
return $this->morphMany(PrivateKey::class, 'private_key_morph'); return $this->morphToMany(PrivateKey::class, 'private_keyable');
} }
} }

View File

@ -3,11 +3,12 @@
use App\Actions\RemoteProcess\DispatchRemoteProcess; use App\Actions\RemoteProcess\DispatchRemoteProcess;
use App\Data\RemoteProcessArgs; use App\Data\RemoteProcessArgs;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Facades\Storage;
use Spatie\Activitylog\Contracts\Activity; use Spatie\Activitylog\Contracts\Activity;
if (!function_exists('remoteProcess')) { if (!function_exists('remoteProcess')) {
/** /**
* Run a Coolify Process, which SSH's asynchronously into a machine to run the command(s). * Run a Remote Process, which SSH's asynchronously into a machine to run the command(s).
* @TODO Change 'root' to 'coolify' when it's able to run Docker commands without sudo * @TODO Change 'root' to 'coolify' when it's able to run Docker commands without sudo
* *
*/ */
@ -15,21 +16,36 @@ function remoteProcess(
string $command, string $command,
string $destination string $destination
): Activity { ): Activity {
$found_server = Server::where('name', $destination)->first(); $found_server = checkServer($destination);
if (!$found_server) { checkTeam($found_server->team_id);
throw new \RuntimeException('Server not found.');
} $temp_file = 'id.rsa_'.'root'.'@'.$found_server->ip;
$found_team = auth()->user()->teams->pluck('id')->contains($found_server->team_id); Storage::disk('local')->put($temp_file, $found_server->privateKeys->first()->private_key, 'private');
if (!$found_team) { $private_key_location = '/var/www/html/storage/app/'.$temp_file;
throw new \RuntimeException('You do not have access to this server.');
}
return resolve(DispatchRemoteProcess::class, [ return resolve(DispatchRemoteProcess::class, [
'remoteProcessArgs' => new RemoteProcessArgs( 'remoteProcessArgs' => new RemoteProcessArgs(
destination: $found_server->ip, destination: $found_server->ip,
private_key_location: $private_key_location,
command: $command, command: $command,
port: $found_server->port, port: $found_server->port,
user: $found_server->user, user: $found_server->user,
), ),
])(); ])();
} }
function checkServer(string $destination){
// @TODO: Use UUID instead of name
$found_server = Server::where('name', $destination)->first();
if (!$found_server) {
throw new \RuntimeException('Server not found.');
};
return $found_server;
}
function checkTeam(string $team_id){
$found_team = auth()->user()->teams->pluck('id')->contains($team_id);
if (!$found_team) {
throw new \RuntimeException('You do not have access to this server.');
}
}
} }

View File

@ -17,7 +17,7 @@ public function up(): void
$table->string('name'); $table->string('name');
$table->string('description')->nullable(); $table->string('description')->nullable();
$table->longText('private_key'); $table->longText('private_key');
$table->nullableMorphs('private_key_morph'); $table->nullableMorphs('private_keys_morph');
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('private_keyables', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('private_key_id');
$table->unsignedBigInteger('private_keyable_id');
$table->string('private_keyable_type');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('private_keyables');
}
};

View File

@ -15,16 +15,20 @@ class PrivateKeySeeder extends Seeder
public function run(): void public function run(): void
{ {
$server = Server::find(1); $server = Server::find(1);
PrivateKey::create([ $server2 = Server::find(2);
$private_key = PrivateKey::create([
"name" => "Testing-host", "name" => "Testing-host",
"description" => "This is a test docker container", "description" => "This is a test docker container",
"private_key" => "-----BEGIN OPENSSH PRIVATE KEY-----\ "private_key" => "-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk\ QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA\ hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV\ AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==\ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
-----END OPENSSH PRIVATE KEY-----", -----END OPENSSH PRIVATE KEY-----
])->private_key_morph()->associate($server)->save(); ",
]);
$server->privateKeys()->attach($private_key);
$server2->privateKeys()->attach($private_key);
} }
} }

View File

@ -22,5 +22,12 @@ public function run(): void
'ip' => "coolify-testing-host", 'ip' => "coolify-testing-host",
'team_id' => $root_team->id, 'team_id' => $root_team->id,
]); ]);
Server::create([
'id' => 2,
'name' => "testing-host2",
'description' => "This is a test docker container",
'ip' => "coolify-testing-host-2",
'team_id' => $root_team->id,
]);
} }
} }

View File

@ -53,6 +53,17 @@ services:
- ./docker/testing-host/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf - ./docker/testing-host/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf
networks: networks:
- coolify - coolify
testing-host2:
container_name: coolify-testing-host-2
image: coolify-testing-host
build:
dockerfile: Dockerfile
context: ./docker/testing-host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./docker/testing-host/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf
networks:
- coolify
volumes: volumes:
db-coolify: db-coolify:

View File

@ -56,16 +56,6 @@ RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2
RUN groupadd --force -g $WWWGROUP sail RUN groupadd --force -g $WWWGROUP sail
RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail
USER sail
RUN mkdir -p ~/.ssh
RUN echo "-----BEGIN OPENSSH PRIVATE KEY-----\n\
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n\
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk\n\
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA\n\
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV\n\
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==\n\
-----END OPENSSH PRIVATE KEY-----" >> ~/.ssh/id_ed25519
RUN chmod 0600 ~/.ssh/id_ed25519
USER root USER root
@ -78,7 +68,7 @@ RUN chmod +x /usr/local/bin/start-container
RUN mkdir -p ~/.docker/cli-plugins RUN mkdir -p ~/.docker/cli-plugins
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose -o /home/sail/.docker/cli-plugins/docker-compose RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose -o /home/sail/.docker/cli-plugins/docker-compose
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack
RUN curl -sSL https://nixpacks.com/install.sh | bash RUN curl -sSL https://nixpacks.com/install.sh | bash
RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack

View File

@ -1,11 +1,14 @@
<div> <div>
<div> <div>
<label for="command"> <label for="command">
<input autofocus id="command" wire:model="command" type="text" wire:keydown.enter="runCommand" /> <input autofocus id="command" wire:model.defer="command" type="text" wire:keydown.enter="runCommand" />
<input id="command" wire:model="server" type="text" /> <select wire:model.defer="server">
@foreach ($servers as $server)
<option value="{{ $server }}">{{ $server }}</option>
@endforeach
</select>
</label> </label>
<button wire:click="runCommand">Run command</button> <button wire:click="runCommand">Run command</button>
<button wire:click="runSleepingBeauty">Run sleeping beauty</button> <button wire:click="runSleepingBeauty">Run sleeping beauty</button>
<button wire:click="runDummyProjectBuild">Build DummyProject</button> <button wire:click="runDummyProjectBuild">Build DummyProject</button>
</div> </div>