Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
Binary file modified .gitignore
Binary file not shown.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM microsoft/dotnet:2.1-sdk-alpine AS build
FROM microsoft/dotnet:2.2-sdk-alpine AS build
# Set the working directory witin the container
WORKDIR /src

Expand All @@ -17,8 +17,8 @@ RUN dotnet publish -c release ./src/LetsEncrypt.Azure.Runner/LetsEncrypt.Azure.R


# Build runtime image
FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine AS app
FROM microsoft/dotnet:2.2-aspnetcore-runtime-alpine AS app
WORKDIR /app
COPY --from=build /src/src/LetsEncrypt.Azure.Runner/bin/release/netcoreapp2.1/publish .
COPY --from=build /src/src/LetsEncrypt.Azure.Runner/bin/release/netcoreapp2.2/publish .

ENTRYPOINT ["dotnet", "LetsEncrypt.Azure.Runner.dll"]
42 changes: 27 additions & 15 deletions examples/LetsEncrypt.Azure.FunctionV2/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,72 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace LetsEncrypt.Azure.FunctionV2
{
public class Helper
{
/// <summary>
/// Requests a Let's Encrypt wild card certificate using DNS challenge.
/// Requests a Let's Encrypt wild card certificate using DNS challenge.
/// The DNS provider used is Azure DNS.
/// The certificate is saved to Azure Key Vault.
/// The Certificate is finally install to an Azure App Service.
/// Configuration values are stored in Environment Variables.
/// The Certificate is finally install to an Azure App Service.
/// Configuration values are stored in Environment Variables.
/// </summary>
/// <param name="log"></param>
/// <returns></returns>
public static async Task InstallOrRenewCertificate(ILogger log)
{
var vaultBaseUrl = $"https://{Environment.GetEnvironmentVariable("Vault")}.vault.azure.net/";
log.LogInformation("C# HTTP trigger function processed a request.");
var Configuration = new ConfigurationBuilder()
var configuration = new ConfigurationBuilder()
.AddAzureKeyVault(vaultBaseUrl) //Use MSI to get token
.AddEnvironmentVariables()
.Build();
var tokenProvider = new AzureServiceTokenProvider();
//Create the Key Vault client
var kvClient = new KeyVaultClient((authority, resource, scope) => tokenProvider.KeyVaultTokenCallback(authority, resource, scope), new MessageLoggingHandler(log));

ValidationContext validationContext;
IServiceCollection serviceCollection = new ServiceCollection();

serviceCollection.AddSingleton<ILogger>(log)
.Configure<LoggerFilterOptions>(options => options.MinLevel = LogLevel.Information);
var certificateConsumer = Configuration.GetValue<string>("CertificateConsumer");
serviceCollection
.AddSingleton<ILogger>(log)
.Configure<LoggerFilterOptions>(options => options.MinLevel = LogLevel.Information);
var certificateConsumer = configuration.GetValue<string>("CertificateConsumer");
if (string.IsNullOrEmpty(certificateConsumer))
{
serviceCollection.AddAzureAppService(Configuration.GetSection("AzureAppService").Get<AzureWebAppSettings>());
var webAppSettings = configuration.GetSection("AzureAppService").Get<AzureWebAppSettings>();
validationContext = new ValidationContext(webAppSettings);
Validator.ValidateObject(webAppSettings, validationContext);
serviceCollection.AddAzureAppService(webAppSettings);
}
else if (certificateConsumer.Equals("NullCertificateConsumer"))
{
serviceCollection.AddNullCertificateConsumer();
}

serviceCollection.AddSingleton<IKeyVaultClient>(kvClient)
.AddKeyVaultCertificateStore(vaultBaseUrl);

serviceCollection
.AddSingleton<IKeyVaultClient>(kvClient)
.AddKeyVaultCertificateStore(vaultBaseUrl);

serviceCollection.AddAcmeClient<AzureDnsProvider>(Configuration.GetSection("DnsSettings").Get<AzureDnsSettings>());
var dnsProviderConfig = configuration.GetSection("DnsSettings").Get<AzureDnsSettings>();
validationContext = new ValidationContext(dnsProviderConfig);
Validator.ValidateObject(dnsProviderConfig, validationContext);
serviceCollection
.AddAcmeClient<AzureDnsProvider>(dnsProviderConfig);

var serviceProvider = serviceCollection.BuildServiceProvider();

var app = serviceProvider.GetService<LetsencryptService>();

var dnsRequest = Configuration.GetSection("AcmeDnsRequest").Get<AcmeDnsRequest>();
var dnsRequest = configuration.GetSection("AcmeDnsRequest").Get<AcmeDnsRequest>();
validationContext = new ValidationContext(dnsRequest);
Validator.ValidateObject(dnsRequest, validationContext);

await app.Run(dnsRequest, Configuration.GetValue<int?>("RenewXNumberOfDaysBeforeExpiration") ?? 22);
await app.Run(dnsRequest, configuration.GetValue<int?>("RenewXNumberOfDaysBeforeExpiration") ?? 22);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AzureFunctionsVersion>v2</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Host.Storage" Version="3.0.14" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.30" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\LetsEncrypt.Azure.Core.V2\LetsEncrypt.Azure.Core.V2.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ public static async Task<IActionResult> Run(
await Helper.InstallOrRenewCertificate(log);

return new OkResult();
} catch(Exception ex)
}
catch (Exception ex)
{
log.LogError(ex.ToString());
return new ExceptionResult(ex, true);

}
}
}
Expand Down
47 changes: 21 additions & 26 deletions src/LetsEncrypt.Azure.Core.Test/AcmeClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,20 @@ public class AcmeClientTest
{
private readonly ILogger<AcmeClient> logger;

public AcmeClientTest()
{
public AcmeClientTest() { }

}
public AcmeClientTest(ILogger<AcmeClient> logger) => this.logger = logger;

public AcmeClientTest(ILogger<AcmeClient> logger)
{
this.logger = logger;
}
[TestMethod]
public async Task TestEndToEndAzure()
{
var config = TestHelper.AzureDnsSettings;
var settings = TestHelper.AzureDnsSettings;

var manager = new AcmeClient(new AzureDnsProvider(config), new DnsLookupService(), null, this.logger);
var manager = new AcmeClient(new AzureDnsProvider(settings), new DnsLookupService(), new NullCertificateStore(), this.logger);

var dnsRequest = new AcmeDnsRequest()
IAcmeDnsRequest dnsRequest = new AcmeDnsRequest()
{
Host = "*.ai4bots.com",
Hosts = "*.ai4bots.com",
PFXPassword = "Pass@word",
RegistrationEmail = "[email protected]",
AcmeEnvironment = new LetsEncryptStagingV2(),
Expand All @@ -56,32 +51,32 @@ public async Task TestEndToEndAzure()

Assert.IsNotNull(res);

File.WriteAllBytes($"{dnsRequest.Host.Substring(2)}.pfx", res.CertificateInfo.PfxCertificate);

var pass = new System.Security.SecureString();
Array.ForEach(dnsRequest.PFXPassword.ToCharArray(), c =>
string hostsPlusSeparated = AcmeClient.GetHostsPlusSeparated(dnsRequest.Hosts);
File.WriteAllBytes($"{hostsPlusSeparated}.pfx", res.CertificateInfo.PfxCertificate);
using (var pass = new System.Security.SecureString())
{
pass.AppendChar(c);
});
File.WriteAllBytes($"exported-{dnsRequest.Host.Substring(2)}.pfx", res.CertificateInfo.Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, pass));
Array.ForEach(dnsRequest.PFXPassword.ToCharArray(), c =>
{
pass.AppendChar(c);
});
File.WriteAllBytes($"exported-{hostsPlusSeparated}.pfx", res.CertificateInfo.Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, pass));

var certService = new AzureWebAppService(new[] { TestHelper.AzureWebAppSettings });

var certService = new AzureWebAppService(new[] { TestHelper.AzureWebAppSettings });

await certService.Install(res);
await certService.Install(res);
}
}

[TestMethod]
public async Task TestEndToEndUnoEuro()
{

var dnsProvider = TestHelper.UnoEuroDnsProvider;

var manager = new AcmeClient(dnsProvider, new DnsLookupService(), new NullCertificateStore());

var dnsRequest = new AcmeDnsRequest()
{
Host = "*.tiimo.dk",
Hosts = "*.tiimo.dk",
PFXPassword = "Pass@word",
RegistrationEmail = "[email protected]",
AcmeEnvironment = new LetsEncryptStagingV2(),
Expand All @@ -99,7 +94,7 @@ public async Task TestEndToEndUnoEuro()

Assert.IsNotNull(res);

File.WriteAllBytes($"{dnsRequest.Host.Substring(2)}.pfx", res.CertificateInfo.PfxCertificate);
File.WriteAllBytes($"{dnsRequest.Hosts.Substring(2)}.pfx", res.CertificateInfo.PfxCertificate);
}


Expand All @@ -113,7 +108,7 @@ public async Task TestEndToEndGoDaddy()

var dnsRequest = new AcmeDnsRequest()
{
Host = "*.åbningstider.info",
Hosts = "*.åbningstider.info",
PFXPassword = "Pass@word",
RegistrationEmail = "[email protected]",
AcmeEnvironment = new LetsEncryptStagingV2(),
Expand All @@ -131,7 +126,7 @@ public async Task TestEndToEndGoDaddy()

Assert.IsNotNull(res);

File.WriteAllBytes($"{dnsRequest.Host.Substring(2)}.pfx", res.CertificateInfo.PfxCertificate);
File.WriteAllBytes($"{dnsRequest.Hosts.Substring(2)}.pfx", res.CertificateInfo.PfxCertificate);

var certService = new AzureWebAppService(new[] { TestHelper.AzureWebAppSettings });

Expand Down
15 changes: 10 additions & 5 deletions src/LetsEncrypt.Azure.Core.Test/AzureDnsServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
using LetsEncrypt.Azure.Core.V2;
using LetsEncrypt.Azure.Core.V2.DnsProviders;
using LetsEncrypt.Azure.Core.V2.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Rest.Azure.Authentication;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Letsencrypt.Azure.Core.Test
{
[TestClass]
public class AzureDnsServiceTest
{
private const string Domain = "ai4bots.com";
private const string HostName = "*." + Domain;

[TestMethod]
public async Task AzureDnsTest()
{
var config = TestHelper.AzureDnsSettings;

var service = new AzureDnsProvider(config);

var id = Guid.NewGuid().ToString();
await service.PersistChallenge("_acme-challenge", id);
await service.PersistChallenge(zoneName: Domain, recordSetName: "_acme-challenge", recordValue: id);


var exists = await new DnsLookupService().Exists("*.ai4bots.com", id, service.MinimumTtl);
var exists = await new DnsLookupService().Exists(HostName, id, service.MinimumTtl);
Assert.IsTrue(exists);

await service.Cleanup("_acme-challenge");
}
await service.Cleanup(Domain, "_acme-challenge");
}
}
}
6 changes: 3 additions & 3 deletions src/LetsEncrypt.Azure.Core.Test/GoDaddyDnsProviderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class GoDaddyDnsProviderTest

public GoDaddyDnsProviderTest()
{
this.Configuration = new ConfigurationBuilder()
this.Configuration = new ConfigurationBuilder()
.AddUserSecrets<GoDaddyDnsProviderTest>()
.Build();

Expand All @@ -37,13 +37,13 @@ public GoDaddyDnsProviderTest()
public async Task TestPersistChallenge()
{
var id = Guid.NewGuid().ToString();
await DnsService.PersistChallenge("_acme-challenge", id);
await DnsService.PersistChallenge(zoneName: Domain, recordSetName: "_acme-challenge", recordValue: id);


var exists = await new DnsLookupService().Exists("*." + Domain, id);
Assert.IsTrue(exists);

await DnsService.Cleanup("_acme-challenge");
await DnsService.Cleanup(Domain, "_acme-challenge");
}
}
}
Loading