private static ProcessStartInfo GetProcessStartInfo(TLocator locator) { if (File.Exists(locator.ServerPath) == false) { throw new FileNotFoundException($"Server file was not found at '{locator.ServerPath}'.", locator.ServerPath); } using (var currentProcess = Process.GetCurrentProcess()) { var commandArguments = new List <string> { locator.CommandArguments, $"-c {CommandLineArgumentEscaper.EscapeSingleArg(EmptySettingsFile.FullName)}", "--ServerUrl=http://127.0.0.1:0", "--RunInMemory=true", "--Testing.ParentProcessId=" + currentProcess.Id, "--Setup.Mode=None", "--License.Eula.Accepted=true" }; var argumentsString = string.Join(" ", commandArguments); return(new ProcessStartInfo { FileName = locator.Command, Arguments = argumentsString, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, UseShellExecute = false, }); } }
private static void Execute(string args, int processId, string output) { var ravenDebugExec = Path.Combine(AppContext.BaseDirectory, PlatformDetails.RunningOnPosix ? "Raven.Debug" : "Raven.Debug.exe" ); if (File.Exists(ravenDebugExec) == false) { throw new FileNotFoundException($"Could not find debugger tool at '{ravenDebugExec}'"); } var sb = new StringBuilder($"{args} --pid {processId} --output {CommandLineArgumentEscaper.EscapeSingleArg(output)}"); var startup = new ProcessStartInfo { Arguments = sb.ToString(), FileName = ravenDebugExec, WindowStyle = ProcessWindowStyle.Normal, RedirectStandardError = true, RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; if (PlatformDetails.RunningOnPosix == false) { #pragma warning disable CA1416 // Validate platform compatibility startup.LoadUserProfile = false; #pragma warning restore CA1416 // Validate platform compatibility } var process = new Process { StartInfo = startup, EnableRaisingEvents = true }; sb.Clear(); process.ErrorDataReceived += (sender, args) => sb.Append(args.Data); process.Start(); process.BeginErrorReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { throw new InvalidOperationException($"Could not read stack traces, exit code: {process.ExitCode}, error: {sb}"); } AssertOutputExists(output, ravenDebugExec, sb.ToString()); }
private static IDocumentStore RunServer() { var options = _globalServerOptions ?? new TestServerOptions(); options.CommandLineArgs.Insert(0, $"-c {CommandLineArgumentEscaper.EscapeSingleArg(EmptySettingsFile.FullName)}"); options.CommandLineArgs.Add("--RunInMemory=true"); TestServer.StartServer(options); var url = AsyncHelpers.RunSync(() => TestServer.GetServerUriAsync()); var store = new DocumentStore { Urls = new[] { url.AbsoluteUri } }; store.Initialize(); return(store); }
public void CertificateAndMasterKeyExecTest() { string script; IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string>(); var keyPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var buffer = new byte[256 / 8]; using (var cryptoRandom = new RNGCryptoServiceProvider()) { cryptoRandom.GetBytes(buffer); } File.WriteAllBytes(keyPath, buffer); var certPath = GenerateAndSaveSelfSignedCertificate(); if (PlatformDetails.RunningOnPosix) { var scriptPath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var keyArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { scriptPath, keyPath }); var certArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { scriptPath, certPath }); customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExec)] = "bash"; customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExecArguments)] = $"{keyArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "bash"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{certArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; script = "#!/bin/bash\ncat \"$1\""; File.WriteAllText(scriptPath, script); Process.Start("chmod", $"700 {scriptPath}"); } else { var scriptPath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var keyArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", scriptPath, keyPath }); var certArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", scriptPath, certPath }); customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExecArguments)] = $"{keyArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{certArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; script = @"param([string]$userArg) try { $bytes = Get-Content -path $userArg -encoding Byte $stdout = [System.Console]::OpenStandardOutput() $stdout.Write($bytes, 0, $bytes.Length) } catch { Write-Error $_.Exception exit 1 } exit 0"; File.WriteAllText(scriptPath, script); } UseNewLocalServer(customSettings: customSettings, runInMemory: false); // The master key loading is lazy, let's put a database secret key to invoke it. var dbName = GetDatabaseName(); var databaseKey = new byte[32]; using (var rand = RandomNumberGenerator.Create()) { rand.GetBytes(databaseKey); } var base64Key = Convert.ToBase64String(databaseKey); // sometimes when using `dotnet xunit` we get platform not supported from ProtectedData try { ProtectedData.Protect(Encoding.UTF8.GetBytes("Is supported?"), null, DataProtectionScope.CurrentUser); } catch (PlatformNotSupportedException) { return; } Server.ServerStore.PutSecretKey(base64Key, dbName, true); X509Certificate2 serverCertificate; try { serverCertificate = new X509Certificate2(certPath, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); } catch (CryptographicException e) { throw new CryptographicException($"Failed to load the test certificate from {certPath}.", e); } using (var store = GetDocumentStore(new Options { AdminCertificate = serverCertificate, ClientCertificate = serverCertificate, ModifyDatabaseName = s => dbName, ModifyDatabaseRecord = record => record.Encrypted = true, Path = NewDataPath() })) { } var secrets = Server.ServerStore.Secrets; var serverMasterKey = (Lazy <byte[]>) typeof(SecretProtection).GetField("_serverMasterKey", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(secrets); Assert.True(serverMasterKey.Value.SequenceEqual(buffer)); Assert.True(Server.Certificate.Certificate.Equals(serverCertificate)); }
public static void OnDirectoryInitialize(StorageEnvironmentOptions options, DirectoryParameters parameters, Logger log) { Process process = null; try { var journalPath = string.Empty; if (options is StorageEnvironmentOptions.DirectoryStorageEnvironmentOptions dirOptions) { journalPath = dirOptions.JournalPath.FullPath; } var userArgs = parameters.OnDirectoryInitializeExecArguments ?? string.Empty; var args = $"{userArgs} {parameters.Type} {parameters.DatabaseName} " + $"{CommandLineArgumentEscaper.EscapeSingleArg(options.BasePath.ToString())} " + $"{CommandLineArgumentEscaper.EscapeSingleArg(options.TempPath.ToString())} " + $"{CommandLineArgumentEscaper.EscapeSingleArg(journalPath)}"; var startInfo = new ProcessStartInfo { FileName = parameters.OnDirectoryInitializeExec, Arguments = args, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }; var sw = Stopwatch.StartNew(); try { process = Process.Start(startInfo); } catch (Exception e) { throw new InvalidOperationException($"Unable to execute '{parameters.OnDirectoryInitializeExec} {args}'. Failed to start process.", e); } var readStdOut = process.StandardOutput.ReadToEndAsync(); var readErrors = process.StandardError.ReadToEndAsync(); string GetStdError() { try { return(readErrors.Result); } catch (Exception e) { return($"Unable to get stderr, got exception: {e}"); } } string GetStdOut() { try { return(readStdOut.Result); } catch (Exception e) { return($"Unable to get stdout, got exception: {e}"); } } if (process.WaitForExit((int)parameters.OnDirectoryInitializeExecTimeout.TotalMilliseconds) == false) { process.Kill(); throw new InvalidOperationException($"Unable to execute '{parameters.OnDirectoryInitializeExec} {args}', waited for {(int)parameters.OnDirectoryInitializeExecTimeout.TotalMilliseconds} ms but the process didn't exit. Output: {GetStdOut()}{Environment.NewLine}Errors: {GetStdError()}"); } try { readStdOut.Wait(parameters.OnDirectoryInitializeExecTimeout); readErrors.Wait(parameters.OnDirectoryInitializeExecTimeout); } catch (Exception e) { throw new InvalidOperationException($"Unable to read redirected stderr and stdout when executing '{parameters.OnDirectoryInitializeExec} {args}'", e); } // Can have exit code o (success) but still get errors. We log the errors anyway. if (log.IsOperationsEnabled) { log.Operations(string.Format($"Executing '{parameters.OnDirectoryInitializeExec} {args}' took {sw.ElapsedMilliseconds:#,#;;0} ms. Exit code: {process.ExitCode}{Environment.NewLine}Output: {GetStdOut()}{Environment.NewLine}Errors: {GetStdError()}{Environment.NewLine}")); } if (process.ExitCode != 0) { throw new InvalidOperationException( $"Command or executable '{parameters.OnDirectoryInitializeExec} {args}' failed. Exit code: {process.ExitCode}{Environment.NewLine}Output: {GetStdOut()}{Environment.NewLine}Errors: {GetStdError()}{Environment.NewLine}"); } } finally { process?.Dispose(); } }
public static Process Run(ServerOptions options) { if (string.IsNullOrWhiteSpace(options.ServerDirectory)) { throw new ArgumentNullException(nameof(options.ServerDirectory)); } if (string.IsNullOrWhiteSpace(options.DataDirectory)) { throw new ArgumentNullException(nameof(options.DataDirectory)); } if (string.IsNullOrWhiteSpace(options.LogsPath)) { throw new ArgumentNullException(nameof(options.LogsPath)); } (string exec, string fstArg) = GetExecAndFirstArgument(options); var commandLineArgs = new List <string>(options.CommandLineArgs); using (var currentProcess = Process.GetCurrentProcess()) { commandLineArgs.Add($"--Embedded.ParentProcessId={currentProcess.Id}"); } commandLineArgs.Add($"--License.Eula.Accepted={options.AcceptEula}"); commandLineArgs.Add("--Setup.Mode=None"); commandLineArgs.Add($"--DataDir={CommandLineArgumentEscaper.EscapeSingleArg(options.DataDirectory)}"); commandLineArgs.Add($"--Logs.Path={CommandLineArgumentEscaper.EscapeSingleArg(options.LogsPath)}"); if (options.Security != null) { if (string.IsNullOrWhiteSpace(options.ServerUrl)) { options.ServerUrl = "https://127.0.0.1:0"; } if (options.Security.CertificatePath != null) { commandLineArgs.Add($"--Security.Certificate.Path={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificatePath)}"); if (options.Security.CertificatePassword != null) { commandLineArgs.Add($"--Security.Certificate.Password={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificatePassword)}"); } } else { commandLineArgs.Add($"--Security.Certificate.Load.Exec={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificateLoadExec)}"); commandLineArgs.Add($"--Security.Certificate.Load.Exec.Arguments={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificateLoadExecArguments)}"); } commandLineArgs.Add($"--Security.WellKnownCertificates.Admin={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.ClientCertificate.Thumbprint)}"); } else { if (string.IsNullOrWhiteSpace(options.ServerUrl)) { options.ServerUrl = "http://127.0.0.1:0"; } } commandLineArgs.Add($"--ServerUrl={options.ServerUrl}"); if (fstArg != null) { commandLineArgs.Insert(0, CommandLineArgumentEscaper.EscapeSingleArg(fstArg)); if (string.IsNullOrWhiteSpace(options.FrameworkVersion) == false) { commandLineArgs.Insert(0, $"--fx-version {options.FrameworkVersion}"); } } var argumentsString = string.Join(" ", commandLineArgs); var processStartInfo = new ProcessStartInfo { FileName = exec, Arguments = argumentsString, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, UseShellExecute = false }; RemoveEnvironmentVariables(processStartInfo); Process process = null; try { process = Process.Start(processStartInfo); process.EnableRaisingEvents = true; } catch (Exception e) { process?.Kill(); throw new InvalidOperationException("Unable to execute server." + Environment.NewLine + "Command was: " + Environment.NewLine + (processStartInfo.WorkingDirectory ?? Directory.GetCurrentDirectory()) + "> " + processStartInfo.FileName + " " + processStartInfo.Arguments, e); } return(process); }
public static Process Run(ServerOptions options) { if (string.IsNullOrWhiteSpace(options.ServerDirectory)) { throw new ArgumentNullException(nameof(options.ServerDirectory)); } if (string.IsNullOrWhiteSpace(options.DataDirectory)) { throw new ArgumentNullException(nameof(options.DataDirectory)); } var serverDllPath = Path.Combine(options.ServerDirectory, "Raven.Server.dll"); if (File.Exists(serverDllPath) == false) { throw new FileNotFoundException("Server file was not found", serverDllPath); } if (string.IsNullOrWhiteSpace(options.DotNetPath)) { throw new ArgumentNullException(nameof(options.DotNetPath)); } using (var currentProcess = Process.GetCurrentProcess()) { options.CommandLineArgs.Add($"--Embedded.ParentProcessId={currentProcess.Id}"); } options.CommandLineArgs.Add($"--License.Eula.Accepted={options.AcceptEula}"); options.CommandLineArgs.Add("--Setup.Mode=None"); options.CommandLineArgs.Add($"--DataDir={CommandLineArgumentEscaper.EscapeSingleArg(options.DataDirectory)}"); if (options.Security != null) { if (string.IsNullOrWhiteSpace(options.ServerUrl)) { options.ServerUrl = "https://127.0.0.1:0"; } if (options.Security.CertificatePath != null) { options.CommandLineArgs.Add($"--Security.Certificate.Path={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificatePath)}"); if (options.Security.CertificatePassword != null) { options.CommandLineArgs.Add($"--Security.Certificate.Password={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificatePassword)}"); } } else { options.CommandLineArgs.Add($"--Security.Certificate.Exec={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificateExec)}"); options.CommandLineArgs.Add($"--Security.Certificate.Exec.Arguments={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.CertificateArguments)}"); } options.CommandLineArgs.Add($"--Security.WellKnownCertificates.Admin={CommandLineArgumentEscaper.EscapeSingleArg(options.Security.ClientCertificate.Thumbprint)}"); } else { if (string.IsNullOrWhiteSpace(options.ServerUrl)) { options.ServerUrl = "http://127.0.0.1:0"; } } options.CommandLineArgs.Add($"--ServerUrl={options.ServerUrl}"); options.CommandLineArgs.Insert(0, CommandLineArgumentEscaper.EscapeSingleArg(serverDllPath)); if (string.IsNullOrWhiteSpace(options.FrameworkVersion) == false) { options.CommandLineArgs.Insert(0, $"--fx-version {options.FrameworkVersion}"); } var argumentsString = string.Join(" ", options.CommandLineArgs); var processStartInfo = new ProcessStartInfo { FileName = options.DotNetPath, Arguments = argumentsString, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, UseShellExecute = false }; RemoveEnvironmentVariables(processStartInfo); Process process = null; try { process = Process.Start(processStartInfo); } catch (Exception e) { process?.Kill(); throw new InvalidOperationException("Unable to execute server." + Environment.NewLine + "Command was: " + Environment.NewLine + (processStartInfo.WorkingDirectory ?? Directory.GetCurrentDirectory()) + "> " + processStartInfo.FileName + " " + processStartInfo.Arguments, e); } return(process); }
public async Task CanReplaceClusterCertWithExtensionPoint() { string loadAndRenewScript; string certChangedScript; IDictionary <string, string> customSettings1 = new ConcurrentDictionary <string, string>(); IDictionary <string, string> customSettings2 = new ConcurrentDictionary <string, string>(); IDictionary <string, string> customSettings3 = new ConcurrentDictionary <string, string>(); var certPath = GenerateAndSaveSelfSignedCertificate(); var cert2Path = GenerateAndSaveSelfSignedCertificate(createNew: true); var outputFile = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".txt")); var firstServerCert = new X509Certificate2(certPath, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); var newServerCert = new X509Certificate2(cert2Path, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); if (PlatformDetails.RunningOnPosix) { var loadAndRenewScriptNode1Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var loadAndRenewScriptNode2Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var loadAndRenewScriptNode3Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var certChangedScriptNode1Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var certChangedScriptNode2Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var certChangedScriptNode3Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var loadArgsNode1 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { loadAndRenewScriptNode1Path, certPath }); var renewArgsNode1 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { loadAndRenewScriptNode1Path, cert2Path }); var certChangedArgsNode1 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { certChangedScriptNode1Path, outputFile }); var loadArgsNode2 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { loadAndRenewScriptNode2Path, certPath }); var renewArgsNode2 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { loadAndRenewScriptNode2Path, cert2Path }); var certChangedArgsNode2 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { certChangedScriptNode2Path, outputFile }); var loadArgsNode3 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { loadAndRenewScriptNode3Path, certPath }); var renewArgsNode3 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { loadAndRenewScriptNode3Path, cert2Path }); var certChangedArgsNode3 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { certChangedScriptNode3Path, outputFile }); customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "bash"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{loadArgsNode1}"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "bash"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExecArguments)] = $"{renewArgsNode1}"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "bash"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExecArguments)] = $"{certChangedArgsNode1}"; customSettings1[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "bash"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{loadArgsNode2}"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "bash"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExecArguments)] = $"{renewArgsNode2}"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "bash"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExecArguments)] = $"{certChangedArgsNode2}"; customSettings2[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "bash"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{loadArgsNode3}"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "bash"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExecArguments)] = $"{renewArgsNode3}"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "bash"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExecArguments)] = $"{certChangedArgsNode3}"; customSettings3[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; loadAndRenewScript = "#!/bin/bash\ncat -u \"$1\""; certChangedScript = "#!/bin/bash\nwhile read line; do\n\techo \"$line\" >> \"$1\"\ndone < /dev/stdin"; File.WriteAllText(loadAndRenewScriptNode1Path, loadAndRenewScript); File.WriteAllText(loadAndRenewScriptNode2Path, loadAndRenewScript); File.WriteAllText(loadAndRenewScriptNode3Path, loadAndRenewScript); File.WriteAllText(certChangedScriptNode1Path, certChangedScript); File.WriteAllText(certChangedScriptNode2Path, certChangedScript); File.WriteAllText(certChangedScriptNode3Path, certChangedScript); Process.Start("chmod", $"700 {loadAndRenewScriptNode1Path}"); Process.Start("chmod", $"700 {loadAndRenewScriptNode2Path}"); Process.Start("chmod", $"700 {loadAndRenewScriptNode3Path}"); Process.Start("chmod", $"700 {certChangedScriptNode1Path}"); Process.Start("chmod", $"700 {certChangedScriptNode2Path}"); Process.Start("chmod", $"700 {certChangedScriptNode3Path}"); } else { var loadAndRenewScriptNode1Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var loadAndRenewScriptNode2Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var loadAndRenewScriptNode3Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var certChangedScriptNode1Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var certChangedScriptNode2Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var certChangedScriptNode3Path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var loadArgsNode1 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", loadAndRenewScriptNode1Path, certPath }); var renewArgsNode1 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", loadAndRenewScriptNode1Path, cert2Path }); var certChangedArgsNode1 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", certChangedScriptNode1Path, outputFile }); var loadArgsNode2 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", loadAndRenewScriptNode2Path, certPath }); var renewArgsNode2 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", loadAndRenewScriptNode2Path, cert2Path }); var certChangedArgsNode2 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", certChangedScriptNode2Path, outputFile }); var loadArgsNode3 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", loadAndRenewScriptNode3Path, certPath }); var renewArgsNode3 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", loadAndRenewScriptNode3Path, cert2Path }); var certChangedArgsNode3 = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", certChangedScriptNode3Path, outputFile }); customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "powershell"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{loadArgsNode1}"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "powershell"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExecArguments)] = $"{renewArgsNode1}"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "powershell"; customSettings1[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExecArguments)] = $"{certChangedArgsNode1}"; customSettings1[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "powershell"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{loadArgsNode2}"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "powershell"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExecArguments)] = $"{renewArgsNode2}"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "powershell"; customSettings2[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExecArguments)] = $"{certChangedArgsNode2}"; customSettings2[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "powershell"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{loadArgsNode3}"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "powershell"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExecArguments)] = $"{renewArgsNode3}"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "powershell"; customSettings3[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExecArguments)] = $"{certChangedArgsNode3}"; customSettings3[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; loadAndRenewScript = @"param([string]$userArg1) try { $bytes = Get-Content -path $userArg1 -encoding Byte $stdout = [System.Console]::OpenStandardOutput() $stdout.Write($bytes, 0, $bytes.Length) } catch { Write-Error $_.Exception exit 1 } exit 0"; certChangedScript = @"param([string]$userArg1) try { $certBase64 = [System.Console]::ReadLine() Add-Content $userArg1 $certBase64 } catch { Write-Error $_.Exception exit 1 } exit 0"; File.WriteAllText(loadAndRenewScriptNode1Path, loadAndRenewScript); File.WriteAllText(loadAndRenewScriptNode2Path, loadAndRenewScript); File.WriteAllText(loadAndRenewScriptNode3Path, loadAndRenewScript); File.WriteAllText(certChangedScriptNode1Path, certChangedScript); File.WriteAllText(certChangedScriptNode2Path, certChangedScript); File.WriteAllText(certChangedScriptNode3Path, certChangedScript); } var clusterSize = 3; var databaseName = GetDatabaseName(); var leader = await CreateRaftClusterAndGetLeader(clusterSize, false, useSsl : true, customSettingsList : new List <IDictionary <string, string> > { customSettings1, customSettings2, customSettings3 }); Assert.True(leader?.Certificate?.Certificate?.Thumbprint?.Equals(firstServerCert.Thumbprint), "Cert is identical"); var adminCertificate = AskServerForClientCertificate(certPath, new Dictionary <string, DatabaseAccess>(), SecurityClearance.ClusterAdmin, server: leader); DatabasePutResult databaseResult; using (var store = new DocumentStore { Urls = new[] { leader.WebUrl }, Database = databaseName, Certificate = adminCertificate, Conventions = { DisableTopologyUpdates = true } }.Initialize()) { var doc = new DatabaseRecord(databaseName); databaseResult = await store.Maintenance.Server.SendAsync(new CreateDatabaseOperation(doc, clusterSize)); } Assert.Equal(clusterSize, databaseResult.Topology.AllNodes.Count()); foreach (var server in Servers) { await server.ServerStore.Cluster.WaitForIndexNotification(databaseResult.RaftCommandIndex); } foreach (var server in Servers.Where(s => databaseResult.NodesAddedTo.Any(n => n == s.WebUrl))) { await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(databaseName); } using (var store = new DocumentStore() { Urls = new[] { databaseResult.NodesAddedTo[0] }, Database = databaseName, Certificate = adminCertificate, Conventions = { DisableTopologyUpdates = true } }.Initialize()) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Karmelush" }, "users/1"); await session.SaveChangesAsync(); } var mre = new ManualResetEventSlim(); leader.ServerCertificateChanged += (sender, args) => mre.Set(); // This will initiate the refresh cycle, and ask a new certificate from the executable leader.RefreshClusterCertificate(false, RaftIdGenerator.NewId()); Assert.True(mre.Wait(Debugger.IsAttached ? TimeSpan.FromMinutes(10) : TimeSpan.FromMinutes(2)), "Waited too long"); Assert.NotNull(leader.Certificate.Certificate.Thumbprint); Assert.True(leader.Certificate.Certificate.Thumbprint.Equals(newServerCert.Thumbprint), "New cert is identical"); var base64NewCertWrittenByExecutable = File.ReadAllLines(outputFile)[0]; var loadedCertificate = new X509Certificate2(Convert.FromBase64String(base64NewCertWrittenByExecutable)); Assert.Equal(newServerCert.Thumbprint, loadedCertificate.Thumbprint); using (var session = store.OpenSession()) { var user1 = session.Load <User>("users/1"); Assert.NotNull(user1); Assert.Equal("Karmelush", user1.Name); } } }
private CachedValue CheckExternalCertificateValidation(string senderHostname, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors, Logger log) { var base64Cert = Convert.ToBase64String(certificate.Export(X509ContentType.Cert)); var timeout = _server.Configuration.Security.CertificateValidationExecTimeout.AsTimeSpan; var args = $"{_server.Configuration.Security.CertificateValidationExecArguments ?? string.Empty} " + $"{CommandLineArgumentEscaper.EscapeSingleArg(senderHostname)} " + $"{CommandLineArgumentEscaper.EscapeSingleArg(base64Cert)} " + $"{CommandLineArgumentEscaper.EscapeSingleArg(sslPolicyErrors.ToString())}"; var startInfo = new ProcessStartInfo { FileName = _server.Configuration.Security.CertificateValidationExec, Arguments = args, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }; var sw = Stopwatch.StartNew(); Process process; try { process = Process.Start(startInfo); } catch (Exception e) { throw new InvalidOperationException($"Unable to execute '{_server.Configuration.Security.CertificateValidationExec} {args}'. Failed to start process.", e); } var readStdOut = process.StandardOutput.ReadToEndAsync(); var readErrors = process.StandardError.ReadToEndAsync(); string GetStdError() { try { return readErrors.Result; } catch (Exception e) { return $"Unable to get stderr, got exception: {e}"; } } string GetStdOut() { try { return readStdOut.Result; } catch (Exception e) { return $"Unable to get stdout, got exception: {e}"; } } if (process.WaitForExit((int)timeout.TotalMilliseconds) == false) { process.Kill(); throw new InvalidOperationException($"Unable to execute '{_server.Configuration.Security.CertificateValidationExec} {args}', waited for {(int)timeout.TotalMilliseconds} ms but the process didn't exit. Output: {GetStdOut()}{Environment.NewLine}Errors: {GetStdError()}"); } try { readStdOut.Wait(timeout); readErrors.Wait(timeout); } catch (Exception e) { throw new InvalidOperationException($"Unable to read redirected stderr and stdout when executing '{_server.Configuration.Security.CertificateValidationExec} {args}'", e); } string output = GetStdOut(); string errors = GetStdError(); // Can have exit code 0 (success) but still get errors. We log the errors anyway. if (log.IsOperationsEnabled) log.Operations($"Executing '{_server.Configuration.Security.CertificateValidationExec} {args}' took {sw.ElapsedMilliseconds:#,#;;0} ms. Exit code: {process.ExitCode}{Environment.NewLine}Output: {output}{Environment.NewLine}Errors: {errors}{Environment.NewLine}"); if (process.ExitCode != 0) { throw new InvalidOperationException( $"Command or executable '{_server.Configuration.Security.CertificateValidationExec} {args}' failed. Exit code: {process.ExitCode}{Environment.NewLine}Output: {output}{Environment.NewLine}Errors: {errors}{Environment.NewLine}"); } if (bool.TryParse(output, out bool result) == false) { throw new InvalidOperationException( $"Cannot parse to boolean the result of Command or executable '{_server.Configuration.Security.CertificateValidationExec} {args}'. Exit code: {process.ExitCode}{Environment.NewLine}Output: {output}{Environment.NewLine}Errors: {errors}{Environment.NewLine}"); } return new CachedValue { Valid = result, Until = result ? DateTime.UtcNow.AddMinutes(15) : DateTime.UtcNow.AddSeconds(30) }; }