From 63e01f9aee1686c923a4543940c03802e4dc2c4d Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sat, 2 Oct 2021 08:21:04 -0700
Subject: [PATCH 1/9] Update SECURITY.md
---
SECURITY.md | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/SECURITY.md b/SECURITY.md
index 5b412f853..a00178658 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -5,14 +5,9 @@ The following versions of Pterodactyl are receiving active support and maintenan
| Panel | Daemon | Supported |
| ----- | ------------ | ------------------ |
-| 1.4.x | wings@1.4.x | :white_check_mark: |
-| 1.3.x | wings@1.3.x | :x: |
-| 1.2.x | wings@1.2.x | :x: |
-| 1.1.x | wings@1.1.x | :x: |
-| 1.0.x | wings@1.0.x | :x: |
+| 1.6.x | wings@1.5.x | :white_check_mark: |
| 0.7.x | daemon@0.6.x | :x: |
-| 0.6.x | daemon@0.5.x | :x: |
-| 0.5.x | daemon@0.4.x | :x: |
+
## Reporting a Vulnerability
From 81ba333270b7ed9594f72c1917d6675a9f9c590d Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sun, 3 Oct 2021 12:59:44 -0700
Subject: [PATCH 2/9] If uptime is present in stats output, display it for the
server; closes #3653
---
.../components/server/ServerDetailsBlock.tsx | 14 +++++++++++---
.../scripts/components/server/UptimeDuration.tsx | 14 ++++++++++++++
2 files changed, 25 insertions(+), 3 deletions(-)
create mode 100644 resources/scripts/components/server/UptimeDuration.tsx
diff --git a/resources/scripts/components/server/ServerDetailsBlock.tsx b/resources/scripts/components/server/ServerDetailsBlock.tsx
index 6df4a764c..a87dc3b01 100644
--- a/resources/scripts/components/server/ServerDetailsBlock.tsx
+++ b/resources/scripts/components/server/ServerDetailsBlock.tsx
@@ -7,14 +7,16 @@ import TitledGreyBox from '@/components/elements/TitledGreyBox';
import { ServerContext } from '@/state/server';
import CopyOnClick from '@/components/elements/CopyOnClick';
import { SocketEvent, SocketRequest } from '@/components/server/events';
+import UptimeDuration from '@/components/server/UptimeDuration';
interface Stats {
memory: number;
cpu: number;
disk: number;
+ uptime: number;
}
-function statusToColor (status: string|null, installing: boolean): TwStyle {
+function statusToColor (status: string | null, installing: boolean): TwStyle {
if (installing) {
status = '';
}
@@ -30,7 +32,7 @@ function statusToColor (status: string|null, installing: boolean): TwStyle {
}
const ServerDetailsBlock = () => {
- const [ stats, setStats ] = useState({ memory: 0, cpu: 0, disk: 0 });
+ const [ stats, setStats ] = useState({ memory: 0, cpu: 0, disk: 0, uptime: 0 });
const status = ServerContext.useStoreState(state => state.status.value);
const connected = ServerContext.useStoreState(state => state.socket.connected);
@@ -48,6 +50,7 @@ const ServerDetailsBlock = () => {
memory: stats.memory_bytes,
cpu: stats.cpu_absolute,
disk: stats.disk_bytes,
+ uptime: stats.uptime || 0,
});
};
@@ -69,7 +72,7 @@ const ServerDetailsBlock = () => {
const isTransferring = ServerContext.useStoreState(state => state.server.data!.isTransferring);
const limits = ServerContext.useStoreState(state => state.server.data!.limits);
const primaryAllocation = ServerContext.useStoreState(state => state.server.data!.allocations.filter(alloc => alloc.isDefault).map(
- allocation => (allocation.alias || allocation.ip) + ':' + allocation.port
+ allocation => (allocation.alias || allocation.ip) + ':' + allocation.port,
)).toString();
const diskLimit = limits.disk ? megabytesToHuman(limits.disk) : 'Unlimited';
@@ -88,6 +91,11 @@ const ServerDetailsBlock = () => {
]}
/>
{!status ? 'Connecting...' : (isInstalling ? 'Installing' : (isTransferring) ? 'Transferring' : status)}
+ {stats.uptime > 0 &&
+
+ ()
+
+ }
diff --git a/resources/scripts/components/server/UptimeDuration.tsx b/resources/scripts/components/server/UptimeDuration.tsx
new file mode 100644
index 000000000..1406450fa
--- /dev/null
+++ b/resources/scripts/components/server/UptimeDuration.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+
+export default ({ uptime }: { uptime: number }) => {
+ const hours = Math.floor(Math.floor(uptime) / 60 / 60);
+ const remainder = Math.floor(uptime - (hours * 60 * 60));
+ const minutes = Math.floor(remainder / 60);
+ const seconds = remainder % 60;
+
+ return (
+ <>
+ {hours.toString().padStart(2, '0')}:{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}
+ >
+ );
+};
From de0d5c9b8afd88cff9bb345a376f818853b5090f Mon Sep 17 00:00:00 2001
From: Cyra
Date: Mon, 4 Oct 2021 08:23:10 -0700
Subject: [PATCH 3/9] Updated CHS sponsor entry to use new domain (#3659)
Updated CHS sponsor entry to use new domain
Updated from captiolsolutions.cloud to chs.gg
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 42e02d5c0..6d0eec5ad 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f
| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims for inexpensive services on quality servers. Premium i9-9900K processors will run your game like a dream. |
| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. |
| [**HostBend**](https://hostbend.com/) | HostBend offers a variety of solutions for developers, students, and others who have a tight budget but don't want to compromise quality and support. |
-| [**Capitol Hosting Solutions**](https://capitolsolutions.cloud/) | CHS is *the* budget friendly hosting company for Australian and American gamers, offering a variety of plans from Web Hosting to Game Servers; Custom Solutions too! |
+| [**Capitol Hosting Solutions**](https://chs.gg/) | CHS is *the* budget friendly hosting company for Australian and American gamers, offering a variety of plans from Web Hosting to Game Servers; Custom Solutions too! |
| [**ByteAnia**](https://byteania.com/?utm_source=pterodactyl) | ByteAnia offers the best performing and most affordable **Ryzen 5000 Series hosting** on the market for *unbeatable prices*! |
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa.|
From 4fa38b8e9cb5f35f3bbd3a0dd7d96bf2f8a8b39e Mon Sep 17 00:00:00 2001
From: Matthew Penner
Date: Thu, 7 Oct 2021 09:46:09 -0600
Subject: [PATCH 4/9] Fix wings receiving wrong suspended status on sync
(#3667)
Due to wings pulling the server configuration rather than the Panel pushing it,
wings gets the wrong status for a server if both the status update and sync request
are ran in a transaction due to the status not being persisted in the database.
Fixes #3639
---
app/Services/Servers/SuspensionService.php | 23 +++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/app/Services/Servers/SuspensionService.php b/app/Services/Servers/SuspensionService.php
index f7d0f77b1..27bc622f5 100644
--- a/app/Services/Servers/SuspensionService.php
+++ b/app/Services/Servers/SuspensionService.php
@@ -58,15 +58,20 @@ class SuspensionService
throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.');
}
- $this->connection->transaction(function () use ($action, $server, $isSuspending) {
- $server->update([
- 'status' => $isSuspending ? Server::STATUS_SUSPENDED : null,
- ]);
+ // Update the server's suspension status.
+ $server->update([
+ 'status' => $isSuspending ? Server::STATUS_SUSPENDED : null,
+ ]);
- // Only trigger a Wings server sync if it is not currently being transferred.
- if (is_null($server->transfer)) {
- $this->daemonServerRepository->setServer($server)->sync();
- }
- });
+ try {
+ // Tell wings to re-sync the server state.
+ $this->daemonServerRepository->setServer($server)->sync();
+ } catch (\Exception $exception) {
+ // Rollback the server's suspension status if wings fails to sync the server.
+ $server->update([
+ 'status' => $isSuspending ? null : Server::STATUS_SUSPENDED,
+ ]);
+ throw $exception;
+ }
}
}
From 8b236c6907f84a46b885f6d71e6969ce9c97e19c Mon Sep 17 00:00:00 2001
From: Waseem Hassan Shahid
Date: Sat, 9 Oct 2021 19:31:29 +0200
Subject: [PATCH 5/9] Fix SSL config docker (#3616)
* Don't copy default nginx config at build time
* Use http.d folder for nginx configs
* Add default config back
* Change the panel config name
---
.github/docker/entrypoint.sh | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/.github/docker/entrypoint.sh b/.github/docker/entrypoint.sh
index e6e1b0966..e2df6439c 100644
--- a/.github/docker/entrypoint.sh
+++ b/.github/docker/entrypoint.sh
@@ -30,7 +30,7 @@ else
fi
echo "Checking if https is required."
-if [ -f /etc/nginx/conf.d/default.conf ]; then
+if [ -f /etc/nginx/http.d/panel.conf ]; then
echo "Using nginx config already in place."
if [ $LE_EMAIL ]; then
echo "Checking for cert update"
@@ -42,15 +42,17 @@ else
echo "Checking if letsencrypt email is set."
if [ -z $LE_EMAIL ]; then
echo "No letsencrypt email is set using http config."
- cp .github/docker/default.conf /etc/nginx/conf.d/default.conf
+ cp .github/docker/default.conf /etc/nginx/http.d/panel.conf
else
echo "writing ssl config"
- cp .github/docker/default_ssl.conf /etc/nginx/conf.d/default.conf
+ cp .github/docker/default_ssl.conf /etc/nginx/http.d/panel.conf
echo "updating ssl config for domain"
- sed -i "s||$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/conf.d/default.conf
+ sed -i "s||$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/http.d/panel.conf
echo "generating certs"
certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n
fi
+ echo "Removing the default nginx config"
+ rm -rf /etc/nginx/http.d/default.conf
fi
## check for DB up before starting the panel
From 5b6de4df6f76d971944ad3c5b950ddda3007c776 Mon Sep 17 00:00:00 2001
From: Alex
Date: Sat, 9 Oct 2021 20:31:47 +0300
Subject: [PATCH 6/9] eggs(rust): custom map url (#3625)
Introduces custom map URL variable. If none is provided, it will default to using normal map size and seed. Otherwise, it will use the custom map and remove map size/seed from the startup as required.
---
database/Seeders/eggs/rust/egg-rust.json | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/database/Seeders/eggs/rust/egg-rust.json b/database/Seeders/eggs/rust/egg-rust.json
index 70eaadbad..60144bfa7 100644
--- a/database/Seeders/eggs/rust/egg-rust.json
+++ b/database/Seeders/eggs/rust/egg-rust.json
@@ -4,7 +4,7 @@
"version": "PTDL_v1",
"update_url": null
},
- "exported_at": "2021-05-29T19:02:43-04:00",
+ "exported_at": "2021-09-15T17:07:50-04:00",
"name": "Rust",
"author": "support@pterodactyl.io",
"description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.",
@@ -13,11 +13,11 @@
"quay.io\/pterodactyl\/core:rust"
],
"file_denylist": [],
- "startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} {{ADDITIONAL_ARGS}}",
+ "startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}",
"config": {
"files": "{}",
- "startup": "{\r\n \"done\": \"Server startup complete\",\r\n \"userInteraction\": []\r\n}",
- "logs": "{\r\n \"custom\": false,\r\n \"location\": \"latest.log\"\r\n}",
+ "startup": "{\r\n \"done\": \"Server startup complete\"\r\n}",
+ "logs": "{}",
"stop": "quit"
},
"scripts": {
@@ -162,6 +162,15 @@
"user_viewable": true,
"user_editable": true,
"rules": "nullable|url"
+ },
+ {
+ "name": "Custom Map URL",
+ "description": "Overwrites the map with the one from the direct download URL. Invalid URLs will cause the server to crash.",
+ "env_variable": "MAP_URL",
+ "default_value": "",
+ "user_viewable": true,
+ "user_editable": true,
+ "rules": "nullable|url"
}
]
-}
+}
\ No newline at end of file
From c12f1463b084f27fcc4074cc1639a4733fd5fffe Mon Sep 17 00:00:00 2001
From: Alex
Date: Sun, 10 Oct 2021 20:50:01 +0300
Subject: [PATCH 7/9] eggs(forge): Add support for 1.17+ Forge (#3676)
Support new 1.17+ Forge JPMS arguments that don't ship any executable jar. It will use unix_args.txt file for 1.17+ when one exists, otherwise defaults to using the jar file
Fix forge latest build version option to actually use latest instead of recommended
Set build version input rules to only accept valid values of the latest and recommended
Remove spaces from the version variables to avoid issues with curl. Forge site displays versions with spaces to end users
---
.../Seeders/eggs/minecraft/egg-forge-minecraft.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json
index fa750a449..6ac6eaaf7 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-07-04T19:18:55-04:00",
+ "exported_at": "2021-10-10T07:10:13-04: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.",
@@ -18,16 +18,16 @@
"ghcr.io\/pterodactyl\/yolks:java_16"
],
"file_denylist": [],
- "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}",
+ "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\" )",
"config": {
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\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": {
"installation": {
- "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\n#Go into main direction\r\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\n FORGE_JAR=forge-${FORGE_VERSION}*.jar\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [ \"${MC_VERSION}\" == \"latest\" ] || [ \"${MC_VERSION}\" == \"\" ] ; then\r\n echo -e \"getting latest recommended version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"recommended\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n \tBUILD_TYPE=recommended\r\n fi\r\n\r\n if [ \"${BUILD_TYPE}\" != \"recommended\" ] && [ \"${BUILD_TYPE}\" != \"latest\" ]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n\tFILE_SITE=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [ \"${VERSION_KEY}\" == \"\" ] && [ \"${BUILD_TYPE}\" == \"recommended\" ]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"recommended\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n \techo -e \"The install failed because there is no valid version of forge for the version on minecraft selected.\"\r\n \texit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ] || [ \"${MC_VERSION}\" == \"1.8.9\" ]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\necho -e \"Download link is ${DOWNLOAD_LINK}\"\r\nif [ ! -z \"${DOWNLOAD_LINK}\" ]; then \r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid closing out\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link closing out\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n#Checking if downloaded jars exist\r\nif [ ! -f .\/installer.jar ]; then\r\n echo \"!!! Error by downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed\"; exit 4; }\r\n\r\nmv $FORGE_JAR $SERVER_JARFILE\r\n\r\n#Deleting installer.jar\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar",
+ "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n# Remove spaces from the version number to avoid issues with curl\r\nFORGE_VERSION=\"$(echo \"$FORGE_VERSION\" | tr -d ' ')\"\r\nMC_VERSION=\"$(echo \"$MC_VERSION\" | tr -d ' ')\"\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\n FORGE_JAR=forge-${FORGE_VERSION}*.jar\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [ \"${MC_VERSION}\" == \"latest\" ] || [ \"${MC_VERSION}\" == \"\" ] ; then\r\n echo -e \"getting latest version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"recommended\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n \tBUILD_TYPE=latest\r\n fi\r\n\r\n if [ \"${BUILD_TYPE}\" != \"recommended\" ] && [ \"${BUILD_TYPE}\" != \"latest\" ]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n\tFILE_SITE=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [ \"${VERSION_KEY}\" == \"\" ] && [ \"${BUILD_TYPE}\" == \"recommended\" ]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"recommended\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n \techo -e \"The install failed because there is no valid version of forge for the version of minecraft selected.\"\r\n \texit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ] || [ \"${MC_VERSION}\" == \"1.8.9\" ]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\necho -e \"Download link is ${DOWNLOAD_LINK}\"\r\n\r\nif [ ! -z \"${DOWNLOAD_LINK}\" ]; then\r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid. Exiting now\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link provided. Exiting now\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n\r\n#Checking if downloaded jars exist\r\nif [ ! -f .\/installer.jar ]; then\r\n echo \"!!! Error by downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\n# Delete args file to support downgrading to versions below 1.17\r\nif [ -f unix_args.txt ]; then\r\n rm unix_args.txt\r\n exit\r\nfi\r\n\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed\"; exit 4; }\r\n\r\nif [[ $MC_VERSION == *\"1.17.\"* || $MC_VERSION == *\"1.18.\"* || $FORGE_VERSION == *\"1.17.\"* || $FORGE_VERSION == *\"1.18.\"* ]]; then\r\n # Create a symlink for 1.17+ Forge args\r\n ln -sf libraries\/net\/minecraftforge\/forge\/*\/unix_args.txt unix_args.txt\r\n else\r\n # For versions below 1.17 that ship with jar delete installer.jar\r\n mv $FORGE_JAR $SERVER_JARFILE\r\n echo -e \"Deleting installer.jar file.\\n\"\r\n rm -rf installer.jar\r\nfi",
"container": "openjdk:8-jdk-slim",
"entrypoint": "bash"
}
@@ -35,7 +35,7 @@
"variables": [
{
"name": "Server Jar File",
- "description": "The name of the Jarfile to use when running Forge Mod.",
+ "description": "The name of the Jarfile to use when running Forge version below 1.17.",
"env_variable": "SERVER_JARFILE",
"default_value": "server.jar",
"user_viewable": true,
@@ -58,7 +58,7 @@
"default_value": "recommended",
"user_viewable": true,
"user_editable": true,
- "rules": "required|string|max:20"
+ "rules": "required|string|in:recommended,latest"
},
{
"name": "Forge Version",
From f77932a6174ba5d9df7ede454bf05646cdb6a491 Mon Sep 17 00:00:00 2001
From: Alex
Date: Sun, 10 Oct 2021 21:08:22 +0300
Subject: [PATCH 8/9] cmd(upgrade): Attempt to gain users attention during
upgrade (#3678)
* cmd(upgrade): Attempt to gain users attention during upgrade
Changes color of the user and group to gain attention, common issue is having wrong user/group which breaks the panel. Outputs termination message when users spam enter skipping the upgrade wondering why it didn't upgrade.
Reminder to update wings, because users forget it.
* cmd(upgrade): Display wings upgrade documentation link
---
app/Console/Commands/UpgradeCommand.php | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/Console/Commands/UpgradeCommand.php b/app/Console/Commands/UpgradeCommand.php
index feb21ec60..224329869 100644
--- a/app/Console/Commands/UpgradeCommand.php
+++ b/app/Console/Commands/UpgradeCommand.php
@@ -57,7 +57,7 @@ class UpgradeCommand extends Command
$userDetails = posix_getpwuid(fileowner('public'));
$user = $userDetails['name'] ?? 'www-data';
- if (!$this->confirm("Your webserver user has been detected as [{$user}]: is this correct?", true)) {
+ if (!$this->confirm("Your webserver user has been detected as [{$user}]:> is this correct?", true)) {
$user = $this->anticipate(
'Please enter the name of the user running your webserver process. This varies from system to system, but is generally "www-data", "nginx", or "apache".',
[
@@ -73,7 +73,7 @@ class UpgradeCommand extends Command
$groupDetails = posix_getgrgid(filegroup('public'));
$group = $groupDetails['name'] ?? 'www-data';
- if (!$this->confirm("Your webserver group has been detected as [{$group}]: is this correct?", true)) {
+ if (!$this->confirm("Your webserver group has been detected as [{$group}]:> is this correct?", true)) {
$group = $this->anticipate(
'Please enter the name of the group running your webserver process. Normally this is the same as your user.',
[
@@ -86,6 +86,7 @@ class UpgradeCommand extends Command
}
if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) {
+ $this->warn('Upgrade process terminated by user.');
return;
}
}
@@ -173,8 +174,8 @@ class UpgradeCommand extends Command
$this->call('up');
});
- $this->newLine();
- $this->info('Finished running upgrade.');
+ $this->newLine(2);
+ $this->info('Panel has been successfully upgraded. Please ensure you also update any Wings instances: https://pterodactyl.io/wings/1.0/upgrading.html');
}
protected function withProgress(ProgressBar $bar, Closure $callback)
From 22a8b2b3a2e3277155440b8e73a070b21ea402f7 Mon Sep 17 00:00:00 2001
From: Dane Everitt
Date: Sat, 23 Oct 2021 12:17:16 -0700
Subject: [PATCH 9/9] Use more standardized rate limiting in Laravel; apply
limits to auth routes
---
app/Http/Kernel.php | 1 -
app/Providers/RouteServiceProvider.php | 106 ++++++++++++++++++-------
routes/auth.php | 20 +++--
3 files changed, 90 insertions(+), 37 deletions(-)
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index d2e0f2cc7..30fcd8ce4 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -112,7 +112,6 @@ class Kernel extends HttpKernel
'bindings' => SubstituteBindings::class,
'recaptcha' => VerifyReCaptcha::class,
'node.maintenance' => MaintenanceMiddleware::class,
-
// API Specific Middleware
'api..key' => AuthenticateKey::class,
];
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index 0ea33b5da..2dedacb4a 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -2,7 +2,10 @@
namespace Pterodactyl\Providers;
+use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
+use Illuminate\Cache\RateLimiting\Limit;
+use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
@@ -17,43 +20,86 @@ class RouteServiceProvider extends ServiceProvider
protected $namespace = 'Pterodactyl\Http\Controllers';
/**
- * Define the routes for the application.
+ * Define your route model bindings, pattern filters, etc.
*/
- public function map()
+ public function boot()
{
- Route::middleware(['web', 'auth', 'csrf'])
- ->namespace($this->namespace . '\Base')
- ->group(base_path('routes/base.php'));
+ $this->configureRateLimiting();
- Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
- ->namespace($this->namespace . '\Admin')
- ->group(base_path('routes/admin.php'));
+ $this->routes(function () {
+ Route::middleware(['web', 'auth', 'csrf'])
+ ->namespace("$this->namespace\\Base")
+ ->group(base_path('routes/base.php'));
- Route::middleware(['web', 'csrf'])->prefix('/auth')
- ->namespace($this->namespace . '\Auth')
- ->group(base_path('routes/auth.php'));
+ Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
+ ->namespace("$this->namespace\\Admin")
+ ->group(base_path('routes/admin.php'));
- Route::middleware(['web', 'csrf', 'auth', 'server', 'node.maintenance'])
- ->prefix('/api/server/{server}')
- ->namespace($this->namespace . '\Server')
- ->group(base_path('routes/server.php'));
+ Route::middleware(['web', 'csrf'])->prefix('/auth')
+ ->namespace("$this->namespace\\Auth")
+ ->group(base_path('routes/auth.php'));
- Route::middleware([
- sprintf('throttle:%s,%s', config('http.rate_limit.application'), config('http.rate_limit.application_period')),
- 'api',
- ])->prefix('/api/application')
- ->namespace($this->namespace . '\Api\Application')
- ->group(base_path('routes/api-application.php'));
+ Route::middleware(['web', 'csrf', 'auth', 'server', 'node.maintenance'])
+ ->prefix('/api/server/{server}')
+ ->namespace("$this->namespace\\Server")
+ ->group(base_path('routes/server.php'));
- Route::middleware([
- sprintf('throttle:%s,%s', config('http.rate_limit.client'), config('http.rate_limit.client_period')),
- 'client-api',
- ])->prefix('/api/client')
- ->namespace($this->namespace . '\Api\Client')
- ->group(base_path('routes/api-client.php'));
+ Route::middleware(['api', 'throttle:api.application'])
+ ->prefix('/api/application')
+ ->namespace("$this->namespace\\Api\\Application")
+ ->group(base_path('routes/api-application.php'));
- Route::middleware(['daemon'])->prefix('/api/remote')
- ->namespace($this->namespace . '\Api\Remote')
- ->group(base_path('routes/api-remote.php'));
+ Route::middleware(['client-api', 'throttle:api.client'])
+ ->prefix('/api/client')
+ ->namespace("$this->namespace\\Api\\Client")
+ ->group(base_path('routes/api-client.php'));
+
+ Route::middleware(['daemon'])->prefix('/api/remote')
+ ->namespace("$this->namespace\\Api\\Remote")
+ ->group(base_path('routes/api-remote.php'));
+ });
+ }
+
+ /**
+ * Configure the rate limiters for the application.
+ */
+ protected function configureRateLimiting()
+ {
+ // Authentication rate limiting. For login and checkpoint endpoints we'll apply
+ // a limit of 10 requests per minute, for the forgot password endpoint apply a
+ // limit of two per minute for the requester so that there is less ability to
+ // trigger email spam.
+ RateLimiter::for('authentication', function (Request $request) {
+ if ($request->route()->named('auth.post.forgot-password')) {
+ return Limit::perMinute(2)->by($request->ip());
+ }
+
+ return Limit::perMinute(10);
+ });
+
+ // Configure the throttles for both the application and client APIs below.
+ // This is configurable per-instance in "config/http.php". By default this
+ // limiter will be tied to the specific request user, and falls back to the
+ // request IP if there is no request user present for the key.
+ //
+ // This means that an authenticated API user cannot use IP switching to get
+ // around the limits.
+ RateLimiter::for('api.client', function (Request $request) {
+ $key = optional($request->user())->uuid ?: $request->ip();
+
+ return Limit::perMinutes(
+ config('http.rate_limit.client_period'),
+ config('http.rate_limit.client')
+ )->by($key);
+ });
+
+ RateLimiter::for('api.application', function (Request $request) {
+ $key = optional($request->user())->uuid ?: $request->ip();
+
+ return Limit::perMinutes(
+ config('http.rate_limit.application_period'),
+ config('http.rate_limit.application')
+ )->by($key);
+ });
}
}
diff --git a/routes/auth.php b/routes/auth.php
index 4bdb72206..2e9a01eaf 100644
--- a/routes/auth.php
+++ b/routes/auth.php
@@ -15,13 +15,21 @@ Route::group(['middleware' => 'guest'], function () {
Route::get('/password', 'LoginController@index')->name('auth.forgot-password');
Route::get('/password/reset/{token}', 'LoginController@index')->name('auth.reset');
- // Login endpoints.
- Route::post('/login', 'LoginController@login')->middleware('recaptcha');
- Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.login-checkpoint');
+ // Apply a throttle to authentication action endpoints, in addition to the
+ // recaptcha endpoints to slow down manual attack spammers even more. 🤷
+ //
+ // @see \Pterodactyl\Providers\RouteServiceProvider
+ Route::middleware(['throttle:authentication'])->group(function () {
+ // Login endpoints.
+ Route::post('/login', 'LoginController@login')->middleware('recaptcha');
+ Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.login-checkpoint');
- // Forgot password route. A post to this endpoint will trigger an
- // email to be sent containing a reset token.
- Route::post('/password', 'ForgotPasswordController@sendResetLinkEmail')->middleware('recaptcha');
+ // Forgot password route. A post to this endpoint will trigger an
+ // email to be sent containing a reset token.
+ Route::post('/password', 'ForgotPasswordController@sendResetLinkEmail')
+ ->name('auth.post.forgot-password')
+ ->middleware('recaptcha');
+ });
// Password reset routes. This endpoint is hit after going through
// the forgot password routes to acquire a token (or after an account