onConnection('provisioning'); $this->onQueue('provisioning'); } /** Anything that escapes handle() still has to reach the waiting console. */ public function failed(?Throwable $e): void { ConfigHandoff::put(json_encode(['error' => 'failed']), $this->handoffToken); } public function handle(ProxmoxClient $proxmox): void { $instance = Instance::query()->with('host')->where('uuid', $this->instanceUuid)->first(); // Only a live instance: a closed one's VMID can have been reused on the // same host, and the reset would then land on a stranger's VM. if ($instance === null || $instance->status !== 'active' || $instance->host === null || blank($instance->vmid) || blank($instance->nc_admin_ref)) { ConfigHandoff::put(json_encode(['error' => 'unavailable']), $this->handoffToken); return; } $username = $instance->nc_admin_ref; $password = Str::password(24, symbols: false); try { $result = $proxmox->forHost($instance->host)->guestExec( $instance->host->node ?? 'pve', (int) $instance->vmid, 'cd /opt/nextcloud && OC_PASS='.escapeshellarg($password). ' docker compose exec -T -e OC_PASS app php occ user:resetpassword --password-from-env '. escapeshellarg($username), ); } catch (Throwable $e) { // An unreachable Proxmox or guest agent throws rather than // returning a code. Without this the console would poll forever // instead of saying what happened. Log::warning('instance admin access errored', [ 'instance' => $instance->uuid, 'error' => $e->getMessage(), ]); ConfigHandoff::put(json_encode(['error' => 'failed']), $this->handoffToken); return; } if ((int) ($result['exitcode'] ?? 1) !== 0) { Log::warning('instance admin access failed', [ 'instance' => $instance->uuid, 'user' => $this->requestedBy, ]); ConfigHandoff::put(json_encode(['error' => 'failed']), $this->handoffToken); return; } // Auditable on its own: who asked, for which instance, when. The // credentials themselves are never logged. Log::info('instance admin access issued', [ 'instance' => $instance->uuid, 'user' => $this->requestedBy, ]); ConfigHandoff::put(json_encode([ 'url' => 'https://'.($instance->custom_domain ?: $instance->subdomain.'.'.config('provisioning.dns.zone')), 'username' => $username, 'password' => $password, ]), $this->handoffToken); } }