From fb3fe094666f44d57a0aaa6bb181b2472f0aae3b Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Wed, 4 Mar 2026 17:43:32 +0100 Subject: [PATCH] Remove tlsCa option The migration from phin to fetch in #755 was hampred by the lack of an option to set the CA when using fetch. This meant places that used phin to talk to the controller with the tlsCa option could not be migrated to use fetch. Remove the tlsCa option to unblock this dependency. The replacement for this option is to set the NODE_EXTRA_CA_CERTS environment variable to a path to the certificate file instead, which works with fetch. --- docs/configuration.md | 16 ---------------- docs/setting-up-tls.md | 23 +++++++++++++++++++---- package.json | 2 +- packages/create/create.js | 18 ++++++++++++------ packages/ctl/ctl.ts | 17 +++-------------- packages/ctl/src/commands_controller.ts | 1 - packages/ctl/src/commands_instance.ts | 2 -- packages/ctl/src/commands_mod.ts | 2 -- packages/host/host.ts | 11 +---------- packages/host/src/Host.ts | 9 --------- packages/host/src/Instance.ts | 1 - packages/lib/src/config/definitions.ts | 13 ------------- packages/lib/src/link/connectors.ts | 13 +------------ packages/web_ui/src/bootstrap.tsx | 2 +- test/host.js | 4 ++-- test/integration/clusterio.js | 12 ++++-------- test/integration/index.js | 24 ++++++++++++++++-------- test/integration/link.js | 10 +++------- test/integration/routing.js | 2 +- test/messages/host.test.js | 2 +- test/messages/plugin.test.js | 2 +- 21 files changed, 66 insertions(+), 120 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8d8e73b7..2285e319 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -182,14 +182,6 @@ You can generate an access token with `clusterioctl host generate-token --id ; /** Keep the control connection alive after the command completes. */ @@ -64,12 +62,10 @@ export class Control extends lib.Link { constructor( connector: ControlConnector, controlConfig: lib.ControlConfig, - tlsCa: string | undefined, ctlPlugins: Map ) { super(connector); this.config = controlConfig; - this.tlsCa = tlsCa; this.plugins = ctlPlugins; this.handle(lib.LogMessageEvent, this.handleLogMessageEvent.bind(this)); @@ -299,20 +295,13 @@ async function startControl() { return; } - let tlsCa: string | undefined; - let tlsCaPath = controlConfig.get("control.tls_ca"); - if (tlsCaPath) { - tlsCa = await fs.readFile(tlsCaPath, "utf8"); - } - let controlConnector = new ControlConnector( controlConfig.get("control.controller_url")!, controlConfig.get("control.max_reconnect_delay"), - tlsCa, controlConfig.get("control.controller_token")!, ); - let control = new Control(controlConnector, controlConfig, tlsCa, ctlPlugins); + let control = new Control(controlConnector, controlConfig, ctlPlugins); try { await controlConnector.connect(); } catch (err) { diff --git a/packages/ctl/src/commands_controller.ts b/packages/ctl/src/commands_controller.ts index 9b6481d5..9c2837f6 100644 --- a/packages/ctl/src/commands_controller.ts +++ b/packages/ctl/src/commands_controller.ts @@ -169,7 +169,6 @@ controllerPluginCommands.add(new lib.Command({ let response = await phin<[]>({ url, parse: "json", - core: { ca: control.tlsCa } as object, }); print(asTable(response.body)); }, diff --git a/packages/ctl/src/commands_instance.ts b/packages/ctl/src/commands_instance.ts index 28a4efa4..20d5b92d 100644 --- a/packages/ctl/src/commands_instance.ts +++ b/packages/ctl/src/commands_instance.ts @@ -345,7 +345,6 @@ instanceSaveCommands.add(new lib.Command({ "X-Access-Token": control.config.get("control.controller_token"), "Content-Type": "application/zip", }, - core: { ca: control.tlsCa } as object, data: content, parse: "json", }); @@ -420,7 +419,6 @@ instanceSaveCommands.add(new lib.Command({ url.pathname += `api/stream/${streamId}`; let response = await phin({ url, method: "GET", - core: { ca: control.tlsCa } as object, stream: true, }); diff --git a/packages/ctl/src/commands_mod.ts b/packages/ctl/src/commands_mod.ts index d4f9a3ec..1fff3f42 100644 --- a/packages/ctl/src/commands_mod.ts +++ b/packages/ctl/src/commands_mod.ts @@ -160,7 +160,6 @@ modCommands.add(new lib.Command({ "X-Access-Token": control.config.get("control.controller_token"), "Content-Type": "application/zip", }, - core: { ca: control.tlsCa } as object, data: content, parse: "json", }); @@ -199,7 +198,6 @@ modCommands.add(new lib.Command({ url.pathname += `api/stream/${streamId}`; let response = await phin({ url, method: "GET", - core: { ca: control.tlsCa } as object, stream: true, }); diff --git a/packages/host/host.ts b/packages/host/host.ts index 19127713..8b06fdf0 100755 --- a/packages/host/host.ts +++ b/packages/host/host.ts @@ -51,13 +51,11 @@ void new lib.Gauge( export class HostConnector extends lib.WebSocketClientConnector { constructor( public hostConfig: lib.HostConfig, - tlsCa: string | undefined, public pluginInfos: lib.PluginNodeEnvInfo[] ) { super( hostConfig.get("host.controller_url"), hostConfig.get("host.max_reconnect_delay"), - tlsCa ); } @@ -230,17 +228,10 @@ async function startHost() { return; } - let tlsCa: string | undefined; - let tlsCaPath = hostConfig.get("host.tls_ca"); - if (tlsCaPath) { - tlsCa = await fs.readFile(tlsCaPath, "utf8"); - } - - let hostConnector = new HostConnector(hostConfig, tlsCa, pluginInfos); + let hostConnector = new HostConnector(hostConfig, pluginInfos); host = new Host( hostConnector, hostConfig, - tlsCa, pluginInfos, Boolean(args.canRestart), Boolean(args.recovery), diff --git a/packages/host/src/Host.ts b/packages/host/src/Host.ts index e59d6593..2b41decf 100644 --- a/packages/host/src/Host.ts +++ b/packages/host/src/Host.ts @@ -259,10 +259,6 @@ export default class Host extends lib.Link { declare "connector": HostConnector; router = new HostRouter(this); - /** - * Certificate authority used to validate TLS connections to the controller. - */ - tlsCa?: string; pluginInfos: lib.PluginNodeEnvInfo[]; config: lib.HostConfig; @@ -304,7 +300,6 @@ export default class Host extends lib.Link { constructor( connector: HostConnector, hostConfig: lib.HostConfig, - tlsCa: string | undefined, pluginInfos: lib.PluginNodeEnvInfo[], /** * If true indicates that there is a process monitor present that @@ -319,7 +314,6 @@ export default class Host extends lib.Link { public modStore = new lib.ModStore(hostConfig.get("host.mods_directory"), new Map()), ) { super(connector); - this.tlsCa = tlsCa; this.pluginInfos = pluginInfos; this.config = hostConfig; @@ -593,7 +587,6 @@ export default class Host extends lib.Link { url.pathname += `api/stream/${streamId}`; let response = await phin({ url, method: "GET", - core: { ca: this.tlsCa } as object, stream: true, }); @@ -1027,7 +1020,6 @@ export default class Host extends lib.Link { url.pathname += `api/stream/${streamId}`; let response = await phin({ url, method: "GET", - core: { ca: this.tlsCa } as object, stream: true, }); @@ -1082,7 +1074,6 @@ export default class Host extends lib.Link { url.pathname += `api/stream/${streamId}`; phin({ url, method: "PUT", - core: { ca: this.tlsCa } as object, data: content, }).catch(err => { logger.error(`Error pushing save to controller:\n${err.stack}`, this.instanceLogMeta(instanceId)); diff --git a/packages/host/src/Instance.ts b/packages/host/src/Instance.ts index af5b9ef4..57cf2e03 100644 --- a/packages/host/src/Instance.ts +++ b/packages/host/src/Instance.ts @@ -1295,7 +1295,6 @@ end`.replace(/\r?\n/g, " "); let response = await phin({ url, method: "PUT", data: content, - core: { ca: this._host.tlsCa } as object, headers: { "Content-Type": "application/zip", "x-access-token": this._host.config.get("host.controller_token"), diff --git a/packages/lib/src/config/definitions.ts b/packages/lib/src/config/definitions.ts index 18d30609..3b19512b 100644 --- a/packages/lib/src/config/definitions.ts +++ b/packages/lib/src/config/definitions.ts @@ -259,7 +259,6 @@ export interface HostConfigFields { "host.instances_directory": string; "host.controller_url": string; "host.controller_token": string; - "host.tls_ca": string | null; "host.public_address": string; "host.factorio_port_range": string; "host.factorio_username": string | null, @@ -327,12 +326,6 @@ export class HostConfig extends classes.Config { type: "string", initialValue: "enter token here", }, - "host.tls_ca": { - description: "Path to Certificate Authority to validate TLS connection to controller against.", - restartRequired: true, - type: "string", - optional: true, - }, "host.public_address": { description: "Public facing address players should connect to in order to join instances on this host", type: "string", @@ -624,7 +617,6 @@ export class InstanceConfig extends classes.Config { export interface ControlConfigFields { "control.controller_url": string | null; "control.controller_token": string | null; - "control.tls_ca": string | null; "control.max_reconnect_delay": number; } @@ -647,11 +639,6 @@ export class ControlConfig extends classes.Config { type: "string", optional: true, }, - "control.tls_ca": { - description: "Path to Certificate Authority to validate TLS connection to controller against.", - type: "string", - optional: true, - }, "control.max_reconnect_delay": { title: "Max Reconnect Delay", description: "Maximum delay to wait before attempting to reconnect WebSocket", diff --git a/packages/lib/src/link/connectors.ts b/packages/lib/src/link/connectors.ts index dd7df5d4..ebed9978 100644 --- a/packages/lib/src/link/connectors.ts +++ b/packages/lib/src/link/connectors.ts @@ -431,7 +431,6 @@ export abstract class WebSocketClientConnector extends WebSocketBaseConnector [p.name, p]))); control.plugins = await loadPlugins(pluginInfos, control); control.inputComponents = inputComponentsFromPlugins(control.plugins); diff --git a/test/host.js b/test/host.js index 63aa61bc..ad7a16aa 100644 --- a/test/host.js +++ b/test/host.js @@ -127,8 +127,8 @@ describe("Host testing", function() { version: hostVersion, }]; const hostConfig = new lib.HostConfig("host", { "host.version": hostVersion }); - const hostConnector = new HostConnector(hostConfig, undefined, pluginInfos); - host = new Host(hostConnector, hostConfig, undefined, pluginInfos); + const hostConnector = new HostConnector(hostConfig, pluginInfos); + host = new Host(hostConnector, hostConfig, pluginInfos); }); it("returns false when no changes are present", async function() { host.pluginInfos[0].hostEntrypoint = true; diff --git a/test/integration/clusterio.js b/test/integration/clusterio.js index 6052973f..59782207 100644 --- a/test/integration/clusterio.js +++ b/test/integration/clusterio.js @@ -41,7 +41,6 @@ async function startAltHost() { await fs.remove(path.join("temp", "test", "alt-instances")); await fs.remove(path.join("temp", "test", "alt-mods")); await execCtl(`host create-config --id 5 --name alt-host --generate-token --output ${config}`); - await execHost(`--config ${config} config set host.tls_ca ../../test/file/tls/cert.pem`); await execHost(`--config ${config} config set host.instances_directory alt-instances`); const dir = path.isAbsolute(factorioDir) ? factorioDir : path.join("..", "..", factorioDir); await execHost(`--config ${config} config set host.factorio_directory ${dir}`); @@ -168,8 +167,7 @@ describe("Integration of Clusterio", function() { describe("run", function() { it("should handle resume of an active connection", async function() { slowTest(this); - let tlsCa = await fs.readFile("test/file/tls/cert.pem"); - let connectorA = new TestControlConnector(url, 2, tlsCa); + let connectorA = new TestControlConnector(url, 2); connectorA.token = controlToken; let controlA = new TestControl(connectorA); await connectorA.connect(); @@ -177,7 +175,7 @@ describe("Integration of Clusterio", function() { connectorA.stopHeartbeat(); connectorA.on("error", () => {}); - let connectorB = new TestControlConnector(url, 2, tlsCa); + let connectorB = new TestControlConnector(url, 2); connectorB.token = controlToken; connectorB.src = connectorA.src; let controlB = new TestControl(connectorB); @@ -1679,8 +1677,7 @@ describe("Integration of Clusterio", function() { it("should kick existing sessions for the user", async function() { slowTest(this); await getControl().send(new lib.UserCreateRequest("revokee")); - let tlsCa = await fs.readFile("test/file/tls/cert.pem"); - let connector = new TestControlConnector(url, 2, tlsCa); + let connector = new TestControlConnector(url, 2); connector.token = jwt.sign( { aud: "user", user: "revokee" }, Buffer.from("TestSecretDoNotUse", "base64") ); @@ -1709,8 +1706,7 @@ describe("Integration of Clusterio", function() { await execCtl("user set-roles temp Player"); let tempControl; try { - let tlsCa = await fs.readFile("test/file/tls/cert.pem"); - let connector = new TestControlConnector(url, 2, tlsCa); + let connector = new TestControlConnector(url, 2); connector.token = jwt.sign( { aud: "user", user: "temp" }, Buffer.from("TestSecretDoNotUse", "base64") ); diff --git a/test/integration/index.js b/test/integration/index.js index aa6f24ca..fcaf41d0 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -194,11 +194,23 @@ let controllerConfigPath = path.join("temp", "test", "config-controller.json"); let hostConfigPath = path.join("temp", "test", "config-host.json"); let controlConfigPath = path.join("temp", "test", "config-control.json"); +function childOptions(options) { + return { + cwd: path.join("temp", "test"), + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS + ? path.resolve(process.env.NODE_EXTRA_CA_CERTS) + : undefined, + }, + ...options, + }; +} + function exec(command, options = {}) { // Uncomment to show commands run in tests // console.log(command); - options = { cwd: path.join("temp", "test"), ...options }; - return util.promisify(child_process.exec)(command, options); + return util.promisify(child_process.exec)(command, childOptions(options)); } function execController(...args) { @@ -264,7 +276,7 @@ function spawn(name, cmd, waitFor, options = {}) { return new Promise((resolve, reject) => { log(cmd); let parts = cmd.split(" "); - let process = child_process.spawn(parts[0], parts.slice(1), { cwd: path.join("temp", "test"), ...options }); + let process = child_process.spawn(parts[0], parts.slice(1), childOptions(options)); let stdout = new LineSplitter({ readableObjectMode: true }); let stderr = new LineSplitter({ readableObjectMode: true }); let onDataOut = line => { @@ -354,7 +366,6 @@ before(async function() { console.log("Bootstrapping"); await execController("bootstrap create-admin test"); await execController("bootstrap create-ctl-config test"); - await execCtlProcess("control-config set control.tls_ca ../../test/file/tls/cert.pem"); controllerProcess = await spawnNode("controller:", "../../packages/controller run", /Started controller/); @@ -362,12 +373,10 @@ before(async function() { const relativeFactorioDir = path.isAbsolute(factorioDir) ? factorioDir : path.join("..", "..", factorioDir); await execCtlProcess("host create-config --id 4 --name host --generate-token"); await execHost(`config set host.factorio_directory ${relativeFactorioDir}`); - await execHost("config set host.tls_ca ../../test/file/tls/cert.pem"); hostProcess = await spawnNode("host:", "../../packages/host run", /Started host/); - const tlsCa = await fs.readFile("test/file/tls/cert.pem"); - const controlConnector = new TestControlConnector(url, 2, tlsCa); + const controlConnector = new TestControlConnector(url, 2); controlConnector.token = controlToken; control = new TestControl(controlConnector); await controlConnector.connect(); @@ -375,7 +384,6 @@ before(async function() { const initArgs = await initializeCtl(`--plugin-list ${pluginListPath} control-config list`, undefined, true); control.config = initArgs.controlConfig; control.plugins = initArgs.ctlPlugins; - control.tlsCa = tlsCa; const testPack = lib.ModPack.fromJSON({}); testPack.id = 12; diff --git a/test/integration/link.js b/test/integration/link.js index be19acd6..3d75fbfd 100644 --- a/test/integration/link.js +++ b/test/integration/link.js @@ -11,18 +11,14 @@ let token = jwt.sign({ aud: "user", user: "test" }, Buffer.from("TestSecretDoNot let tokenHost = jwt.sign({ aud: "host", host: 0 }, Buffer.from("TestSecretDoNotUse", "base64")); describe("Integration of lib/link", function() { - let tlsCa; let control; let controlConnector; let hostConnector; - before(async function() { - tlsCa = await fs.readFile("test/file/tls/cert.pem"); - }); beforeEach(async function() { - controlConnector = new TestControlConnector(url, 0.2, tlsCa); + controlConnector = new TestControlConnector(url, 0.2); controlConnector.token = token; control = new TestControl(controlConnector, false); - hostConnector = new TestHostConnector(url, 0.2, tlsCa); + hostConnector = new TestHostConnector(url, 0.2); hostConnector.token = tokenHost; hostConnector.hostId = 0; }); @@ -123,7 +119,7 @@ describe("Integration of lib/link", function() { it("should close old connections from the same address", async function() { slowTest(this); - const hostConnectorFirst = new TestHostConnector(url, 0.2, tlsCa); + const hostConnectorFirst = new TestHostConnector(url, 0.2); hostConnectorFirst.token = hostConnector.token; hostConnectorFirst.hostId = hostConnector.hostId; const onceClose = events.once(hostConnectorFirst, "close"); diff --git a/test/integration/routing.js b/test/integration/routing.js index 02c5fcff..a4672305 100644 --- a/test/integration/routing.js +++ b/test/integration/routing.js @@ -23,7 +23,7 @@ function connectHost(controller, hostId, plugins) { controller.wsServer.hostConnections.set(hostId, hostConnection); const hostConfig = new lib.HostConfig("host"); hostConfig.set("host.id", hostId); - return new Host(hostSide, hostConfig, undefined, []); + return new Host(hostSide, hostConfig, []); } /** diff --git a/test/messages/host.test.js b/test/messages/host.test.js index faf3093d..56b1d121 100644 --- a/test/messages/host.test.js +++ b/test/messages/host.test.js @@ -14,7 +14,7 @@ describe("messages/host", function() { lib.Address.fromShorthand({ hostId: 1 }), lib.Address.fromShorthand("controller"), ); - host = new Host(hostConnector, hostConfig, undefined, []); + host = new Host(hostConnector, hostConfig, []); }); describe("HostUpdateRequest", function() { diff --git a/test/messages/plugin.test.js b/test/messages/plugin.test.js index 5b83bc8f..e2c7d9d8 100644 --- a/test/messages/plugin.test.js +++ b/test/messages/plugin.test.js @@ -28,7 +28,7 @@ describe("messages/plugin", function() { lib.Address.fromShorthand({ hostId: 1 }), lib.Address.fromShorthand("controller"), ); - host = new Host(hostConnector, hostConfig, undefined, []); + host = new Host(hostConnector, hostConfig, []); }); describe("PluginDetails", function() {