예제 #1
0
        /// <summary>
        /// Update/create bindings for all host names in the certificate
        /// </summary>
        /// <param name="target"></param>
        /// <param name="flags"></param>
        /// <param name="thumbprint"></param>
        /// <param name="store"></param>
        public void AddOrUpdateBindings(Target target, SSLFlags flags, CertificateInfo newCertificate, CertificateInfo oldCertificate)
        {
            try
            {
                var allBindings = WebSites.
                                  SelectMany(site => site.Bindings, (site, binding) => new { site, binding }).
                                  ToList();

                var bindingsUpdated = 0;
                var found           = new List <string>();
                var oldThumbprint   = oldCertificate?.Certificate?.GetCertHash();
                if (oldThumbprint != null)
                {
                    var siteBindings = allBindings.
                                       Where(sb => StructuralComparisons.StructuralEqualityComparer.Equals(sb.binding.CertificateHash, oldThumbprint)).
                                       ToList();

                    // Update all bindings created using the previous certificate
                    foreach (var sb in siteBindings)
                    {
                        try
                        {
                            UpdateBinding(sb.site,
                                          sb.binding,
                                          flags,
                                          newCertificate.Certificate.GetCertHash(),
                                          newCertificate.Store?.Name);
                            found.Add(sb.binding.Host);
                            bindingsUpdated += 1;
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "Error updating binding {host}", sb.binding.BindingInformation);
                            throw;
                        }
                    }
                }

                // Find all hostnames which are not covered by any of the already updated
                // bindings yet, because we will want to make sure that those are accessable
                // in the target site
                var targetSite            = GetWebSite(target.InstallationSiteId ?? target.TargetSiteId ?? -1);
                IEnumerable <string> todo = target.GetHosts(true);
                while (todo.Count() > 0)
                {
                    // Filter by previously matched bindings
                    todo = todo.Where(host => !found.Any(binding => Fits(binding, host, flags) > 0));
                    if (todo.Count() > 0)
                    {
                        var current = todo.First();
                        try
                        {
                            var binding = AddOrUpdateBindings(
                                targetSite,
                                current,
                                flags,
                                newCertificate.Certificate.GetCertHash(),
                                newCertificate.Store?.Name,
                                target.SSLPort,
                                true);

                            // Allow a single newly created binding to match with
                            // multiple hostnames on the todo list, e.g. the *.example.com binding
                            // matches with both a.example.com and b.example.com
                            found.Add(binding);
                            bindingsUpdated += 1;
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "Error creating binding {host}: {ex}", current, ex.Message);

                            // Prevent infinite retry loop, we just skip the domain when
                            // an error happens creating a new binding for it. User can
                            // always change/add the bindings manually after all.
                            found.Add(current);
                        }
                    }
                }

                if (bindingsUpdated > 0)
                {
                    _log.Information("Committing {count} {type} binding changes to IIS", bindingsUpdated, "https");
                    Commit();
                    _log.Information("IIS will serve the new certificates after the Application Pool IdleTimeout has been reached.");
                }
                else
                {
                    _log.Warning("No bindings have been changed");
                }
            }
            catch (Exception ex)
            {
                _log.Error(ex, "Error installing");
                throw;
            }
        }
예제 #2
0
        /// <summary>
        /// Update/create bindings for all host names in the certificate
        /// </summary>
        /// <param name="target"></param>
        /// <param name="flags"></param>
        /// <param name="thumbprint"></param>
        /// <param name="store"></param>
        public void AddOrUpdateBindings(IEnumerable <string> identifiers, BindingOptions bindingOptions, byte[] oldThumbprint)
        {
            // Helper function to get updated sites
            IEnumerable <(IISSiteWrapper site, Binding binding)> GetAllSites() => WebSites.
            SelectMany(site => site.Site.Bindings, (site, binding) => (site, binding)).
            ToList();

            try
            {
                var allBindings     = GetAllSites();
                var bindingsUpdated = 0;
                var found           = new List <string>();
                if (oldThumbprint != null)
                {
                    var siteBindings = allBindings.
                                       Where(sb => StructuralComparisons.StructuralEqualityComparer.Equals(sb.binding.CertificateHash, oldThumbprint)).
                                       ToList();

                    // Update all bindings created using the previous certificate
                    foreach (var(site, binding) in siteBindings)
                    {
                        try
                        {
                            UpdateBinding(site.Site, binding, bindingOptions);
                            found.Add(binding.Host);
                            bindingsUpdated += 1;
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "Error updating binding {host}", binding.BindingInformation);
                            throw;
                        }
                    }
                }

                // Find all hostnames which are not covered by any of the already updated
                // bindings yet, because we will want to make sure that those are accessable
                // in the target site
                var targetSite            = GetWebSite(bindingOptions.SiteId ?? -1);
                IEnumerable <string> todo = identifiers;
                while (todo.Any())
                {
                    // Filter by previously matched bindings
                    todo = todo.Where(cert => !found.Any(iis => Fits(iis, cert, bindingOptions.Flags) > 0));
                    if (!todo.Any())
                    {
                        break;
                    }

                    allBindings = GetAllSites();
                    var current = todo.First();
                    try
                    {
                        var binding = AddOrUpdateBindings(
                            allBindings.Select(x => x.binding).ToArray(),
                            targetSite,
                            bindingOptions.WithHost(current),
                            !bindingOptions.Flags.HasFlag(SSLFlags.CentralSSL));

                        // Allow a single newly created binding to match with
                        // multiple hostnames on the todo list, e.g. the *.example.com binding
                        // matches with both a.example.com and b.example.com
                        if (binding == null)
                        {
                            // We were unable to create the binding because it would
                            // lead to a duplicate. Pretend that we did add it to
                            // still be able to get out of the loop;
                            found.Add(current);
                        }
                        else
                        {
                            found.Add(binding);
                            bindingsUpdated += 1;
                        }
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Error creating binding {host}: {ex}", current, ex.Message);

                        // Prevent infinite retry loop, we just skip the domain when
                        // an error happens creating a new binding for it. User can
                        // always change/add the bindings manually after all.
                        found.Add(current);
                    }
                }

                if (bindingsUpdated > 0)
                {
                    _log.Information("Committing {count} {type} binding changes to IIS", bindingsUpdated, "https");
                    Commit();
                }
                else
                {
                    _log.Warning("No bindings have been changed");
                }
            }
            catch (Exception ex)
            {
                _log.Error(ex, "Error installing");
                throw;
            }
        }