mirror of
https://github.com/bitwarden/self-host.git
synced 2026-06-29 14:55:46 +00:00
Add config output
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Bit.SelfHost.Deployments;
|
||||
using Bit.SelfHost.Commands;
|
||||
using Bit.SelfHost.Deployments;
|
||||
using Bit.SelfHost.Setup;
|
||||
using Xunit;
|
||||
|
||||
@@ -69,6 +70,26 @@ public class StandardTopologyTests
|
||||
=> Assert.Equal(13, new StandardDeployment().BuildTopology(CtxWith(true, true)).Count);
|
||||
}
|
||||
|
||||
public class ConfigRedactionTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("SA_PASSWORD")]
|
||||
[InlineData("globalSettings__sqlServer__connectionString")]
|
||||
[InlineData("globalSettings__identityServer__certificatePassword")]
|
||||
[InlineData("globalSettings__internalIdentityKey")]
|
||||
[InlineData("globalSettings__mail__smtp__password")]
|
||||
[InlineData("BW_INSTALLATION_KEY")]
|
||||
public void Secrets_are_redacted(string key) => Assert.True(ConfigCommand.IsSecret(key));
|
||||
|
||||
[Theory]
|
||||
[InlineData("globalSettings__mail__smtp__host")]
|
||||
[InlineData("globalSettings__mail__smtp__username")]
|
||||
[InlineData("globalSettings__installation__id")]
|
||||
[InlineData("BW_DOMAIN")]
|
||||
[InlineData("DATABASE")]
|
||||
public void Non_secrets_are_shown(string key) => Assert.False(ConfigCommand.IsSecret(key));
|
||||
}
|
||||
|
||||
public class StandardAssetBuilderTests
|
||||
{
|
||||
private static StandardConfig Config() => new() { Url = "http://localhost", GenerateNginxConfig = false };
|
||||
|
||||
@@ -7,19 +7,17 @@ public static class ConfigCommand
|
||||
{
|
||||
public static Command Build()
|
||||
{
|
||||
var cmd = new Command("config", "Get or set deployment configuration (e.g. config key=value).");
|
||||
var cmd = new Command("config", "Show the current config, or get/set a key (config key=value).");
|
||||
|
||||
var assignment = new Argument<string?>("assignment")
|
||||
{ Description = "key=value to set, or key to get. Omit with --show to list.", Arity = ArgumentArity.ZeroOrOne };
|
||||
{ Description = "key=value to set, or key to get. Omit to print the current config.", Arity = ArgumentArity.ZeroOrOne };
|
||||
var deployment = Cli.DeploymentOption();
|
||||
var root = new Option<string>("--root")
|
||||
{ Description = "Data directory (bwdata).", DefaultValueFactory = _ => "./bwdata" };
|
||||
var show = new Option<bool>("--show") { Description = "Show resolved config files." };
|
||||
|
||||
cmd.Arguments.Add(assignment);
|
||||
cmd.Options.Add(deployment);
|
||||
cmd.Options.Add(root);
|
||||
cmd.Options.Add(show);
|
||||
|
||||
cmd.SetAction(parseResult =>
|
||||
{
|
||||
@@ -28,15 +26,8 @@ public static class ConfigCommand
|
||||
var rootDir = parseResult.GetValue(root)!;
|
||||
var arg = parseResult.GetValue(assignment);
|
||||
|
||||
if (parseResult.GetValue(show) || arg is null)
|
||||
{
|
||||
Console.WriteLine($"{kind} deployment config files (under {rootDir}):");
|
||||
Console.WriteLine(kind == DeploymentKind.Standard
|
||||
? " config.yml (structural; run `bwsh apply`)\n" +
|
||||
" env/global.override.env (SMTP / admin / globalSettings; run `bwsh apply`)"
|
||||
: " settings.env (all BW_* / globalSettings__*; run `bwsh apply`)");
|
||||
return 0;
|
||||
}
|
||||
if (arg is null)
|
||||
return PrintConfig(dep, kind, rootDir);
|
||||
|
||||
var eq = arg.IndexOf('=');
|
||||
var key = eq >= 0 ? arg[..eq] : arg;
|
||||
@@ -74,4 +65,47 @@ public static class ConfigCommand
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// <summary>Prints the deployment's on-disk config files, redacting secret values.</summary>
|
||||
private static int PrintConfig(IDeployment dep, DeploymentKind kind, string rootDir)
|
||||
{
|
||||
var printed = false;
|
||||
foreach (var rel in dep.ConfigFiles)
|
||||
{
|
||||
var path = Path.Combine(rootDir, rel);
|
||||
if (!File.Exists(path)) continue;
|
||||
printed = true;
|
||||
|
||||
Console.WriteLine($"# {rel}");
|
||||
foreach (var raw in File.ReadLines(path))
|
||||
{
|
||||
var line = raw.TrimEnd();
|
||||
var eq = line.IndexOf('=');
|
||||
if (eq > 0 && !line.TrimStart().StartsWith('#'))
|
||||
{
|
||||
var key = line[..eq];
|
||||
Console.WriteLine(IsSecret(key.Trim()) ? $"{key}=<redacted>" : line);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(line); // YAML (config.yml), comments, blanks
|
||||
}
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
if (!printed)
|
||||
{
|
||||
Console.Error.WriteLine($"No {kind} deployment config found under {rootDir}. Run `install` first.");
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Values worth hiding: anything that ends in a key, or names a password/connection string.</summary>
|
||||
internal static bool IsSecret(string key)
|
||||
{
|
||||
var k = key.ToLowerInvariant();
|
||||
return k.Contains("password") || k.Contains("connectionstring") || k.EndsWith("key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ public interface IDeployment
|
||||
/// <summary>The container graph the Orchestrator brings up.</summary>
|
||||
IReadOnlyList<ServiceSpec> BuildTopology(InstallContext ctx);
|
||||
|
||||
/// <summary>Resolve a `config set key=value` key to the file it lives in + the action to apply it.</summary>
|
||||
/// <summary>Relative paths of the on-disk config files, for `config` to print (secrets redacted).</summary>
|
||||
IReadOnlyList<string> ConfigFiles { get; }
|
||||
|
||||
/// <summary>Resolve a `config key=value` key to the file it lives in + the action to apply it.</summary>
|
||||
bool TryResolveConfigKey(string key, out ConfigBinding binding);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ public sealed class LiteDeployment : IDeployment
|
||||
|
||||
public string InstalledMarker => SettingsFile;
|
||||
|
||||
public IReadOnlyList<string> ConfigFiles { get; } = [SettingsFile];
|
||||
|
||||
public string ResolveUrl(string root)
|
||||
{
|
||||
var env = new Dictionary<string, string>();
|
||||
|
||||
@@ -12,6 +12,14 @@ public sealed class StandardDeployment : IDeployment
|
||||
|
||||
public string InstalledMarker => "config.yml";
|
||||
|
||||
public IReadOnlyList<string> ConfigFiles { get; } =
|
||||
[
|
||||
"config.yml",
|
||||
"env/global.override.env",
|
||||
"env/mssql.override.env",
|
||||
"env/key-connector.override.env",
|
||||
];
|
||||
|
||||
public string ResolveUrl(string root) => Setup.StandardConfig.Load(root).Url;
|
||||
|
||||
public IReadOnlyList<NetworkSpec> Networks { get; } =
|
||||
|
||||
@@ -66,6 +66,7 @@ This is how you change config (e.g. add SMTP under the manifest's `config:` bloc
|
||||
|
||||
```bash
|
||||
dotnet run -- status # health, versions, and vault URL
|
||||
dotnet run -- config # print the current config (secrets redacted); config key=value to set
|
||||
dotnet run -- logs identity # a service's logs; --export bundles all to a zip
|
||||
dotnet run -- update # pull latest images and recreate changed services
|
||||
dotnet run -- backup # snapshot config + secrets + database to a .tar.gz
|
||||
|
||||
Reference in New Issue
Block a user