From 7127cc2aabd40d840373dacfb7073bf8eaef81ad Mon Sep 17 00:00:00 2001 From: "Simon J.K. Pedersen" Date: Thu, 10 Jan 2019 22:36:38 +0100 Subject: [PATCH 1/5] 257 --- LetsEncrypt.SiteExtension.Core/ArmHelper.cs | 38 +++++++++++- .../CertificateManager.cs | 12 +++- .../LetsEncrypt.Azure.Core.csproj | 61 ++++++++++++++++++- .../AppServiceCerticiateCertificateService.cs | 2 +- .../Services/ICertificateService.cs | 2 +- .../Services/WebAppCertificateService.cs | 14 +++-- .../packages.config | 48 +++++++++++++++ .../LetsEncrypt.SiteExtension.Test.csproj | 4 ++ LetsEncrypt.SiteExtension.Test/UnitTest1.cs | 26 ++++++++ .../packages.config | 1 + 10 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 LetsEncrypt.SiteExtension.Test/UnitTest1.cs diff --git a/LetsEncrypt.SiteExtension.Core/ArmHelper.cs b/LetsEncrypt.SiteExtension.Core/ArmHelper.cs index 58a15e3..f92cf44 100644 --- a/LetsEncrypt.SiteExtension.Core/ArmHelper.cs +++ b/LetsEncrypt.SiteExtension.Core/ArmHelper.cs @@ -4,10 +4,13 @@ using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Rest; using Microsoft.Rest.Azure.Authentication; +using Polly; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; using System.Web; namespace LetsEncrypt.Azure.Core @@ -46,11 +49,44 @@ public static HttpClient GetHttpClient(IAzureWebAppEnvironment model) { AuthenticationResult token = GetToken(model); - var client = new HttpClient(); + var client = HttpClientFactory.Create(new HttpClientHandler(), new TimeoutHandler()); client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token.AccessToken); client.BaseAddress = model.ManagementEndpoint; return client; } + + public static Polly.Retry.RetryPolicy ExponentialBackoff(int retryCount = 3, int firstBackOffDelay = 2) + { + return Policy + .Handle() + .WaitAndRetryAsync(retryCount, retryAttempt => + TimeSpan.FromSeconds(Math.Pow(firstBackOffDelay, retryAttempt)) + ); + } + + public class TimeoutHandler : DelegatingHandler + { + private static TimeSpan Timeout = TimeSpan.FromSeconds(120); + + protected override async Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(Timeout); + var timeoutToken = cts.Token; + + var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken); + + try + { + return await base.SendAsync(request, linkedToken.Token); + } + catch (OperationCanceledException) when (timeoutToken.IsCancellationRequested) + { + throw new TimeoutException(); + } + } + } } } \ No newline at end of file diff --git a/LetsEncrypt.SiteExtension.Core/CertificateManager.cs b/LetsEncrypt.SiteExtension.Core/CertificateManager.cs index 0aae129..cda8c34 100644 --- a/LetsEncrypt.SiteExtension.Core/CertificateManager.cs +++ b/LetsEncrypt.SiteExtension.Core/CertificateManager.cs @@ -150,11 +150,17 @@ public async Task> RenewCertificate(bool skipInsta using (var client = ArmHelper.GetWebSiteManagementClient(settings)) using (var httpClient = 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 httpClient.GetAsync($"/subscriptions/{settings.SubscriptionId}/providers/Microsoft.Web/certificates?api-version=2016-03-01"); - response.EnsureSuccessStatusCode(); - var body = await response.Content.ReadAsStringAsync(); + await retryPolicy.ExecuteAsync(async () => + { + var response = await httpClient.GetAsync($"/subscriptions/{settings.SubscriptionId}/providers/Microsoft.Web/certificates?api-version=2016-03-01"); + response.EnsureSuccessStatusCode(); + body = await response.Content.ReadAsStringAsync(); + + }); IEnumerable 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"))); diff --git a/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj b/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj index c6f849c..cd6abb0 100644 --- a/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj +++ b/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj @@ -106,23 +106,82 @@ ..\packages\Microsoft.Rest.ClientRuntime.Azure.Authentication.2.2.0-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.Authentication.dll True + + ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll + ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Polly.6.1.2\lib\netstandard1.1\Polly.dll + + + ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll + + + + ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + + + ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll + + + ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll + + + + ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll + + + ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll + + + ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll + + + ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll + + + + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll + - + + ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + diff --git a/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs b/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs index aca1d23..5dc8f9b 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs @@ -9,7 +9,7 @@ namespace LetsEncrypt.Azure.Core.Services { class AppServiceCerticiateCertificateService : ICertificateService { - public void Install(ICertificateInstallModel model) + public async Task Install(ICertificateInstallModel model) { throw new NotImplementedException(); } diff --git a/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs b/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs index 1e0a074..5be0970 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs @@ -9,7 +9,7 @@ namespace LetsEncrypt.Azure.Core.Services { public interface ICertificateService { - void Install(ICertificateInstallModel model); + Task Install(ICertificateInstallModel model); List RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0); } diff --git a/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs b/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs index 57157cf..fea2095 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs @@ -10,6 +10,7 @@ using System.Diagnostics; using System.Net.Http; using Newtonsoft.Json; +using Polly; namespace LetsEncrypt.Azure.Core.Services { @@ -26,7 +27,7 @@ public WebAppCertificateService(IAzureWebAppEnvironment azureEnvironment, IWebAp this.azureEnvironment = azureEnvironment; this.settings = settings; } - public void Install(ICertificateInstallModel model) + public async Task Install(ICertificateInstallModel model) { var cert = model.CertificateInfo; using (var webSiteClient = ArmHelper.GetWebSiteManagementClient(azureEnvironment)) @@ -53,10 +54,13 @@ public void Install(ICertificateInstallModel model) var body = JsonConvert.SerializeObject(newCert, JsonHelper.DefaultSerializationSettings); - var t = client.PutAsync($"/subscriptions/{azureEnvironment.SubscriptionId}/resourceGroups/{azureEnvironment.ServicePlanResourceGroupName}/providers/Microsoft.Web/certificates/{newCert.Name}?api-version=2016-03-01", new StringContent(body, Encoding.UTF8, "application/json")).Result; - - t.EnsureSuccessStatusCode(); + Polly.Retry.RetryPolicy retryPolicy = ArmHelper.ExponentialBackoff(); + await retryPolicy.ExecuteAsync(async () => + { + var t = await client.PutAsync($"/subscriptions/{azureEnvironment.SubscriptionId}/resourceGroups/{azureEnvironment.ServicePlanResourceGroupName}/providers/Microsoft.Web/certificates/{newCert.Name}?api-version=2016-03-01", new StringContent(body, Encoding.UTF8, "application/json")); + t.EnsureSuccessStatusCode(); + }); foreach (var dnsName in model.AllDnsIdentifiers) { @@ -83,7 +87,7 @@ public void Install(ICertificateInstallModel model) } - } + } public List RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0) { diff --git a/LetsEncrypt.SiteExtension.Core/packages.config b/LetsEncrypt.SiteExtension.Core/packages.config index 77aff90..8e49574 100644 --- a/LetsEncrypt.SiteExtension.Core/packages.config +++ b/LetsEncrypt.SiteExtension.Core/packages.config @@ -1,13 +1,61 @@  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj b/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj index 7825b53..0a2b6eb 100644 --- a/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj +++ b/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj @@ -93,6 +93,9 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True + + ..\packages\Polly.6.1.2\lib\netstandard2.0\Polly.dll + @@ -130,6 +133,7 @@ + diff --git a/LetsEncrypt.SiteExtension.Test/UnitTest1.cs b/LetsEncrypt.SiteExtension.Test/UnitTest1.cs new file mode 100644 index 0000000..dd30e8b --- /dev/null +++ b/LetsEncrypt.SiteExtension.Test/UnitTest1.cs @@ -0,0 +1,26 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using LetsEncrypt.Azure.Core; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static LetsEncrypt.Azure.Core.ArmHelper; + +namespace LetsEncrypt.SiteExtension.Test +{ + [TestClass] + public class HttpTets + { + [TestMethod] + [Ignore] + public async Task RetryTest() + { + var client = HttpClientFactory.Create(new HttpClientHandler(), new TimeoutHandler()); + + var retry = ArmHelper.ExponentialBackoff(); + await retry.ExecuteAsync(async () => + { + await client.PostAsJsonAsync("https://en8zkq5hogjyi.x.pipedream.net", new { text = "hello" }); + }); + } + } +} diff --git a/LetsEncrypt.SiteExtension.Test/packages.config b/LetsEncrypt.SiteExtension.Test/packages.config index d43c508..1549a90 100644 --- a/LetsEncrypt.SiteExtension.Test/packages.config +++ b/LetsEncrypt.SiteExtension.Test/packages.config @@ -6,4 +6,5 @@ + \ No newline at end of file From fcc737a1667d14af9660e6756c69105247341f2a Mon Sep 17 00:00:00 2001 From: "Simon J.K. Pedersen" Date: Sat, 12 Jan 2019 13:20:31 +0100 Subject: [PATCH 2/5] updated package references. --- .../DesignTimeBuild/.dtbcache | Bin 1087106 -> 1087106 bytes LetsEncrypt-SiteExtension.sln | 1 + .../Controllers/HomeController.cs | 30 ++-- .../LetsEncrypt.SiteExtension.csproj | 137 ++++++++++-------- LetsEncrypt-SiteExtension/Web.config | 77 +++++++--- LetsEncrypt-SiteExtension/packages.config | 46 +++--- LetsEncrypt.SiteExtension.Core/ArmHelper.cs | 40 +++-- .../CertificateManager.cs | 19 +-- .../LetsEncrypt.Azure.Core.csproj | 61 ++++---- .../LetsEncrypt.SiteExtension.Core.nuspec | 8 +- .../AppServiceCerticiateCertificateService.cs | 2 +- .../AzureDnsAuthorizationChallengeProvider.cs | 12 +- .../Services/ICertificateService.cs | 2 +- ...ileSystemAuthorizationChallengeProvider.cs | 12 +- .../Services/WebAppCertificateService.cs | 44 +++--- LetsEncrypt.SiteExtension.Core/app.config | 14 +- .../packages.config | 33 ++--- LetsEncrypt.SiteExtension.Test/App.config | 28 +++- .../CertificateControllerTest.cs | 8 +- .../CertificateServicetest.cs | 11 +- .../LetsEncrypt.SiteExtension.Test.csproj | 54 +++++-- .../PublishingCrendentialsTest.cs | 2 +- LetsEncrypt.SiteExtension.Test/UnitTest1.cs | 4 +- .../packages.config | 16 +- LetsEncrypt.SiteExtension.WebJob/App.config | 30 +++- LetsEncrypt.SiteExtension.WebJob/Functions.cs | 4 +- .../LetsEncrypt.SiteExtension.WebJob.csproj | 64 +++++--- .../packages.config | 32 ++-- LetsEncrypt.WebAppOnly.nuspec | 2 +- LetsEncrypt.nuspec | 2 +- 30 files changed, 472 insertions(+), 323 deletions(-) diff --git a/.vs/LetsEncrypt-SiteExtension/DesignTimeBuild/.dtbcache b/.vs/LetsEncrypt-SiteExtension/DesignTimeBuild/.dtbcache index 89a2b262a8f9cd74c275cde09d3361aed1621125..cca23c557ef622e45af4e95cd7aa716ff8524c9a 100644 GIT binary patch delta 154 zcmZo#>(sQ?X@bbcCo>rBEg2FSEE&ug%o!{gj2Vm=Qh~HNP|OfWf@pIF6NWUP3R8w; zAZ-X#VaSjIWTip$P2SB^$Z|)%F0eU)sXc*-5r{!FGZ3=?F)I+W0Wmuea{w_X5OV=B VHxTmxF)tAF0Wtsf1SSEcDFA%8BNPAt delta 114 zcmZo#>(sQ?X@Ur2^7O{*9O=`gthm%Bhw$kMBr_B<6fhJqx&f+$b=IvAKq+ wy@rVqh(RrFA(zqG5_`&CIPl70M?Ws^8f$< diff --git a/LetsEncrypt-SiteExtension.sln b/LetsEncrypt-SiteExtension.sln index f1fe8ca..7553999 100644 --- a/LetsEncrypt-SiteExtension.sln +++ b/LetsEncrypt-SiteExtension.sln @@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{ED36B174 build.cmd = build.cmd build.core.cmd = build.core.cmd LetsEncrypt.nuspec = LetsEncrypt.nuspec + LetsEncrypt.SiteExtension.Core\LetsEncrypt.SiteExtension.Core.nuspec = LetsEncrypt.SiteExtension.Core\LetsEncrypt.SiteExtension.Core.nuspec LetsEncrypt.WebAppOnly.nuspec = LetsEncrypt.WebAppOnly.nuspec EndProjectSection EndProject diff --git a/LetsEncrypt-SiteExtension/Controllers/HomeController.cs b/LetsEncrypt-SiteExtension/Controllers/HomeController.cs index f23a03e..abc2183 100644 --- a/LetsEncrypt-SiteExtension/Controllers/HomeController.cs +++ b/LetsEncrypt-SiteExtension/Controllers/HomeController.cs @@ -1,17 +1,8 @@ -using ARMExplorer.Controllers; -using ARMExplorer.Modules; -using LetsEncrypt.Azure.Core; +using LetsEncrypt.Azure.Core; using LetsEncrypt.Azure.Core.Models; using LetsEncrypt.SiteExtension.Models; -using Microsoft.Azure.Graph.RBAC; -using Microsoft.Azure.Graph.RBAC.Models; -using Microsoft.Azure.Management.Resources; using Microsoft.Azure.Management.WebSites; using Microsoft.Azure.Management.WebSites.Models; -using Microsoft.IdentityModel.Clients.ActiveDirectory; -using Microsoft.Rest; -using Microsoft.Rest.Azure; -using Microsoft.Rest.Azure.Authentication; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -35,13 +26,13 @@ public ActionResult Index() } [HttpPost] - public ActionResult Index(AuthenticationModel model) + public async Task Index(AuthenticationModel model) { if (ModelState.IsValid) { try { - using (var client = ArmHelper.GetWebSiteManagementClient(model)) + using (var client = await ArmHelper.GetWebSiteManagementClient(model)) { //Update web config. var site = client.WebApps.GetSiteOrSlot(model.ResourceGroupName, model.WebAppName, model.SiteSlotName); @@ -138,14 +129,14 @@ public ActionResult PleaseWait() return View(); } - public ActionResult Hostname(string id) + public async Task Hostname(string id) { var settings = new AppSettingsAuthConfig(); var model = new HostnameModel(); List validationResult = null; if (settings.IsValid(out validationResult)) { - var client = ArmHelper.GetWebSiteManagementClient(settings); + var client = await ArmHelper.GetWebSiteManagementClient(settings); var site = client.WebApps.GetSiteOrSlot(settings.ResourceGroupName, settings.WebAppName, settings.SiteSlotName); model.HostNames = site.HostNames; @@ -183,10 +174,10 @@ public ActionResult Install() ); } - private void SetViewBagHostnames() + private async Task SetViewBagHostnames() { var settings = new AppSettingsAuthConfig(); - var client = ArmHelper.GetWebSiteManagementClient(settings); + var client = await ArmHelper.GetWebSiteManagementClient(settings); var site = client.WebApps.GetSiteOrSlot(settings.ResourceGroupName, settings.WebAppName, settings.SiteSlotName); var model = new HostnameModel(); @@ -234,10 +225,10 @@ public async Task Install(RequestAndInstallModel model) return View(model); } - public ActionResult AddHostname() + public async Task AddHostname() { var settings = new AppSettingsAuthConfig(); - using (var client = ArmHelper.GetWebSiteManagementClient(settings)) + using (var client = await ArmHelper.GetWebSiteManagementClient(settings)) { var s = client.WebApps.GetSiteOrSlot(settings.ResourceGroupName, settings.WebAppName, settings.SiteSlotName); foreach (var hostname in settings.Hostnames) @@ -246,8 +237,7 @@ public ActionResult AddHostname() { CustomHostNameDnsRecordType = CustomHostNameDnsRecordType.CName, HostNameType = HostNameType.Verified, - SiteName = settings.WebAppName, - Location = s.Location + SiteName = settings.WebAppName, }); } } diff --git a/LetsEncrypt-SiteExtension/LetsEncrypt.SiteExtension.csproj b/LetsEncrypt-SiteExtension/LetsEncrypt.SiteExtension.csproj index ad720a8..5c830c9 100644 --- a/LetsEncrypt-SiteExtension/LetsEncrypt.SiteExtension.csproj +++ b/LetsEncrypt-SiteExtension/LetsEncrypt.SiteExtension.csproj @@ -1,5 +1,7 @@  + + Debug @@ -50,17 +52,18 @@ ..\packages\Microsoft.Azure.Graph.RBAC.2.2.0-preview\lib\net45\Microsoft.Azure.Graph.RBAC.dll True - - ..\packages\Microsoft.Azure.Management.Websites.1.6.0-preview\lib\net45\Microsoft.Azure.Management.Websites.dll - True + + ..\packages\Microsoft.Azure.KeyVault.Core.3.0.1\lib\net452\Microsoft.Azure.KeyVault.Core.dll + + + ..\packages\Microsoft.Azure.Management.Websites.2.0.1\lib\net452\Microsoft.Azure.Management.Websites.dll ..\packages\Microsoft.Azure.Management.Resources.3.4.0-preview\lib\net45\Microsoft.Azure.ResourceManager.dll True - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - True + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll @@ -72,36 +75,35 @@ ..\packages\Microsoft.Data.Services.Client.5.8.4\lib\net40\Microsoft.Data.Services.Client.dll - - ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.28.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll - True + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.14.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll - - ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.28.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll - True + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.14.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll + + + ..\packages\Microsoft.IdentityModel.Logging.1.1.2\lib\net451\Microsoft.IdentityModel.Logging.dll + + + ..\packages\Microsoft.IdentityModel.Tokens.5.1.2\lib\net451\Microsoft.IdentityModel.Tokens.dll - ..\packages\Microsoft.Rest.ClientRuntime.2.3.5\lib\net45\Microsoft.Rest.ClientRuntime.dll - True + ..\packages\Microsoft.Rest.ClientRuntime.2.3.18\lib\net452\Microsoft.Rest.ClientRuntime.dll - ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.5\lib\net45\Microsoft.Rest.ClientRuntime.Azure.dll - True + ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.18\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll - - ..\packages\Microsoft.Rest.ClientRuntime.Azure.Authentication.2.2.0-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.Authentication.dll - True + + ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll - - ..\packages\Microsoft.WindowsAzure.ConfigurationManager.1.8.0.0\lib\net35-full\Microsoft.WindowsAzure.Configuration.dll - True + + ..\packages\WindowsAzure.Storage.9.3.3\lib\net45\Microsoft.WindowsAzure.Storage.dll - - ..\packages\WindowsAzure.Storage.4.3.0\lib\net40\Microsoft.WindowsAzure.Storage.dll - True + + ..\packages\ncrontab.3.3.0\lib\net35\NCrontab.dll - - ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\Swashbuckle.Core.5.6.0\lib\net40\Swashbuckle.Core.dll @@ -114,9 +116,27 @@ ..\dlls\System.IdentityModel.Tokens.Jwt.dll - + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + ..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll @@ -133,47 +153,43 @@ + + ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll + + + ..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll + + + ..\packages\Microsoft.AspNet.Razor.3.2.7\lib\net45\System.Web.Razor.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Deployment.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Razor.dll + - ..\packages\WebActivatorEx.2.0\lib\net40\WebActivatorEx.dll - True + ..\packages\WebActivatorEx.2.2.0\lib\net40\WebActivatorEx.dll - - ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.Deployment.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.Razor.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Helpers.dll - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - - - ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll - @@ -320,6 +336,13 @@ xcopy "$(TargetDir)LetsEncrypt.SiteExtension.XML" "$(TargetDir)..\App_Data\*" /y xcopy "$(TargetDir)LetsEncrypt.Azure.Core.xml" "$(TargetDir)..\App_Data\*" /y + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + - + + + + + + - - - - - - - - - + - - - - - + @@ -91,11 +86,11 @@ - + - + @@ -109,6 +104,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LetsEncrypt-SiteExtension/packages.config b/LetsEncrypt-SiteExtension/packages.config index 7e7fac4..7334f7d 100644 --- a/LetsEncrypt-SiteExtension/packages.config +++ b/LetsEncrypt-SiteExtension/packages.config @@ -2,35 +2,41 @@ - - - - - - - - + + + + + + + + - - + + - - - - - + + + + + + - + - - + + + + + + + - - + + \ No newline at end of file diff --git a/LetsEncrypt.SiteExtension.Core/ArmHelper.cs b/LetsEncrypt.SiteExtension.Core/ArmHelper.cs index f92cf44..399e2c1 100644 --- a/LetsEncrypt.SiteExtension.Core/ArmHelper.cs +++ b/LetsEncrypt.SiteExtension.Core/ArmHelper.cs @@ -3,23 +3,21 @@ using Microsoft.Azure.Management.WebSites; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Rest; -using Microsoft.Rest.Azure.Authentication; using Polly; using System; -using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using System.Web; namespace LetsEncrypt.Azure.Core { public static class ArmHelper { - public static WebSiteManagementClient GetWebSiteManagementClient(IAzureWebAppEnvironment model) + public static async Task GetWebSiteManagementClient(IAzureWebAppEnvironment model) { - AuthenticationResult token = GetToken(model); + AuthenticationResult token = await GetToken(model); var creds = new TokenCredentials(token.AccessToken); var websiteClient = new WebSiteManagementClient(model.ManagementEndpoint, creds); @@ -27,9 +25,9 @@ public static WebSiteManagementClient GetWebSiteManagementClient(IAzureWebAppEnv return websiteClient; } - public static DnsManagementClient GetDnsManagementClient(IAzureDnsEnvironment model) + public static async Task GetDnsManagementClient(IAzureDnsEnvironment model) { - AuthenticationResult token = GetToken(model); + AuthenticationResult token = await GetToken(model); var creds = new TokenCredentials(token.AccessToken); var dnsClient = new DnsManagementClient(model.ManagementEndpoint, creds); @@ -37,17 +35,17 @@ public static DnsManagementClient GetDnsManagementClient(IAzureDnsEnvironment mo return dnsClient; } - private static AuthenticationResult GetToken(IAzureEnvironment model) + private static async Task GetToken(IAzureEnvironment model) { var authContext = new AuthenticationContext(model.AuthenticationEndpoint + model.Tenant); - var token = authContext.AcquireToken(model.TokenAudience.ToString(), new ClientCredential(model.ClientId.ToString(), model.ClientSecret)); + var token = await authContext.AcquireTokenAsync(model.TokenAudience.ToString(), new ClientCredential(model.ClientId.ToString(), model.ClientSecret)); return token; } - public static HttpClient GetHttpClient(IAzureWebAppEnvironment model) + public static async Task GetHttpClient(IAzureWebAppEnvironment model) { - AuthenticationResult token = GetToken(model); + AuthenticationResult token = await GetToken(model); var client = HttpClientFactory.Create(new HttpClientHandler(), new TimeoutHandler()); client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token.AccessToken); @@ -56,15 +54,31 @@ public static HttpClient GetHttpClient(IAzureWebAppEnvironment model) return client; } - public static Polly.Retry.RetryPolicy ExponentialBackoff(int retryCount = 3, int firstBackOffDelay = 2) + public static Polly.Retry.RetryPolicy ExponentialBackoff(int retryCount = 3, int firstBackOffDelay = 2) { return Policy - .Handle() + .HandleResult((resp) => + { + return IsTransient(resp.StatusCode); + + }) .WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(firstBackOffDelay, retryAttempt)) ); } + private static bool IsTransient(HttpStatusCode statusCode) + { + return new HttpStatusCode[] + { + HttpStatusCode.BadGateway, + HttpStatusCode.GatewayTimeout, + HttpStatusCode.ServiceUnavailable, + HttpStatusCode.RequestTimeout, + HttpStatusCode.Unauthorized + }.Contains(statusCode); + } + public class TimeoutHandler : DelegatingHandler { private static TimeSpan Timeout = TimeSpan.FromSeconds(120); diff --git a/LetsEncrypt.SiteExtension.Core/CertificateManager.cs b/LetsEncrypt.SiteExtension.Core/CertificateManager.cs index cda8c34..cee4c53 100644 --- a/LetsEncrypt.SiteExtension.Core/CertificateManager.cs +++ b/LetsEncrypt.SiteExtension.Core/CertificateManager.cs @@ -147,20 +147,21 @@ public async Task> RenewCertificate(bool skipInsta { Trace.TraceInformation("Checking certificate"); var ss = SettingsStore.Instance.Load(); - using (var client = ArmHelper.GetWebSiteManagementClient(settings)) - using (var httpClient = ArmHelper.GetHttpClient(settings)) + 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 - await retryPolicy.ExecuteAsync(async () => + var response = await retryPolicy.ExecuteAsync(async () => { - var response = await httpClient.GetAsync($"/subscriptions/{settings.SubscriptionId}/providers/Microsoft.Web/certificates?api-version=2016-03-01"); - response.EnsureSuccessStatusCode(); - body = await response.Content.ReadAsStringAsync(); + return await httpClient.GetAsync($"/subscriptions/{settings.SubscriptionId}/providers/Microsoft.Web/certificates?api-version=2016-03-01"); + }); + response.EnsureSuccessStatusCode(); + body = await response.Content.ReadAsStringAsync(); IEnumerable 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"))); @@ -241,13 +242,13 @@ internal async Task RequestAndInstallInternalAsync(IAcm { Trace.TraceInformation("RequestAndInstallInternal"); var model = await RequestInternalAsync(config); - this.certificateService.Install(model); + await this.certificateService.Install(model); return model; } - public List Cleanup() + public async Task> Cleanup() { - return this.certificateService.RemoveExpired(); + return await this.certificateService.RemoveExpired(); } } } diff --git a/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj b/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj index cd6abb0..9c020b1 100644 --- a/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj +++ b/LetsEncrypt.SiteExtension.Core/LetsEncrypt.Azure.Core.csproj @@ -69,16 +69,14 @@ MinimumRecommendedRules.ruleset - - ..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll + + ..\packages\Microsoft.Azure.KeyVault.Core.3.0.1\lib\net452\Microsoft.Azure.KeyVault.Core.dll - - ..\packages\Microsoft.Azure.Management.Dns.1.9.0-preview\lib\net452\Microsoft.Azure.Management.Dns.dll - True + + ..\packages\Microsoft.Azure.Management.Dns.3.0.1\lib\net452\Microsoft.Azure.Management.Dns.dll - - ..\packages\Microsoft.Azure.Management.Websites.1.6.0-preview\lib\net45\Microsoft.Azure.Management.Websites.dll - True + + ..\packages\Microsoft.Azure.Management.Websites.2.0.1\lib\net452\Microsoft.Azure.Management.Websites.dll ..\packages\Microsoft.Azure.Storage.Blob.9.4.2\lib\net452\Microsoft.Azure.Storage.Blob.dll @@ -86,31 +84,23 @@ ..\packages\Microsoft.Azure.Storage.Common.9.4.2\lib\net452\Microsoft.Azure.Storage.Common.dll - - ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.28.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll - True + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.14.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll - - ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.28.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll - True + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.14.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll - ..\packages\Microsoft.Rest.ClientRuntime.2.3.8\lib\net452\Microsoft.Rest.ClientRuntime.dll - True + ..\packages\Microsoft.Rest.ClientRuntime.2.3.18\lib\net452\Microsoft.Rest.ClientRuntime.dll - ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.7\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll - True - - - ..\packages\Microsoft.Rest.ClientRuntime.Azure.Authentication.2.2.0-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.Authentication.dll - True + ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.18\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll - - ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\Polly.6.1.2\lib\netstandard1.1\Polly.dll @@ -122,12 +112,12 @@ - - ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + + ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll - - ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.4.5.1\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll @@ -146,23 +136,24 @@ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll - ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll + ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net46\System.Security.Cryptography.Algorithms.dll ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll @@ -171,7 +162,7 @@ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll + ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net46\System.Security.Cryptography.X509Certificates.dll @@ -180,7 +171,7 @@ - ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + ..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll diff --git a/LetsEncrypt.SiteExtension.Core/LetsEncrypt.SiteExtension.Core.nuspec b/LetsEncrypt.SiteExtension.Core/LetsEncrypt.SiteExtension.Core.nuspec index b15caeb..48db96c 100644 --- a/LetsEncrypt.SiteExtension.Core/LetsEncrypt.SiteExtension.Core.nuspec +++ b/LetsEncrypt.SiteExtension.Core/LetsEncrypt.SiteExtension.Core.nuspec @@ -3,7 +3,7 @@ letsencrypt.azure.core Azure Let's Encrypt - 0.8.5-prerelease + 0.9.0 SJKP http://opensource.org/licenses/Apache-2.0 https://github.com/sjkp/letsencrypt-siteextension @@ -13,9 +13,9 @@ letsencrypt ssl https - - - + + + diff --git a/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs b/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs index 5dc8f9b..0c848f9 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/AppServiceCerticiateCertificateService.cs @@ -14,7 +14,7 @@ public async Task Install(ICertificateInstallModel model) throw new NotImplementedException(); } - public List RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0) + public async Task> RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0) { throw new NotImplementedException(); } diff --git a/LetsEncrypt.SiteExtension.Core/Services/AzureDnsAuthorizationChallengeProvider.cs b/LetsEncrypt.SiteExtension.Core/Services/AzureDnsAuthorizationChallengeProvider.cs index 406cfe1..139a7d2 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/AzureDnsAuthorizationChallengeProvider.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/AzureDnsAuthorizationChallengeProvider.cs @@ -13,20 +13,18 @@ namespace LetsEncrypt.Azure.Core.Services { public class AzureDnsAuthorizationChallengeProvider : BaseDnsAuthorizationChallengeProvider { - private readonly DnsManagementClient dnsClient; private IAzureDnsEnvironment environment; public AzureDnsAuthorizationChallengeProvider(IAzureDnsEnvironment azureDnsSettings) - { - this.dnsClient = ArmHelper.GetDnsManagementClient(azureDnsSettings); + { this.environment = azureDnsSettings; } public override async Task CleanupChallenge(DnsChallenge dnsChallenge) { var existingRecords = await SafeGetExistingRecords(dnsChallenge); - - await this.dnsClient.RecordSets.DeleteAsync(this.environment.ResourceGroupName, this.environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT); + var dnsClient = await ArmHelper.GetDnsManagementClient(this.environment); + await dnsClient.RecordSets.DeleteAsync(this.environment.ResourceGroupName, this.environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT); } public override async Task PersistsChallenge(DnsChallenge dnsChallenge) @@ -35,6 +33,7 @@ public override async Task PersistsChallenge(DnsChallenge dnsChallenge) { new TxtRecord() { Value = new[] { dnsChallenge.RecordValue } } }; + var dnsClient = await ArmHelper.GetDnsManagementClient(this.environment); if ((await dnsClient.RecordSets.ListByTypeAsync(environment.ResourceGroupName, environment.ZoneName, RecordType.TXT)).Any()) { var existingRecords = await SafeGetExistingRecords(dnsChallenge); @@ -50,7 +49,7 @@ public override async Task PersistsChallenge(DnsChallenge dnsChallenge) } } } - await this.dnsClient.RecordSets.CreateOrUpdateAsync(this.environment.ResourceGroupName, this.environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT, new RecordSet() + await dnsClient.RecordSets.CreateOrUpdateAsync(this.environment.ResourceGroupName, this.environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT, new RecordSet() { TxtRecords = records, TTL = 60 @@ -66,6 +65,7 @@ private async Task SafeGetExistingRecords(DnsChallenge dnsChallenge) { try { + var dnsClient = await ArmHelper.GetDnsManagementClient(this.environment); return await dnsClient.RecordSets.GetAsync(environment.ResourceGroupName, environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT); } diff --git a/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs b/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs index 5be0970..4d139a1 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/ICertificateService.cs @@ -11,6 +11,6 @@ public interface ICertificateService { Task Install(ICertificateInstallModel model); - List RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0); + Task> RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0); } } diff --git a/LetsEncrypt.SiteExtension.Core/Services/KuduFileSystemAuthorizationChallengeProvider.cs b/LetsEncrypt.SiteExtension.Core/Services/KuduFileSystemAuthorizationChallengeProvider.cs index 893c342..9b89844 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/KuduFileSystemAuthorizationChallengeProvider.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/KuduFileSystemAuthorizationChallengeProvider.cs @@ -14,15 +14,14 @@ namespace LetsEncrypt.Azure.Core.Services { public class KuduFileSystemAuthorizationChallengeProvider : BaseHttpAuthorizationChallengeProvider { - private readonly KuduRestClient kuduClient; + private readonly IAuthorizationChallengeProviderConfig config; private readonly IAzureWebAppEnvironment azureEnvironment; public KuduFileSystemAuthorizationChallengeProvider(IAzureWebAppEnvironment azureEnvironment, IAuthorizationChallengeProviderConfig config) { this.config = config; - var website = ArmHelper.GetWebSiteManagementClient(azureEnvironment); - this.kuduClient = KuduHelper.GetKuduClient(website, azureEnvironment); + this.azureEnvironment = azureEnvironment; } @@ -54,7 +53,7 @@ private async Task WriteFile(string answerPath, string content) var streamwriter = new StreamWriter(ms); streamwriter.Write(content); streamwriter.Flush(); - await kuduClient.PutFile(answerPath, ms); + await (await GetKuduRestClient()).PutFile(answerPath, ms); } } @@ -76,5 +75,10 @@ private string WebRootPath() //Ensure this is a backwards compatible with the LocalFileSystemProvider that was the only option before return azureEnvironment.WebRootPath.Replace(Environment.ExpandEnvironmentVariables("%HOME%"), "").Replace('\\', '/'); } + private async Task GetKuduRestClient() + { + var website = await ArmHelper.GetWebSiteManagementClient(azureEnvironment); + return KuduHelper.GetKuduClient(website, azureEnvironment); + } } } diff --git a/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs b/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs index fea2095..4fc9ba0 100644 --- a/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs +++ b/LetsEncrypt.SiteExtension.Core/Services/WebAppCertificateService.cs @@ -30,37 +30,30 @@ public WebAppCertificateService(IAzureWebAppEnvironment azureEnvironment, IWebAp public async Task Install(ICertificateInstallModel model) { var cert = model.CertificateInfo; - using (var webSiteClient = ArmHelper.GetWebSiteManagementClient(azureEnvironment)) + using (var webSiteClient = await ArmHelper.GetWebSiteManagementClient(azureEnvironment)) { var s = webSiteClient.WebApps.GetSiteOrSlot(azureEnvironment.ResourceGroupName, azureEnvironment.WebAppName, azureEnvironment.SiteSlotName); Trace.TraceInformation(String.Format("Installing certificate {0} on azure with server farm id {1}", cert.Name, s.ServerFarmId)); - var newCert = new Certificate() - { - PfxBlob = cert.PfxCertificate, - Password = cert.Password, - Location = s.Location, - ServerFarmId = s.ServerFarmId, - Name = model.Host + "-" + cert.Certificate.Thumbprint - - }; + var newCert = new Certificate(s.Location, cert.Password, name: model.Host + "-" + cert.Certificate.Thumbprint, pfxBlob: cert.PfxCertificate, serverFarmId: s.ServerFarmId); //BUG https://github.com/sjkp/letsencrypt-siteextension/issues/99 //using this will not install the certificate with the correct webSpace property set, //and the app service will be unable to find the certificate if the app service plan has been moved between resource groups. //webSiteClient.Certificates.CreateOrUpdate(azureEnvironment.ServicePlanResourceGroupName, cert.Certificate.Subject.Replace("CN=", ""), newCert); - var client = ArmHelper.GetHttpClient(azureEnvironment); + var client = await ArmHelper.GetHttpClient(azureEnvironment); var body = JsonConvert.SerializeObject(newCert, JsonHelper.DefaultSerializationSettings); - Polly.Retry.RetryPolicy retryPolicy = ArmHelper.ExponentialBackoff(); + var retryPolicy = ArmHelper.ExponentialBackoff(); - await retryPolicy.ExecuteAsync(async () => + var t = await retryPolicy.ExecuteAsync(async () => { - var t = await client.PutAsync($"/subscriptions/{azureEnvironment.SubscriptionId}/resourceGroups/{azureEnvironment.ServicePlanResourceGroupName}/providers/Microsoft.Web/certificates/{newCert.Name}?api-version=2016-03-01", new StringContent(body, Encoding.UTF8, "application/json")); - t.EnsureSuccessStatusCode(); + return await client.PutAsync($"/subscriptions/{azureEnvironment.SubscriptionId}/resourceGroups/{azureEnvironment.ServicePlanResourceGroupName}/providers/Microsoft.Web/certificates/{newCert.Name}?api-version=2016-03-01", new StringContent(body, Encoding.UTF8, "application/json")); }); + Trace.TraceInformation(await t.Content.ReadAsStringAsync()); + t.EnsureSuccessStatusCode(); foreach (var dnsName in model.AllDnsIdentifiers) { @@ -89,23 +82,26 @@ await retryPolicy.ExecuteAsync(async () => } - public List RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0) + public async Task> RemoveExpired(int removeXNumberOfDaysBeforeExpiration = 0) { - using (var webSiteClient = ArmHelper.GetWebSiteManagementClient(azureEnvironment)) + using (var webSiteClient = await ArmHelper.GetWebSiteManagementClient(azureEnvironment)) { var certs = webSiteClient.Certificates.ListByResourceGroup(azureEnvironment.ServicePlanResourceGroupName); - - var tobeRemoved = certs.Where(s => s.ExpirationDate < DateTime.UtcNow.AddDays(removeXNumberOfDaysBeforeExpiration) && (s.Issuer.Contains("Let's Encrypt") || s.Issuer.Contains("Fake LE"))).ToList(); - - tobeRemoved.ForEach(s => RemoveCertificate(webSiteClient, s)); + var site = webSiteClient.WebApps.GetSiteOrSlot(azureEnvironment.ResourceGroupName, azureEnvironment.WebAppName, azureEnvironment.SiteSlotName); + + var tobeRemoved = certs.Where(s => s.ExpirationDate < DateTime.UtcNow.AddDays(removeXNumberOfDaysBeforeExpiration) && (s.Issuer.Contains("Let's Encrypt") || s.Issuer.Contains("Fake LE")) && !site.HostNameSslStates.Any(hostNameBindings => hostNameBindings.Thumbprint == s.Thumbprint)).ToList(); + foreach (var cert in tobeRemoved) + { + await RemoveCertificate(webSiteClient, cert); + } return tobeRemoved.Select(s => s.Thumbprint).ToList(); } } - private void RemoveCertificate(WebSiteManagementClient webSiteClient, Certificate s) - { - webSiteClient.Certificates.Delete(azureEnvironment.ServicePlanResourceGroupName, s.Name); + private async Task RemoveCertificate(WebSiteManagementClient webSiteClient, Certificate s) + { + await webSiteClient.Certificates.DeleteAsync(azureEnvironment.ServicePlanResourceGroupName, s.Name); } diff --git a/LetsEncrypt.SiteExtension.Core/app.config b/LetsEncrypt.SiteExtension.Core/app.config index 0ae7ba9..b4c3921 100644 --- a/LetsEncrypt.SiteExtension.Core/app.config +++ b/LetsEncrypt.SiteExtension.Core/app.config @@ -4,11 +4,19 @@ - + + + + + + + + + - - + + diff --git a/LetsEncrypt.SiteExtension.Core/packages.config b/LetsEncrypt.SiteExtension.Core/packages.config index 8e49574..8e63676 100644 --- a/LetsEncrypt.SiteExtension.Core/packages.config +++ b/LetsEncrypt.SiteExtension.Core/packages.config @@ -1,26 +1,25 @@  - - - - + + + + - - - - - + + + + - - + + - + - + @@ -32,7 +31,7 @@ - + @@ -46,16 +45,16 @@ - + - + - + \ No newline at end of file diff --git a/LetsEncrypt.SiteExtension.Test/App.config b/LetsEncrypt.SiteExtension.Test/App.config index e51dd11..642ed8d 100644 --- a/LetsEncrypt.SiteExtension.Test/App.config +++ b/LetsEncrypt.SiteExtension.Test/App.config @@ -16,11 +16,11 @@ - + - + @@ -32,11 +32,11 @@ - + - + @@ -50,6 +50,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/LetsEncrypt.SiteExtension.Test/CertificateControllerTest.cs b/LetsEncrypt.SiteExtension.Test/CertificateControllerTest.cs index 374a3a2..795ad24 100644 --- a/LetsEncrypt.SiteExtension.Test/CertificateControllerTest.cs +++ b/LetsEncrypt.SiteExtension.Test/CertificateControllerTest.cs @@ -22,7 +22,7 @@ public class CertificateControllerTest public async Task TestRenewCertificate() { var config = new AppSettingsAuthConfig(); - var client = ArmHelper.GetWebSiteManagementClient(config); + var client = await ArmHelper.GetWebSiteManagementClient(config); var kuduClient = KuduHelper.GetKuduClient(client, config); var res = await kuduClient.HttpClient.PostAsync("https://webappcfmv5fy7lcq7o.scm.azurewebsites.net/letsencrypt/api/certificates/renew?api-version=2017-09-01", new StringContent("")); @@ -42,7 +42,7 @@ public async Task TestRenewCertificate() public async Task TestRequestAndInstallCertificate() { var config = new AppSettingsAuthConfig(); - var client = ArmHelper.GetWebSiteManagementClient(config); + var client = await ArmHelper.GetWebSiteManagementClient(config); var kuduClient = KuduHelper.GetKuduClient(client, config); var body = new HttpKuduInstallModel() @@ -71,7 +71,7 @@ public async Task TestRequestAndInstallCertificate() public async Task TestRequestAndInstallDnsCertificate() { var config = new AppSettingsAuthConfig(); - var client = ArmHelper.GetWebSiteManagementClient(config); + var client = await ArmHelper.GetWebSiteManagementClient(config); var kuduClient = KuduHelper.GetKuduClient(client, config); var body = new DnsAzureInstallModel() @@ -114,7 +114,7 @@ private static async Task ValidateResponse(IAcmeConfig acmeConfig, HttpResponseM public async Task TestRequestDnsCertificate() { var config = new AppSettingsAuthConfig(); - var client = ArmHelper.GetWebSiteManagementClient(config); + var client = await ArmHelper.GetWebSiteManagementClient(config); var kuduClient = KuduHelper.GetKuduClient(client, config); var body = new DnsAzureModel() diff --git a/LetsEncrypt.SiteExtension.Test/CertificateServicetest.cs b/LetsEncrypt.SiteExtension.Test/CertificateServicetest.cs index 670a4bc..9ec3849 100644 --- a/LetsEncrypt.SiteExtension.Test/CertificateServicetest.cs +++ b/LetsEncrypt.SiteExtension.Test/CertificateServicetest.cs @@ -4,6 +4,7 @@ using LetsEncrypt.Azure.Core.Models; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; namespace LetsEncrypt.SiteExtension.Test { @@ -11,14 +12,16 @@ namespace LetsEncrypt.SiteExtension.Test public class CertificateServiceTest { [DeploymentItem("letsencrypt.sjkp.dk-all.pfx")] + [DeploymentItem("App.secret.config")] [TestMethod] - public void TestInstall() + public async Task TestInstall() { + Console.WriteLine(typeof(Microsoft.IdentityModel.Clients.ActiveDirectory.AdalOption).AssemblyQualifiedName); var config = new AppSettingsAuthConfig(); var service = new WebAppCertificateService(config, new CertificateServiceSettings { }); var pfx = File.ReadAllBytes("letsencrypt.sjkp.dk-all.pfx"); - service.Install(new CertificateInstallModel + await service.Install(new CertificateInstallModel { AllDnsIdentifiers = new List() { "letsencrypt.sjkp.dk" }, Host = "letsencrypt.sjkp.dk", @@ -34,12 +37,12 @@ public void TestInstall() }); } [TestMethod] - public void TestRemove() + public async Task TestRemove() { var config = new AppSettingsAuthConfig(); var service = new WebAppCertificateService(config, new CertificateServiceSettings { }); - service.RemoveExpired(180); + await service.RemoveExpired(180); } } } diff --git a/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj b/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj index 0a2b6eb..0857c93 100644 --- a/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj +++ b/LetsEncrypt.SiteExtension.Test/LetsEncrypt.SiteExtension.Test.csproj @@ -76,22 +76,24 @@ ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll True - - ..\packages\Microsoft.Azure.Management.Websites.1.6.0-preview\lib\net45\Microsoft.Azure.Management.Websites.dll - True + + ..\packages\Microsoft.Azure.Management.Websites.2.0.1\lib\net452\Microsoft.Azure.Management.Websites.dll + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.14.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll + + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.14.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll + - ..\packages\Microsoft.Rest.ClientRuntime.2.3.5\lib\net45\Microsoft.Rest.ClientRuntime.dll - True + ..\packages\Microsoft.Rest.ClientRuntime.2.3.18\lib\net452\Microsoft.Rest.ClientRuntime.dll - ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.5\lib\net45\Microsoft.Rest.ClientRuntime.Azure.dll - True + ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.18\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\Polly.6.1.2\lib\netstandard2.0\Polly.dll @@ -100,13 +102,37 @@ - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll True + True + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll + True + True + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + True + True + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + True + True + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + True + True + @@ -142,7 +168,7 @@ Designer - PreserveNewest + Always Always diff --git a/LetsEncrypt.SiteExtension.Test/PublishingCrendentialsTest.cs b/LetsEncrypt.SiteExtension.Test/PublishingCrendentialsTest.cs index 660bee7..2aa0d8b 100644 --- a/LetsEncrypt.SiteExtension.Test/PublishingCrendentialsTest.cs +++ b/LetsEncrypt.SiteExtension.Test/PublishingCrendentialsTest.cs @@ -20,7 +20,7 @@ public async Task GetPublishingCredentials() { var model = new AppSettingsAuthConfig(); - var helper = ArmHelper.GetWebSiteManagementClient(model); + var helper = await ArmHelper.GetWebSiteManagementClient(model); var kuduClient = KuduHelper.GetKuduClient(helper, model); diff --git a/LetsEncrypt.SiteExtension.Test/UnitTest1.cs b/LetsEncrypt.SiteExtension.Test/UnitTest1.cs index dd30e8b..7b6c5a7 100644 --- a/LetsEncrypt.SiteExtension.Test/UnitTest1.cs +++ b/LetsEncrypt.SiteExtension.Test/UnitTest1.cs @@ -17,9 +17,9 @@ public async Task RetryTest() var client = HttpClientFactory.Create(new HttpClientHandler(), new TimeoutHandler()); var retry = ArmHelper.ExponentialBackoff(); - await retry.ExecuteAsync(async () => + var resp = await retry.ExecuteAsync(async () => { - await client.PostAsJsonAsync("https://en8zkq5hogjyi.x.pipedream.net", new { text = "hello" }); + return await client.PostAsJsonAsync("https://en8zkq5hogjyi.x.pipedream.net", new { text = "hello" }); }); } } diff --git a/LetsEncrypt.SiteExtension.Test/packages.config b/LetsEncrypt.SiteExtension.Test/packages.config index 1549a90..74a7042 100644 --- a/LetsEncrypt.SiteExtension.Test/packages.config +++ b/LetsEncrypt.SiteExtension.Test/packages.config @@ -1,10 +1,16 @@  - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/LetsEncrypt.SiteExtension.WebJob/App.config b/LetsEncrypt.SiteExtension.WebJob/App.config index 64ae67f..56fcf5d 100644 --- a/LetsEncrypt.SiteExtension.WebJob/App.config +++ b/LetsEncrypt.SiteExtension.WebJob/App.config @@ -13,16 +13,12 @@ - + - - - - - - + + @@ -47,6 +43,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/LetsEncrypt.SiteExtension.WebJob/Functions.cs b/LetsEncrypt.SiteExtension.WebJob/Functions.cs index 3e26f27..00122b6 100644 --- a/LetsEncrypt.SiteExtension.WebJob/Functions.cs +++ b/LetsEncrypt.SiteExtension.WebJob/Functions.cs @@ -69,10 +69,10 @@ public static async Task RenewCertificate([TimerTrigger(typeof(MyDailySchedule), Console.WriteLine($"Completed renewal of '{count}' certificates"); } - public static void Cleanup([TimerTrigger(typeof(MyDailySchedule), RunOnStartup = true)] TimerInfo timerInfo) + public static async Task Cleanup([TimerTrigger(typeof(MyDailySchedule), RunOnStartup = true)] TimerInfo timerInfo) { Console.WriteLine("Clean up"); - var res = new CertificateManager(new AppSettingsAuthConfig()).Cleanup(); + var res = await new CertificateManager(new AppSettingsAuthConfig()).Cleanup(); res.ForEach(s => Console.WriteLine($"Removed certificate with thumbprint {s}")); } diff --git a/LetsEncrypt.SiteExtension.WebJob/LetsEncrypt.SiteExtension.WebJob.csproj b/LetsEncrypt.SiteExtension.WebJob/LetsEncrypt.SiteExtension.WebJob.csproj index 1bbb29a..962e485 100644 --- a/LetsEncrypt.SiteExtension.WebJob/LetsEncrypt.SiteExtension.WebJob.csproj +++ b/LetsEncrypt.SiteExtension.WebJob/LetsEncrypt.SiteExtension.WebJob.csproj @@ -12,6 +12,8 @@ v4.6.1 512 + + AnyCPU @@ -69,9 +71,8 @@ MinimumRecommendedRules.ruleset - - ..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll - True + + ..\packages\Microsoft.Azure.KeyVault.Core.3.0.1\lib\net452\Microsoft.Azure.KeyVault.Core.dll ..\packages\Microsoft.Azure.WebJobs.Core.2.3.0\lib\net45\Microsoft.Azure.WebJobs.dll @@ -91,34 +92,38 @@ ..\packages\Microsoft.Data.Services.Client.5.8.4\lib\net40\Microsoft.Data.Services.Client.dll - - ..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.2.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll - - ..\packages\WindowsAzure.Storage.8.2.1\lib\net45\Microsoft.WindowsAzure.Storage.dll - True + + ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + + ..\packages\WindowsAzure.Storage.9.3.3\lib\net45\Microsoft.WindowsAzure.Storage.dll ..\packages\ncrontab.3.3.0\lib\net35\NCrontab.dll True - - ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll - - ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + + ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll + True + True - - ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.4.5.1\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll @@ -136,8 +141,10 @@ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll + True + True ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll @@ -147,7 +154,9 @@ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll - ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll + ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll + True + True ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll @@ -155,8 +164,10 @@ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll + True + True ..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll @@ -170,12 +181,10 @@ - - False - ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll - - ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + ..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll + True + True @@ -194,6 +203,13 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + +