diff --git a/build/Dockerfile b/build/Dockerfile
index b301591833e..c281147ddd5 100644
--- a/build/Dockerfile
+++ b/build/Dockerfile
@@ -69,6 +69,7 @@ EXPOSE 5000
ENV General__ValidInstancePaths__0 /tgs_instances
ENV FileLogging__Directory /tgs_logs
+ENV Internal__UsingDocker true
WORKDIR /app
diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs
index c86f5c8eded..5f6a101260c 100644
--- a/src/Tgstation.Server.Host/Components/InstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs
@@ -130,6 +130,11 @@ sealed class InstanceManager :
///
readonly SwarmConfiguration swarmConfiguration;
+ ///
+ /// The for the .
+ ///
+ readonly InternalConfiguration internalConfiguration;
+
///
/// The for .
///
@@ -177,6 +182,7 @@ sealed class InstanceManager :
/// The value of .
/// The containing the value of .
/// The containing the value of .
+ /// The containing the value of .
/// The value of .
public InstanceManager(
IInstanceFactory instanceFactory,
@@ -193,6 +199,7 @@ public InstanceManager(
IPlatformIdentifier platformIdentifier,
IOptions generalConfigurationOptions,
IOptions swarmConfigurationOptions,
+ IOptions internalConfigurationOptions,
ILogger logger)
{
this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
@@ -209,6 +216,7 @@ public InstanceManager(
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
+ internalConfiguration = internalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(internalConfigurationOptions));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
originalConsoleTitle = console.Title;
@@ -675,6 +683,11 @@ void CheckSystemCompatibility()
{
if (!systemIdentity.CanCreateSymlinks)
throw new InvalidOperationException($"The user running {Constants.CanonicalPackageName} cannot create symlinks! Please try running as an administrative user!");
+
+ if (!platformIdentifier.IsWindows && systemIdentity.IsSuperUser && !internalConfiguration.UsingDocker)
+ {
+ logger.LogWarning("TGS is being run as the root account. This is not recommended.");
+ }
}
// This runs before the real socket is opened, ensures we don't perform reattaches unless we're fairly certain the bind won't fail
diff --git a/src/Tgstation.Server.Host/Configuration/InternalConfiguration.cs b/src/Tgstation.Server.Host/Configuration/InternalConfiguration.cs
index 58f2ad4720b..1d2723e26db 100644
--- a/src/Tgstation.Server.Host/Configuration/InternalConfiguration.cs
+++ b/src/Tgstation.Server.Host/Configuration/InternalConfiguration.cs
@@ -25,6 +25,11 @@ public sealed class InternalConfiguration
///
public bool UsingSystemD { get; set; }
+ ///
+ /// If the server is running inside of a Docker container.
+ ///
+ public bool UsingDocker { get; set; }
+
///
/// The base path for the app settings configuration files.
///
diff --git a/src/Tgstation.Server.Host/Security/ISystemIdentity.cs b/src/Tgstation.Server.Host/Security/ISystemIdentity.cs
index bd63d784dea..f976066e50a 100644
--- a/src/Tgstation.Server.Host/Security/ISystemIdentity.cs
+++ b/src/Tgstation.Server.Host/Security/ISystemIdentity.cs
@@ -24,6 +24,12 @@ public interface ISystemIdentity : IDisposable
///
bool CanCreateSymlinks { get; }
+ ///
+ /// Is this identity a SuperUser for the OS.
+ /// See Administrator on Windows or root on Linux.
+ ///
+ bool IsSuperUser { get; }
+
///
/// Clone the creating another copy that must have called on it.
///
diff --git a/src/Tgstation.Server.Host/Security/PosixSystemIdentity.cs b/src/Tgstation.Server.Host/Security/PosixSystemIdentity.cs
index 67f0e3ac838..ea8e23d3e84 100644
--- a/src/Tgstation.Server.Host/Security/PosixSystemIdentity.cs
+++ b/src/Tgstation.Server.Host/Security/PosixSystemIdentity.cs
@@ -2,6 +2,8 @@
using System.Threading;
using System.Threading.Tasks;
+using Mono.Unix.Native;
+
namespace Tgstation.Server.Host.Security
{
///
@@ -9,6 +11,9 @@ namespace Tgstation.Server.Host.Security
///
sealed class PosixSystemIdentity : ISystemIdentity
{
+ ///
+ public bool IsSuperUser => Syscall.getuid() == 0;
+
///
public string Uid => throw new NotImplementedException();
diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs
index 136c3c24e7e..609f33d2a28 100644
--- a/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs
+++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs
@@ -22,7 +22,10 @@ sealed class WindowsSystemIdentity : ISystemIdentity
public string Username => userPrincipal?.Name ?? identity!.Name;
///
- public bool CanCreateSymlinks => canCreateSymlinks ?? throw new NotSupportedException();
+ public bool CanCreateSymlinks => IsSuperUser;
+
+ ///
+ public bool IsSuperUser => isAdmin ?? throw new NotSupportedException();
///
/// The for the .
@@ -35,9 +38,9 @@ sealed class WindowsSystemIdentity : ISystemIdentity
readonly UserPrincipal? userPrincipal;
///
- /// Backing field for .
+ /// Backing field for .
///
- readonly bool? canCreateSymlinks;
+ readonly bool? isAdmin;
///
/// Initializes a new instance of the class.
@@ -49,7 +52,7 @@ public WindowsSystemIdentity(WindowsIdentity identity)
if (identity.IsAnonymous)
throw new ArgumentException($"Cannot use anonymous {nameof(WindowsIdentity)} as a {nameof(WindowsSystemIdentity)}!", nameof(identity));
- canCreateSymlinks = new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator);
+ isAdmin = new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator);
}
///