Created
March 17, 2016 06:08
-
-
Save pksorensen/44f9cedb1307b29d41b1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| namespace SInnovations.VSTeamServices.TasksBuilder.KeyVault.ResourceTypes | |
| { | |
| using System.Collections.Generic; | |
| using System.ComponentModel.DataAnnotations; | |
| using System.Linq; | |
| using CommandLine; | |
| using SInnovations.VSTeamServices.TasksBuilder.Attributes; | |
| using SInnovations.VSTeamServices.TasksBuilder.ResourceTypes; | |
| /// <summary> | |
| /// Parsing options for <see cref="KeyVaultOutput"/> that is used to generate input options in vsts task. | |
| /// </summary> | |
| public class KeyVaultOptions | |
| { | |
| private readonly KeyVaultOutput options; | |
| public KeyVaultOptions(KeyVaultOutput options){ | |
| this.options = options; | |
| } | |
| [ArmResourceIdPicker("Microsoft.KeyVault/vaults", "2015-06-01")] | |
| [Display(Description = "The keyvault namespace", Name = "KeyVault Name")] | |
| [Required] | |
| [Option("KeyVaultName")] | |
| public string VaultName { get { return options.VaultName; } set { options.VaultName = value.StartsWith("/subscriptions") ? value.Split('/').Last() :value; } } | |
| [ArmResourceProviderPicker("$(KeyVaultName)","secrets", "2015-06-01")] | |
| [Display(Description = "The keyvault secret name to store value in", Name = "Secret Name")] | |
| [Required] | |
| [Option("SecretName")] | |
| public string SecretName { get { return options.SecretName; } set { options.SecretName = value.StartsWith("/subscriptions")? value.Substring(value.IndexOf("secrets/")+ "secrets/".Length).Split('/').First():value; } } | |
| [Display(ResourceType = typeof(Tags), | |
| Description = "Tags, seperate tags with comma and key:value with semicolon.", | |
| Name = "Secret Tags", | |
| ShortName = "KeyVaultSecretTags")] | |
| public Dictionary<string, string> Tags { get; set; } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.ComponentModel.DataAnnotations; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Reflection; | |
| using System.Security.Cryptography.X509Certificates; | |
| using System.Text; | |
| using CommandLine; | |
| using Newtonsoft.Json.Linq; | |
| using SInnovations.DnsMadeEasy; | |
| using SInnovations.LetsEncrypt.DnsMadeEasyManager; | |
| using SInnovations.LetsEncrypt.Services.Defaults; | |
| using SInnovations.LetsEncrypt.Stores.Defaults; | |
| using SInnovations.VSTeamServices.TasksBuilder.Attributes; | |
| using SInnovations.VSTeamServices.TasksBuilder.AzureResourceManager.ResourceTypes; | |
| using SInnovations.VSTeamServices.TasksBuilder.ConsoleUtils; | |
| using SInnovations.VSTeamServices.TasksBuilder.KeyVault.ResourceTypes; | |
| using SInnovations.VSTeamServices.TasksBuilder.ResourceTypes; | |
| [assembly: AssemblyInformationalVersion("1.0.10")] | |
| namespace SInnovations.LetsEncrypt.VSTSTask | |
| { | |
| public class DnsMadeEasyOptions | |
| { | |
| private DnsMadeEasyResource DnsMadeEasyClientCredetials { get; set; } | |
| public DnsMadeEasyOptions(DnsMadeEasyResource resouce) | |
| { | |
| DnsMadeEasyClientCredetials = resouce; | |
| } | |
| [Option("DnsMadeEasyUsername", HelpText = "PrincipalKey")] | |
| public string ApiKey | |
| { | |
| get { return DnsMadeEasyClientCredetials.ApiKey; } | |
| set { DnsMadeEasyClientCredetials.ApiKey = value; } | |
| } | |
| [Option("DnsMadeEasyPassword", HelpText = "PrincipalId")] | |
| public string ApiSecret | |
| { | |
| get { return DnsMadeEasyClientCredetials.ApiSecret; } | |
| set { DnsMadeEasyClientCredetials.ApiSecret = value; } | |
| } | |
| } | |
| [ResourceType(TaskInputType = "connectedService:Generic")] | |
| public class DnsMadeEasyResource : DnsMadeEasyClientCredetials, IConsoleReader | |
| { | |
| public void OnConsoleParsing(Parser parser, string[] args, object options, PropertyInfo info) | |
| { | |
| var dnsMadeEasyOptions = new DnsMadeEasyOptions(this); | |
| var disp = info.GetCustomAttribute<DisplayAttribute>().ShortName; | |
| parser.ParseArguments(args, dnsMadeEasyOptions); | |
| } | |
| } | |
| [EntryPoint("Creating Certificate $(hostingPlanName)")] | |
| [Group(DisplayName = "Add keys to KeyVault", Name = "KeyVault", isExpanded = false)] | |
| public class ProgramOptions | |
| { | |
| public ProgramOptions() | |
| { | |
| KeyVault = new KeyVaultOutput<ProgramOptions>(k => k.ConnectedServiceName); | |
| } | |
| [Display(Name ="DnsMadeEasy", ShortName = "DnsMadeEasy", Description ="The DnsMadeEasy connection", ResourceType = typeof(DnsMadeEasyResource))] | |
| public DnsMadeEasyClientCredetials DnsMadeEasyConnection { get; set; } | |
| [Option("DomainDNSIdentifier")] | |
| public string DomainDNSIdentifier { get; set; } | |
| [Option("LetsEncryptBaseUri", HelpText = "The BaseUri used for lets encrypt api.", DefaultValue = "https://acme-staging.api.letsencrypt.org/")] | |
| public string LetsEncryptBaseUri { get; set; } | |
| [Option("SignerEmail", HelpText = "The signer email")] | |
| public string SignerEmail { get; set; } | |
| [Option("PfxPassword", HelpText = "The password for the generated pfx file")] | |
| public string PfxPassword { get; set; } | |
| [Display(ResourceType = typeof(GlobPath))] | |
| [Option("OutputPath", HelpText = "Filesystem output to store the pfx file")] | |
| public string OutputPath { get; set; } | |
| [Display(Name = "Service Principal", GroupName = "KeyVault", ShortName = "ConnectedServiceName", ResourceType = typeof(ServiceEndpoint), Description = "Azure Service Principal used to communicate with keyvault")] | |
| public ServiceEndpoint ConnectedServiceName { get; set; } | |
| [ConnectedServiceRelation(typeof(ConnectedServiceRelation))] | |
| [Display(GroupName = "KeyVault", ResourceType = typeof(KeyVaultOutput<ProgramOptions>))] | |
| public KeyVaultOutput<ProgramOptions> KeyVault { get; set; } | |
| public class ConnectedServiceRelation : PropertyRelation<ProgramOptions, ServiceEndpoint> | |
| { | |
| public ConnectedServiceRelation() | |
| : base(@class => @class.ConnectedServiceName) | |
| { | |
| } | |
| } | |
| } | |
| class Program | |
| { | |
| static void Main(string[] args) | |
| { | |
| #if LOCAL | |
| var creds = JObject.Parse(File.ReadAllText(@"C:\dev\dnscred")); | |
| args = args.LoadFrom<ProgramOptions>(@"c:\dev\credsSinno.txt") | |
| .LoadFrom<ProgramOptions>(null, | |
| o => o.SignerEmail ?? "pks@s-innovations.net", | |
| o => o.PfxPassword ?? "1234562", | |
| o => o.OutputPath ?? @"c:\dev\test.pfx", | |
| o => o.DomainDNSIdentifier ?? "earthml.xyz" | |
| ).Concat(new[] { | |
| "--DnsMadeEasyUsername",creds["apiKey"].ToString(), | |
| "--DnsMadeEasyPassword",creds["apiSecret"].ToString(), | |
| }).ToArray(); | |
| //args = new[] { "--build" }; | |
| #endif | |
| var options = ConsoleHelper.ParseAndHandleArguments<ProgramOptions>("Create LetsEncrypt Certificate", args); | |
| LetsEncryptService letsEncrypt = CreateInMemoryService( | |
| letsEncryptOptions: new LetsEncryptServiceOptions | |
| { | |
| BaseUri = options.LetsEncryptBaseUri | |
| }, | |
| dnsCredentials: options.DnsMadeEasyConnection); | |
| var cert = letsEncrypt.GenerateCertificateAsync( | |
| new GenerateCertificateRequestOptions | |
| { | |
| DnsIdentifier = options.DomainDNSIdentifier, | |
| SignerEmail = options.SignerEmail, | |
| PfxPassword = options.PfxPassword | |
| }).GetAwaiter().GetResult(); | |
| X509Certificate2 X509Certificate = FixFriendlyName(options, ref cert); | |
| if (!string.IsNullOrEmpty(options.OutputPath)) | |
| { | |
| File.WriteAllBytes(options.OutputPath, cert); | |
| } | |
| if (options.KeyVault.IsPresent()) | |
| { | |
| var certBase64 = Convert.ToBase64String(cert); | |
| var value = Convert.ToBase64String(Encoding.UTF8.GetBytes(new JObject( | |
| new JProperty("data", certBase64), | |
| new JProperty("dataType", "pfx"), | |
| new JProperty("password", options.PfxPassword) | |
| ).ToString())); | |
| var tags = options.KeyVault.Tags; | |
| tags.Add("letsEncryptDnsIdentifier", options.DomainDNSIdentifier); | |
| tags.Add("letsEncryptChallenge", "dns"); | |
| tags.Add("thumbprint", X509Certificate.Thumbprint); | |
| tags.Add("friendlyName", X509Certificate.FriendlyName); | |
| options.KeyVault.SetSecret(value, tags, contentType: "application/x-pkcs12"); | |
| } | |
| } | |
| private static X509Certificate2 FixFriendlyName(ProgramOptions options, ref byte[] cert) | |
| { | |
| var X509Certificate = new X509Certificate2(cert, options.PfxPassword, X509KeyStorageFlags.Exportable); | |
| X509Certificate.FriendlyName = "CN=" + options.DomainDNSIdentifier; | |
| cert = X509Certificate.Export(X509ContentType.Pkcs12, options.PfxPassword); | |
| return X509Certificate; | |
| } | |
| private static LetsEncryptService CreateInMemoryService(LetsEncryptServiceOptions letsEncryptOptions, DnsMadeEasyClientCredetials dnsCredentials) | |
| { | |
| var signerService = new DefaultRS256SignerService(new InMemoryRS256SignerStore()); | |
| var acmeService = new DefaultAcmeClientService(signerService, new InMemoryAcmeRegistrationStore()); | |
| var challengeService = new DefaultDnsChallengeService(new LetsEncryptDnsMadeEasyManager(dnsCredentials)); | |
| var letsEncrypt = new LetsEncryptService( | |
| letsEncryptOptions, | |
| acmeService, | |
| challengeService); | |
| return letsEncrypt; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "id": "53df2e5d-57a6-434c-92ef-431a4257bf4e", | |
| "name": "LetsEncryptCertificateTask", | |
| "friendlyName": "LetsEncrypt Certificate Generation", | |
| "description": "Creates a LetsEncrypt Certificate using DNS Challenge. Currently only dnsmadeeasy is supported", | |
| "category": "Utility", | |
| "visibility": [ | |
| "Build", | |
| "Release" | |
| ], | |
| "demands": [ | |
| "azureps" | |
| ], | |
| "author": "S-Innovations v/Poul Kjeldager Sørensen", | |
| "version": { | |
| "major": 1, | |
| "minor": 0, | |
| "patch": 10 | |
| }, | |
| "minimumAgentVersion": "1.92.0", | |
| "groups": [ | |
| { | |
| "name": "KeyVault", | |
| "displayName": "Add keys to KeyVault", | |
| "isExpanded": false | |
| } | |
| ], | |
| "inputs": [ | |
| { | |
| "name": "DnsMadeEasy", | |
| "type": "connectedService:Generic", | |
| "label": "DnsMadeEasy", | |
| "defaultValue": "", | |
| "required": false, | |
| "helpMarkDown": "The DnsMadeEasy connection", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "DomainDNSIdentifier", | |
| "type": "string", | |
| "label": "DomainDNSIdentifier", | |
| "defaultValue": "", | |
| "required": false, | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "LetsEncryptBaseUri", | |
| "type": "string", | |
| "label": "LetsEncryptBaseUri", | |
| "defaultValue": "https://acme-staging.api.letsencrypt.org/", | |
| "required": false, | |
| "helpMarkDown": "The BaseUri used for lets encrypt api.", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "SignerEmail", | |
| "type": "string", | |
| "label": "SignerEmail", | |
| "defaultValue": "", | |
| "required": false, | |
| "helpMarkDown": "The signer email", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "PfxPassword", | |
| "type": "string", | |
| "label": "PfxPassword", | |
| "defaultValue": "", | |
| "required": false, | |
| "helpMarkDown": "The password for the generated pfx file", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "OutputPath", | |
| "type": "filePath", | |
| "label": "OutputPath", | |
| "defaultValue": "", | |
| "required": false, | |
| "helpMarkDown": "Filesystem output to store the pfx file", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "ConnectedServiceName", | |
| "type": "connectedService:AzureRM", | |
| "label": "Service Principal", | |
| "defaultValue": "", | |
| "required": false, | |
| "groupName": "KeyVault", | |
| "helpMarkDown": "Azure Service Principal used to communicate with keyvault", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "KeyVaultName", | |
| "type": "pickList", | |
| "label": "KeyVault Name", | |
| "defaultValue": "", | |
| "required": true, | |
| "groupName": "KeyVault", | |
| "helpMarkDown": "The keyvault namespace", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "SecretName", | |
| "type": "pickList", | |
| "label": "Secret Name", | |
| "defaultValue": "", | |
| "required": true, | |
| "groupName": "KeyVault", | |
| "helpMarkDown": "The keyvault secret name to store value in", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| }, | |
| { | |
| "name": "KeyVaultSecretTags", | |
| "type": "string", | |
| "label": "Secret Tags", | |
| "defaultValue": "", | |
| "required": false, | |
| "groupName": "KeyVault", | |
| "helpMarkDown": "Tags, seperate tags with comma and key:value with semicolon.", | |
| "properties": { | |
| "EditableOptions": "True" | |
| } | |
| } | |
| ], | |
| "instanceNameFormat": "Creating Certificate $(hostingPlanName)", | |
| "execution": { | |
| "PowerShell": { | |
| "target": "$(currentDirectory)\\OauthBroker.ps1", | |
| "workingDirectory": "$(currentDirectory)", | |
| "argumentFormat": "" | |
| } | |
| }, | |
| "sourceDefinitions": [ | |
| { | |
| "endpoint": "https://management.azure.com/subscriptions/$(authKey.SubscriptionId)/providers/Microsoft.KeyVault/vaults?api-version=2015-06-01", | |
| "target": "KeyVaultName", | |
| "authKey": "$(ConnectedServiceName)", | |
| "selector": "jsonpath:$.value[*].name", | |
| "keySelector": "jsonpath:$.value[*].id" | |
| }, | |
| { | |
| "endpoint": "https://management.azure.com/$(KeyVaultName)/secrets?api-version=2015-06-01", | |
| "target": "SecretName", | |
| "authKey": "$(ConnectedServiceName)", | |
| "selector": "jsonpath:$.value[*].name", | |
| "keySelector": "jsonpath:$.value[*].id" | |
| } | |
| ] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment