Implemented support for enabling/disabling encryption
This commit is contained in:
@@ -39,6 +39,7 @@ namespace Duplicati.Server.Database
|
||||
public const int ANY_BACKUP_ID = -1;
|
||||
public const int SERVER_SETTINGS_ID = -2;
|
||||
private readonly Dictionary<string, Backup> m_temporaryBackups = new Dictionary<string, Backup>();
|
||||
private readonly bool m_encryptSensitiveFields;
|
||||
private static HashSet<string> _encryptedFields =
|
||||
BackendLoader.Backends.SelectMany(x => x.SupportedCommands ?? [])
|
||||
.Concat(EncryptionLoader.Modules.SelectMany(x => x.SupportedCommands ?? []))
|
||||
@@ -54,7 +55,9 @@ namespace Duplicati.Server.Database
|
||||
])
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public Connection(System.Data.IDbConnection connection, bool disableFieldEncryption)
|
||||
{
|
||||
m_encryptSensitiveFields = !disableFieldEncryption;
|
||||
m_connection = connection;
|
||||
m_errorcmd = m_connection.CreateCommand();
|
||||
m_errorcmd.CommandText = @"INSERT INTO ""ErrorLog"" (""BackupID"", ""Message"", ""Exception"", ""Timestamp"") VALUES (?,?,?,?)";
|
||||
@@ -64,6 +67,31 @@ namespace Duplicati.Server.Database
|
||||
this.ApplicationSettings = new ServerSettings(this);
|
||||
}
|
||||
|
||||
public void ReWriteAllFieldsIfEncryptionChanged()
|
||||
{
|
||||
var activeToken = m_encryptSensitiveFields
|
||||
? "enc-v1-canary"
|
||||
: "no-encryption";
|
||||
|
||||
// The token is automatically decrypted when the settings are loaded
|
||||
// In case the password has changed, this will fail and return the encrypted
|
||||
// hex-string, but will crash before reaching this point
|
||||
if (this.ApplicationSettings.EncryptionCanaryToken != activeToken)
|
||||
{
|
||||
var backups = this.Backups;
|
||||
foreach (var b in backups)
|
||||
{
|
||||
((Backup)b).LoadChildren(this);
|
||||
AddOrUpdateBackup(b, false, null);
|
||||
}
|
||||
|
||||
this.SetSettings(this.GetSettings(ANY_BACKUP_ID), ANY_BACKUP_ID);
|
||||
this.ApplicationSettings.EncryptionCanaryToken = m_encryptSensitiveFields
|
||||
? EncryptedFieldHelper.Encrypt(activeToken)
|
||||
: activeToken;
|
||||
}
|
||||
}
|
||||
|
||||
public void LogError(string backupid, string message, Exception ex)
|
||||
{
|
||||
lock (m_lock)
|
||||
@@ -222,7 +250,7 @@ namespace Duplicati.Server.Database
|
||||
lock (m_lock)
|
||||
using (var tr = transaction == null ? m_connection.BeginTransaction() : null)
|
||||
{
|
||||
List<ISetting> intercepted = values.ToList();
|
||||
if (m_encryptSensitiveFields)
|
||||
values = values.Select(x => new Setting
|
||||
{
|
||||
Filter = x.Filter,
|
||||
@@ -582,7 +610,7 @@ namespace Duplicati.Server.Database
|
||||
n.Name,
|
||||
n.Description ?? "" , // Description is optional but the column is set to NOT NULL, an additional check is welcome
|
||||
string.Join(",", n.Tags ?? new string[0]),
|
||||
EncryptedFieldHelper.Encrypt(n.TargetURL)
|
||||
m_encryptSensitiveFields ? EncryptedFieldHelper.Encrypt(n.TargetURL) : n.TargetURL,
|
||||
update ? item.ID : n.DBPath
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2024, The Duplicati Team
|
||||
// Copyright (C) 2024, The Duplicati Team
|
||||
// https://duplicati.com, hello@duplicati.com
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@@ -59,6 +59,7 @@ namespace Duplicati.Server.Database
|
||||
public const string PBKDF_CONFIG = "pbkdf-config";
|
||||
public const string AUTOGENERATED_PASSPHRASE = "autogenerated-passphrase";
|
||||
public const string DISABLE_VISUAL_CAPTCHA = "disable-visual-captcha";
|
||||
public const string ENCRYPTION_CANARY_TOKEN = "encryption-canary-token";
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, string> settings;
|
||||
@@ -651,6 +652,20 @@ namespace Duplicati.Server.Database
|
||||
SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public string EncryptionCanaryToken
|
||||
{
|
||||
get
|
||||
{
|
||||
return settings[CONST.ENCRYPTION_CANARY_TOKEN];
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (databaseConnection.m_lock)
|
||||
settings[CONST.ENCRYPTION_CANARY_TOKEN] = value;
|
||||
SaveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ Error message: {0}", error); }
|
||||
public static string TempdirShort { get { return LC.L(@"Temporary storage folder"); } }
|
||||
public static string WebserverResetJwtConfigDescription { get { return LC.L(@"Reset the JWT configuration, invalidating any issued login tokens"); } }
|
||||
public static string WebserverDisableVisualCaptchaDescription { get { return LC.L(@"Disable the visual captcha"); } }
|
||||
public static string DisabledbencryptionLong { get { return LC.L(@"Use this option to disable database encryption of sensitive fields"); } }
|
||||
public static string DisabledbencryptionShort { get { return LC.L(@"Disable database encryption"); } }
|
||||
}
|
||||
internal static class Scheduler
|
||||
{
|
||||
|
||||
@@ -252,7 +252,6 @@ namespace Duplicati.Server
|
||||
DataConnection.FixInvalidBackupId();
|
||||
|
||||
DataConnection.ApplicationSettings.UpgradePasswordToKBDF();
|
||||
|
||||
CreateApplicationInstance(writeToConsole);
|
||||
|
||||
StartOrStopUsageReporter();
|
||||
@@ -290,6 +289,8 @@ namespace Duplicati.Server
|
||||
PingPongThread.Start();
|
||||
}
|
||||
|
||||
DataConnection.ReWriteAllFieldsIfEncryptionChanged();
|
||||
|
||||
ServerStartedEvent.Set();
|
||||
ApplicationExitEvent.WaitOne();
|
||||
}
|
||||
@@ -705,7 +706,7 @@ namespace Duplicati.Server
|
||||
throw new Exception(Strings.Program.DatabaseOpenError(ex.Message));
|
||||
}
|
||||
|
||||
return new Database.Connection(con);
|
||||
return new Database.Connection(con, Library.Utility.Utility.ParseBoolOption(commandlineOptions, "disable-db-encryption"));
|
||||
}
|
||||
|
||||
public static void StartOrStopUsageReporter()
|
||||
@@ -829,6 +830,7 @@ namespace Duplicati.Server
|
||||
new Duplicati.Library.Interface.CommandLineArgument("ping-pong-keepalive", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.PingpongkeepaliveShort, Strings.Program.PingpongkeepaliveLong),
|
||||
new Duplicati.Library.Interface.CommandLineArgument("log-retention", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Timespan, Strings.Program.LogretentionShort, Strings.Program.LogretentionLong, DEFAULT_LOG_RETENTION),
|
||||
new Duplicati.Library.Interface.CommandLineArgument("server-datafolder", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Path, Strings.Program.ServerdatafolderShort, Strings.Program.ServerdatafolderLong(DATAFOLDER_ENV_NAME), System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Library.AutoUpdater.AutoUpdateSettings.AppName)),
|
||||
new Duplicati.Library.Interface.CommandLineArgument("disable-db-encryption", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.DisabledbencryptionShort, Strings.Program.DisabledbencryptionLong),
|
||||
];
|
||||
|
||||
private static bool ReadOptionsFromFile(string filename, ref Library.Utility.IFilter filter, List<string> cargs, Dictionary<string, string> options)
|
||||
|
||||
Reference in New Issue
Block a user