mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 09:14:09 +00:00
use admin key token for admin auth
This commit is contained in:
@@ -72,6 +72,8 @@ export interface ServerConfig {
|
|||||||
workerPort(gameID: GameID): number;
|
workerPort(gameID: GameID): number;
|
||||||
workerPortByIndex(workerID: number): number;
|
workerPortByIndex(workerID: number): number;
|
||||||
env(): GameEnv;
|
env(): GameEnv;
|
||||||
|
adminToken(): string;
|
||||||
|
adminHeader(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ import { pastelTheme } from "./PastelTheme";
|
|||||||
import { pastelThemeDark } from "./PastelThemeDark";
|
import { pastelThemeDark } from "./PastelThemeDark";
|
||||||
|
|
||||||
export abstract class DefaultServerConfig implements ServerConfig {
|
export abstract class DefaultServerConfig implements ServerConfig {
|
||||||
|
adminHeader(): string {
|
||||||
|
return "x-admin-key";
|
||||||
|
}
|
||||||
|
adminToken(): string {
|
||||||
|
return process.env.ADMIN_TOKEN;
|
||||||
|
}
|
||||||
numWorkers(): number {
|
numWorkers(): number {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -183,7 +189,7 @@ export class DefaultConfig implements Config {
|
|||||||
return {
|
return {
|
||||||
cost: (p: Player) =>
|
cost: (p: Player) =>
|
||||||
p.type() == PlayerType.Human && this.infiniteGold()
|
p.type() == PlayerType.Human && this.infiniteGold()
|
||||||
? 0
|
? 25
|
||||||
: 25_000_000,
|
: 25_000_000,
|
||||||
territoryBound: false,
|
territoryBound: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,9 +5,14 @@ import { GameEnv, ServerConfig } from "./Config";
|
|||||||
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
|
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
|
||||||
|
|
||||||
export class DevServerConfig extends DefaultServerConfig {
|
export class DevServerConfig extends DefaultServerConfig {
|
||||||
|
adminToken(): string {
|
||||||
|
return "WARNING_DEV_ADMIN_KEY_DO_NOT_USE_IN_PRODUCTION";
|
||||||
|
}
|
||||||
|
|
||||||
env(): GameEnv {
|
env(): GameEnv {
|
||||||
return GameEnv.Dev;
|
return GameEnv.Dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameCreationRate(highTraffic: boolean): number {
|
gameCreationRate(highTraffic: boolean): number {
|
||||||
return 5 * 1000;
|
return 5 * 1000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ async function fetchLobbies(): Promise<void> {
|
|||||||
async function schedulePublicGame() {
|
async function schedulePublicGame() {
|
||||||
const gameID = generateID();
|
const gameID = generateID();
|
||||||
publicLobbyIDs.add(gameID);
|
publicLobbyIDs.add(gameID);
|
||||||
|
|
||||||
// Create the default public game config (from your GameManager)
|
// Create the default public game config (from your GameManager)
|
||||||
const defaultGameConfig = {
|
const defaultGameConfig = {
|
||||||
gameMap: getNextMap(),
|
gameMap: getNextMap(),
|
||||||
@@ -192,9 +191,7 @@ async function schedulePublicGame() {
|
|||||||
disableNPCs: false,
|
disableNPCs: false,
|
||||||
bots: 400,
|
bots: 400,
|
||||||
};
|
};
|
||||||
|
|
||||||
const workerPath = config.workerPath(gameID);
|
const workerPath = config.workerPath(gameID);
|
||||||
|
|
||||||
// Send request to the worker to start the game
|
// Send request to the worker to start the game
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -203,7 +200,8 @@ async function schedulePublicGame() {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Internal-Request": "true", // Special header for internal requests
|
"X-Internal-Request": "true",
|
||||||
|
[config.adminHeader()]: config.adminToken(),
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
gameID: gameID,
|
gameID: gameID,
|
||||||
@@ -211,11 +209,9 @@ async function schedulePublicGame() {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to schedule public game: ${response.statusText}`);
|
throw new Error(`Failed to schedule public game: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -226,11 +222,9 @@ async function schedulePublicGame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map rotation management (moved from GameManager)
|
|
||||||
let mapsPlaylist: GameMapType[] = [];
|
let mapsPlaylist: GameMapType[] = [];
|
||||||
const random = new PseudoRandom(123);
|
const random = new PseudoRandom(123);
|
||||||
|
|
||||||
// Get the next map in rotation
|
|
||||||
function getNextMap(): GameMapType {
|
function getNextMap(): GameMapType {
|
||||||
if (mapsPlaylist.length > 0) {
|
if (mapsPlaylist.length > 0) {
|
||||||
return mapsPlaylist.shift()!;
|
return mapsPlaylist.shift()!;
|
||||||
|
|||||||
+4
-25
@@ -79,9 +79,9 @@ export function startWorker() {
|
|||||||
// TODO: if game is public make sure request came from localhohst!!!
|
// TODO: if game is public make sure request came from localhohst!!!
|
||||||
const clientIP = req.ip || req.socket.remoteAddress || "unknown";
|
const clientIP = req.ip || req.socket.remoteAddress || "unknown";
|
||||||
const gc = req.body?.gameConfig as GameConfig;
|
const gc = req.body?.gameConfig as GameConfig;
|
||||||
if (gc?.gameType == GameType.Public && !isLocalhost(req)) {
|
if (gc?.gameType == GameType.Public && !isAdmin(req)) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`cannot create public game ${id}, ip ${clientIP} not localhost`,
|
`cannot create public game ${id}, ip ${clientIP} not admin`,
|
||||||
);
|
);
|
||||||
return res.status(400);
|
return res.status(400);
|
||||||
}
|
}
|
||||||
@@ -320,27 +320,6 @@ export function startWorker() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLocalhost = (req: Request): boolean => {
|
const isAdmin = (req: Request): boolean => {
|
||||||
// Get client IP address from various possible sources
|
return req.headers[config.adminHeader()] === config.adminToken();
|
||||||
const clientIP =
|
|
||||||
req.ip ||
|
|
||||||
req.socket.remoteAddress ||
|
|
||||||
(req.headers["x-forwarded-for"] as string)?.split(",").shift() ||
|
|
||||||
"unknown";
|
|
||||||
|
|
||||||
// Check if the request is from a loopback address
|
|
||||||
const isLoopbackIP =
|
|
||||||
// IPv4 localhost
|
|
||||||
clientIP === "127.0.0.1" ||
|
|
||||||
// IPv6 localhost
|
|
||||||
clientIP === "::1" ||
|
|
||||||
// Full loopback range
|
|
||||||
clientIP.startsWith("127.");
|
|
||||||
|
|
||||||
// Check hostname
|
|
||||||
const isLocalHostname =
|
|
||||||
req.hostname === "localhost" || req.headers.host?.startsWith("localhost:");
|
|
||||||
|
|
||||||
// Consider request local if either IP is loopback or hostname is localhost
|
|
||||||
return isLoopbackIP || isLocalHostname;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user