From fa08f03984d3a6d5d36b7c861a93c1cdc5527e0f Mon Sep 17 00:00:00 2001 From: lex Date: Thu, 22 Oct 2026 23:11:27 +0300 Subject: [PATCH 1/2] Read rcon password from settings.json The rcon password was a hardcoded constant, so every server started with the same, publicly known password. Since #132 this also applies on Linux. Add an optional rcon_password setting to the wrapper's settings.json. If it's empty or missing, a random password is generated each time the wrapper starts, so existing settings files keep working. The password is also no longer written to the wrapper log. Co-Authored-By: Claude Opus 5.5 --- FactorioWrapper/FactorioWrapper.cs | 8 ++++++++ FactorioWrapper/MainLoop.cs | 19 ++++++++++++------- FactorioWrapper/Settings.cs | 4 ++++ FactorioWrapper/settings.template.json | 3 ++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/FactorioWrapper/FactorioWrapper.cs b/FactorioWrapper/FactorioWrapper.cs index 979c03cb..cffb1ae5 100644 --- a/FactorioWrapper/FactorioWrapper.cs +++ b/FactorioWrapper/FactorioWrapper.cs @@ -2,6 +2,7 @@ using Serilog; using System; using System.IO; +using System.Linq; namespace FactorioWrapper { @@ -55,6 +56,13 @@ public static void Main(string[] args) settings.Url = url; } + // The password is passed to factorio as a command line argument, so it can't contain whitespace or quotes. + if (settings.RconPassword != null && settings.RconPassword.Any(c => char.IsWhiteSpace(c) || c == '"')) + { + Log.Fatal("rcon_password in settings.json must not contain whitespace or quotes"); + return; + } + if (args.Length < 3) { Log.Fatal("Missing arguments"); diff --git a/FactorioWrapper/MainLoop.cs b/FactorioWrapper/MainLoop.cs index 4af72b03..2a42065b 100644 --- a/FactorioWrapper/MainLoop.cs +++ b/FactorioWrapper/MainLoop.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; using System.Linq; +using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -25,7 +26,7 @@ public class MainLoop : IDisposable private readonly Settings settings; private readonly string serverId; private readonly string factorioFileName; - private string factorioArguments; + private readonly string factorioArguments; // This is to stop multiple threads writing to the factorio process concurrently. private readonly SemaphoreSlim factorioProcessLock = new SemaphoreSlim(1, 1); @@ -45,9 +46,9 @@ public class MainLoop : IDisposable private volatile RconMessenger rcon; private int rconPort; - private const string rconPassword = "no_one_will_guess_this_awesome_password."; + private readonly string rconPassword; - private void PrependRconArguments() + private void SetRconPort() { int index = factorioArguments.IndexOf("--port"); @@ -61,8 +62,6 @@ private void PrependRconArguments() string port = factorioArguments.Substring(start, end - start); int.TryParse(port, out rconPort); - - factorioArguments += $" --rcon-port {rconPort} --rcon-password {rconPassword}"; } private async Task ConnectRCON() @@ -88,7 +87,12 @@ public MainLoop(Settings settings, string serverId, string factorioFileName, str this.factorioFileName = factorioFileName; this.factorioArguments = factorioArguments; - PrependRconArguments(); + // Fall back to a random password so RCON is never left with a known password. + rconPassword = string.IsNullOrWhiteSpace(settings.RconPassword) + ? Convert.ToHexString(RandomNumberGenerator.GetBytes(32)) + : settings.RconPassword; + + SetRconPort(); messageQueue = new SingleConsumerQueue>(maxMessageQueueSize, async func => { @@ -384,7 +388,8 @@ private async Task StartFactorioProcess() factorioProcess = new Process(); var startInfo = factorioProcess.StartInfo; startInfo.FileName = factorioFileName; - startInfo.Arguments = factorioArguments; + // The rcon password is added after logging so that it doesn't end up in the log file. + startInfo.Arguments = $"{factorioArguments} --rcon-port {rconPort} --rcon-password {rconPassword}"; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; startInfo.RedirectStandardInput = true; diff --git a/FactorioWrapper/Settings.cs b/FactorioWrapper/Settings.cs index e0f119ef..72642cba 100644 --- a/FactorioWrapper/Settings.cs +++ b/FactorioWrapper/Settings.cs @@ -12,5 +12,9 @@ public class Settings [JsonProperty(PropertyName = "url")] [JsonPropertyName("url")] public string Url { get; set; } + + [JsonProperty(PropertyName = "rcon_password")] + [JsonPropertyName("rcon_password")] + public string RconPassword { get; set; } } } diff --git a/FactorioWrapper/settings.template.json b/FactorioWrapper/settings.template.json index 4b0e1263..4ee04aa5 100644 --- a/FactorioWrapper/settings.template.json +++ b/FactorioWrapper/settings.template.json @@ -1,4 +1,5 @@ { "token": "asdasdjuteoinbjnlkrutpeoplp.fkgcldfooklisfgsdlafhlfreFGTRydegsJJKGHURTDSwaerqsdfdfsgtJFHDfbgsDASDASDFERGDFBGsdfsdfSFQAWSEAZBBVJNHGJdfghfghSERWSFSVXBGFDgtDRTDGFJHJKykjHjHBKJfyjutygj_bLJyuhHUIOgJKBLnoOUiohuiGh_JKBjkGj-guiGhfu", - "url": "http://localhost/factorioProcessHub" + "url": "http://localhost/factorioProcessHub", + "rcon_password": "" } From 3074550cab294ad4abbd0faa5aab5a419d985231 Mon Sep 17 00:00:00 2001 From: lex Date: Thu, 22 Oct 2026 23:19:34 +0300 Subject: [PATCH 2/2] Bind rcon to localhost only --rcon-port makes Factorio listen for rcon on all interfaces. The wrapper only connects to rcon on 127.0.0.1, so bind it there and keep it off the public network. Co-Authored-By: Claude Opus 5.5 --- FactorioWrapper/MainLoop.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/FactorioWrapper/MainLoop.cs b/FactorioWrapper/MainLoop.cs index 2a42065b..d57ba869 100644 --- a/FactorioWrapper/MainLoop.cs +++ b/FactorioWrapper/MainLoop.cs @@ -389,7 +389,8 @@ private async Task StartFactorioProcess() var startInfo = factorioProcess.StartInfo; startInfo.FileName = factorioFileName; // The rcon password is added after logging so that it doesn't end up in the log file. - startInfo.Arguments = $"{factorioArguments} --rcon-port {rconPort} --rcon-password {rconPassword}"; + // Rcon is bound to localhost so that it isn't reachable from outside this machine. + startInfo.Arguments = $"{factorioArguments} --rcon-bind 127.0.0.1:{rconPort} --rcon-password {rconPassword}"; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; startInfo.RedirectStandardInput = true;