From 01871d8a6c45e4fbe61e394d9fc380f58e93623b Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 15 Nov 2021 18:15:27 +0200 Subject: [PATCH 01/30] add Java 17 LTS image to Minecraft eggs (#3744) * feat: add Java 17 LTS for Minecraft * feat: add java 17 option to java modal --- database/Seeders/eggs/minecraft/egg-bungeecord.json | 7 ++++--- database/Seeders/eggs/minecraft/egg-forge-minecraft.json | 5 +++-- database/Seeders/eggs/minecraft/egg-paper.json | 5 +++-- database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json | 7 ++++--- .../components/server/features/JavaVersionModalFeature.tsx | 1 + 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/database/Seeders/eggs/minecraft/egg-bungeecord.json b/database/Seeders/eggs/minecraft/egg-bungeecord.json index 0df945cf0..9a538a8e1 100644 --- a/database/Seeders/eggs/minecraft/egg-bungeecord.json +++ b/database/Seeders/eggs/minecraft/egg-bungeecord.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-07-04T19:18:34-04:00", + "exported_at": "2021-11-14T19:23:12+00:00", "name": "Bungeecord", "author": "support@pterodactyl.io", "description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.", @@ -15,14 +15,15 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "file_denylist": [], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "config": { "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \"Listening on \"\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"proxy.log.0\"\r\n}", + "logs": "{}", "stop": "end" }, "scripts": { diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json index 804f65a8d..3b6f70153 100644 --- a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-10-22T19:29:26+02:00", + "exported_at": "2021-11-14T19:22:27+00:00", "name": "Forge Minecraft", "author": "support@pterodactyl.io", "description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.", @@ -15,7 +15,8 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "file_denylist": [], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true $( [ ! -f unix_args.txt ] && printf %s \"-jar {{SERVER_JARFILE}}\" || printf %s \"@unix_args.txt\" )", diff --git a/database/Seeders/eggs/minecraft/egg-paper.json b/database/Seeders/eggs/minecraft/egg-paper.json index 2c3f130c2..284ee97fb 100644 --- a/database/Seeders/eggs/minecraft/egg-paper.json +++ b/database/Seeders/eggs/minecraft/egg-paper.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-10-22T19:19:11+02:00", + "exported_at": "2021-11-14T19:21:07+00:00", "name": "Paper", "author": "parker@pterodactyl.io", "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", @@ -15,7 +15,8 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "file_denylist": [], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", diff --git a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json index 64310fcf1..9106d1a72 100644 --- a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-10-22T19:19:23+02:00", + "exported_at": "2021-11-14T19:18:30+00:00", "name": "Vanilla Minecraft", "author": "support@pterodactyl.io", "description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.", @@ -15,14 +15,15 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "file_denylist": [], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "config": { "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"logs\/latest.log\"\r\n}", + "logs": "{}", "stop": "stop" }, "scripts": { diff --git a/resources/scripts/components/server/features/JavaVersionModalFeature.tsx b/resources/scripts/components/server/features/JavaVersionModalFeature.tsx index 3756b55b0..397f4b725 100644 --- a/resources/scripts/components/server/features/JavaVersionModalFeature.tsx +++ b/resources/scripts/components/server/features/JavaVersionModalFeature.tsx @@ -13,6 +13,7 @@ const dockerImageList = [ { name: 'Java 8', image: 'ghcr.io/pterodactyl/yolks:java_8' }, { name: 'Java 11', image: 'ghcr.io/pterodactyl/yolks:java_11' }, { name: 'Java 16', image: 'ghcr.io/pterodactyl/yolks:java_16' }, + { name: 'Java 17', image: 'ghcr.io/pterodactyl/yolks:java_17' }, ]; const JavaVersionModalFeature = () => { From cc31a0a6d028fbcba94e68e67aaef3be9d683a71 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 15 Nov 2021 11:29:22 -0700 Subject: [PATCH 02/30] tests(integration): don't expect non-required fields --- tests/Integration/Api/Application/Nests/EggControllerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Api/Application/Nests/EggControllerTest.php b/tests/Integration/Api/Application/Nests/EggControllerTest.php index ad4316574..0482e91eb 100644 --- a/tests/Integration/Api/Application/Nests/EggControllerTest.php +++ b/tests/Integration/Api/Application/Nests/EggControllerTest.php @@ -26,7 +26,7 @@ class EggControllerTest extends ApplicationApiIntegrationTestCase } /** - * Test that all of the eggs belonging to a given nest can be returned. + * Test that all the eggs belonging to a given nest can be returned. */ public function testListAllEggsInNest() { @@ -47,7 +47,7 @@ class EggControllerTest extends ApplicationApiIntegrationTestCase 'files' => [], 'startup' => ['done'], 'stop', - 'logs' => ['custom', 'location'], + 'logs' => [], 'extends', ], ], From bf9cbe2c6d5266c6914223e067c56175de7fc3a5 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 16 Nov 2021 20:02:18 -0800 Subject: [PATCH 03/30] Add consistent CSRF token verification to API endpoints; address security concern with non-CSRF protected endpoints --- app/Http/Kernel.php | 2 + app/Http/Middleware/Api/AuthenticateKey.php | 3 +- app/Http/Middleware/VerifyCsrfToken.php | 41 +++++++++++++++---- resources/scripts/api/http.ts | 13 +++++- .../admin/nodes/view/configuration.blade.php | 6 ++- resources/views/admin/settings/mail.blade.php | 4 +- routes/admin.php | 4 +- 7 files changed, 59 insertions(+), 14 deletions(-) diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 04a663f33..3fac902c1 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -75,6 +75,7 @@ class Kernel extends HttpKernel ApiSubstituteBindings::class, 'api..key:' . ApiKey::TYPE_APPLICATION, AuthenticateApplicationUser::class, + VerifyCsrfToken::class, AuthenticateIPAccess::class, ], 'client-api' => [ @@ -85,6 +86,7 @@ class Kernel extends HttpKernel SubstituteClientApiBindings::class, 'api..key:' . ApiKey::TYPE_ACCOUNT, AuthenticateIPAccess::class, + VerifyCsrfToken::class, // This is perhaps a little backwards with the Client API, but logically you'd be unable // to create/get an API key without first enabling 2FA on the account, so I suppose in the // end it makes sense. diff --git a/app/Http/Middleware/Api/AuthenticateKey.php b/app/Http/Middleware/Api/AuthenticateKey.php index 397ba8c23..eb25dac6f 100644 --- a/app/Http/Middleware/Api/AuthenticateKey.php +++ b/app/Http/Middleware/Api/AuthenticateKey.php @@ -8,6 +8,7 @@ use Illuminate\Http\Request; use Pterodactyl\Models\User; use Pterodactyl\Models\ApiKey; use Illuminate\Auth\AuthManager; +use Illuminate\Support\Facades\Session; use Illuminate\Contracts\Encryption\Encrypter; use Symfony\Component\HttpKernel\Exception\HttpException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; @@ -55,7 +56,7 @@ class AuthenticateKey public function handle(Request $request, Closure $next, int $keyType) { if (is_null($request->bearerToken()) && is_null($request->user())) { - throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']); + throw new HttpException(401, 'A bearer token or valid user session cookie must be provided to access this endpoint.', null, ['WWW-Authenticate' => 'Bearer']); } // This is a request coming through using cookies, we have an authenticated user diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 08d960353..de10dc1a8 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -2,18 +2,45 @@ namespace Pterodactyl\Http\Middleware; +use Closure; +use Pterodactyl\Models\ApiKey; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier; class VerifyCsrfToken extends BaseVerifier { /** - * The URIs that should be excluded from CSRF verification. + * The URIs that should be excluded from CSRF verification. These are + * never hit by the front-end, and require specific token validation + * to work. * - * @var array + * @var string[] */ - protected $except = [ - 'remote/*', - 'daemon/*', - 'api/*', - ]; + protected $except = ['remote/*', 'daemon/*']; + + /** + * Manually apply CSRF protection to routes depending on the authentication + * mechanism being used. If the API request is using an API key that exists + * in the database we can safely ignore CSRF protections, since that would be + * a manually initiated request by a user or server. + * + * All other requests should go through the standard CSRF protections that + * Laravel affords us. This code will be removed in v2 since we have switched + * to using Sanctum for the API endpoints, which handles that for us automatically. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + * + * @throws \Illuminate\Session\TokenMismatchException + */ + public function handle($request, Closure $next) + { + $key = $request->attributes->get('api_key'); + + if ($key instanceof ApiKey && $key->exists) { + return $next($request); + } + + return parent::handle($request, $next); + } } diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts index a642bb16e..d9f64ede2 100644 --- a/resources/scripts/api/http.ts +++ b/resources/scripts/api/http.ts @@ -7,10 +7,21 @@ const http: AxiosInstance = axios.create({ 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json', 'Content-Type': 'application/json', - 'X-CSRF-Token': (window as any).X_CSRF_TOKEN as string || '', }, }); +http.interceptors.request.use(req => { + const cookies = document.cookie.split(';').reduce((obj, val) => { + const [ key, value ] = val.trim().split('=').map(decodeURIComponent); + + return { ...obj, [key]: value }; + }, {} as Record); + + req.headers['X-XSRF-TOKEN'] = cookies['XSRF-TOKEN'] || 'nil'; + + return req; +}); + http.interceptors.request.use(req => { if (!req.url?.endsWith('/resources') && (req.url?.indexOf('_debugbar') || -1) < 0) { store.getActions().progress.startContinuous(); diff --git a/resources/views/admin/nodes/view/configuration.blade.php b/resources/views/admin/nodes/view/configuration.blade.php index 9c7e987b5..35e3f5f0a 100644 --- a/resources/views/admin/nodes/view/configuration.blade.php +++ b/resources/views/admin/nodes/view/configuration.blade.php @@ -70,7 +70,11 @@ @parent