コード例 #1
0
        private static async Task <bool> CheckCertAddition(
            RenewalParameters renewalParams,
            AzureWebAppEnvironment webAppEnvironment,
            AcmeConfig acmeConfig,
            bool staging)
        {
            if (renewalParams.RenewXNumberOfDaysBeforeExpiration <= 0)
            {
                return(true);
            }

            var letsEncryptHostNames = await CertificateHelper.GetLetsEncryptHostNames(webAppEnvironment, staging).ConfigureAwait(false);

            Trace.TraceInformation("Let's Encrypt host names (staging: {0}): {1}", staging, String.Join(", ", letsEncryptHostNames));

            ICollection <string> missingHostNames = acmeConfig.Hostnames.Except(letsEncryptHostNames, StringComparer.OrdinalIgnoreCase).ToArray();

            if (missingHostNames.Count > 0)
            {
                Trace.TraceInformation(
                    "Detected host name(s) with no associated Let's Encrypt certificates, will add a new certificate: {0}",
                    String.Join(", ", missingHostNames));
                return(true);
            }

            Trace.TraceInformation("All host names associated with Let's Encrypt certificates, will perform cert renewal");
            return(false);
        }
コード例 #2
0
        private async Task <bool> HandleDns(AcmeContext acme, AcmeConfig config, IEnumerable <IChallengeContext> authorizations, List <INotifyConfig> notificationsList)
        {
            bool isValid = true;
            Dictionary <string, string> dnsValidation = new Dictionary <string, string>();
            var index = -1;

            foreach (var challenge in authorizations)
            {
                index++;
                var domainName = config.DNS.DomainNames[index].Replace("*.", "");
                var acmeDomain = "_acme-challenge." + domainName;
                var dnsText    = acme.AccountKey.DnsTxt(challenge.Token);
                dnsValidation.Add(acmeDomain, dnsText);
                _log.LogInfo($"Add TXT dns for {acmeDomain} to '{dnsText}'");
            }

            await AwaitDnsChanges(dnsValidation, notificationsList);

            Task.WaitAll(authorizations.Select(c => Task.Run(async() =>
            {
                var validation = await c.Validate();
                do
                {
                    if (validation.Status == Certes.Acme.Resource.ChallengeStatus.Pending)
                    {
                        System.Threading.Thread.Sleep(2000);
                        validation = await c.Resource();
                    }
                } while (validation.Status == Certes.Acme.Resource.ChallengeStatus.Pending);
                isValid = isValid && (validation.Status != Certes.Acme.Resource.ChallengeStatus.Invalid);
            })).ToArray());
            return(isValid);
        }
コード例 #3
0
        public async Task <List <CertificateInstallModel> > RenewCertificate(bool skipInstallCertificate = false, int renewXNumberOfDaysBeforeExpiration = 0)
        {
            Trace.TraceInformation("Checking certificate");
            var ss = SettingsStore.Instance.Load();

            using (var client = await ArmHelper.GetWebSiteManagementClient(settings))
                using (var httpClient = await ArmHelper.GetHttpClient(settings))
                {
                    var retryPolicy = ArmHelper.ExponentialBackoff();
                    var body        = string.Empty;
                    //Cant just get certificates by resource group, because sites that have been moved, have their certs sitting in the old RG.
                    //Also cant use client.Certificates.List() due to bug in the nuget
                    var response = await retryPolicy.ExecuteAsync(async() =>
                    {
                        return(await httpClient.GetAsync($"/subscriptions/{settings.SubscriptionId}/providers/Microsoft.Web/certificates?api-version=2016-03-01"));
                    });

                    response.EnsureSuccessStatusCode();
                    body = await response.Content.ReadAsStringAsync();

                    IEnumerable <Certificate> certs = ExtractCertificates(body);

                    var expiringCerts = certs.Where(s => s.ExpirationDate < DateTime.UtcNow.AddDays(renewXNumberOfDaysBeforeExpiration) && (s.Issuer.Contains("Let's Encrypt") || s.Issuer.Contains("Fake LE")));

                    if (expiringCerts.Count() == 0)
                    {
                        Trace.TraceInformation(string.Format("No certificates installed issued by Let's Encrypt that are about to expire within the next {0} days. Skipping.", renewXNumberOfDaysBeforeExpiration));
                    }
                    var res = new List <CertificateInstallModel>();
                    foreach (var toExpireCert in expiringCerts)
                    {
                        Trace.TraceInformation("Starting renew of certificate " + toExpireCert.Name + " expiration date " + toExpireCert.ExpirationDate);
                        var site      = client.WebApps.GetSiteOrSlot(settings.ResourceGroupName, settings.WebAppName, settings.SiteSlotName);
                        var sslStates = site.HostNameSslStates.Where(s => s.Thumbprint == toExpireCert.Thumbprint);
                        if (!sslStates.Any())
                        {
                            Trace.TraceInformation(String.Format("Certificate {0} was not assigned any hostname, skipping update", toExpireCert.Thumbprint));
                            continue;
                        }
                        var target = new AcmeConfig()
                        {
                            RegistrationEmail = this.acmeConfig.RegistrationEmail ?? ss.FirstOrDefault(s => s.Name == "email").Value,
                            Host           = sslStates.First().Name,
                            BaseUri        = this.acmeConfig.BaseUri,
                            UseProduction  = !bool.Parse(ss.FirstOrDefault(s => s.Name == "useStaging")?.Value ?? false.ToString()),
                            AlternateNames = sslStates.Skip(1).Select(s => s.Name).ToList(),
                            PFXPassword    = this.acmeConfig.PFXPassword,
                            RSAKeyLength   = this.acmeConfig.RSAKeyLength
                        };
                        if (!skipInstallCertificate)
                        {
                            res.Add(await RequestAndInstallInternalAsync(target));
                        }
                    }
                    return(res);
                }
        }
コード例 #4
0
 public async Task <AcmeContext> CreateContext(AcmeConfig config)
 {
     try
     {
         return(await Login(config));
     }
     catch (Exception ex)
     {
         throw;
     }
 }
コード例 #5
0
        public async Task <bool> Validate(AcmeContext acme, AcmeConfig config, List <INotifyConfig> notificationsList, Func <string[], Task <IOrderContext> > getOrder)
        {
            _log.LogInfo("Acme: Validating DNS order");
            var order = await getOrder(config.DNS.DomainNames);

            _log.LogInfo("Acme: getting authorizations");
            var authz = await order.Authorizations();

            var authorizations = authz.Select(a => a.Dns().Result);

            return(await HandleDns(acme, config, authorizations, notificationsList));
        }
コード例 #6
0
        private async Task <AcmeContext> Login(AcmeConfig config)
        {
            _log.LogInfo("Logging in to Acme");
            AcmeContext acme = default(AcmeContext);

            if (string.IsNullOrWhiteSpace(config.AccountKey) || !IsKeyValid(config.AccountKey))
            {
                acme = new AcmeContext(WellKnownServers.LetsEncryptV2);
                await acme.NewAccount(config.Email, true);

                var accountKey = acme.AccountKey.ToPem();
                _log.LogWarning($"New accountkey for '{config.Email}':");
                _log.LogWarning(accountKey);
            }
            else
            {
                string accountKey = File.ReadAllText(config.AccountKey);
                var    key        = KeyFactory.FromPem(accountKey);
                acme = new AcmeContext(WellKnownServers.LetsEncryptV2, key);
                await acme.Account();
            }
            return(acme);
        }
コード例 #7
0
 public async Task <ActionResult> Install(RequestAndInstallModel model)
 {
     if (ModelState.IsValid)
     {
         var s = SettingsStore.Instance.Load();
         s.Clear();
         s.Add(new SettingEntry()
         {
             Name  = "email",
             Value = model.Email
         });
         var baseUri = model.UseStaging == false ? "https://acme-v01.api.letsencrypt.org/" : "https://acme-staging.api.letsencrypt.org/";
         s.Add(new SettingEntry()
         {
             Name  = "baseUri",
             Value = baseUri
         });
         SettingsStore.Instance.Save(s);
         var settings = new AppSettingsAuthConfig();
         var target   = new AcmeConfig()
         {
             RegistrationEmail = model.Email,
             Host           = model.Hostnames.First(),
             BaseUri        = baseUri,
             AlternateNames = model.Hostnames.Skip(1).ToList(),
             PFXPassword    = settings.PFXPassword,
             RSAKeyLength   = settings.RSAKeyLength,
         };
         var thumbprint = await new CertificateManager(settings).RequestAndInstallInternalAsync(target);
         if (thumbprint != null)
         {
             return(RedirectToAction("Hostname", new { id = thumbprint }));
         }
     }
     SetViewBagHostnames();
     return(View(model));
 }
コード例 #8
0
        internal async Task <bool> Renew(AcmeConfig config, List <INotifyConfig> notificationsList)
        {
            Model.Config        = config;
            Model.Notifications = notificationsList;
            _log.LogInfo("Renewing certificate");
            var acme = _contextFactory(config);


            IOrderContext order = default(IOrderContext);

            bool validated = await _validators[config.Validation].Validate(acme, config, notificationsList, async(domainNames) => {
                _log.LogInfo("Acme: getting order");
                order = await acme.NewOrder(domainNames);
                return(order);
            });

            if (validated)
            {
                var privateKey = KeyFactory.NewKey(KeyAlgorithm.ES256);
                var cert       = await order.Generate(new CsrInfo
                {
                    CountryName      = "BE",
                    State            = "Antwerp",
                    Locality         = "Belgium",
                    Organization     = "stovem",
                    OrganizationUnit = "stovem",
                    CommonName       = config.DNS.DomainNames.First(),
                }, privateKey);

                var certPem    = cert.ToPem();
                var privatePem = privateKey.ToPem();

                Model.Certificate = privatePem + certPem;
            }

            return(validated);
        }
コード例 #9
0
 public async Task <ActionResult> Install(RequestAndInstallModel model)
 {
     if (ModelState.IsValid)
     {
         var s = SettingsStore.Instance.Load();
         s.Clear();
         s.Add(new SettingEntry()
         {
             Name  = "email",
             Value = model.Email
         });
         s.Add(new SettingEntry()
         {
             Name  = "useStaging",
             Value = model.UseStaging.ToString()
         });
         SettingsStore.Instance.Save(s);
         var settings = new AppSettingsAuthConfig();
         var target   = new AcmeConfig()
         {
             RegistrationEmail = model.Email,
             Host           = model.Hostnames.First(),
             UseProduction  = !model.UseStaging,
             AlternateNames = model.Hostnames.Skip(1).ToList(),
             PFXPassword    = settings.PFXPassword,
             RSAKeyLength   = settings.RSAKeyLength,
         };
         var certModel = await new CertificateManager(settings).RequestAndInstallInternalAsync(target);
         if (certModel != null)
         {
             return(RedirectToAction("Hostname", new { id = certModel.CertificateInfo.Certificate.Thumbprint }));
         }
     }
     SetViewBagHostnames();
     return(View(model));
 }
コード例 #10
0
 public async Task <bool> Validate(AcmeContext acme, AcmeConfig config, List <INotifyConfig> notificationsList, Func <string[], Task <IOrderContext> > getOrder)
 {
     return(await Task.FromResult(true));
 }