Skip to content

Commit

Permalink
fix: fix log4j, add repl for RCON, update config
Browse files Browse the repository at this point in the history
  • Loading branch information
ariscript committed Dec 16, 2021
1 parent 532f03a commit d1208d6
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 30 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"postcss-loader": "^6.2.0",
"prettier": "^2.5.0",
"style-loader": "^3.0.0",
"tailwind-scrollbar": "^1.3.1",
"tailwindcss": "^2.2.19",
"ts-loader": "^9.2.2",
"typescript": "^4.0.2"
Expand Down
9 changes: 5 additions & 4 deletions resources/server.properties
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#Minecraft server properties
enable-jmx-monitoring=false
rcon.port=25575
enable-command-block=false
gamemode=survival
enable-command-block=false
enable-query=true
level-name=world
motd=A Minecraft Server
query.port=25565
pvp=true
difficulty=easy
network-compression-threshold=256
max-tick-time=60000
require-resource-pack=false
max-players=20
max-tick-time=60000
use-native-transport=true
max-players=20
online-mode=true
enable-status=true
allow-flight=false
Expand All @@ -27,8 +27,10 @@ enable-rcon=true
sync-chunk-writes=true
op-permission-level=4
prevent-proxy-connections=false
hide-online-players=false
resource-pack=
entity-broadcast-range-percentage=100
simulation-distance=10
rcon.password=multiserver
player-idle-timeout=0
force-gamemode=false
Expand All @@ -38,7 +40,6 @@ white-list=false
broadcast-console-to-ops=true
spawn-npcs=true
spawn-animals=true
snooper-enabled=true
function-permission-level=2
text-filtering-config=
spawn-monsters=true
Expand Down
3 changes: 1 addition & 2 deletions src/app.global.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif;
margin: auto;
max-width: 38rem;
padding: 2rem;
padding: 1rem;
}
87 changes: 87 additions & 0 deletions src/components/Repl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState, useEffect, useRef } from "react";

import "../app.global.css";

interface ReplCommandData {
command: string;
data: string;
}

interface ReplProps {
exec(command: string): Promise<string>;
}

const Repl = ({ exec }: ReplProps): JSX.Element => {
const [history, setHistory] = useState<ReplCommandData[]>([]);
const [commands, setCommands] = useState<ReplCommandData[]>([]);
const [command, setCommand] = useState("");
const [isExec, setIsExec] = useState(false);
const [placeholder, setPlaceholder] = useState("");
const [index, setIndex] = useState(0);

const log = useRef<HTMLDivElement>(null);
const term = useRef<HTMLPreElement>(null);

useEffect(() => {
setCommands(history.filter(({ command }) => command));

if (term.current) term.current.scrollTop = term.current?.scrollHeight;
}, [history]);

useEffect(() => {
if (index && commands[index - 1])
setCommand(commands[index - 1].command);
else if (!index) setCommand("");
}, [index]);

return (
<pre ref={term} className="overflow-scroll">
<div ref={log}>
{[...history].reverse().map((e, i) => (
<div key={i}>
<span>
{">"} {e.command}
</span>
<div>{e.data}</div>
</div>
))}
</div>
{placeholder && (
<span>
{">"} {placeholder}
</span>
)}
<div>
{!isExec && <span>{"> "}</span>}
<input
autoFocus
type="text"
value={command}
onChange={(e) => setCommand(e.target.value)}
onKeyDown={async (e) => {
if (e.code === "Enter") {
if (command) {
setPlaceholder(command);
setCommand("");

const data = await exec(command);
setPlaceholder("");

setHistory([{ command, data }, ...history]);
return;
}
setHistory([{ command, data: "" }, ...history]);
} else if (e.code === "ArrowUp") {
if (index < commands.length)
return setIndex(index + 1);
} else if (e.code === "ArrowDown") {
if (index > 0) return setIndex(index - 1);
}
}}
/>
</div>
</pre>
);
};

export default Repl;
60 changes: 59 additions & 1 deletion src/lib/instances/createInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export async function createInstance(
log.silly("Writing eula.txt");
await fs.writeFile(path.join(instanceRoot, "eula.txt"), "eula=true");

log.silly("Writing server.properties using 1.17.1 template");
log.silly("Writing server.properties using 1.18.1 template");
await fs.copyFile(
path.join(resourcesPath, "server.properties"),
path.join(instanceRoot, "server.properties")
Expand Down Expand Up @@ -120,6 +120,64 @@ export async function createInstance(
stream.on("finish", () => stream.close());
});

// apply fix for log4j vulnerability (CVE-2021-44228)
const [_, versionMajor] = opts.version.split(".").map(Number);

if (versionMajor < 18 || opts.version == "1.18") {
let log4jArg = "";

if (versionMajor >= 7 && versionMajor <= 11) {
log.info("Applying log4j fix for 1.7-1.11.2");

https.get(
"https://launcher.mojang.com/v1/objects/4bb89a97a66f350bc9f73b3ca8509632682aea2e/log4j2_17-111.xml",
(res) => {
const stream = createWriteStream(
path.join(instanceRoot, "log4j2_17-111.xml")
);
res.pipe(stream);
stream.on("finish", () => stream.close());
}
);

log4jArg = "-Dlog4j.configurationFile=log4j2_17-111.xml";
} else if (versionMajor >= 12 && versionMajor <= 16) {
log.info("Applying log4j fix for 1.12-1.16.5");

https.get(
"https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml",
(res) => {
const stream = createWriteStream(
path.join(instanceRoot, "log4j2_112-116.xml")
);
res.pipe(stream);
stream.on("finish", () => stream.close());
}
);

log4jArg = "-Dlog4j.configurationFile=log4j2_112-116.xml";
} else if (versionMajor === 17 || opts.version === "1.18") {
log4jArg = "-Dlog4j2.formatMsgNoLookups=true";
}

log.debug("Rewriting config file to add log4j fix JVM argument");
await fs.writeFile(
path.join(instanceRoot, "multiserver.config.json"),
JSON.stringify(
{
...opts,
jvmArgs:
(opts.jvmArgs ?? "") +
`${
(opts.jvmArgs?.length ?? 0) > 0 ? " " : ""
}${log4jArg}`,
},
undefined,
4
)
);
}

log.info("Server creation complete");
return true;
} catch (e) {
Expand Down
6 changes: 2 additions & 4 deletions src/lib/instances/runInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function runInstance(
let oldPlayers: string[] = [];

server.stdout.on("data", (data) => {
log.debug(`SERVER ${info.name} info: ${String(data)}`);
log.debug(`SERVER ${info.name} info: ${String(data).trim()}`);

if (String(data).includes("RCON running on 0.0.0.0:25575")) {
log.info("Server loading complete, RCON connecting");
Expand All @@ -57,15 +57,13 @@ export async function runInstance(
});

server.stderr.on("data", (data) => {
log.debug(`SERVER ${info.name} error: ${String(data)}`);
log.debug(`SERVER ${info.name} error: ${String(data).trim()}`);
if (!window.isDestroyed())
window.webContents.send("stderr", String(data));
});

// eslint-disable-next-line @typescript-eslint/no-misused-promises
const playerQuery = setInterval(async () => {
log.debug("interval running");

try {
const results = await queryFull("localhost");

Expand Down
52 changes: 35 additions & 17 deletions src/windows/RunWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,72 @@ import { Button, TextField } from "@mui/material";
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import Helmet from "react-helmet";
import Repl from "../components/Repl";

import "../app.global.css";
import { InstanceInfo } from "../types";

interface ServerOutput {
type: "stdout" | "stderr";
content: string;
}

const RunWindow = (): JSX.Element => {
const [info, setInfo] = useState<InstanceInfo | null>(null);
const [players, setPlayers] = useState<string[]>([]);
const [stdout, setStdout] = useState<string[]>([]);
const [stderr, setStderr] = useState<string[]>([]);
const [output, setOutput] = useState<ServerOutput[]>([]);

const [command, setCommand] = useState<string>("");

useEffect(() => {
server.onInfo(setInfo);
server.onPlayers(setPlayers);

server.onStdout((out) => setStdout([...stdout, out]));
server.onStderr((err) => setStderr([...stderr, err]));
server.onStdout((out) =>
setOutput((s) => [...s, { type: "stdout", content: out }])
);
server.onStderr((err) =>
setOutput((s) => [...s, { type: "stderr", content: err }])
);
}, []);

return (
<div>
<div className="overflow-y-hidden">
<Helmet>
<title>{`Running server ${info?.info.name ?? ""}`}</title>
</Helmet>

<div className="grid grid-cols-2">
<div>
<div className="grid grid-cols-3 max-h-full">
<div className="col-span-1">
<h3>Players</h3>
<ul>
{players.map((p) => (
<li key={p}>{p}</li>
))}
</ul>
</div>
<div>
<h3>Log</h3>
<div className="grid grid-rows-2 h-full max-h-full col-span-2">
<div>
<h3>Log</h3>
<pre className="overflow-scroll rounded-md bg-gray-400 max-h-full scrollbar-thin scrollbar-track-transparent scrollbar-thumb-rounded scrollbar-thumb-blue-700">
{output.map((o) => (
<div
key={o.content}
className={
o.type === "stderr"
? "text-red-600"
: ""
}
>
{o.content}
</div>
))}
</pre>
</div>
<div>
<pre>{stdout.join("\n")}</pre>
<pre className="text-red-600">{stderr.join("\n")}</pre>
<Repl exec={server.rcon} />
</div>
</div>
<TextField
label="Command"
value={command}
onChange={(e) => setCommand(e.target.value)}
/>
<Button onClick={() => server.rcon(command)}>Send</Button>
</div>
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const colors = require("tailwindcss/colors");

module.exports = {
mode: "jit",
purge: [
"./src/windows/**/*.{js,jsx,ts,tsx}",
"./src/components/**/*.{js,jsx,ts,tsx}",
Expand All @@ -19,5 +20,8 @@ module.exports = {
variants: {
extend: {},
},
plugins: [],
plugins: [require("tailwind-scrollbar")],
variants: {
scrollbar: ["rounded"],
},
};
9 changes: 8 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6660,7 +6660,14 @@ table@^6.0.9:
string-width "^4.2.3"
strip-ansi "^6.0.1"

tailwindcss@^2.2.19:
tailwind-scrollbar@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/tailwind-scrollbar/-/tailwind-scrollbar-1.3.1.tgz#271d7c405bcddb7b5f5f77d7f43758c89e389767"
integrity sha512-FeYuLxLtCRMO4PmjPJCzm5wQouFro2BInZXKPxqg54DR/55NAHoS8uNYWMiRG5l6qsLkWBfVEM34gq2XAQUwVg==
dependencies:
tailwindcss ">1.9.6"

tailwindcss@>1.9.6, tailwindcss@^2.2.19:
version "2.2.19"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.19.tgz#540e464832cd462bb9649c1484b0a38315c2653c"
integrity sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==
Expand Down

0 comments on commit d1208d6

Please # to comment.