Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into #152-Dont-use-keyring…
Browse files Browse the repository at this point in the history
…-on-linux
  • Loading branch information
forsthug committed Jan 31, 2024
2 parents 9c09947 + b280c30 commit a39b116
Show file tree
Hide file tree
Showing 15 changed files with 267 additions and 101 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ Middleware packages each provide specific fiscalization-, data source- and secur

Below, we illustrate a minimal sample configuration with the international SQLite _Queue_ package (with a configured HTTP endpoint) and a German _Signature Creation Unit_ (with a gRPC endpoint) that abstracts a Swissbit TSS.

<div align="center">
<img src="./doc/images/overview.png" alt="overview" />
</div>
![Overview](https://raw.githubusercontent.com/fiskaltrust/middleware-launcher/main/doc/images/overview.png)

## Getting Started

Expand Down Expand Up @@ -292,16 +290,22 @@ HttpSysBinding has some limitations:
* The Launcher has access problems when writing to the keyring on Linux if run as a service.
The launcher configuration parameter `useLegacyDataProtection` needs to be set to `true` as a workaround. ([#100](https://github.com/fiskaltrust/middleware-launcher/issues/100)
<!-- BEGIN CONTRIBUTING -->
## Contributing
We welcome all kinds of contributions and feedback, e.g. via issues or pull requests, and want to thank every future contributors in advance!
Please check out the [contribution guidelines](CONTRIBUTING.md) for more detailed information about how to proceed.
<!-- END CONTRIBUTING -->
<!-- BEGIN LICENSE -->
## License
The fiskaltrust Middleware is released under the [EUPL 1.2](./LICENSE).
As a Compliance-as-a-Service provider, the security and authenticity of the products installed on our users' endpoints is essential to us. To ensure that only peer-reviewed binaries are distributed by maintainers, fiskaltrust explicitly reserves the sole right to use the brand name "fiskaltrust Middleware" (and the brand names of related products and services) for the software provided here as open source - regardless of the spelling or abbreviation, as long as conclusions can be drawn about the original product name.
The fiskaltrust Middleware (and related products and services) as contained in these repositories may therefore only be used in the form of binaries signed by fiskaltrust.
<!-- END LICENSE -->
26 changes: 20 additions & 6 deletions azure-pipelines/templates/stages/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,34 @@ stages:
$hashbytes = $hash.Hash -split '([A-F0-9]{2})' | foreach-object { if ($_) {[System.Convert]::ToByte($_,16)}}
$hashstring = [System.Convert]::ToBase64String($hashbytes)
$hashstring | Set-Content $(Build.ArtifactStagingDirectory)/package-$(target)/fiskaltrust.Launcher-$version.zip.hash
displayName: Pagkage executables
displayName: Package executables
- pwsh: |
$version = (Select-Xml -Path ./Directory.Build.props -XPath 'Project/PropertyGroup/Version').Node.InnerText
Copy-Item -Path ./README.md -Destination $(Build.ArtifactStagingDirectory)/scripts-$(target)/README.md
displayName: "Copy README.md to scripts artifacts"
- pwsh: |
$readmeContent = Get-Content $(Build.ArtifactStagingDirectory)/scripts-$(target)/README.md -Raw
$updatedContent = $readmeContent -replace '(?s)<!-- BEGIN (CONTRIBUTING|LICENSE) -->.*?<!-- END (CONTRIBUTING|LICENSE) -->\s*', ''
$updatedContent | Set-Content $(Build.ArtifactStagingDirectory)/scripts-$(target)/README.md
displayName: "Update README.md"
- pwsh: |
$version = (Select-Xml -Path ./Directory.Build.props -XPath 'Project/PropertyGroup/Version').Node.InnerText
$scriptTargetPath = "$(Build.ArtifactStagingDirectory)/scripts-$(target)/fiskaltrust.Launcher.Scripts-$version.zip"
Copy-Item -Path ./scripts/$(scriptFolder)/* -Destination $(Build.ArtifactStagingDirectory)/scripts-$(target)
if("$(vmImage)" -eq "windows-latest") {
Compress-Archive -Path ./scripts/$(scriptFolder)/* -DestinationPath $scriptTargetPath
Compress-Archive -Path $(Build.ArtifactStagingDirectory)/scripts-$(target)/* -DestinationPath $scriptTargetPath
} else {
bash -c "chmod +x ./scripts/$(scriptFolder)/*"
bash -c "cd ./scripts/$(scriptFolder)/`nzip -r $scriptTargetPath ./"
bash -c "chmod +x $(Build.ArtifactStagingDirectory)/scripts-$(target)/*"
bash -c "cd $(Build.ArtifactStagingDirectory)/scripts-$(target) && zip -r $scriptTargetPath ./"
}
displayName: Package scripts
displayName: "Package scripts with README.md"
- pwsh: |
Get-ChildItem -Path $(Build.ArtifactStagingDirectory)/scripts-$(target)/* -Exclude *.zip | Remove-Item
displayName: "Clean up scripts directory"
- publish: $(Build.ArtifactStagingDirectory)/package-$(target)
artifact: package-$(target)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static LoggerConfiguration AddLoggingConfiguration(this LoggerConfigurati
.MinimumLevel.Override("Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware", aspLogging ? LogEventLevel.Information : LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Grpc", LogEventLevel.Warning)
.MinimumLevel.Override("ProtoBuf", LogEventLevel.Warning);
.MinimumLevel.Override("ProtoBuf", LogEventLevel.Error);

return loggerConfiguration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="SemanticVersioning" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="7.0.12" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="8.0.1" />
</ItemGroup>

</Project>
</Project>
28 changes: 20 additions & 8 deletions src/fiskaltrust.Launcher/Commands/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,23 @@ public static async Task<int> HandleAsync<O, S>(
ECDiffieHellman? clientEcdh = null;
try
{
clientEcdh = await LoadCurve(launcherConfiguration.CashboxId!.Value, launcherConfiguration.AccessToken!, launcherConfiguration.ServiceFolder!, launcherConfiguration.UseOffline!.Value, useFallback: launcherConfiguration.UseLegacyDataProtection!.Value);
using var downloader = new ConfigurationDownloader(launcherConfiguration);
var exists = await downloader.DownloadConfigurationAsync(clientEcdh);
if (launcherConfiguration.UseOffline!.Value && !exists)
clientEcdh = await LoadCurve(launcherConfiguration.CashboxId!.Value, launcherConfiguration.AccessToken!, launcherConfiguration.ServiceFolder!, launcherConfiguration.UseOffline!.Value);
}
catch (Exception e)
{
Log.Fatal(e, "Could not load client curve.");
}

try
{
if (clientEcdh is not null)
{
Log.Warning("Cashbox configuration was not downloaded because UseOffline is set.");
using var downloader = new ConfigurationDownloader(launcherConfiguration);
var exists = await downloader.DownloadConfigurationAsync(clientEcdh);
if (launcherConfiguration.UseOffline!.Value && !exists)
{
Log.Warning("Cashbox configuration was not downloaded because UseOffline is set.");
}
}
}
catch (Exception e)
Expand Down Expand Up @@ -192,7 +203,7 @@ public static async Task<int> HandleAsync<O, S>(
try
{
cashboxConfiguration = CashBoxConfigurationExt.Deserialize(await File.ReadAllTextAsync(launcherConfiguration.CashboxConfigurationFile!));
cashboxConfiguration.Decrypt(launcherConfiguration, clientEcdh);
if (clientEcdh is not null) { cashboxConfiguration.Decrypt(launcherConfiguration, clientEcdh); }
}
catch (Exception e)
{
Expand Down Expand Up @@ -224,6 +235,7 @@ public static async Task<int> HandleAsync<O, S>(
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<ServiceType>().Type));

var dataProtectionProvider = DataProtectionExtensions.Create(launcherConfiguration.AccessToken, useFallback: launcherConfiguration.UseLegacyDataProtection!.Value);

Expand All @@ -236,12 +248,12 @@ public static async Task<int> HandleAsync<O, S>(
Log.Warning(e, "Error decrypring launcher configuration file.");
}

return await handler(options, new CommonProperties(launcherConfiguration, cashboxConfiguration, clientEcdh, dataProtectionProvider), specificOptions, host.Services.GetRequiredService<S>());
return await handler(options, new CommonProperties(launcherConfiguration, cashboxConfiguration, clientEcdh!, dataProtectionProvider), specificOptions, host.Services.GetRequiredService<S>());
}

private static async Task EnsureServiceDirectoryExists(LauncherConfiguration config)
{
var serviceDirectory = config.ServiceFolder;
var serviceDirectory = config.ServiceFolder!;
try
{
if (!Directory.Exists(serviceDirectory))
Expand Down
7 changes: 0 additions & 7 deletions src/fiskaltrust.Launcher/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.CommandLine;
using System.CommandLine.Invocation;
using fiskaltrust.Launcher.ProcessHost;
using fiskaltrust.Launcher.Services;
using Serilog;
Expand All @@ -8,12 +7,6 @@
using fiskaltrust.Launcher.Extensions;
using fiskaltrust.Launcher.Helpers;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using fiskaltrust.Launcher.Common.Configuration;
using fiskaltrust.storage.serialization.V0;
using System.Security.Cryptography;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;


namespace fiskaltrust.Launcher.Commands
Expand Down
103 changes: 101 additions & 2 deletions src/fiskaltrust.Launcher/Extensions/LifetimeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.Extensions.Hosting.Systemd;
using Microsoft.Extensions.Hosting.WindowsServices;
using Microsoft.Extensions.Options;

namespace fiskaltrust.Launcher.Extensions
{
public record ServiceType(ServiceTypes Type);

public enum ServiceTypes
{
WindowsService,
SystemdService,
ConsoleApplication
}

static class LifetimeExtensions
{
public static IHostBuilder UseCustomHostLifetime(this IHostBuilder builder)
Expand All @@ -15,6 +27,7 @@ public static IHostBuilder UseCustomHostLifetime(this IHostBuilder builder)

return builder.ConfigureServices(services =>
{
services.AddSingleton(new ServiceType(ServiceTypes.WindowsService));
var lifetime = services.FirstOrDefault(s => s.ImplementationType == typeof(WindowsServiceLifetime));

if (lifetime != null)
Expand All @@ -28,10 +41,29 @@ public static IHostBuilder UseCustomHostLifetime(this IHostBuilder builder)
#pragma warning restore CA1416
});
}
else if (SystemdHelpers.IsSystemdService())
{
builder.UseSystemd();

return builder.ConfigureServices(services =>
{
services
.AddSingleton(new ServiceType(ServiceTypes.SystemdService))
.AddSingleton<ILifetime, Lifetime>();

// #pragma warning disable CA1416
// services.AddSingleton<ILifetime, CustomSystemDServiceLifetime>();
// services.AddSingleton<IHostLifetime>(sp => sp.GetRequiredService<ILifetime>());
// #pragma warning restore CA1416
});
}
else
{
Console.OutputEncoding = Encoding.UTF8;
builder.ConfigureServices(services => services.AddSingleton<ILifetime, Lifetime>());
builder.ConfigureServices(services => services
.AddSingleton<ILifetime, Lifetime>()
.AddSingleton(new ServiceType(ServiceTypes.ConsoleApplication)));

builder.UseConsoleLifetime();
return builder;
}
Expand Down Expand Up @@ -96,7 +128,7 @@ public CustomWindowsServiceLifetime(

public void ServiceStartupCompleted()
{
ApplicationLifetime.ApplicationStarted.Register(() => _started.Set());
ApplicationLifetime.ApplicationStarted.Register(_started.Set);
}

public new async Task WaitForStartAsync(CancellationToken cancellationToken)
Expand Down Expand Up @@ -133,4 +165,71 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
}

[SupportedOSPlatform("linux")]
public class CustomSystemDServiceLifetime : ILifetime, IHostLifetime, IDisposable
{
private readonly CancellationTokenSource _started = new();
private readonly ISystemdNotifier _systemdNotifier;
public IHostApplicationLifetime ApplicationLifetime { get; init; }

private CancellationTokenRegistration _applicationStartedRegistration;
private CancellationTokenRegistration _applicationStoppingRegistration;
private PosixSignalRegistration? _sigTermRegistration;

public CustomSystemDServiceLifetime(
IHostApplicationLifetime applicationLifetime,
ISystemdNotifier systemdNotifier)
{
ApplicationLifetime = applicationLifetime;
_systemdNotifier = systemdNotifier;
}

public void ServiceStartupCompleted() => _started.Cancel();

public Task WaitForStartAsync(CancellationToken cancellationToken)
{
_applicationStartedRegistration = ApplicationLifetime.ApplicationStarted.Register(OnApplicationStarted);
_applicationStoppingRegistration = ApplicationLifetime.ApplicationStopping.Register(OnApplicationStopping);

RegisterShutdownHandlers();

return Task.CompletedTask;
}

private void OnApplicationStarted()
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(_started.Token, ApplicationLifetime.ApplicationStopping);

cts.Token.Register(() =>
{
_systemdNotifier.Notify(ServiceState.Stopping);
});
}

private void OnApplicationStopping() => _systemdNotifier.Notify(ServiceState.Stopping);

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

private void RegisterShutdownHandlers() => _sigTermRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, HandlePosixSignal);

private void HandlePosixSignal(PosixSignalContext context)
{
Debug.Assert(context.Signal == PosixSignal.SIGTERM);

context.Cancel = true;
ApplicationLifetime.StopApplication();
}

private void UnregisterShutdownHandlers() => _sigTermRegistration?.Dispose();

public void Dispose()
{
_started.Cancel();

UnregisterShutdownHandlers();
_applicationStartedRegistration.Dispose();
_applicationStoppingRegistration.Dispose();
}
}
}
8 changes: 4 additions & 4 deletions src/fiskaltrust.Launcher/Helpers/ProcessHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ namespace fiskaltrust.Launcher.Helpers;
public static class ProcessHelper
{
public static async Task<(int exitCode, string output)> RunProcess(
string fileName,
IEnumerable<string> arguments,
LogEventLevel logLevel = LogEventLevel.Information)
string fileName,
IEnumerable<string> arguments,
LogEventLevel? logLevel = LogEventLevel.Information)
{
var process = new Process
{
Expand All @@ -30,7 +30,7 @@ public static class ProcessHelper
var stdOut = await process.StandardOutput.ReadToEndAsync();
if (!string.IsNullOrEmpty(stdOut))
{
Log.Write(logLevel, stdOut);
if (logLevel is not null) { Log.Write(logLevel.Value, stdOut); }
}

var stdErr = await process.StandardError.ReadToEndAsync();
Expand Down
14 changes: 7 additions & 7 deletions src/fiskaltrust.Launcher/ProcessHost/ProcessHostMonarch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ public ProcessHostMonarch(ILogger<ProcessHostMonarch> logger, LauncherConfigurat

_stopped = new TaskCompletionSource();
_started = new TaskCompletionSource();

// if (Debugger.IsAttached)
// {
// _process.StartInfo.Arguments += " --debugging";
// }
}

private void Setup()
Expand All @@ -61,11 +56,11 @@ private void Setup()
UseShellExecute = false,
FileName = _launcherExecutablePath.Path,
CreateNoWindow = false,
Arguments = string.Join(" ", new string[] {
Arguments = string.Join(" ", [
"host",
"--plebeian-configuration", $"\"{Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(new PlebeianConfiguration { PackageType = _packageType, PackageId = _packageConfiguration.Id }.Serialize()))}\"",
"--launcher-configuration", $"\"{Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(_launcherConfiguration.Serialize()))}\"",
}),
]),
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true
Expand All @@ -75,6 +70,11 @@ private void Setup()

_process.OutputDataReceived += ReceiveStdOut;
_process.ErrorDataReceived += ReceiveStdOut;

// if (Debugger.IsAttached && _packageType == PackageType.Helper)
// {
// _process.StartInfo.Arguments += " --debugging";
// }
}

private void ReceiveStdOut(object sender, DataReceivedEventArgs e)
Expand Down
2 changes: 1 addition & 1 deletion src/fiskaltrust.Launcher/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

if (!args.Any())
{
args = new[] { runCommand.Name };
args = [runCommand.Name];
}

var subArguments = new SubArguments(args.SkipWhile(a => a != "--").Skip(1));
Expand Down
Loading

0 comments on commit a39b116

Please sign in to comment.