Fix up node autodeployment

This commit is contained in:
Dane Everitt 2020-04-12 17:20:09 -07:00
parent fc31d6347e
commit fda88940f7
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
6 changed files with 119 additions and 20 deletions

View file

@ -0,0 +1,88 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\ApiKey;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Services\Api\KeyCreationService;
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
class NodeAutoDeployController extends Controller
{
/**
* @var \Pterodactyl\Services\Api\KeyCreationService
*/
private $keyCreationService;
/**
* @var \Pterodactyl\Repositories\Eloquent\ApiKeyRepository
*/
private $repository;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
private $encrypter;
/**
* NodeAutoDeployController constructor.
*
* @param \Pterodactyl\Repositories\Eloquent\ApiKeyRepository $repository
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @param \Pterodactyl\Services\Api\KeyCreationService $keyCreationService
*/
public function __construct(
ApiKeyRepository $repository,
Encrypter $encrypter,
KeyCreationService $keyCreationService
) {
$this->keyCreationService = $keyCreationService;
$this->repository = $repository;
$this->encrypter = $encrypter;
}
/**
* Generates a new API key for the logged in user with only permission to read
* nodes, and returns that as the deployment key for a node.
*
* @param \Illuminate\Http\Request $request
* @param \Pterodactyl\Models\Node $node
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function __invoke(Request $request, Node $node)
{
/** @var \Pterodactyl\Models\ApiKey|null $key */
$key = $this->repository->getApplicationKeys($request->user())
->filter(function (ApiKey $key) {
foreach ($key->getAttributes() as $permission => $value) {
if ($permission === 'r_nodes' && $value === 1) {
return true;
}
}
return false;
})
->first();
// We couldn't find a key that exists for this user with only permission for
// reading nodes. Go ahead and create it now.
if (! $key) {
$key = $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([
'user_id' => $request->user()->id,
'memo' => 'Automatically generated node deployment key.',
'allowed_ips' => [],
], ['r_nodes' => 1]);
}
return JsonResponse::create([
'node' => $node->id,
'token' => $key->identifier . $this->encrypter->decrypt($key->token),
]);
}
}

View file

@ -9,7 +9,6 @@
namespace Pterodactyl\Http\Controllers\Admin; namespace Pterodactyl\Http\Controllers\Admin;
use Cake\Chronos\Chronos;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -300,18 +299,4 @@ class NodesController extends Controller
return redirect()->route('admin.nodes'); return redirect()->route('admin.nodes');
} }
/**
* Returns the configuration token to auto-deploy a node.
*
* @param \Pterodactyl\Models\Node $node
* @return \Illuminate\Http\JsonResponse
*/
public function setToken(Node $node)
{
$token = bin2hex(random_bytes(16));
$this->cache->put('Node:Configuration:' . $token, $node->id, Chronos::now()->addMinutes(5));
return response()->json(['token' => $token]);
}
} }

View file

@ -0,0 +1,25 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Nodes;
use Pterodactyl\Models\Node;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class NodeConfigurationController extends ApplicationApiController
{
/**
* Returns the configuration information for a node. This allows for automated deployments
* to remote machines so long as an API key is provided to the machine to make the request
* with, and the node is known.
*
* @param \Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest $request
* @param \Pterodactyl\Models\Node $node
* @return \Illuminate\Http\JsonResponse
*/
public function __invoke(GetNodeRequest $request, Node $node)
{
return JsonResponse::create($node->getConfiguration());
}
}

View file

@ -71,8 +71,7 @@
swal({ swal({
type: 'success', type: 'success',
title: 'Token created.', title: 'Token created.',
text: 'Your token will expire <strong>in 5 minutes.</strong><br /><br />' + text: '<p>To auto-configure your node run the following command:<br /><small><pre>cd /srv/wings && ./wings configure --panel-url {{ config('app.url') }} --token ' + data.token + ' --node ' + data.node + '{{ config('app.debug') ? ' --allow-insecure' : '' }}</pre></small></p>',
'<p>To auto-configure your node run the following command:<br /><small><pre>npm run configure -- --panel-url {{ config('app.url') }} --token ' + data.token + '</pre></small></p>',
html: true html: true
}) })
}).fail(function () { }).fail(function () {

View file

@ -151,7 +151,7 @@ Route::group(['prefix' => 'nodes'], function () {
Route::get('/view/{node}/allocation', 'Nodes\NodeViewController@allocations')->name('admin.nodes.view.allocation'); Route::get('/view/{node}/allocation', 'Nodes\NodeViewController@allocations')->name('admin.nodes.view.allocation');
Route::get('/view/{node}/servers', 'Nodes\NodeViewController@servers')->name('admin.nodes.view.servers'); Route::get('/view/{node}/servers', 'Nodes\NodeViewController@servers')->name('admin.nodes.view.servers');
Route::get('/view/{node}/system-information', 'Nodes\SystemInformationController'); Route::get('/view/{node}/system-information', 'Nodes\SystemInformationController');
Route::get('/view/{node}/settings/token', 'NodesController@setToken')->name('admin.nodes.view.configuration.token'); Route::get('/view/{node}/settings/token', 'NodeAutoDeployController')->name('admin.nodes.view.configuration.token');
Route::post('/new', 'NodesController@store'); Route::post('/new', 'NodesController@store');
Route::post('/view/{node}/allocation', 'NodesController@createAllocation'); Route::post('/view/{node}/allocation', 'NodesController@createAllocation');

View file

@ -1,5 +1,7 @@
<?php <?php
use Illuminate\Support\Facades\Route;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| User Controller Routes | User Controller Routes
@ -8,6 +10,7 @@
| Endpoint: /api/application/users | Endpoint: /api/application/users
| |
*/ */
Route::group(['prefix' => '/users'], function () { Route::group(['prefix' => '/users'], function () {
Route::get('/', 'Users\UserController@index')->name('api.application.users'); Route::get('/', 'Users\UserController@index')->name('api.application.users');
Route::get('/{user}', 'Users\UserController@view')->name('api.application.users.view'); Route::get('/{user}', 'Users\UserController@view')->name('api.application.users.view');
@ -30,6 +33,7 @@ Route::group(['prefix' => '/users'], function () {
Route::group(['prefix' => '/nodes'], function () { Route::group(['prefix' => '/nodes'], function () {
Route::get('/', 'Nodes\NodeController@index')->name('api.application.nodes'); Route::get('/', 'Nodes\NodeController@index')->name('api.application.nodes');
Route::get('/{node}', 'Nodes\NodeController@view')->name('api.application.nodes.view'); Route::get('/{node}', 'Nodes\NodeController@view')->name('api.application.nodes.view');
Route::get('/{node}/configuration', 'Nodes\NodeConfigurationController');
Route::post('/', 'Nodes\NodeController@store'); Route::post('/', 'Nodes\NodeController@store');
Route::patch('/{node}', 'Nodes\NodeController@update'); Route::patch('/{node}', 'Nodes\NodeController@update');
@ -38,9 +42,7 @@ Route::group(['prefix' => '/nodes'], function () {
Route::group(['prefix' => '/{node}/allocations'], function () { Route::group(['prefix' => '/{node}/allocations'], function () {
Route::get('/', 'Nodes\AllocationController@index')->name('api.application.allocations'); Route::get('/', 'Nodes\AllocationController@index')->name('api.application.allocations');
Route::post('/', 'Nodes\AllocationController@store'); Route::post('/', 'Nodes\AllocationController@store');
Route::delete('/{allocation}', 'Nodes\AllocationController@delete')->name('api.application.allocations.view'); Route::delete('/{allocation}', 'Nodes\AllocationController@delete')->name('api.application.allocations.view');
}); });
}); });