justeat/Topshelf.Nancy

Installing Self-Signed x509 Certificates should be integrated into Nancy and Topshelf

StewartScottRogers opened this issue · 2 comments

public static void Main() {
    Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
    var loggingManager = new LoggingManager(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    loggingManager.TraceAndTrapExceptions(() => {
        loggingManager.Log.Debug(() => "Beggin: " + ConfigurationManagement.DisplayDescription);

        HostFactory.Run(hostConfiguration => {

            hostConfiguration.BeforeInstall(() => {
                X509Certificate2Manager.EnsureDefaultServerCertificatesAreInstalled(ApplicationInformation.Product);

                foreach (var serverX509CertificatesForMicroService in X509Certificate2Manager.SelectServerX509CertificatesForMicroServices(ApplicationInformation.Product)) {
                    if (serverX509CertificatesForMicroService.TryNetshDelete(ConfigurationManagement.GetLocalhostDomainPortNumber()))
                        serverX509CertificatesForMicroService.NetshAdd(ConfigurationManagement.GetLocalhostDomainPortNumber());

                    if (serverX509CertificatesForMicroService.TryNetshDelete(ConfigurationManagement.GetMachineDomainPortNumber()))
                        serverX509CertificatesForMicroService.NetshAdd(ConfigurationManagement.GetMachineDomainPortNumber());
                }
            });

            hostConfiguration.AfterUninstall(() => {
                foreach (var serverX509CertificatesForMicroService in X509Certificate2Manager.SelectServerX509CertificatesForMicroServices(ApplicationInformation.Product)) {
                    serverX509CertificatesForMicroService.TryNetshDelete(ConfigurationManagement.GetLocalhostDomainPortNumber());
                    serverX509CertificatesForMicroService.TryNetshDelete(ConfigurationManagement.GetMachineDomainPortNumber());
                }
            });

            hostConfiguration.Service<NancySelfHost>(serviceFactoryBuilder => {

                serviceFactoryBuilder.WithNancyEndpoint(hostConfiguration, nancyConfigurator => {

                    nancyConfigurator.ConfigureNancy(nancyHostConfigurator => {
                        nancyHostConfigurator.RewriteLocalhost = false;   // Lets you run at other than admin rights then you can ignore "Run As Administrator".
                        loggingManager.Log.Info(() => $"RewriteLocalhost: {nancyHostConfigurator.RewriteLocalhost}");

                        nancyHostConfigurator.UnhandledExceptionCallback = exception => loggingManager.Log.Error(() => new LoggingRequest("NancySelfHost.hostConfiguration.UnhandledExceptionCallback.", exception));

                        nancyHostConfigurator.EnableClientCertificates = ConfigurationManagement.GetEnableClientCertificates();
                        loggingManager.Log.Info(() => $"EnableClientCertificates: {nancyHostConfigurator.EnableClientCertificates}");

                        nancyHostConfigurator.AllowChunkedEncoding = false;
                        loggingManager.Log.Info(() => $"AllowChunkedEncoding: {nancyHostConfigurator.AllowChunkedEncoding}");

                        nancyHostConfigurator.AllowAuthorityFallback = false;
                        loggingManager.Log.Info(() => $"AllowAuthorityFallback: {nancyHostConfigurator.AllowAuthorityFallback}");

                        nancyHostConfigurator.UrlReservations.CreateAutomatically = true;
                        loggingManager.Log.Info(() => $"UrlReservations.CreateAutomatically: {nancyHostConfigurator.UrlReservations.CreateAutomatically}");
                        loggingManager.Log.Info(() => $"UrlReservations.User: {nancyHostConfigurator.UrlReservations.User}");
                    });

                    var scheme = "http";
                    if (ConfigurationManagement.GetEnableServerCertificates())
                        scheme = "https";

                    nancyConfigurator.AddHost(scheme: scheme, domain: ConfigurationManagement.GetLocalhostDomainName(), port: ConfigurationManagement.GetLocalhostDomainPortNumber());
                    loggingManager.Log.Info(() => $"Host => scheme: {scheme}, domain: {ConfigurationManagement.GetLocalhostDomainName()}, port: {ConfigurationManagement.GetLocalhostDomainPortNumber()}");

                    nancyConfigurator.AddHost(scheme: scheme, domain: ConfigurationManagement.GetMachineDomainName(), port: ConfigurationManagement.GetMachineDomainPortNumber());
                    loggingManager.Log.Info(() => $"Host => scheme: {scheme}, domain: {ConfigurationManagement.GetMachineDomainName()}, port: {ConfigurationManagement.GetMachineDomainPortNumber()}");

                    nancyConfigurator.DeleteReservationsOnUnInstall();
                    nancyConfigurator.CreateUrlReservationsOnInstall();
                    nancyConfigurator.OpenFirewallPortsOnInstall(firewallRuleName: $"{ConfigurationManagement.ApplicationName}:{ConfigurationManagement.InstanceName}.FirewallRule");
                    loggingManager.Log.Info(() => $"ShouldOpenFirewallPorts '{nancyConfigurator.ShouldOpenFirewallPorts}', FirewallRuleName: '{nancyConfigurator.FirewallRuleName}'");
                });

                serviceFactoryBuilder.ConstructUsing(hostSettings => new NancySelfHost());

                serviceFactoryBuilder.AfterStartingService(hostStartContext => { });
                serviceFactoryBuilder.BeforeStoppingService(hostStartContext => { });
                serviceFactoryBuilder.WhenStarted(nancySelfHost => nancySelfHost.Start());
                serviceFactoryBuilder.WhenStopped(nancySelfHost => { nancySelfHost.Stop(); });
                serviceFactoryBuilder.WhenPaused(nancySelfHost => nancySelfHost.Pause());
                serviceFactoryBuilder.WhenContinued(nancySelfHost => nancySelfHost.Continue());
                serviceFactoryBuilder.WhenShutdown(nancySelfHost => { nancySelfHost.Shutdown(); });
                serviceFactoryBuilder.BeforeStartingService(hostStartContext => { hostStartContext.RequestAdditionalTime(TimeSpan.FromSeconds(120)); });
                serviceFactoryBuilder.AfterStoppingService(hostStartContext => { });
            });


            hostConfiguration.SetServiceName(ConfigurationManagement.ApplicationName);
            hostConfiguration.SetInstanceName(ConfigurationManagement.InstanceName);
            hostConfiguration.SetDisplayName(ConfigurationManagement.DisplayName);
            hostConfiguration.SetDescription(ConfigurationManagement.DisplayDescription);

            hostConfiguration.RunAsNetworkService();
            hostConfiguration.StartAutomatically();

            hostConfiguration.EnablePauseAndContinue();
            hostConfiguration.EnableShutdown();

            hostConfiguration.EnableServiceRecovery(serviceRecovers => {
                serviceRecovers.OnCrashOnly();
                serviceRecovers.SetResetPeriod(2);
                serviceRecovers.RestartService(3);
                //serviceRecovers.RestartComputer(5, $"Restarting the server '{Environment.MachineName}' because '{ConfigurationManagement.ApplicationName}' has scheduled  a restart due to multiple failures starting the windows service.");
            });
        });
        loggingManager.Log.Debug(() => "Ending: " + ConfigurationManagement.DisplayDescription);
    });
}

}

Should this be a PR, so we can compare the code?

Anthony,

I am not sure of the correct way to deal with this or your team. So I will just take a stab at it, and can adjust me.

I am currently developing micro-services for my day job and using both Nancy and Topshelf as the foundation. The code I submitted was an opening statement about where I am currently at. I have self-signed certificates generated on the fly and installing as the code demonstrates. However, it is awkward and troublesome due to the order in which x509 certificate are installed compared with the other fluent interface flags like “DeleteReservationsOnUnInstall, CreateUrlReservationsOnInstall, OpenFirewallPortsOnInstall”. I am currently deploying using “Octopus Deploy” but find it difficult to know exactly what is happening when I use BeforeInstall and AfterUninstall compared to the previously mentioned fluent interfaces flags internal execution order.

I feel turning over the code base and working with your team to make Self-Signed x509 certificates creation, installation, and removal part of the open source project would greatly benefit your open source project and simplify my codebase in the long run.

How can I help this happen?