Exemplo n.º 1
0
        public static void Renew(
            [Optional(false, "xt", Description = "Skip authentication test")]
            bool skipTest,
            [Optional(false, "wi", Description = "What if - only show hosts to be renewed")]
            bool whatIf,
            [Optional(null, "cfg", Description = "Custom configuration file name")]
            string cfgFileName,
            [Optional(false, Description = "Show verbose error messages")]
            bool verbose)
        {
            Log.VerboseMode = verbose;
            if (AcmeEnvironment.CfgStore == null)
            {
                AcmeEnvironment.LoadConfig(cfgFileName);
            }

            // Get hosts expiring in near future
            Log.Write($"Loading hosts expiring in {AcmeEnvironment.CfgStore.RenewDaysBeforeExpiration} days...");
            var expiringHosts = AcmeEnvironment.CfgStore.Hosts
                                .Where(x => x.NotAfter <= DateTime.Now.AddDays(AcmeEnvironment.CfgStore.RenewDaysBeforeExpiration))
                                .OrderBy(x => x.NotAfter);

            if (!expiringHosts.Any())
            {
                Log.WriteLine("OK, no hosts to renew");
                return;
            }
            Log.WriteLine($"OK, {expiringHosts.Count()} hosts to renew");
            using (var ac = new AutoAcmeContext(AcmeEnvironment.CfgStore.ServerUri)) {
                try {
                    ac.ChallengeVerificationRetryCount = AcmeEnvironment.CfgStore.ChallengeVerificationRetryCount;
                    ac.ChallengeVerificationWait       = TimeSpan.FromSeconds(AcmeEnvironment.CfgStore.ChallengeVerificationWaitSeconds);
                    if (string.IsNullOrEmpty(AcmeEnvironment.CfgStore.SerializedAccountData))
                    {
                        AcmeEnvironment.CfgStore.SerializedAccountData = ac.RegisterAndLogin(AcmeEnvironment.CfgStore.EmailAddress);
                        AcmeEnvironment.SaveConfig(cfgFileName);
                    }
                    else
                    {
                        ac.Login(AcmeEnvironment.CfgStore.SerializedAccountData);
                    }
                }
                catch (Exception ex) {
                    Log.Exception(ex, "Login failed");
                    AcmeEnvironment.CrashExit("Unable to login or create account.");
                }

                // Renew them
                using (var challengeManager = AcmeEnvironment.CreateChallengeManager()) {
                    foreach (var host in expiringHosts)
                    {
                        // Display info
                        var dte = Math.Floor(host.NotAfter.Subtract(DateTime.Now).TotalDays);
                        if (dte < 0)
                        {
                            Log.WriteLine($"Host {host.CommonName} expired {-dte} days ago ({host.NotAfter:D})");
                        }
                        else
                        {
                            Log.WriteLine($"Host {host.CommonName} expires in {dte} days ({host.NotAfter:D})");
                        }
                        if (whatIf)
                        {
                            continue;
                        }
                        Log.Indent();

                        // Request certificate
                        CertificateRequestResult result = null;
                        try {
                            result = ac.GetCertificate(host.GetNames(), AcmeEnvironment.CfgStore.PfxPassword, challengeManager, skipTest);
                        }
                        catch (Exception ex) {
                            Log.Exception(ex, "Renewal failed");
                        }
                        if (result != null)
                        {
                            // Display certificate info
                            Log.WriteLine("Certificate information:");
                            Log.Indent();
                            Log.WriteLine($"Issuer:        {result.Certificate.Issuer}");
                            Log.WriteLine($"Subject:       {result.Certificate.Subject}");
                            Log.WriteLine($"Serial number: {result.Certificate.SerialNumber}");
                            Log.WriteLine($"Not before:    {result.Certificate.NotBefore:o}");
                            Log.WriteLine($"Not after:     {result.Certificate.NotAfter:o}");
                            Log.WriteLine($"Thumbprint:    {result.Certificate.Thumbprint}");
                            Log.Unindent();

                            // Export files
                            Log.WriteLine("Exporting files:");
                            Log.Indent();
                            foreach (var name in host.GetNames())
                            {
                                result.Export(name, AcmeEnvironment.CfgStore.PfxFolder, AcmeEnvironment.CfgStore.PemFolder);
                            }
                            Log.Unindent();

                            // Update database entry
                            Log.Write("Updating database entry...");
                            host.NotBefore    = result.Certificate.NotBefore;
                            host.NotAfter     = result.Certificate.NotAfter;
                            host.SerialNumber = result.Certificate.SerialNumber;
                            host.Thumbprint   = result.Certificate.Thumbprint;
                            Log.WriteLine("OK");

                            // Save configuration
                            AcmeEnvironment.SaveConfig(cfgFileName);
                        }
                        Log.Unindent();
                    }
                }
            }
        }
Exemplo n.º 2
0
        public static void AddHosts(
            [Optional(false, "ccs", Description = "Add CCS binding to hosts without one and add them as well")]
            bool addCcsBinding,
            [Optional(false, "sni", Description = "Require SNI for newly created bindings")]
            bool requireSni,
            [Optional("localhost", "s", Description = "IIS server name")]
            string serverName,
            [Optional(AcmeEnvironment.DEFAULT_CONFIG_NAME, "cfg", Description = "Configuration file name")]
            string cfgFileName,
            [Optional(false, Description = "Show verbose error messages")]
            bool verbose)
        {
            Log.VerboseMode = verbose;
            AcmeEnvironment.LoadConfig(cfgFileName);

            using (var sc = new ServerContext(serverName)) {
                IEnumerable <BindingInfo> bindings = null;
                try {
                    Log.Write($"Getting bindings from '{serverName}'...");
                    // Get all bindings
                    bindings = sc.GetBindings();
                }
                catch (Exception ex) {
                    AcmeEnvironment.CrashExit(ex);
                }

                // Get only bindings matching the following criteria
                //   - host name specified
                //   - site is running
                //   - site is running on default port
                bindings = from b in bindings
                           where !string.IsNullOrEmpty(b.Host) && b.SiteStarted && b.IsDefaultPort
                           select b;

                // Get only CCS enabled sites, unless overriden
                if (!addCcsBinding)
                {
                    bindings = bindings.Where(x => x.CentralCertStore);
                }
                Log.WriteLine($"OK, {bindings.Count()} bindings found");

                // Find new hosts
                Log.Write("Finding new hosts to add...");
                bindings = bindings.Where(x => !AcmeEnvironment.CfgStore.Hosts.SelectMany(h => h.GetNames()).Any(h => h.Equals(x.Host, StringComparison.OrdinalIgnoreCase)));
                if (!bindings.Any())
                {
                    Log.WriteLine("None");
                    return;
                }
                Log.WriteLine($"OK");

                using (var ac = new AutoAcmeContext(AcmeEnvironment.CfgStore.ServerUri)) {
                    ac.ChallengeVerificationRetryCount = AcmeEnvironment.CfgStore.ChallengeVerificationRetryCount;
                    ac.ChallengeVerificationWait       = TimeSpan.FromSeconds(AcmeEnvironment.CfgStore.ChallengeVerificationWaitSeconds);

                    // Login to Let's Encrypt service
                    if (string.IsNullOrEmpty(AcmeEnvironment.CfgStore.SerializedAccountData))
                    {
                        AcmeEnvironment.CfgStore.SerializedAccountData = ac.RegisterAndLogin(AcmeEnvironment.CfgStore.EmailAddress);
                        AcmeEnvironment.SaveConfig(cfgFileName);
                    }
                    else
                    {
                        ac.Login(AcmeEnvironment.CfgStore.SerializedAccountData);
                    }

                    // Add new hosts
                    Log.Indent();
                    using (var challengeManager = AcmeEnvironment.CreateChallengeManager()) {
                        foreach (var binding in bindings.ToArray())
                        {
                            // Check if was already added before
                            if (AcmeEnvironment.CfgStore.Hosts.SelectMany(h => h.GetNames()).Any(h => h.Equals(binding.Host, StringComparison.OrdinalIgnoreCase)))
                            {
                                continue;
                            }

                            Log.WriteLine($"Adding new host {binding.Host.ExplainHostName()}:");
                            Log.Indent();

                            // Request certificate
                            CertificateRequestResult result = null;
                            try {
                                result = ac.GetCertificate(new[] { binding.Host }, AcmeEnvironment.CfgStore.PfxPassword, challengeManager);
                            }
                            catch (Exception ex) {
                                Log.Exception(ex, "Request failed");
                                continue;
                            }

                            // Export files
                            Log.WriteLine("Exporting files:");
                            Log.Indent();
                            result.Export(binding.Host, AcmeEnvironment.CfgStore.PfxFolder, AcmeEnvironment.CfgStore.PemFolder);
                            Log.Unindent();

                            // Update database entry
                            Log.Write("Updating database entry...");
                            AcmeEnvironment.CfgStore.Hosts.Add(new Host {
                                CommonName   = binding.Host,
                                NotBefore    = result.Certificate.NotBefore,
                                NotAfter     = result.Certificate.NotAfter,
                                SerialNumber = result.Certificate.SerialNumber,
                                Thumbprint   = result.Certificate.Thumbprint
                            });
                            Log.WriteLine("OK");
                            AcmeEnvironment.SaveConfig(cfgFileName);

                            // Add HTTPS + CCS binding
                            var alreadyHasHttpsWithCcs = bindings.Any(b =>
                                                                      b.Host.Equals(binding.Host, StringComparison.OrdinalIgnoreCase) &&
                                                                      b.Protocol.Equals("https", StringComparison.OrdinalIgnoreCase) &&
                                                                      b.CentralCertStore);
                            if (addCcsBinding && !alreadyHasHttpsWithCcs)
                            {
                                try {
                                    Log.Write($"Adding HTTPS CCS binding for {binding.Host.ExplainHostName()}...");
                                    sc.AddCcsBinding(binding.SiteName, binding.Host, requireSni);
                                    Log.WriteLine("OK");
                                }
                                catch (Exception ex) {
                                    AcmeEnvironment.CrashExit(ex);
                                }
                            }

                            Log.Unindent();
                        }

                        Log.Unindent();
                    }
                }
            }
        }
Exemplo n.º 3
0
        public static void AddHost(
            [Required(Description = "Host name (multiple names allowed)")]
            string hostNames,
            [Optional(false, "xt", Description = "Skip authentication test")]
            bool skipTest,
            [Optional(null, "cfg", Description = "Custom configuration file name")]
            string cfgFileName,
            [Optional(null, "c", Description = "Certificate Country")]
            string csrCountryName,
            [Optional(null, "st", Description = "Certificate State")]
            string csrState,
            [Optional(null, "l", Description = "Certificate Locality")]
            string csrLocality,
            [Optional(null, "o", Description = "Certificate Organization")]
            string csrOrganization,
            [Optional(null, "ou", Description = "Certificate Organizational Unit")]
            string csrOrdganizationUnit,
            [Optional(false, Description = "Show verbose error messages")]
            bool verbose)
        {
            Log.VerboseMode = verbose;
            if (AcmeEnvironment.CfgStore == null)
            {
                AcmeEnvironment.LoadConfig(cfgFileName);
            }
            hostNames = hostNames.ToAsciiHostNames();

            // Check if there already is host with this name
            Log.Write("Checking host...");
            var existingHostnames = new HashSet <string>(AcmeEnvironment.CfgStore.Hosts.SelectMany(h => h.GetNames()), StringComparer.OrdinalIgnoreCase);

            foreach (var hostName in hostNames.SplitNames())
            {
                if (existingHostnames.Contains(hostName))
                {
                    AcmeEnvironment.CrashExit($"Host '{hostName.ExplainHostName()}' is already managed.");
                }
            }
            Log.WriteLine("OK");

            // Request certificate
            Log.WriteLine($"Requesting certificate for {hostNames}:");
            Log.Indent();
            CertificateRequestResult result = null;

            try {
                using (var ac = new AutoAcmeContext(AcmeEnvironment.CfgStore.ServerUri)) {
                    ac.ChallengeVerificationRetryCount = AcmeEnvironment.CfgStore.ChallengeVerificationRetryCount;
                    ac.ChallengeVerificationWait       = TimeSpan.FromSeconds(AcmeEnvironment.CfgStore.ChallengeVerificationWaitSeconds);
                    if (string.IsNullOrEmpty(AcmeEnvironment.CfgStore.SerializedAccountData))
                    {
                        AcmeEnvironment.CfgStore.SerializedAccountData = ac.RegisterAndLogin(AcmeEnvironment.CfgStore.EmailAddress);
                        AcmeEnvironment.SaveConfig(cfgFileName);
                    }
                    else
                    {
                        ac.Login(AcmeEnvironment.CfgStore.SerializedAccountData);
                    }
                    using (var challengeManager = AcmeEnvironment.CreateChallengeManager()) {
                        result = ac.GetCertificate(hostNames.SplitNames(), AcmeEnvironment.CfgStore.PfxPassword, challengeManager, skipTest);
                    }
                }
            }
            catch (Exception ex) {
                Log.Exception(ex, "Request failed");
                AcmeEnvironment.CrashExit("Unable to get certificate for new host.");
            }
            if (result != null)
            {
                // Display certificate info
                Log.Indent();
                Log.WriteLine("Certificate information:");
                Log.WriteLine($"Issuer:        {result.Certificate.Issuer}");
                Log.WriteLine($"Subject:       {result.Certificate.Subject}");
                Log.WriteLine($"Serial number: {result.Certificate.SerialNumber}");
                Log.WriteLine($"Not before:    {result.Certificate.NotBefore:o}");
                Log.WriteLine($"Not after:     {result.Certificate.NotAfter:o}");
                Log.WriteLine($"Thumbprint:    {result.Certificate.Thumbprint}");
                Log.Unindent();
                Log.Unindent();

                // Export files
                Log.WriteLine("Exporting files:");
                Log.Indent();
                foreach (var hostName in hostNames.SplitNames())
                {
                    result.Export(hostName, AcmeEnvironment.CfgStore.PfxFolder, AcmeEnvironment.CfgStore.PemFolder);
                }
                Log.Unindent();

                // Update database entry
                Log.Write("Updating database entry...");
                var host = new Host {
                    CommonName   = hostNames,
                    NotBefore    = result.Certificate.NotBefore,
                    NotAfter     = result.Certificate.NotAfter,
                    SerialNumber = result.Certificate.SerialNumber,
                    Thumbprint   = result.Certificate.Thumbprint
                };
                AcmeEnvironment.CfgStore.Hosts.Add(host);
                Log.WriteLine("OK");

                // Save configuration
                AcmeEnvironment.SaveConfig(cfgFileName);
            }
        }