mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-01-28 16:02:22 -05:00
139 lines
5.7 KiB
C#
139 lines
5.7 KiB
C#
//-----------------------------------------------------------------------
|
|
// <copyright file="SecretReader.cs" company="aliasvault">
|
|
// Copyright (c) aliasvault. All rights reserved.
|
|
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
|
|
// </copyright>
|
|
//-----------------------------------------------------------------------
|
|
|
|
namespace AliasVault.Shared.Server.Utilities;
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
|
|
/// <summary>
|
|
/// Utility class for reading secrets from environment variables or container secret files.
|
|
/// </summary>
|
|
public static class SecretReader
|
|
{
|
|
/// <summary>
|
|
/// Determines if the application is running in a container.
|
|
/// </summary>
|
|
/// <returns>True if running in a container, false otherwise.</returns>
|
|
public static bool IsRunningInContainer()
|
|
{
|
|
return Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the JWT key from either the container secrets file or environment variable.
|
|
/// </summary>
|
|
/// <returns>The JWT key.</returns>
|
|
/// <exception cref="KeyNotFoundException">Thrown when the JWT key cannot be found or is invalid.</exception>
|
|
public static string GetJwtKey()
|
|
{
|
|
if (IsRunningInContainer())
|
|
{
|
|
return ReadSecretFromFile("/secrets/jwt_key", "JWT key");
|
|
}
|
|
|
|
var jwtKey = Environment.GetEnvironmentVariable("JWT_KEY");
|
|
if (string.IsNullOrEmpty(jwtKey))
|
|
{
|
|
throw new KeyNotFoundException("JWT_KEY environment variable is not set.");
|
|
}
|
|
|
|
return jwtKey;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the admin password hash and generation timestamp.
|
|
/// </summary>
|
|
/// <returns>A tuple containing the password hash and the timestamp when it was generated.</returns>
|
|
/// <exception cref="KeyNotFoundException">Thrown when the admin password hash cannot be found.</exception>
|
|
/// <exception cref="InvalidOperationException">Thrown when the admin password hash file has an invalid format.</exception>
|
|
public static (string PasswordHash, DateTime GeneratedAt) GetAdminPasswordHash()
|
|
{
|
|
if (IsRunningInContainer())
|
|
{
|
|
var secretsFilePath = "/secrets/admin_password_hash";
|
|
if (!File.Exists(secretsFilePath))
|
|
{
|
|
throw new KeyNotFoundException($"Admin password hash file not found at {secretsFilePath}. Container initialization may have failed.");
|
|
}
|
|
|
|
var secretContent = File.ReadAllText(secretsFilePath).Trim();
|
|
if (string.IsNullOrEmpty(secretContent))
|
|
{
|
|
throw new KeyNotFoundException($"Admin password hash file at {secretsFilePath} is empty.");
|
|
}
|
|
|
|
// Parse hash and timestamp separated by |
|
|
var parts = secretContent.Split('|');
|
|
if (parts.Length != 2)
|
|
{
|
|
throw new InvalidOperationException($"Invalid format in {secretsFilePath}. Expected format: hash|timestamp");
|
|
}
|
|
|
|
var passwordHash = parts[0];
|
|
if (!DateTime.TryParse(parts[1], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var generatedAt))
|
|
{
|
|
throw new InvalidOperationException($"Invalid timestamp format in {secretsFilePath}: {parts[1]}");
|
|
}
|
|
|
|
return (passwordHash, generatedAt);
|
|
}
|
|
else
|
|
{
|
|
// Not in container - use environment variables
|
|
var passwordHash = Environment.GetEnvironmentVariable("ADMIN_PASSWORD_HASH")
|
|
?? throw new KeyNotFoundException("ADMIN_PASSWORD_HASH environment variable is not set.");
|
|
|
|
var generatedAtStr = Environment.GetEnvironmentVariable("ADMIN_PASSWORD_GENERATED")
|
|
?? throw new KeyNotFoundException("ADMIN_PASSWORD_GENERATED environment variable is not set.");
|
|
|
|
var generatedAt = DateTime.Parse(generatedAtStr, CultureInfo.InvariantCulture);
|
|
return (passwordHash, generatedAt);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the data protection certificate password.
|
|
/// </summary>
|
|
/// <returns>The data protection certificate password.</returns>
|
|
/// <exception cref="KeyNotFoundException">Thrown when the certificate password cannot be found.</exception>
|
|
public static string GetDataProtectionCertPassword()
|
|
{
|
|
if (IsRunningInContainer())
|
|
{
|
|
return ReadSecretFromFile("/secrets/data_protection_cert_pass", "Certificate password");
|
|
}
|
|
|
|
return Environment.GetEnvironmentVariable("DATA_PROTECTION_CERT_PASS")
|
|
?? throw new KeyNotFoundException("DATA_PROTECTION_CERT_PASS is not set in configuration or environment variables.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a secret from a file in the container secrets directory.
|
|
/// </summary>
|
|
/// <param name="filePath">The path to the secret file.</param>
|
|
/// <param name="secretName">The name of the secret for error messages.</param>
|
|
/// <returns>The secret value.</returns>
|
|
/// <exception cref="KeyNotFoundException">Thrown when the secret file cannot be found or is invalid.</exception>
|
|
private static string ReadSecretFromFile(string filePath, string secretName)
|
|
{
|
|
if (!File.Exists(filePath))
|
|
{
|
|
throw new KeyNotFoundException($"{secretName} file not found at {filePath}. Container initialization may have failed.");
|
|
}
|
|
|
|
var secretValue = File.ReadAllText(filePath).Trim();
|
|
if (string.IsNullOrEmpty(secretValue))
|
|
{
|
|
throw new KeyNotFoundException($"{secretName} file at {filePath} is empty.");
|
|
}
|
|
|
|
return secretValue;
|
|
}
|
|
}
|