aboutsummaryrefslogtreecommitdiff
path: root/VNLib.Plugins.Extensions.Loading/VaultSecrets.cs
diff options
context:
space:
mode:
Diffstat (limited to 'VNLib.Plugins.Extensions.Loading/VaultSecrets.cs')
-rw-r--r--VNLib.Plugins.Extensions.Loading/VaultSecrets.cs122
1 files changed, 116 insertions, 6 deletions
diff --git a/VNLib.Plugins.Extensions.Loading/VaultSecrets.cs b/VNLib.Plugins.Extensions.Loading/VaultSecrets.cs
index c429312..468600f 100644
--- a/VNLib.Plugins.Extensions.Loading/VaultSecrets.cs
+++ b/VNLib.Plugins.Extensions.Loading/VaultSecrets.cs
@@ -39,11 +39,15 @@ using VaultSharp.V1.AuthMethods.Token;
using VaultSharp.V1.AuthMethods.AppRole;
using VaultSharp.V1.SecretsEngines.PKI;
+using VNLib.Utils;
+using VNLib.Utils.Memory;
using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
+using VNLib.Hashing.IdentityUtility;
namespace VNLib.Plugins.Extensions.Loading
{
+
/// <summary>
/// Adds loading extensions for secure/centralized configuration secrets
/// </summary>
@@ -76,19 +80,19 @@ namespace VNLib.Plugins.Extensions.Loading
/// <returns>The element from the configuration file with the given name, or null if the configuration or property does not exist</returns>
/// <exception cref="KeyNotFoundException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
- public static Task<string?> TryGetSecretAsync(this PluginBase plugin, string secretName)
+ public static Task<SecretResult?> TryGetSecretAsync(this PluginBase plugin, string secretName)
{
//Get the secret from the config file raw
string? rawSecret = TryGetSecretInternal(plugin, secretName);
if (rawSecret == null)
{
- return Task.FromResult<string?>(null);
+ return Task.FromResult<SecretResult?>(null);
}
//Secret is a vault path, or return the raw value
if (!rawSecret.StartsWith(VAULT_URL_SCHEME, StringComparison.OrdinalIgnoreCase))
{
- return Task.FromResult<string?>(rawSecret);
+ return Task.FromResult<SecretResult?>(new(rawSecret.AsSpan()));
}
return GetSecretFromVaultAsync(plugin, rawSecret);
}
@@ -102,7 +106,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// <exception cref="UriFormatException"></exception>
/// <exception cref="KeyNotFoundException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
- public static Task<string?> GetSecretFromVaultAsync(this PluginBase plugin, ReadOnlySpan<char> vaultPath)
+ public static Task<SecretResult?> GetSecretFromVaultAsync(this PluginBase plugin, ReadOnlySpan<char> vaultPath)
{
//print the path for debug
if (plugin.IsDebug())
@@ -128,7 +132,7 @@ namespace VNLib.Plugins.Extensions.Loading
string mount = path[..lastSep].ToString();
string secret = path[(lastSep + 1)..].ToString();
- async Task<string?> execute()
+ async Task<SecretResult?> execute()
{
//Try load client
IVaultClient? client = _vaults.GetValue(plugin, TryGetVaultLoader).Value;
@@ -137,7 +141,7 @@ namespace VNLib.Plugins.Extensions.Loading
//run read async
Secret<SecretData> result = await client.V1.Secrets.KeyValue.V2.ReadSecretAsync(path:secret, mountPoint:mount);
//Read the secret
- return result.Data.Data[secretTableKey].ToString();
+ return SecretResult.ToSecret(result.Data.Data[secretTableKey].ToString());
}
return Task.Run(execute);
@@ -321,5 +325,111 @@ namespace VNLib.Plugins.Extensions.Loading
//init lazy
return new (LoadVault, LazyThreadSafetyMode.PublicationOnly);
}
+
+ /// <summary>
+ /// Gets the Secret value as a byte buffer
+ /// </summary>
+ /// <param name="secret"></param>
+ /// <returns>The base64 decoded secret as a byte[]</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="InternalBufferTooSmallException"></exception>
+ public static byte[] GetFromBase64(this SecretResult secret)
+ {
+ _ = secret ?? throw new ArgumentNullException(nameof(secret));
+
+ //Temp buffer
+ using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(secret.Result.Length);
+
+ //Get base64
+ if(Convert.TryFromBase64Chars(secret.Result, buffer, out int count))
+ {
+ //Copy to array
+ byte[] value = buffer.Span[..count].ToArray();
+ //Clear block before returning
+ Memory.InitializeBlock<byte>(buffer);
+
+ return value;
+ }
+
+ throw new InternalBufferTooSmallException("internal buffer too small");
+ }
+
+ /// <summary>
+ /// Recovers a certificate from a PEM encoded secret
+ /// </summary>
+ /// <param name="secret"></param>
+ /// <returns>The <see cref="X509Certificate2"/> parsed from the PEM encoded data</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static X509Certificate2 GetCertificate(this SecretResult secret)
+ {
+ _ = secret ?? throw new ArgumentNullException(nameof(secret));
+ return X509Certificate2.CreateFromPem(secret.Result);
+ }
+
+ /// <summary>
+ /// Gets the secret value as a secret result
+ /// </summary>
+ /// <param name="secret"></param>
+ /// <returns>The document parsed from the secret value</returns>
+ public static JsonDocument GetJsonDocument(this SecretResult secret)
+ {
+ _ = secret ?? throw new ArgumentNullException(nameof(secret));
+ //Alloc buffer, utf8 so 1 byte per char
+ using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(secret.Result.Length);
+ //Get utf8 bytes
+ int count = Encoding.UTF8.GetBytes(secret.Result, buffer.Span);
+ //Reader and parse
+ Utf8JsonReader reader = new(buffer.Span[..count]);
+ return JsonDocument.ParseValue(ref reader);
+ }
+
+ /// <summary>
+ /// Gets a SPKI encoded public key from a secret
+ /// </summary>
+ /// <param name="secret"></param>
+ /// <returns>The <see cref="PublicKey"/> parsed from the SPKI public key</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static PublicKey GetPublicKey(this SecretResult secret)
+ {
+ _ = secret ?? throw new ArgumentNullException(nameof(secret));
+ //Alloc buffer, base64 is larger than binary value so char len is large enough
+ using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(secret.Result.Length);
+ //Get base64 bytes
+ ERRNO count = VnEncoding.TryFromBase64Chars(secret.Result, buffer.Span);
+ //Parse the SPKI from base64
+ return PublicKey.CreateFromSubjectPublicKeyInfo(buffer.Span[..(int)count], out _);
+ }
+
+ /// <summary>
+ /// Gets the value of the <see cref="SecretResult"/> as a <see cref="PrivateKey"/>
+ /// container
+ /// </summary>
+ /// <param name="secret"></param>
+ /// <returns>The <see cref="PrivateKey"/> from the secret value</returns>
+ /// <exception cref="FormatException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static PrivateKey GetPrivateKey(this SecretResult secret)
+ {
+ _ = secret ?? throw new ArgumentNullException(nameof(secret));
+ return new PrivateKey(secret);
+ }
+
+ /// <summary>
+ /// Gets a <see cref="ReadOnlyJsonWebKey"/> from a secret value
+ /// </summary>
+ /// <param name="secret"></param>
+ /// <returns>The <see cref="ReadOnlyJsonWebKey"/> from the result</returns>
+ /// <exception cref="JsonException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static ReadOnlyJsonWebKey GetJsonWebKey(this SecretResult secret)
+ {
+ _ = secret ?? throw new ArgumentNullException(nameof(secret));
+ //Alloc buffer, utf8 so 1 byte per char
+ using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(secret.Result.Length);
+ //Get utf8 bytes
+ int count = Encoding.UTF8.GetBytes(secret.Result, buffer.Span);
+ return new ReadOnlyJsonWebKey(buffer.Span[..count]);
+ }
}
}