Ejemplo n.º 1
0
        public async Task CanGetLetsEncryptCertificateAndRenewIt()
        {
            var settingPath         = Path.Combine(NewDataPath(forceCreateDir: true), "settings.json");
            var defaultSettingsPath = new PathSetting("settings.default.json").FullPath;

            File.Copy(defaultSettingsPath, settingPath, true);

            UseNewLocalServer(customConfigPath: settingPath);

            // Use this when testing against pebble
            //var acmeStaging = "https://localhost:14000/";

            var acmeStaging = "https://acme-staging-v02.api.letsencrypt.org/";

            Server.Configuration.Core.AcmeUrl = acmeStaging;
            Server.ServerStore.Configuration.Core.SetupMode = SetupMode.Initial;

            var    domain = "RavenClusterTest" + Environment.MachineName.Replace("-", "");
            string email;
            string rootDomain;

            Server.ServerStore.EnsureNotPassive();
            var license = Server.ServerStore.LoadLicense();

            using (var store = GetDocumentStore())
                using (var commands = store.Commands())
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                    {
                        var command = new ClaimDomainCommand(store.Conventions, context, new ClaimDomainInfo
                        {
                            Domain  = domain,
                            License = license
                        });

                        await commands.RequestExecutor.ExecuteAsync(command, commands.Context);

                        Assert.True(command.Result.RootDomains.Length > 0);
                        rootDomain = command.Result.RootDomains[0];
                        email      = command.Result.Email;
                    }

            var tcpListener = new TcpListener(IPAddress.Loopback, 0);

            tcpListener.Start();
            var port = ((IPEndPoint)tcpListener.LocalEndpoint).Port;

            tcpListener.Stop();

            var setupInfo = new SetupInfo
            {
                Domain             = domain,
                RootDomain         = rootDomain,
                ModifyLocalServer  = false, // N/A here
                RegisterClientCert = false, // N/A here
                Password           = null,
                Certificate        = null,
                LocalNodeTag       = "A",
                License            = license,
                Email          = email,
                NodeSetupInfos = new Dictionary <string, SetupInfo.NodeInfo>()
                {
                    ["A"] = new SetupInfo.NodeInfo
                    {
                        Port      = port,
                        Addresses = new List <string>
                        {
                            "127.0.0.1"
                        }
                    }
                }
            };

            X509Certificate2 serverCert;

            byte[] serverCertBytes;
            string firstServerCertThumbprint;
            BlittableJsonReaderObject settingsJsonObject;

            using (var store = GetDocumentStore())
                using (var commands = store.Commands())
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                    {
                        var command = new SetupLetsEncryptCommand(store.Conventions, context, setupInfo);

                        await commands.RequestExecutor.ExecuteAsync(command, commands.Context);

                        Assert.True(command.Result.Length > 0);

                        var zipBytes = command.Result;

                        try
                        {
                            settingsJsonObject        = SetupManager.ExtractCertificatesAndSettingsJsonFromZip(zipBytes, "A", context, out serverCertBytes, out serverCert, out _, out _, out _, out _);
                            firstServerCertThumbprint = serverCert.Thumbprint;
                        }
                        catch (Exception e)
                        {
                            throw new InvalidOperationException("Unable to extract setup information from the zip file.", e);
                        }

                        // Finished the setup wizard, need to restart the server.
                        // Since cannot restart we'll create a new server loaded with the new certificate and settings and use the server cert to connect to it

                        settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Security.CertificatePassword), out string certPassword);
                        settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Security.CertificateLetsEncryptEmail), out string letsEncryptEmail);
                        settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.PublicServerUrl), out string publicServerUrl);
                        settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.ServerUrls), out string serverUrl);
                        settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.SetupMode), out SetupMode setupMode);
                        settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.ExternalIp), out string externalIp);

                        var tempFileName = Path.GetTempFileName();
                        File.WriteAllBytes(tempFileName, serverCertBytes);

                        IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string>
                        {
                            [RavenConfiguration.GetKey(x => x.Security.CertificatePath)]             = tempFileName,
                            [RavenConfiguration.GetKey(x => x.Security.CertificateLetsEncryptEmail)] = letsEncryptEmail,
                            [RavenConfiguration.GetKey(x => x.Security.CertificatePassword)]         = certPassword,
                            [RavenConfiguration.GetKey(x => x.Core.PublicServerUrl)] = publicServerUrl,
                            [RavenConfiguration.GetKey(x => x.Core.ServerUrls)]      = serverUrl,
                            [RavenConfiguration.GetKey(x => x.Core.SetupMode)]       = setupMode.ToString(),
                            [RavenConfiguration.GetKey(x => x.Core.ExternalIp)]      = externalIp,
                            [RavenConfiguration.GetKey(x => x.Core.AcmeUrl)]         = acmeStaging
                        };

                        Server.Dispose();

                        DoNotReuseServer(customSettings);
                    }

            UseNewLocalServer();


            // Note: because we use a staging lets encrypt cert, the chain is not trusted.
            // It only works because in the TestBase ctor we do:
            // RequestExecutor.ServerCertificateCustomValidationCallback += (msg, cert, chain, errors) => true;

            using (var store = GetDocumentStore(new Options
            {
                AdminCertificate = serverCert,
                ClientCertificate = serverCert
            }))
                using (var commands = store.Commands())
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                    {
                        Server.ServerStore.EnsureNotPassive();
                        Assert.Equal(firstServerCertThumbprint, Server.Certificate.Certificate.Thumbprint);

                        Server.Time.UtcDateTime = () => DateTime.UtcNow.AddDays(80);

                        var mre = new ManualResetEventSlim();

                        Server.ServerCertificateChanged += (sender, args) => mre.Set();

                        var command = new ForceRenewCertCommand(store.Conventions, context);

                        await commands.RequestExecutor.ExecuteAsync(command, commands.Context);

                        Assert.True(command.Result.Success, "ForceRenewCertCommand returned false");

                        var result = mre.Wait(Debugger.IsAttached ? TimeSpan.FromMinutes(10) : TimeSpan.FromMinutes(2));

                        if (result == false && Server.RefreshTask.IsCompleted)
                        {
                            if (Server.RefreshTask.IsFaulted || Server.RefreshTask.IsCanceled)
                            {
                                Assert.True(result,
                                            $"Refresh task failed to complete successfully. Exception: {Server.RefreshTask.Exception}");
                            }

                            Assert.True(result, "Refresh task completed successfully, waited too long for the cluster cert to be replaced");
                        }

                        Assert.True(result, "Refresh task didn't complete. Waited too long for the cluster cert to be replaced");

                        Assert.NotEqual(firstServerCertThumbprint, Server.Certificate.Certificate.Thumbprint);
                    }
        }
        public async Task CanGetLetsEncryptCertificateAndRenewIt()
        {
            UseNewLocalServer();

            var acmeStaging = "https://acme-staging.api.letsencrypt.org/directory";

            Server.Configuration.Core.AcmeUrl = acmeStaging;
            Server.ServerStore.Configuration.Core.SetupMode = SetupMode.Initial;

            var    domain = "RavenClusterTest" + Environment.MachineName.Replace("-", "");
            string email;
            string rootDomain;

            Server.ServerStore.EnsureNotPassive();
            var license = Server.ServerStore.LoadLicense();

            using (var store = GetDocumentStore())
                using (var commands = store.Commands())
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                    {
                        var command = new ClaimDomainCommand(store.Conventions, context, new ClaimDomainInfo
                        {
                            Domain  = domain,
                            License = license
                        });

                        await commands.RequestExecutor.ExecuteAsync(command, commands.Context);

                        Assert.True(command.Result.RootDomains.Length > 0);
                        rootDomain = command.Result.RootDomains[0];
                        email      = command.Result.Email;
                    }

            var setupInfo = new SetupInfo
            {
                Domain             = domain,
                RootDomain         = rootDomain,
                ModifyLocalServer  = false, // N/A here
                RegisterClientCert = false, // N/A here
                Password           = null,
                Certificate        = null,
                License            = license,
                Email          = email,
                NodeSetupInfos = new Dictionary <string, SetupInfo.NodeInfo>()
                {
                    ["A"] = new SetupInfo.NodeInfo
                    {
                        Port      = 8080,
                        Addresses = new List <string>
                        {
                            "127.0.0.1"
                        }
                    }
                }
            };

            X509Certificate2          serverCert;
            string                    firstServerCertThumbprint;
            BlittableJsonReaderObject settingsJsonObject;

            using (var store = GetDocumentStore())
                using (var commands = store.Commands())
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                    {
                        var command = new SetupLetsEncryptCommand(store.Conventions, context, setupInfo);

                        await commands.RequestExecutor.ExecuteAsync(command, commands.Context);

                        Assert.True(command.Result.Length > 0);

                        var zipBytes = command.Result;

                        try
                        {
                            settingsJsonObject        = SetupManager.ExtractCertificatesAndSettingsJsonFromZip(zipBytes, "A", context, out serverCert, out _, out _, out _);
                            firstServerCertThumbprint = serverCert.Thumbprint;
                        }
                        catch (Exception e)
                        {
                            throw new InvalidOperationException("Unable to extract setup information from the zip file.", e);
                        }
                    }

            // Finished the setup wizard, need to restart the server.
            // Since cannot restart we'll create a new server loaded with the new certificate and settings and use the server cert to connect to it

            settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Security.CertificatePassword), out string certPassword);
            settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Security.CertificateLetsEncryptEmail), out string letsEncryptEmail);
            settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.PublicServerUrl), out string publicServerUrl);
            settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.ServerUrls), out string serverUrl);
            settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.SetupMode), out SetupMode setupMode);
            settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.ExternalIp), out string externalIp);

            var tempFileName = Path.GetTempFileName();

            byte[] certData = serverCert.Export(X509ContentType.Pfx);
            File.WriteAllBytes(tempFileName, certData);

            IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string>
            {
                [RavenConfiguration.GetKey(x => x.Security.CertificatePath)]             = tempFileName,
                [RavenConfiguration.GetKey(x => x.Security.CertificateLetsEncryptEmail)] = letsEncryptEmail,
                [RavenConfiguration.GetKey(x => x.Security.CertificatePassword)]         = certPassword,
                [RavenConfiguration.GetKey(x => x.Core.PublicServerUrl)] = publicServerUrl,
                [RavenConfiguration.GetKey(x => x.Core.ServerUrls)]      = serverUrl,
                [RavenConfiguration.GetKey(x => x.Core.SetupMode)]       = setupMode.ToString(),
                [RavenConfiguration.GetKey(x => x.Core.ExternalIp)]      = externalIp,
                [RavenConfiguration.GetKey(x => x.Core.AcmeUrl)]         = acmeStaging
            };

            Server.Dispose();

            DoNotReuseServer(customSettings);
            UseNewLocalServer();

            using (var store = GetDocumentStore(new Options
            {
                AdminCertificate = serverCert,
                ClientCertificate = serverCert
            }))
                using (var commands = store.Commands())
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                    {
                        Server.ServerStore.EnsureNotPassive();
                        Assert.Equal(firstServerCertThumbprint, Server.Certificate.Certificate.Thumbprint);

                        Server.Time.UtcDateTime = () => DateTime.UtcNow.AddDays(80);

                        var mre = new ManualResetEventSlim();

                        Server.ServerCertificateChanged += (sender, args) => mre.Set();

                        var command = new ForceRenewCertCommand(store.Conventions, context);

                        await commands.RequestExecutor.ExecuteAsync(command, commands.Context);

                        mre.Wait(Debugger.IsAttached ? TimeSpan.FromMinutes(10) : TimeSpan.FromMinutes(2));

                        Assert.NotEqual(firstServerCertThumbprint, Server.Certificate.Certificate.Thumbprint);
                    }
        }