-
Notifications
You must be signed in to change notification settings - Fork 26
Opt-in to API key forwarding. Default to using SeqCli's connection se… #406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
ef0b9af
d3f43e0
bc031c9
b4b23e2
07ed32e
ae24fd3
5528c36
b7bfb80
97b3f66
5c3bd6d
67ba3fa
7a9f16d
377e7d4
6e56ff9
419d872
4b2010c
e033329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Seq.Api; | ||
| using SeqCli.Config; | ||
| using SeqCli.Forwarder.Filesystem.System; | ||
| using SeqCli.Forwarder.Storage; | ||
| using Serilog; | ||
|
|
@@ -15,26 +16,28 @@ class ForwardingChannelMap | |
| { | ||
| readonly string _bufferPath; | ||
| readonly SeqConnection _connection; | ||
| readonly SeqCliConfig _config; | ||
| readonly ForwardingChannel _defaultChannel; | ||
| readonly Lock _channelsSync = new(); | ||
| readonly Dictionary<string, ForwardingChannel> _channels = new(); | ||
| readonly CancellationTokenSource _shutdownTokenSource = new(); | ||
| const string DefaultChannelName = "Default"; | ||
|
|
||
| public ForwardingChannelMap(string bufferPath, SeqConnection connection, string? defaultApiKey) | ||
| public ForwardingChannelMap(string bufferPath, SeqConnection connection, SeqCliConfig config, string? seqCliApiKey) | ||
| { | ||
| _bufferPath = bufferPath; | ||
| _connection = connection; | ||
| _defaultChannel = OpenOrCreateChannel(defaultApiKey, "Default"); | ||
| _config = config; | ||
| _defaultChannel = OpenOrCreateChannel(seqCliApiKey, DefaultChannelName); | ||
|
|
||
| // TODO, load other channels at start-up | ||
| ReopenApiKeyChannels(); | ||
| } | ||
|
|
||
| ForwardingChannel OpenOrCreateChannel(string? apiKey, string name) | ||
| { | ||
| // TODO, when it's not the default, persist the API key and validate equality on reopen | ||
|
|
||
| var storePath = Path.Combine(_bufferPath, name); | ||
| var storePath = GetStorePath(name); | ||
| var store = new SystemStoreDirectory(storePath); | ||
|
|
||
| Log.Information("Opening local buffer in {StorePath}", storePath); | ||
|
|
||
| return new ForwardingChannel( | ||
|
|
@@ -45,30 +48,65 @@ ForwardingChannel OpenOrCreateChannel(string? apiKey, string name) | |
| apiKey, | ||
| _shutdownTokenSource.Token); | ||
| } | ||
|
|
||
| public ForwardingChannel Get(string? apiKey) | ||
| void ReopenApiKeyChannels() | ||
| { | ||
| if (string.IsNullOrWhiteSpace(apiKey)) | ||
| if (_config.Forwarder.UseApiKeyForwarding) | ||
| { | ||
| return _defaultChannel; | ||
| foreach (var directoryPath in Directory.EnumerateDirectories(_bufferPath)) | ||
| { | ||
| if (directoryPath.Equals(GetStorePath("Default"))) continue; | ||
|
|
||
| var path = new SystemStoreDirectory(directoryPath); | ||
| var apiKey = path.ReadApiKey(_config); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the read fails, here, perhaps we should skip rather than fall through? Effects are going to be weird otherwise - e.g. the |
||
|
|
||
| if (!string.IsNullOrEmpty(apiKey)) | ||
| { | ||
| var created = OpenOrCreateChannel(apiKey, ApiKeyToName(apiKey)); | ||
|
|
||
| lock (_channelsSync) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is only called in the constructor where there's no parallelism, it might be best to omit the Renaming the method to |
||
| { | ||
| _channels.Add(apiKey, created); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| string GetStorePath(string name) | ||
| { | ||
| return Path.Combine(_bufferPath, name); | ||
| } | ||
|
|
||
| public ForwardingChannel GetSeqCliConnectionChannel() | ||
| { | ||
| return _defaultChannel; | ||
| } | ||
|
|
||
| public ForwardingChannel GetApiKeyChannel(string apiKey) | ||
| { | ||
| lock (_channelsSync) | ||
| { | ||
| if (_channels.TryGetValue(apiKey, out var channel)) | ||
| { | ||
| return channel; | ||
| } | ||
|
|
||
| // Seq API keys begin with four identifying characters that aren't considered part of the | ||
| // confidential key. TODO: we could likely do better than this. | ||
| var name = apiKey[..4]; | ||
| var created = OpenOrCreateChannel(apiKey, name); | ||
| var created = OpenOrCreateChannel(apiKey, ApiKeyToName(apiKey)); | ||
| var store = new SystemStoreDirectory(GetStorePath(ApiKeyToName(apiKey))); | ||
| store.WriteApiKey(_config, apiKey); | ||
| _channels.Add(apiKey, created); | ||
| return created; | ||
| } | ||
| } | ||
|
|
||
| string ApiKeyToName(string apiKey) | ||
| { | ||
| // Seq API keys begin with four identifying characters that aren't considered part of the | ||
| // confidential key. TODO: we could likely do better than this. | ||
| return apiKey[..(Math.Min(apiKey.Length, 4))]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the next pass, if we fix/improve the directory naming scheme, we should include a min-length requirement and ignore payloads/keys below it. Seq's minimum is something like 16 chars IIRC. |
||
| } | ||
|
|
||
| public async Task StopAsync() | ||
| { | ||
| Log.Information("Flushing log buffers"); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,9 @@ | |
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Runtime.InteropServices; | ||
| using System.Text; | ||
| using SeqCli.Config; | ||
| using Serilog; | ||
|
|
||
| #if UNIX | ||
| using SeqCli.Forwarder.Filesystem.System.Unix; | ||
|
|
@@ -34,6 +37,26 @@ public SystemStoreDirectory(string path) | |
| if (!Directory.Exists(_directoryPath)) Directory.CreateDirectory(_directoryPath); | ||
| } | ||
|
|
||
| public void WriteApiKey(SeqCliConfig config, string apiKey) | ||
| { | ||
| File.WriteAllBytes(Path.Combine(_directoryPath, "api.key"), Encoding.UTF8.GetBytes(apiKey)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing the |
||
| } | ||
|
|
||
| public string? ReadApiKey(SeqCliConfig config) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| { | ||
| string? apiKey = null; | ||
| try | ||
| { | ||
| var encrypted = File.ReadAllBytes(Path.Combine(_directoryPath, "api.key")); | ||
| apiKey = Encoding.UTF8.GetString(config.Encryption.DataProtector().Decrypt(encrypted)); | ||
| } | ||
| catch (Exception exception) | ||
| { | ||
| Log.Warning(exception, "Could not read or decrypt api key"); | ||
| } | ||
| return apiKey; | ||
| } | ||
|
|
||
| public override SystemStoreFile Create(string name) | ||
| { | ||
| var filePath = Path.Combine(_directoryPath, name); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: could use
DefaultChannelNamehere?