예제 #1
0
        /// <summary>
        /// creates or updates the https binding for the dns host name specified, assigning the given
        /// certificate selected from the certificate store
        /// </summary>
        /// <param name="managedSite"></param>
        /// <param name="certificate"></param>
        /// <param name="host"></param>
        /// <param name="sslPort"></param>
        /// <param name="useSNI"></param>
        /// <param name="ipAddress"></param>
        public bool InstallCertificateforBinding(ManagedSite managedSite, X509Certificate2 certificate, string host, int sslPort = 443, bool useSNI = true, string ipAddress = null)
        {
            var site = FindManagedSite(managedSite);

            if (site == null)
            {
                return(false);
            }

            return(InstallCertificateforBinding(site, certificate, host, sslPort, useSNI, ipAddress));
        }
예제 #2
0
        public void DeleteManagedSite(ManagedSite site)
        {
            LoadSettings();
            var existingSite = ManagedSites.FirstOrDefault(s => s.Id == site.Id);

            if (existingSite != null)
            {
                ManagedSites.Remove(existingSite);
            }
            StoreSettings();
        }
예제 #3
0
        public static bool IsRenewalRequired(ManagedSite s, int renewalIntervalDays)
        {
            // if we know the last renewal date, check whether we should renew again, otherwise
            // assume it's more than 30 days ago by default and attempt renewal

            var timeSinceLastRenewal = (s.DateRenewed != null ? s.DateRenewed : DateTime.Now.AddDays(-30)) - DateTime.Now;

            bool isRenewalRequired = Math.Abs(timeSinceLastRenewal.Value.TotalDays) > renewalIntervalDays;

            return(isRenewalRequired);
        }
예제 #4
0
 public void UpdatedManagedSite(ManagedSite managedSite, bool loadLatest = true, bool saveAfterUpdate = true)
 {
     if (loadLatest)
     {
         LoadSettings();
     }
     ManagedSites[managedSite.Id] = managedSite;
     if (saveAfterUpdate)
     {
         StoreSettings();
     }
 }
예제 #5
0
        public void DeleteManagedSite(ManagedSite site)
        {
            this.LoadSettings();

            var existingSite = this.managedSites.FirstOrDefault(s => s.SiteId == site.SiteId);

            if (existingSite != null)
            {
                this.managedSites.Remove(existingSite);
            }
            this.StoreSettings();
        }
예제 #6
0
        public void TestCheckAutoRenewalPeriodRequired()
        {
            // setup
            var renewalPeriodDays = 14;
            var managedSite       = new ManagedSite {
                IncludeInAutoRenew = true, DateRenewed = DateTime.Now.AddDays(-15), DateExpiry = DateTime.Now.AddDays(60)
            };

            // perform check
            var isRenewalRequired = Management.CertifyManager.IsRenewalRequired(managedSite, renewalPeriodDays);

            // assert result
            Assert.IsTrue(isRenewalRequired, "Renewal should be required");
        }
예제 #7
0
        internal void DeleteManagedSite(ManagedSite selectedItem)
        {
            var existing = this.ManagedSites.FirstOrDefault(s => s.Id == selectedItem.Id);

            //remove existing

            if (existing != null)
            {
                this.ManagedSites.Remove(existing);
            }

            //save settings
            certifyManager.SaveManagedSites(this.ManagedSites.ToList());
        }
예제 #8
0
        public void UpdatedManagedSite(ManagedSite managedSite)
        {
            this.LoadSettings();

            var existingSite = this.ManagedSites.FirstOrDefault(s => s.Id == managedSite.Id);

            if (existingSite != null)
            {
                this.ManagedSites.Remove(existingSite);
            }

            this.ManagedSites.Add(managedSite);
            this.StoreSettings();
        }
예제 #9
0
        public void TestCheckAutoRenewalPeriodUnknownLastRenewal()
        {
            // setup : set renewal period to 14 days, last renewal unknown.

            var renewalPeriodDays = 14;
            var managedSite       = new ManagedSite {
                IncludeInAutoRenew = true, DateExpiry = DateTime.Now.AddDays(60)
            };

            // perform check
            var isRenewalRequired = CertifyManager.IsRenewalRequired(managedSite, renewalPeriodDays);

            // assert result
            Assert.IsTrue(isRenewalRequired, "Renewal should be required");
        }
예제 #10
0
        public void TestCheckAutoRenewalPeriodNotRequired()
        {
            // setup : set renewal period to 30 days, last renewal 15 days ago. Renewal should not be
            // required yet.
            var renewalPeriodDays = 30;
            var managedSite       = new ManagedSite {
                IncludeInAutoRenew = true, DateRenewed = DateTime.Now.AddDays(-15), DateExpiry = DateTime.Now.AddDays(60)
            };

            // perform check
            var isRenewalRequired = Management.CertifyManager.IsRenewalRequired(managedSite, renewalPeriodDays);

            // assert result
            Assert.IsFalse(isRenewalRequired, "Renewal should not be required");
        }
예제 #11
0
        public ManagedItem AddOrUpdateManagedSite(ManagedSite item)
        {
            var existing = this.ManagedSites.FirstOrDefault(s => s.Id == item.Id);

            //add new or replace existing

            if (existing != null)
            {
                this.ManagedSites.Remove(existing);
            }

            this.ManagedSites.Add(item);

            //save settings
            certifyManager.SaveManagedSites(this.ManagedSites.ToList());

            return(item);
        }
예제 #12
0
 public async Task DeleteManagedSite(ManagedSite site)
 {
     // save modified items into settings database
     using (var db = new SQLiteConnection($"Data Source={GetDbPath()}"))
     {
         db.Open();
         using (var tran = db.BeginTransaction())
         {
             using (var cmd = new SQLiteCommand("DELETE FROM manageditem WHERE id=@id", db))
             {
                 cmd.Parameters.Add(new SQLiteParameter("@id", site.Id));
                 await cmd.ExecuteNonQueryAsync();
             }
             tran.Commit();
             Debug.WriteLine($"DeleteManagedSite: Completed {site.Id}");
             ManagedSitesCache.Remove(site.Id);
         }
     }
 }
예제 #13
0
 private void GenerateMockData()
 {
     // generate 20 mock sites
     ManagedSites = new ObservableCollection <ManagedSite>();
     for (int i = 1; i <= 20; i++)
     {
         var site = new ManagedSite()
         {
             Id            = Guid.NewGuid().ToString(),
             Name          = $"test{i}.example.org",
             ItemType      = ManagedItemType.SSL_LetsEncrypt_LocalIIS,
             DateExpiry    = DateTime.Now.AddDays(60 - 5 * i),
             RequestConfig = new CertRequestConfig()
             {
                 ChallengeType = ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI,
                 PerformAutomatedCertBinding = true,
                 PreRequestPowerShellScript  = @"c:\inetpub\scripts\pre-req-script.ps1",
                 PostRequestPowerShellScript = @"c:\inetpub\scripts\post-req-script.ps1",
                 WebhookTrigger = Webhook.ON_SUCCESS,
                 WebhookUrl     = "https://certifytheweb.com/api/notify?domain=$domain&key=123456",
                 WebhookMethod  = Webhook.METHOD_POST
             },
             CertificatePath = @"C:\ProgramData\ACMESharp\sysVault\99-ASSET\cert_ident1a2b3c4d-all.pfx"
         };
         site.DomainOptions.Add(new DomainOption()
         {
             Domain          = site.Name,
             IsPrimaryDomain = true,
             IsSelected      = true
         });
         // add lots of mock domains
         for (int j = 1; j <= 20; j++)
         {
             site.DomainOptions.Add(new DomainOption()
             {
                 Domain     = $"www{j}.{site.Name}",
                 IsSelected = j <= 3
             });
         }
         ManagedSites.Add(site);
     }
 }
예제 #14
0
        /// <summary>
        /// For current configured environment, show preview of recommended site management (for local IIS, scan sites and recommend actions)
        /// </summary>
        /// <returns></returns>
        public List <ManagedSite> Preview()
        {
            List <ManagedSite> sites = new List <ManagedSite>();

            if (EnableLocalIISMode)
            {
                try
                {
                    var iisSites = new IISManager().GetSiteBindingList(includeOnlyStartedSites: true).OrderBy(s => s.SiteId).ThenBy(s => s.Host);

                    var siteIds = iisSites.GroupBy(x => x.SiteId);

                    foreach (var s in siteIds)
                    {
                        ManagedSite managedSite = new ManagedSite {
                            Id = s.Key
                        };
                        managedSite.ItemType   = ManagedItemType.SSL_LetsEncrypt_LocalIIS;
                        managedSite.TargetHost = "localhost";
                        managedSite.Name       = iisSites.First(i => i.SiteId == s.Key).SiteName;

                        //TODO: replace sute binding with domain options
                        //managedSite.SiteBindings = new List<ManagedSiteBinding>();

                        foreach (var binding in s)
                        {
                            var managedBinding = new ManagedSiteBinding {
                                Hostname = binding.Host, IP = binding.IP, Port = binding.Port, UseSNI = true, CertName = "Certify_" + binding.Host
                            };
                            // managedSite.SiteBindings.Add(managedBinding);
                        }
                        sites.Add(managedSite);
                    }
                }
                catch (Exception)
                {
                    //can't read sites
                    System.Diagnostics.Debug.WriteLine("Can't get IIS site list.");
                }
            }
            return(sites);
        }
예제 #15
0
        public async Task TestCreateDeleteManagedSite()
        {
            var itemManager = new Management.ItemManager();

            itemManager.StorageSubfolder = "Tests";

            var testSite = new ManagedSite
            {
                Id      = Guid.NewGuid().ToString(),
                Name    = "TestSite..",
                GroupId = "test",

                RequestConfig = new CertRequestConfig
                {
                    PrimaryDomain                    = "testsite.com",
                    ChallengeType                    = "http-01",
                    PerformAutoConfig                = true,
                    PerformAutomatedCertBinding      = true,
                    PerformChallengeFileCopy         = true,
                    PerformExtensionlessConfigChecks = true,
                    WebsiteRootPath                  = "c:\\inetpub\\wwwroot"
                },
                ItemType = ManagedItemType.SSL_LetsEncrypt_LocalIIS
            };

            var managedSite = await itemManager.UpdatedManagedSite(testSite);

            Assert.IsNotNull(managedSite, "Create/store managed site");

            //check site now exists
            managedSite = await itemManager.GetManagedSite(testSite.Id);

            Assert.IsNotNull(managedSite, "Retrieve managed site");

            await itemManager.DeleteManagedSite(managedSite);

            managedSite = await itemManager.GetManagedSite(testSite.Id);

            // now check site has been delete
            Assert.IsNull(managedSite, "Managed site deleted");
        }
예제 #16
0
        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.lblInfo.Text = "";

            this.selectedSite = null;

            //selected site
            if (this.listView1.SelectedItems.Count > 0)
            {
                var selectedNode = this.listView1.SelectedItems[0];
                if (selectedNode.Tag != null)
                {
                    var site = siteManager.GetManagedSite(selectedNode.Tag.ToString());
                    //  if (site.RequestConfig != null && site.RequestConfig.PrimaryDomain != null)
                    {
                        this.selectedSite = site;
                        this.PopulateSiteDetails(site);
                    }
                }
            }
        }
예제 #17
0
        /// <summary>
        /// Finds the IIS <see cref="Site" /> corresponding to a <see cref="ManagedSite" />.
        /// </summary>
        /// <param name="managedSite"> Configured site. </param>
        /// <returns> The matching IIS Site if found, otherwise null. </returns>
        private Site FindManagedSite(ManagedSite managedSite)
        {
            if (managedSite == null)
            {
                throw new ArgumentNullException(nameof(managedSite));
            }

            var site = GetSiteById(managedSite.GroupId);

            if (site != null)
            {
                //TODO: check site has bindings for given domains, otherwise set back to null
            }

            if (site == null)
            {
                site = GetSiteByDomain(managedSite.RequestConfig.PrimaryDomain);
            }

            return(site);
        }
예제 #18
0
        public async Task <ManagedSite> GetManagedSite(string siteId)
        {
            ManagedSite result = null;

            if (ManagedSitesCache == null || !ManagedSitesCache.Any())
            {
                Debug.WriteLine("GetManagedSite: No managed sites loaded, will load item directly.");
            }
            else
            {
                // try to get cached version
                result = ManagedSitesCache.TryGetValue(siteId, out var retval) ? retval : null;
            }

            // if we don't have cached copy of info, load it from db
            if (result == null)
            {
                result = await LoadManagedSite(siteId);
            }
            return(result);
        }
예제 #19
0
        /// <summary>
        /// removes the managedSite's https binding for the dns host name specified
        /// </summary>
        /// <param name="managedSite"></param>
        /// <param name="host"></param>
        public void RemoveHttpsBinding(ManagedSite managedSite, string host)
        {
            using (var iisManager = GetDefaultServerManager())
            {
                var site = iisManager.Sites.FirstOrDefault(s => s.Id == long.Parse(managedSite.GroupId));
                if (site != null)
                {
                    string internationalHost = host == "" ? "" : _idnMapping.GetUnicode(host);
                    var    binding           = site.Bindings.Where(b =>
                                                                   b.Host == internationalHost &&
                                                                   b.Protocol == "https"
                                                                   ).FirstOrDefault();

                    if (binding != null)
                    {
                        site.Bindings.Remove(binding);
                        iisManager.CommitChanges();
                    }
                }
            }
        }
예제 #20
0
        private async Task <ManagedSite> LoadManagedSite(string siteId)
        {
            ManagedSite managedSite = null;

            using (var db = new SQLiteConnection($"Data Source={GetDbPath()}"))
                using (var cmd = new SQLiteCommand("SELECT json FROM manageditem WHERE id=@id", db))
                {
                    cmd.Parameters.Add(new SQLiteParameter("@id", siteId));

                    db.Open();
                    using (var reader = await cmd.ExecuteReaderAsync())
                    {
                        if (await reader.ReadAsync())
                        {
                            managedSite                       = JsonConvert.DeserializeObject <ManagedSite>((string)reader["json"]);
                            managedSite.IsChanged             = false;
                            ManagedSitesCache[managedSite.Id] = managedSite;
                        }
                    }
                }

            return(managedSite);
        }
예제 #21
0
        public void UpdatedManagedSite(ManagedSite managedSite, bool loadLatest = true, bool saveAfterUpdate = true)
        {
            if (loadLatest)
            {
                LoadSettings();
            }

            int index = ManagedSites.FindIndex(s => s.Id == managedSite.Id);

            if (index == -1)
            {
                ManagedSites.Add(managedSite);
            }
            else
            {
                ManagedSites[index] = managedSite;
            }

            if (saveAfterUpdate)
            {
                StoreSettings();
            }
        }
예제 #22
0
        public async Task <ManagedSite> UpdatedManagedSite(ManagedSite managedSite, bool saveAfterUpdate = true)
        {
            ManagedSitesCache[managedSite.Id] = managedSite;

            if (saveAfterUpdate)
            {
                using (var db = new SQLiteConnection($"Data Source={GetDbPath()}"))
                {
                    db.Open();
                    using (var tran = db.BeginTransaction())
                    {
                        using (var cmd = new SQLiteCommand("INSERT OR REPLACE INTO manageditem (id,json) VALUES (@id,@json)", db))
                        {
                            cmd.Parameters.Add(new SQLiteParameter("@id", managedSite.Id));
                            cmd.Parameters.Add(new SQLiteParameter("@json", JsonConvert.SerializeObject(managedSite)));
                            await cmd.ExecuteNonQueryAsync();
                        }
                        tran.Commit();
                    }
                }
            }

            return(ManagedSitesCache[managedSite.Id]);
        }
예제 #23
0
        public void TestCheckAutoRenewalPeriodRequiredWithFailures()
        {
            // setup
            var renewalPeriodDays = 14;
            var managedSite       = new ManagedSite
            {
                IncludeInAutoRenew     = true,
                DateRenewed            = DateTime.Now.AddDays(-15),
                DateExpiry             = DateTime.Now.AddDays(60),
                DateLastRenewalAttempt = DateTime.Now.AddHours(-12),
                LastRenewalStatus      = RequestState.Error,
                RenewalFailureCount    = 2
            };

            // perform check
            var isRenewalRequired = Management.CertifyManager.IsRenewalRequired(managedSite, renewalPeriodDays, true);

            // assert result
            Assert.IsTrue(isRenewalRequired, "Renewal should be required");

            managedSite = new ManagedSite
            {
                IncludeInAutoRenew     = true,
                DateRenewed            = DateTime.Now.AddDays(-15),
                DateExpiry             = DateTime.Now.AddDays(60),
                DateLastRenewalAttempt = null,
                LastRenewalStatus      = null,
                RenewalFailureCount    = 0
            };

            // perform check
            isRenewalRequired = Management.CertifyManager.IsRenewalRequired(managedSite, renewalPeriodDays, true);

            // assert result
            Assert.IsTrue(isRenewalRequired, "Site with no previous status - Renewal should be required");
        }
예제 #24
0
        private void PopulateManagedSiteSettings(string siteId, ManagedSite managedSite = null)
        {
            //set defaults first
            this.chkSkipConfigCheck.Checked     = false;
            this.chkAutoBindings.Checked        = true;
            this.chkEnableNotifications.Checked = true;
            this.chkIncludeInAutoRenew.Checked  = true;
            //this.txtManagedSiteName.Text = "";
            this.chkListSAN.Items.Clear();

            //for the given selected web site, allow the user to choose which domains to combine into one certificate
            var allSites = iisManager.GetSiteBindingList(false);

            this.domains = new List <DomainOption>();
            foreach (var d in allSites)
            {
                if (d.SiteId == siteId)
                {
                    DomainOption opt = new DomainOption {
                        Domain = d.Host, IsPrimaryDomain = false, IsSelected = true
                    };
                    domains.Add(opt);
                }
            }

            if (managedSite != null && managedSite.DomainOptions != null)
            {
                //carry over settings from saved managed site
                txtManagedSiteName.Text = managedSite.Name;

                chkIncludeInAutoRenew.Checked = managedSite.IncludeInAutoRenew;
                if (managedSite.RequestConfig != null)
                {
                    chkSkipConfigCheck.Checked     = !managedSite.RequestConfig.PerformExtensionlessConfigChecks;
                    chkAutoBindings.Checked        = managedSite.RequestConfig.PerformAutomatedCertBinding;
                    chkEnableNotifications.Checked = managedSite.RequestConfig.EnableFailureNotifications;
                }

                foreach (var d in domains)
                {
                    var opt = managedSite.DomainOptions.FirstOrDefault(o => o.Domain == d.Domain);
                    if (opt != null)
                    {
                        d.IsPrimaryDomain = opt.IsPrimaryDomain;
                        d.IsSelected      = opt.IsSelected;
                    }
                }
            }

            if (domains.Any())
            {
                //mark first domain as primary, if we have no other settings
                if (!domains.Any(d => d.IsPrimaryDomain == true))
                {
                    domains[0].IsPrimaryDomain = true;
                }

                this.domainListBindingSource.DataSource = domains;

                this.lstPrimaryDomain.DataSource    = this.domainListBindingSource;
                this.lstPrimaryDomain.DisplayMember = "Domain";

                //create filtered view of domains for the san list
                this.PopulateSANList();
            }
            else
            {
                MessageBox.Show("The selected site has no domain bindings setup. Configure the domains first using Edit Bindings in IIS.");
            }
        }
예제 #25
0
        public async Task <APIResult> TestChallengeConfiguration(ManagedSite site)
        {
            var response = await PostAsync($"managedsites/testconfig", site);

            return(JsonConvert.DeserializeObject <APIResult>(await response.Content.ReadAsStringAsync()));
        }
예제 #26
0
        public async Task <ChallengeHelperResult> CompleteDNSChallenge(ManagedSite managedsite, string domain, string txtRecordName, string txtRecordValue)
        {
            // for a given managed site configuration, attempt to complete the required challenge by
            // creating the required TXT record

            // if provider is python based

            // get stored credentials, for passing as arguments to script

            // run script dns_helper_init.py -p <providername> -c <user,pwd> -d <domain> -n <record
            // name> -v <record value>
            string providerType           = "PythonHelper";
            string providerSpecificConfig = "ROUTE53";
            string credentials            = "user,pwd";

            var credentialsManager = new CredentialsManager();

            if (!String.IsNullOrEmpty(managedsite.RequestConfig.ChallengeProvider))
            {
                var providerDetails = Models.Config.ChallengeProviders.Providers.FirstOrDefault(p => p.Id == managedsite.RequestConfig.ChallengeProvider);
                var config          = providerDetails.Config.Split(';');
                //get our driver type
                providerSpecificConfig = config.First(c => c.StartsWith("Driver")).Replace("Driver=", "");
            }

            if (!String.IsNullOrEmpty(managedsite.RequestConfig.ChallengeCredentialKey))
            {
                // decode credentials string array
                string credentialsJson = await credentialsManager.GetUnlockedCredential(managedsite.RequestConfig.ChallengeCredentialKey);

                string[] credentialArray = JsonConvert.DeserializeObject <string[]>(credentialsJson);
                credentials = String.Join(",", credentialArray);
            }

            // Run python helper, specifying driver to use
            var helperResult = RunPythonScript($"dns_helper_util.py -p {providerSpecificConfig} -c {credentials} -d {domain} -n {txtRecordName} -v {txtRecordValue}");

            if (helperResult.IsSuccess)
            {
                // test - wait for DNS changes
                await Task.Delay(15000);

                // do our own txt record query before proceeding with challenge completion

                /*
                 * int attempts = 3;
                 * bool recordCheckedOK = false;
                 * var networkUtil = new NetworkUtils(false);
                 *
                 * while (attempts > 0 && !recordCheckedOK)
                 * {
                 *  recordCheckedOK = networkUtil.CheckDNSRecordTXT(domain, txtRecordName, txtRecordValue);
                 *  attempts--;
                 *  if (!recordCheckedOK)
                 *  {
                 *      await Task.Delay(1000); // hold on a sec
                 *  }
                 * }
                 */
                return(helperResult);
            }
            else
            {
                return(helperResult);
            }
        }
예제 #27
0
 public PendingAuthorization PerformIISAutomatedChallengeResponse(IISManager iisManager, ManagedSite managedSite, PendingAuthorization pendingAuth)
 {
     throw new NotImplementedException();
 }
예제 #28
0
        public List <ManagedSite> ImportManagedSitesFromVault(bool mergeSitesAsSan = false)
        {
            var sites = new List <ManagedSite>();

            if (_iisManager == null || !_iisManager.IsIISAvailable)
            {
                // IIS not enabled, can't match sites to vault items
                return(sites);
            }

            //get dns identifiers from vault
            var identifiers = _vaultProvider.GetDomainIdentifiers();

            // match existing IIS sites to vault items
            var iisSites = _iisManager.GetSiteBindingList(ignoreStoppedSites: Certify.Properties.Settings.Default.IgnoreStoppedSites);

            foreach (var identifier in identifiers)
            {
                //identify IIS site related to this identifier (if any)
                var iisSite = iisSites.FirstOrDefault(d => d.Host == identifier.Dns);
                var site    = new ManagedSite
                {
                    Id                 = Guid.NewGuid().ToString(),
                    GroupId            = iisSite?.SiteId,
                    Name               = identifier.Dns + (iisSite != null ? " : " + iisSite.SiteName : ""),
                    IncludeInAutoRenew = true,
                    Comments           = "Imported from vault",
                    ItemType           = ManagedItemType.SSL_LetsEncrypt_LocalIIS,
                    TargetHost         = "localhost",
                    RequestConfig      = new CertRequestConfig
                    {
                        BindingIPAddress                 = iisSite?.IP,
                        BindingPort                      = iisSite?.Port.ToString(),
                        ChallengeType                    = "http-01",
                        EnableFailureNotifications       = true,
                        PerformAutoConfig                = true,
                        PerformAutomatedCertBinding      = true,
                        PerformChallengeFileCopy         = true,
                        PerformExtensionlessConfigChecks = true,
                        PrimaryDomain                    = identifier.Dns,
                        SubjectAlternativeNames          = new string[] { identifier.Dns }
                    }
                };
                site.AddDomainOption(new DomainOption {
                    Domain = identifier.Dns, IsPrimaryDomain = true, IsSelected = true
                });
                sites.Add(site);
            }

            if (mergeSitesAsSan)
            {
                foreach (var s in sites)
                {
                    //merge sites with same group (iis site etc) and different primary domain
                    if (sites.Any(m => m.GroupId != null && m.GroupId == s.GroupId && m.RequestConfig.PrimaryDomain != s.RequestConfig.PrimaryDomain))
                    {
                        //existing site to merge into
                        //add san for dns
                        var mergedSite = sites.FirstOrDefault(m =>
                                                              m.GroupId != null && m.GroupId == s.GroupId &&
                                                              m.RequestConfig.PrimaryDomain != s.RequestConfig.PrimaryDomain &&
                                                              m.RequestConfig.PrimaryDomain != null
                                                              );
                        if (mergedSite != null)
                        {
                            mergedSite.AddDomainOption(new DomainOption {
                                Domain = s.RequestConfig.PrimaryDomain, IsPrimaryDomain = false, IsSelected = true
                            });

                            //use shortest version of domain name as site name
                            if (mergedSite.RequestConfig.PrimaryDomain.Contains(s.RequestConfig.PrimaryDomain))
                            {
                                mergedSite.Name = mergedSite.Name.Replace(mergedSite.RequestConfig.PrimaryDomain, s.RequestConfig.PrimaryDomain);
                            }

                            //flag spare site config to be discar
                            s.RequestConfig.PrimaryDomain = null;
                        }
                    }
                }

                //discard sites which have been merged into other sites
                sites.RemoveAll(s => s.RequestConfig.PrimaryDomain == null);
            }
            return(sites);
        }
예제 #29
0
        public async Task <CertificateRequestResult> PerformCertificateRequest(ManagedSite managedSite, IProgress <RequestProgressState> progress = null)
        {
            // FIXME: refactor into different concerns, there's way to much being done here

            return(await Task.Run(async() =>
            {
                // start with a failure result, set to success when succeeding
                var result = new CertificateRequestResult {
                    ManagedItem = managedSite, IsSuccess = false, Message = ""
                };

                var config = managedSite.RequestConfig;
                try
                {
                    // run pre-request script, if set
                    if (!string.IsNullOrEmpty(config.PreRequestPowerShellScript))
                    {
                        try
                        {
                            string scriptOutput = await PowerShellManager.RunScript(result, config.PreRequestPowerShellScript);
                            LogMessage(managedSite.Id, $"Pre-Request Script output: \n{scriptOutput}");
                        }
                        catch (Exception ex)
                        {
                            LogMessage(managedSite.Id, $"Pre-Request Script error:\n{ex.Message}");
                        }
                    }

                    // if the script has requested the certificate request to be aborted, skip the request
                    if (result.Abort)
                    {
                        LogMessage(managedSite.Id, $"Certificate Request Aborted: {managedSite.Name}");
                        result.Message = "Certificate Request was aborted by PS script";
                        goto CertRequestAborted;
                    }

                    LogMessage(managedSite.Id, $"Beginning Certificate Request Process: {managedSite.Name}");

                    //enable or disable EFS flag on private key certs based on preference
                    if (Properties.Settings.Default.EnableEFS)
                    {
                        _vaultProvider.EnableSensitiveFileEncryption();
                    }

                    //primary domain and each subject alternative name must now be registered as an identifier with LE and validated
                    ReportProgress(progress, new RequestProgressState {
                        IsRunning = true, CurrentState = RequestState.Running, Message = "Registering Domain Identifiers"
                    });

                    await Task.Delay(200); //allow UI update, we should we using async calls instead

                    List <string> allDomains = new List <string> {
                        config.PrimaryDomain
                    };

                    if (config.SubjectAlternativeNames != null)
                    {
                        allDomains.AddRange(config.SubjectAlternativeNames);
                    }

                    // begin by assuming all identifiers are valid
                    bool allIdentifiersValidated = true;

                    if (config.ChallengeType == null)
                    {
                        config.ChallengeType = "http-01";
                    }

                    List <PendingAuthorization> identifierAuthorizations = new List <PendingAuthorization>();
                    var distinctDomains = allDomains.Distinct();

                    // perform validation process for each domain
                    foreach (var domain in distinctDomains)
                    {
                        //begin authorization process (register identifier, request authorization if not already given)
                        var domainIdentifierId = _vaultProvider.ComputeDomainIdentifierId(domain);

                        LogMessage(managedSite.Id, $"Attempting Domain Validation: {domain}", LogItemType.CertificateRequestStarted);
                        ReportProgress(progress, $"Registering and Validating {domain} ");

                        //TODO: make operations async and yield IO of vault

                        /*var authorization = await Task.Run(() =>
                         * {
                         *  return vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);
                         * });*/

                        // begin authorization by registering the domain identifier. This may return
                        // an already validated authorization or we may still have to complete the
                        // authorization challenge
                        var authorization = _vaultProvider.BeginRegistrationAndValidation(config, domainIdentifierId, challengeType: config.ChallengeType, domain: domain);

                        if (authorization != null && authorization.Identifier != null)
                        {
                            // check if authorization is pending, it may already be valid if an
                            // existing authorization was reused
                            if (authorization.Identifier.IsAuthorizationPending)
                            {
                                if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS)
                                {
                                    ReportProgress(progress, $"Performing Challenge Response via IIS: {domain} ");

                                    //ask LE to check our answer to their authorization challenge (http), LE will then attempt to fetch our answer, if all accessible and correct (authorized) LE will then allow us to request a certificate
                                    //prepare IIS with answer for the LE challenege
                                    authorization = _vaultProvider.PerformIISAutomatedChallengeResponse(_iisManager, managedSite, authorization);

                                    //if we attempted extensionless config checks, report any errors
                                    if (config.PerformAutoConfig && !authorization.ExtensionlessConfigCheckedOK)
                                    {
                                        LogMessage(managedSite.Id, $"Failed prerequisite configuration checks ({ managedSite.ItemType })", LogItemType.CertficateRequestFailed);

                                        _siteManager.StoreSettings();

                                        result.Message = "Automated configuration checks failed. Authorizations will not be able to complete.\nCheck you have http bindings for your site and ensure you can browse to http://" + domain + "/.well-known/acme-challenge/configcheck before proceeding.";
                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Error, Message = result.Message, Result = result
                                        });

                                        break;
                                    }
                                    else
                                    {
                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Running, Message = $"Requesting Validation from Let's Encrypt: {domain}"
                                        });

                                        //ask LE to validate our challenge response
                                        _vaultProvider.SubmitChallenge(domainIdentifierId, config.ChallengeType);

                                        bool identifierValidated = _vaultProvider.CompleteIdentifierValidationProcess(authorization.Identifier.Alias);

                                        if (!identifierValidated)
                                        {
                                            ReportProgress(progress, new RequestProgressState {
                                                CurrentState = RequestState.Error, Message = "Domain validation failed: " + domain
                                            }, managedSite.Id);

                                            allIdentifiersValidated = false;
                                        }
                                        else
                                        {
                                            ReportProgress(progress, new RequestProgressState {
                                                CurrentState = RequestState.Running, Message = "Domain validation completed: " + domain
                                            }, managedSite.Id);

                                            identifierAuthorizations.Add(authorization);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // we already have a completed authorization, check it's valid
                                if (authorization.Identifier.Status == "valid")
                                {
                                    LogMessage(managedSite.Id, $"Domain already has current authorization, skipping verification: { domain }");

                                    identifierAuthorizations.Add(new PendingAuthorization {
                                        Identifier = authorization.Identifier
                                    });
                                }
                                else
                                {
                                    LogMessage(managedSite.Id, $"Domain authorization failed : { domain } ");

                                    allIdentifiersValidated = false;
                                }
                            }
                        }
                        else
                        {
                            // could not begin authorization
                            LogMessage(managedSite.Id, $"Could not begin authorization for domain with Let's Encrypt: { domain } ");
                            allIdentifiersValidated = false;
                        }

                        // abandon authorization attempts if one of our domains has failed verification
                        if (!allIdentifiersValidated)
                        {
                            break;
                        }
                    }

                    //check if all identifiers have a valid authorization
                    if (identifierAuthorizations.Count != distinctDomains.Count())
                    {
                        allIdentifiersValidated = false;
                    }

                    if (allIdentifiersValidated)
                    {
                        string primaryDnsIdentifier = identifierAuthorizations.First().Identifier.Alias;
                        string[] alternativeDnsIdentifiers = identifierAuthorizations.Select(i => i.Identifier.Alias).ToArray();

                        ReportProgress(progress, new RequestProgressState {
                            CurrentState = RequestState.Running, Message = "Requesting Certificate via Lets Encrypt"
                        }, managedSite.Id);

                        // Perform CSR request
                        // FIXME: make call async
                        var certRequestResult = _vaultProvider.PerformCertificateRequestProcess(primaryDnsIdentifier, alternativeDnsIdentifiers);

                        if (certRequestResult.IsSuccess)
                        {
                            ReportProgress(progress, new RequestProgressState {
                                CurrentState = RequestState.Success, Message = "Completed Certificate Request."
                            }, managedSite.Id);

                            string pfxPath = certRequestResult.Result.ToString();

                            // update managed site summary
                            try
                            {
                                var certInfo = new CertificateManager().GetCertificate(pfxPath);
                                managedSite.DateStart = certInfo.NotBefore;
                                managedSite.DateExpiry = certInfo.NotAfter;
                                managedSite.DateRenewed = DateTime.Now;

                                managedSite.CertificatePath = pfxPath;

                                //ensure certificate contains all the requested domains
                                var subjectNames = certInfo.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.UpnName, false);

                                LogMessage(managedSite.Id, "New certificate contains following domains: " + subjectNames, LogItemType.GeneralInfo);
                            }
                            catch (Exception)
                            {
                                LogMessage(managedSite.Id, "Failed to parse certificate dates", LogItemType.GeneralError);
                            }

                            if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS && config.PerformAutomatedCertBinding)
                            {
                                ReportProgress(progress, new RequestProgressState {
                                    CurrentState = RequestState.Running, Message = "Performing Automated Certificate Binding"
                                });

                                // Install certificate into certificate store and bind to IIS site
                                if (_iisManager.InstallCertForRequest(managedSite, pfxPath, cleanupCertStore: true))
                                {
                                    //all done
                                    LogMessage(managedSite.Id, "Completed certificate request and automated bindings update (IIS)", LogItemType.CertificateRequestSuccessful);

                                    _siteManager.UpdatedManagedSite(managedSite);

                                    result.IsSuccess = true;
                                    result.Message = $"Certificate installed and SSL bindings updated for {config.PrimaryDomain }";
                                    ReportProgress(progress, new RequestProgressState {
                                        IsRunning = false, CurrentState = RequestState.Success, Message = result.Message
                                    });
                                }
                                else
                                {
                                    result.Message = $"An error occurred installing the certificate. Certificate file may not be valid: {pfxPath}";
                                    LogMessage(managedSite.Id, result.Message, LogItemType.GeneralError);
                                }
                            }
                            else
                            {
                                //user has opted for manual binding of certificate

                                _siteManager.UpdatedManagedSite(managedSite);

                                result.IsSuccess = true;
                                result.Message = $"Certificate created ready for manual binding: {pfxPath}";
                                LogMessage(managedSite.Id, result.Message, LogItemType.CertificateRequestSuccessful);
                            }
                        }
                        else
                        {
                            result.Message = $"The Let's Encrypt service did not issue a valid certificate in the time allowed. {(certRequestResult.ErrorMessage ?? "")}";
                            LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                        }
                    }
                    else
                    {
                        result.Message = "Validation of the required challenges did not complete successfully. Please ensure all domains to be referenced in the Certificate can be used to access this site without redirection. ";
                        LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                    }

                    // Goto label for aborted certificate request
                    CertRequestAborted : { }
                }
                catch (Exception exp)
                {
                    result.IsSuccess = false;
                    result.Message = managedSite.Name + ": Request failed - " + exp.Message + " " + exp.ToString();
                    LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);

                    System.Diagnostics.Debug.WriteLine(exp.ToString());
                }
                finally
                {
                    // if the request was not aborted, run post-request script, if set
                    if (!result.Abort && !string.IsNullOrEmpty(config.PostRequestPowerShellScript))
                    {
                        try
                        {
                            string scriptOutput = await PowerShellManager.RunScript(result, config.PostRequestPowerShellScript);
                            LogMessage(managedSite.Id, $"Post-Request Script output:\n{scriptOutput}");
                        }
                        catch (Exception ex)
                        {
                            LogMessage(managedSite.Id, $"Post-Request Script error:\n{ex.Message}");
                        }
                    }
                }
                return result;
            }));
        }
예제 #30
0
        /// <summary>
        /// Creates or updates the htttps bindings associated with the dns names in the current
        /// request config, using the requested port/ips or autobinding
        /// </summary>
        /// <param name="requestConfig"></param>
        /// <param name="pfxPath"></param>
        /// <param name="cleanupCertStore"></param>
        /// <returns></returns>
        internal bool InstallCertForRequest(ManagedSite managedSite, string pfxPath, bool cleanupCertStore)
        {
            var requestConfig = managedSite.RequestConfig;

            if (new System.IO.FileInfo(pfxPath).Length == 0)
            {
                throw new ArgumentException("InstallCertForRequest: Invalid PFX File");
            }

            //store cert against primary domain
            var storedCert = CertificateManager.StoreCertificate(requestConfig.PrimaryDomain, pfxPath);

            if (storedCert != null)
            {
                var site = FindManagedSite(managedSite);

                //get list of domains we need to create/update https bindings for
                List <string> dnsHosts = new List <string> {
                    requestConfig.PrimaryDomain
                };
                if (requestConfig.SubjectAlternativeNames != null)
                {
                    dnsHosts.AddRange(requestConfig.SubjectAlternativeNames);
                }

                dnsHosts = dnsHosts.Distinct().ToList();

                // add/update required bindings for each dns hostname
                foreach (var hostname in dnsHosts)
                {
                    //match dns host to IIS site
                    if (String.IsNullOrWhiteSpace(hostname))
                    {
                        throw new ArgumentException("InstallCertForRequest: Invalid (empty) DNS hostname supplied");
                    }

                    if (site != null)
                    {
                        //create/update binding and associate new cert
                        //if any binding elements configured, use those, otherwise auto bind using defaults and SNI
                        InstallCertificateforBinding(site, storedCert, hostname,
                                                     sslPort: !String.IsNullOrWhiteSpace(requestConfig.BindingPort) ? int.Parse(requestConfig.BindingPort) : 443,
                                                     useSNI: (requestConfig.BindingUseSNI != null ? (bool)requestConfig.BindingUseSNI : true),
                                                     ipAddress: !String.IsNullOrWhiteSpace(requestConfig.BindingIPAddress) ? requestConfig.BindingIPAddress : null
                                                     );
                    }
                }

                if (cleanupCertStore)
                {
                    //remove old certs for this primary domain
                    CertificateManager.CleanupCertificateDuplicates(storedCert, requestConfig.PrimaryDomain);
                }

                return(true);
            }
            else
            {
                return(false);
            }
        }