diff --git a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs index 9b2b746c..2f42d414 100644 --- a/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs +++ b/src/fiskaltrust.Launcher.Common/Configuration/Configuration.cs @@ -279,20 +279,20 @@ internal void SetAlternateNames(string text) } } } - - private void MapFieldsWithAttribute(Func action) + private void MapFieldsWithAttribute(Func action) { var errors = new List(); foreach (var field in GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) { var value = field.GetValue(this); + var name = field.Name; - if (field.GetCustomAttributes(typeof(T)).Any()) + if (field.GetCustomAttributes(typeof(T), false).Any()) { try { - field.SetValue(this, action(value)); + field.SetValue(this, action(value, name)); } catch (Exception e) { @@ -306,24 +306,37 @@ private void MapFieldsWithAttribute(Func action) throw new AggregateException(errors); } } - public void Encrypt(IDataProtector dataProtector) { - MapFieldsWithAttribute(value => + MapFieldsWithAttribute((value, name) => { - if (value is null) { return null; } + if (value is null) return null; - return dataProtector.Protect((string)value); + try + { + return dataProtector.Protect((string)value); + } + catch (Exception e) + { + Log.Warning($"Failed to encrypt value of configuration field {name}. Consider using the 'config set' command to set the field's value.", name); + return null; + } }); } - public void Decrypt(IDataProtector dataProtector) { - MapFieldsWithAttribute((value) => + MapFieldsWithAttribute((value, name) => { - if (value is null) { return null; } - - return dataProtector.Unprotect((string)value); + try + { + if (value is null) return null; + return dataProtector.Unprotect((string)value); + } + catch (Exception e) + { + Log.Warning("Failed to decrypt value of configuration field {name}. Consider using the 'config set' command to set the fields value.", name); + return null; + } }); } @@ -337,7 +350,6 @@ public void Decrypt(IDataProtector dataProtector) return null; } } - public record LauncherConfigurationInCashBoxConfiguration { [JsonPropertyName("launcher")] @@ -358,4 +370,4 @@ public record LauncherConfigurationInCashBoxConfiguration return configuration; } } -} +} \ No newline at end of file diff --git a/src/fiskaltrust.Launcher/Commands/Common.cs b/src/fiskaltrust.Launcher/Commands/Common.cs index 37eb8915..2b9a3d10 100644 --- a/src/fiskaltrust.Launcher/Commands/Common.cs +++ b/src/fiskaltrust.Launcher/Commands/Common.cs @@ -37,8 +37,10 @@ public CommonCommand(string name, bool addCliOnlyParameters = true) : base(name) if (addCliOnlyParameters) { - AddOption(new Option("--launcher-configuration-file", getDefaultValue: () => Paths.LauncherConfigurationFileName)); - AddOption(new Option("--legacy-configuration-file", getDefaultValue: () => Paths.LegacyConfigurationFileName)); + AddOption(new Option("--launcher-configuration-file", + getDefaultValue: () => Paths.LauncherConfigurationFileName)); + AddOption(new Option("--legacy-configuration-file", + getDefaultValue: () => Paths.LegacyConfigurationFileName)); AddOption(new Option("--merge-legacy-config-if-exists", getDefaultValue: () => true)); } } @@ -46,7 +48,8 @@ public CommonCommand(string name, bool addCliOnlyParameters = true) : base(name) public class CommonOptions { - public CommonOptions(LauncherConfiguration argsLauncherConfiguration, string launcherConfigurationFile, string legacyConfigurationFile, bool mergeLegacyConfigIfExists) + public CommonOptions(LauncherConfiguration argsLauncherConfiguration, string launcherConfigurationFile, + string legacyConfigurationFile, bool mergeLegacyConfigIfExists) { ArgsLauncherConfiguration = argsLauncherConfiguration; LauncherConfigurationFile = launcherConfigurationFile; @@ -62,7 +65,9 @@ public CommonOptions(LauncherConfiguration argsLauncherConfiguration, string lau public record CommonProperties { - public CommonProperties(LauncherConfiguration launcherConfiguration, ftCashBoxConfiguration cashboxConfiguration, ECDiffieHellman clientEcdh, IDataProtectionProvider dataProtectionProvider) + public CommonProperties(LauncherConfiguration launcherConfiguration, + ftCashBoxConfiguration cashboxConfiguration, ECDiffieHellman clientEcdh, + IDataProtectionProvider dataProtectionProvider) { LauncherConfiguration = launcherConfiguration; CashboxConfiguration = cashboxConfiguration; @@ -96,7 +101,8 @@ public static async Task HandleAsync( try { options.LauncherConfigurationFile = Path.GetFullPath(options.LauncherConfigurationFile); - launcherConfiguration = LauncherConfiguration.Deserialize(await File.ReadAllTextAsync(options.LauncherConfigurationFile)); + launcherConfiguration = + LauncherConfiguration.Deserialize(await File.ReadAllTextAsync(options.LauncherConfigurationFile)); } catch (Exception e) { @@ -104,15 +110,19 @@ public static async Task HandleAsync( { if (File.Exists(options.LauncherConfigurationFile)) { - Log.Warning(e, "Could not parse launcher configuration file \"{LauncherConfigurationFile}\".", options.LauncherConfigurationFile); + Log.Warning(e, "Could not parse launcher configuration file \"{LauncherConfigurationFile}\".", + options.LauncherConfigurationFile); } else { - Log.Warning("Launcher configuration file \"{LauncherConfigurationFile}\" does not exist.", options.LauncherConfigurationFile); + Log.Warning("Launcher configuration file \"{LauncherConfigurationFile}\" does not exist.", + options.LauncherConfigurationFile); } + Log.Warning("Using command line parameters only.", options.LauncherConfigurationFile); } } + Log.Verbose("Merging legacy launcher config file."); if (options.MergeLegacyConfigIfExists && File.Exists(options.LegacyConfigurationFile)) { @@ -136,7 +146,8 @@ public static async Task HandleAsync( launcherConfiguration.OverwriteWith(options.ArgsLauncherConfiguration); await EnsureServiceDirectoryExists(launcherConfiguration); - if (!launcherConfiguration.UseOffline!.Value && (launcherConfiguration.CashboxId is null || launcherConfiguration.AccessToken is null)) + if (!launcherConfiguration.UseOffline!.Value && + (launcherConfiguration.CashboxId is null || launcherConfiguration.AccessToken is null)) { Log.Error("CashBoxId and AccessToken are not provided."); } @@ -157,7 +168,8 @@ public static async Task HandleAsync( ECDiffieHellman? clientEcdh = null; try { - clientEcdh = await LoadCurve(launcherConfiguration.CashboxId!.Value, launcherConfiguration.AccessToken!, launcherConfiguration.ServiceFolder!, launcherConfiguration.UseOffline!.Value); + clientEcdh = await LoadCurve(launcherConfiguration.CashboxId!.Value, launcherConfiguration.AccessToken!, + launcherConfiguration.ServiceFolder!, launcherConfiguration.UseOffline!.Value); } catch (Exception e) { @@ -179,11 +191,13 @@ public static async Task HandleAsync( catch (Exception e) { var message = "Could not download Cashbox configuration. "; - message += $"(Launcher is running in {(launcherConfiguration.Sandbox!.Value ? "sandbox" : "production")} mode."; + message += + $"(Launcher is running in {(launcherConfiguration.Sandbox!.Value ? "sandbox" : "production")} mode."; if (!launcherConfiguration.Sandbox!.Value) { message += " Did you forget the --sandbox flag?"; } + message += ")"; Log.Error(e, message); } @@ -191,7 +205,9 @@ public static async Task HandleAsync( try { var cashboxConfigurationFile = launcherConfiguration.CashboxConfigurationFile!; - launcherConfiguration.OverwriteWith(LauncherConfigurationInCashBoxConfiguration.Deserialize(await File.ReadAllTextAsync(cashboxConfigurationFile))); + launcherConfiguration.OverwriteWith( + LauncherConfigurationInCashBoxConfiguration.Deserialize( + await File.ReadAllTextAsync(cashboxConfigurationFile))); } catch (Exception e) { @@ -202,8 +218,13 @@ public static async Task HandleAsync( var cashboxConfiguration = new ftCashBoxConfiguration(); try { - cashboxConfiguration = CashBoxConfigurationExt.Deserialize(await File.ReadAllTextAsync(launcherConfiguration.CashboxConfigurationFile!)); - if (clientEcdh is not null) { cashboxConfiguration.Decrypt(launcherConfiguration, clientEcdh); } + cashboxConfiguration = + CashBoxConfigurationExt.Deserialize( + await File.ReadAllTextAsync(launcherConfiguration.CashboxConfigurationFile!)); + if (clientEcdh is not null) + { + cashboxConfiguration.Decrypt(launcherConfiguration, clientEcdh); + } } catch (Exception e) { @@ -214,7 +235,8 @@ public static async Task HandleAsync( // Previous log messages will be logged here using this logger. Log.Logger = new LoggerConfiguration() .AddLoggingConfiguration(launcherConfiguration) - .AddFileLoggingConfiguration(launcherConfiguration, new[] { "fiskaltrust.Launcher", launcherConfiguration.CashboxId?.ToString() }) + .AddFileLoggingConfiguration(launcherConfiguration, + new[] { "fiskaltrust.Launcher", launcherConfiguration.CashboxId?.ToString() }) .Enrich.FromLogContext() .CreateLogger(); @@ -232,23 +254,29 @@ public static async Task HandleAsync( } Log.Debug("Launcher Configuration File: {LauncherConfigurationFile}", options.LauncherConfigurationFile); - Log.Debug("Cashbox Configuration File: {CashboxConfigurationFile}", launcherConfiguration.CashboxConfigurationFile); + Log.Debug("Cashbox Configuration File: {CashboxConfigurationFile}", + launcherConfiguration.CashboxConfigurationFile); Log.Debug("Launcher Configuration: {@LauncherConfiguration}", launcherConfiguration.Redacted()); - Log.Debug("Launcher running as {ServiceType}", Enum.GetName(typeof(ServiceTypes), host.Services.GetRequiredService().Type)); + Log.Debug("Launcher running as {ServiceType}", + Enum.GetName(typeof(ServiceTypes), host.Services.GetRequiredService().Type)); - var dataProtectionProvider = DataProtectionExtensions.Create(launcherConfiguration.AccessToken, useFallback: launcherConfiguration.UseLegacyDataProtection!.Value); + var dataProtectionProvider = DataProtectionExtensions.Create(launcherConfiguration.AccessToken, + useFallback: launcherConfiguration.UseLegacyDataProtection!.Value); try { - launcherConfiguration.Decrypt(dataProtectionProvider.CreateProtector(LauncherConfiguration.DATA_PROTECTION_DATA_PURPOSE)); + launcherConfiguration.Decrypt( + dataProtectionProvider.CreateProtector(LauncherConfiguration.DATA_PROTECTION_DATA_PURPOSE)); } catch (Exception e) { Log.Warning(e, "Error decrypring launcher configuration file."); } - return await handler(options, new CommonProperties(launcherConfiguration, cashboxConfiguration, clientEcdh!, dataProtectionProvider), specificOptions, host.Services.GetRequiredService()); + return await handler(options, + new CommonProperties(launcherConfiguration, cashboxConfiguration, clientEcdh!, dataProtectionProvider), + specificOptions, host.Services.GetRequiredService()); } private static async Task EnsureServiceDirectoryExists(LauncherConfiguration config) @@ -260,18 +288,21 @@ private static async Task EnsureServiceDirectoryExists(LauncherConfiguration con { Directory.CreateDirectory(serviceDirectory); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || + RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var user = Environment.GetEnvironmentVariable("USER"); if (!string.IsNullOrEmpty(user)) { - var chownResult = await ProcessHelper.RunProcess("chown", new[] { user, serviceDirectory }, LogEventLevel.Debug); + var chownResult = await ProcessHelper.RunProcess("chown", new[] { user, serviceDirectory }, + LogEventLevel.Debug); if (chownResult.exitCode != 0) { Log.Warning("Failed to change owner of the service directory."); } - var chmodResult = await ProcessHelper.RunProcess("chmod", new[] { "774", serviceDirectory }, LogEventLevel.Debug); + var chmodResult = await ProcessHelper.RunProcess("chmod", new[] { "774", serviceDirectory }, + LogEventLevel.Debug); if (chmodResult.exitCode != 0) { Log.Warning("Failed to change permissions of the service directory."); @@ -279,7 +310,8 @@ private static async Task EnsureServiceDirectoryExists(LauncherConfiguration con } else { - Log.Warning("Service user name is not set. Owner of the service directory will not be changed."); + Log.Warning( + "Service user name is not set. Owner of the service directory will not be changed."); } } else @@ -291,46 +323,62 @@ private static async Task EnsureServiceDirectoryExists(LauncherConfiguration con catch (UnauthorizedAccessException e) { // will exit with non-zero exit code later. - Log.Fatal(e, "Access to the path '{ServiceDirectory}' is denied. Please run the application with sufficient permissions.", serviceDirectory); + Log.Fatal(e, + "Access to the path '{ServiceDirectory}' is denied. Please run the application with sufficient permissions.", + serviceDirectory); } } - public static async Task LoadCurve(Guid cashboxId, string accessToken, string serviceFolder, bool useOffline = false, bool dryRun = false, bool useFallback = false) + public static async Task LoadCurve(Guid cashboxId, string accessToken, string serviceFolder, + bool useOffline = false, bool dryRun = false, bool useFallback = false) { Log.Verbose("Loading Curve."); - var dataProtector = DataProtectionExtensions.Create(accessToken, useFallback: useFallback).CreateProtector(CashBoxConfigurationExt.DATA_PROTECTION_DATA_PURPOSE); + var dataProtector = DataProtectionExtensions.Create(accessToken, useFallback: useFallback) + .CreateProtector(CashBoxConfigurationExt.DATA_PROTECTION_DATA_PURPOSE); var clientEcdhPath = Path.Combine(serviceFolder, $"client-{cashboxId}.ecdh"); + ECDiffieHellman? clientEcdh = null; + if (File.Exists(clientEcdhPath)) { - return ECDiffieHellmanExt.Deserialize(dataProtector.Unprotect(await File.ReadAllTextAsync(clientEcdhPath))); + try + { + clientEcdh = ECDiffieHellmanExt.Deserialize( + dataProtector.Unprotect(await File.ReadAllTextAsync(clientEcdhPath))); + } + catch (Exception e) + { + Log.Warning($"Error loading or decrypting ECDH curve: {e.Message}. Regenerating new curve."); + } } - else - { - const string offlineClientEcdhPath = "/client.ecdh"; - ECDiffieHellman clientEcdh; - if (!dryRun && useOffline && File.Exists(offlineClientEcdhPath)) + // Handling offline client ECDH path + const string offlineClientEcdhPath = "/client.ecdh"; + if (!dryRun && useOffline && File.Exists(offlineClientEcdhPath) && clientEcdh == null) + { + clientEcdh = ECDiffieHellmanExt.Deserialize(await File.ReadAllTextAsync(offlineClientEcdhPath)); + try { - clientEcdh = ECDiffieHellmanExt.Deserialize(await File.ReadAllTextAsync(offlineClientEcdhPath)); - try - { - File.Delete(offlineClientEcdhPath); - } - catch { } + File.Delete(offlineClientEcdhPath); } - else + catch (Exception e) { - clientEcdh = CashboxConfigEncryption.CreateCurve(); + Log.Error(e, "Error occurred while loading or decrypting ECDH curve from file: {ClientEcdhPath}", clientEcdhPath); + throw; } + } + if (clientEcdh == null) + { + // Regenerating the curve if it's not loaded or in case of an error + clientEcdh = CashboxConfigEncryption.CreateCurve(); if (!dryRun) { await File.WriteAllTextAsync(clientEcdhPath, dataProtector.Protect(clientEcdh.Serialize())); } - - return clientEcdh; } + + return clientEcdh; } } -} +} \ No newline at end of file diff --git a/src/fiskaltrust.Launcher/Commands/DoctorCommand.cs b/src/fiskaltrust.Launcher/Commands/DoctorCommand.cs index 49801e07..beee986d 100644 --- a/src/fiskaltrust.Launcher/Commands/DoctorCommand.cs +++ b/src/fiskaltrust.Launcher/Commands/DoctorCommand.cs @@ -91,7 +91,9 @@ public static async Task HandleAsync(CommonOptions commonOptions, CommonPro ftCashBoxConfiguration cashboxConfiguration = new(); if (clientEcdh is null) - { } + { + Log.Warning("Failed to load ECDH curve. Skipping some related doctor checks."); + } else { using var downloader = new ConfigurationDownloader(launcherConfiguration);