public async Task IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var dnsNames = context.GetInput <string[]>();

            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 前提条件をチェック
            await activity.Dns01Precondition(dnsNames);

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(dnsNames);

            // 複数の Authorizations を処理する
            var challenges = new List <ChallengeResult>();

            foreach (var authorization in orderDetails.Payload.Authorizations)
            {
                // ACME Challenge を実行
                var result = await activity.Dns01Authorization((authorization, context.ParentInstanceId ?? context.InstanceId));

                // Azure DNS で正しくレコードが引けるか確認
                await activity.CheckDnsChallenge(result);

                challenges.Add(result);
            }

            // ACME Answer を実行
            await activity.AnswerChallenges(challenges);

            // Order のステータスが ready になるまで 60 秒待機
            await activity.CheckIsReady(orderDetails);

            await activity.FinalizeOrder((dnsNames, orderDetails));
        }
Exemple #2
0
        public async Task IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var dnsNames = context.GetInput <string[]>();

            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 前提条件をチェック
            await activity.Dns01Precondition(dnsNames);

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(dnsNames);

            // ACME Challenge を実行
            var challengeResults = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

            // DNS で正しくレコードが引けるか確認
            await activity.CheckDnsChallenge(challengeResults);

            // ACME Answer を実行
            await activity.AnswerChallenges(challengeResults);

            // Order のステータスが ready になるまで 60 秒待機
            await activity.CheckIsReady(orderDetails);

            var certificate = await activity.FinalizeOrder((dnsNames, orderDetails));

            // 証明書の更新が完了後に Webhook を送信する
            await activity.SendCompletedEvent((certificate.Name, certificate.ExpiresOn, dnsNames));
        }
Exemple #3
0
        public async Task IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var dnsNames = context.GetInput <string[]>();

            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // Check prerequisites
            await activity.Dns01Precondition(dnsNames);

            // Create a new ACME Order
            var orderDetails = await activity.Order(dnsNames);

            // Run ACME Challenge
            var challengeResults = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

            // Verify that DNS can write records correctly
            await activity.CheckDnsChallenge(challengeResults);

            // Run ACME Answer
            await activity.AnswerChallenges(challengeResults);

            // Wait 60 seconds for order status to ready
            await activity.CheckIsReady(orderDetails);

            var certificate = await activity.FinalizeOrder((dnsNames, orderDetails));

            // Send a webhook after the certificate renewal is complete
            await activity.SendCompletedEvent((certificate.SecretIdentifier.Name, certificate.Attributes.Expires, dnsNames));
        }
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <INullActivity>();

            await activity.Nop();
        }
Exemple #5
0
        public async Task Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedActivity>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetExpiringCertificates(context.CurrentUtcDateTime);

            foreach (var certificate in certificates)
            {
                log.LogInformation($"{certificate.SubjectName} - {certificate.ExpirationDate}");
            }

            // 更新対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            // スロットリング対策として 120 秒以内でジッターを追加する
            var jitter = (uint)context.NewGuid().GetHashCode() % 120;

            await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(jitter), CancellationToken.None);

            // リソースグループ単位で証明書の更新を行う
            var resourceGroups = await activity.GetResourceGroups();

            foreach (var resourceGroup in resourceGroups)
            {
                // App Service を取得
                var sites = await activity.GetSites((resourceGroup.Name, true));

                // サイト単位で証明書の更新を行う
                foreach (var site in sites)
                {
                    // 期限切れが近い証明書がバインドされているか確認
                    var boundCertificates = certificates.Where(x => site.HostNameSslStates.Any(xs => xs.Thumbprint == x.Thumbprint))
                                            .ToArray();

                    // 対象となる証明書が存在しない場合はスキップ
                    if (boundCertificates.Length == 0)
                    {
                        continue;
                    }

                    try
                    {
                        // 証明書の更新処理を開始
                        await context.CallSubOrchestratorAsync(nameof(RenewCertificates) + "_" + nameof(SubOrchestrator), (site, boundCertificates));
                    }
                    catch (Exception ex)
                    {
                        // 失敗した場合はログに詳細を書き出して続きを実行する
                        log.LogError($"Failed sub orchestration with Certificates = {string.Join(",", boundCertificates.Select(x => x.Thumbprint))}");
                        log.LogError(ex.Message);
                    }
                }
            }
        }
Exemple #6
0
        public async Task Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedActivity>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetExpiringCertificates(context.CurrentUtcDateTime);

            // 更新対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            // 証明書の更新を行う
            foreach (var certificate in certificates)
            {
                var dnsNames = certificate.DnsNames;

                log.LogInformation($"{certificate.Id} - {certificate.ExpiresOn}");

                try
                {
                    // 証明書の更新処理を開始
                    await context.CallSubOrchestratorWithRetryAsync(nameof(SharedOrchestrator.IssueCertificate), _retryOptions, dnsNames);
                }
                catch (Exception ex)
                {
                    // 失敗した場合はログに詳細を書き出して続きを実行する
                    log.LogError($"Failed sub orchestration with DNS names = {string.Join(",", dnsNames)}");
                    log.LogError(ex.Message);
                }
            }
        }
Exemple #7
0
        public static async Task <string> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <IAliasActivity>();

            return(await activity.SayHello("buchizo"));
        }
Exemple #8
0
        public async Task Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var certificateName = context.GetInput <string>();

            var activity = context.CreateActivityProxy <ISharedActivity>();

            await activity.RevokeCertificate(certificateName);
        }
Exemple #9
0
        public async Task <IList <GetCertificateResponse> > GetCertificates([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            var certificates = await activity.GetAllCertificates();

            return(certificates.Select(x => new GetCertificateResponse(x)).ToArray());
        }
        public async Task <IList <string> > GetDnsZones([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            var zones = await activity.GetZones();

            return(zones.Select(x => x.Name).ToArray());
        }
Exemple #11
0
        public async Task <IReadOnlyList <string> > Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <ISharedActivity>();

            var zones = await activity.GetZones();

            return(zones);
        }
Exemple #12
0
        public async Task <IList <ResourceGroupInformation> > GetResourceGroupsInformation([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            var resourceGroups = await activity.GetResourceGroups();

            return(resourceGroups.Select(x => new ResourceGroupInformation {
                Name = x.Name
            }).ToArray());
        }
        public async Task Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var request = context.GetInput <AddCertificateRequest>();

            var activity = context.CreateActivityProxy <ISharedActivity>();

            var site = await activity.GetSite((request.ResourceGroupName, request.AppName, request.SlotName));

            if (site == null)
            {
                log.LogError($"{request.AppName} is not found");
                return;
            }

            var hostNameSslStates = site.HostNameSslStates
                                    .Where(x => request.DnsNames.Contains(x.Name))
                                    .ToArray();

            if (hostNameSslStates.Length != request.DnsNames.Length)
            {
                foreach (var dnsName in request.DnsNames.Except(hostNameSslStates.Select(x => x.Name)))
                {
                    log.LogError($"{dnsName} is not found");
                }

                return;
            }

            var asciiDnsNames = request.DnsNames.Select(Punycode.Encode).ToArray();

            try
            {
                // 証明書を発行し Azure にアップロード
                var certificate = await context.CallSubOrchestratorAsync <Certificate>(nameof(SharedOrchestrator.IssueCertificate), (site, asciiDnsNames, request.ForceDns01Challenge ?? false));

                // App Service のホスト名に証明書をセットする
                foreach (var hostNameSslState in hostNameSslStates)
                {
                    hostNameSslState.Thumbprint = certificate.Thumbprint;
                    hostNameSslState.SslState   = request.UseIpBasedSsl ?? false ? SslState.IpBasedEnabled : SslState.SniEnabled;
                    hostNameSslState.ToUpdate   = true;
                }

                await activity.UpdateSiteBinding(site);

                // 証明書の更新が完了後に Webhook を送信する
                await activity.SendCompletedEvent((site, certificate.ExpirationDate, asciiDnsNames));
            }
            finally
            {
                // クリーンアップ処理を実行
                await activity.CleanupVirtualApplication(site);
            }
        }
Exemple #14
0
        public async Task Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var certificateName = context.GetInput <string>();

            var activity = context.CreateActivityProxy <ISharedActivity>();

            // 証明書の更新処理を開始
            var certificatePolicyItem = await activity.GetCertificatePolicy(certificateName);

            await context.CallSubOrchestratorAsync(nameof(SharedOrchestrator.IssueCertificate), certificatePolicyItem);
        }
Exemple #15
0
        public async Task <string> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <IHttpGetActivity>();

            // ブチザッキのタイトルを取る
            var content = await activity.HttpGet("https://blog.azure.moe/");

            var match = Regex.Match(content, @"<title>(.+?)<\/title>");

            return(match.Success ? match.Groups[1].Value : "");
        }
        public async Task IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var dnsNames        = context.GetInput <string[]>();
            var certificateName = dnsNames[0].Replace("*", "wildcard").Replace(".", "-");

            var activity = context.CreateActivityProxy <ISharedActivity>();

            // 前提条件をチェック
            await activity.Dns01Precondition(dnsNames);

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(dnsNames);

            // 既に確認済みの場合は Challenge をスキップする
            if (orderDetails.Payload.Status != "ready")
            {
                // ACME Challenge を実行
                var(challengeResults, propagationSeconds) = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

                // DNS Provider が指定した分だけ遅延させる
                await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(propagationSeconds), CancellationToken.None);

                // DNS で正しくレコードが引けるか確認
                await activity.CheckDnsChallenge(challengeResults);

                // ACME Answer を実行
                await activity.AnswerChallenges(challengeResults);

                // Order のステータスが ready になるまで 60 秒待機
                await activity.CheckIsReady((orderDetails, challengeResults));

                // 作成した DNS レコードを削除
                await activity.CleanupDnsChallenge(challengeResults);
            }

            // Key Vault で CSR を作成し Finalize を実行
            orderDetails = await activity.FinalizeOrder((certificateName, dnsNames, orderDetails));

            // Finalize の時点でステータスが valid の時点はスキップ
            if (orderDetails.Payload.Status != "valid")
            {
                // Finalize 後のステータスが valid になるまで 60 秒待機
                await activity.CheckIsValid(orderDetails);
            }

            // 証明書をダウンロードし Key Vault に保存
            var certificate = await activity.MergeCertificate((certificateName, orderDetails));

            // 証明書の更新が完了後に Webhook を送信する
            await activity.SendCompletedEvent((certificate.Name, certificate.ExpiresOn, dnsNames));
        }
        public async Task <Certificate> IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var(site, dnsNames) = context.GetInput <(Site, string[])>();

            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // ワイルドカード、コンテナ、Linux の場合は DNS-01 を利用する
            var useDns01Auth = dnsNames.Any(x => x.StartsWith("*")) || site.Kind.Contains("container") || site.Kind.Contains("linux");

            // 前提条件をチェック
            if (useDns01Auth)
            {
                await activity.Dns01Precondition(dnsNames);
            }
            else
            {
                await activity.Http01Precondition(site);
            }

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(dnsNames);

            // 複数の Authorizations を処理する
            IList <AcmeChallengeResult> challengeResults;

            // ACME Challenge を実行
            if (useDns01Auth)
            {
                challengeResults = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

                // Azure DNS で正しくレコードが引けるか確認
                await activity.CheckDnsChallenge(challengeResults);
            }
            else
            {
                challengeResults = await activity.Http01Authorization((site, orderDetails.Payload.Authorizations));

                // HTTP で正しくアクセスできるか確認
                await activity.CheckHttpChallenge(challengeResults);
            }

            // ACME Answer を実行
            await activity.AnswerChallenges(challengeResults);

            // Order のステータスが ready になるまで 60 秒待機
            await activity.CheckIsReady(orderDetails);

            // Order の最終処理を実行し PFX を作成
            var(thumbprint, pfxBlob) = await activity.FinalizeOrder((dnsNames, orderDetails));

            return(await activity.UploadCertificate((site, $"{dnsNames[0]}-{thumbprint}", pfxBlob)));
Exemple #18
0
        public async Task <IReadOnlyList <SiteItem> > Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var resourceGroup = context.GetInput <string>();

            var activity = context.CreateActivityProxy <ISharedActivity>();

            var result = new List <SiteItem>();

            var certificates = await activity.GetAllCertificates();

            // App Service を取得
            var sites = await activity.GetSites((resourceGroup, true));

            foreach (var site in sites.ToLookup(x => x.SplitName().appName))
            {
                var siteInformation = new SiteItem {
                    Name = site.Key, Slots = new List <SlotItem>()
                };

                foreach (var slot in site)
                {
                    var(_, slotName) = slot.SplitName();

                    var hostNameSslStates = slot.HostNameSslStates
                                            .Where(x => !x.Name.EndsWith(_environment.AppService) && !x.Name.EndsWith(_environment.TrafficManager));

                    var slotInformation = new SlotItem
                    {
                        Name     = slotName ?? "production",
                        DnsNames = hostNameSslStates.Select(x => new DnsNameItem
                        {
                            Name   = x.Name,
                            Issuer = certificates.FirstOrDefault(xs => xs.Thumbprint == x.Thumbprint)?.Issuer ?? "None"
                        }).ToArray()
                    };

                    if (slotInformation.DnsNames.Count != 0)
                    {
                        siteInformation.Slots.Add(slotInformation);
                    }
                }

                if (siteInformation.Slots.Count != 0)
                {
                    result.Add(siteInformation);
                }
            }

            return(result);
        }
        public async Task RenewCertificates([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetCertificates(context.CurrentUtcDateTime);

            foreach (var certificate in certificates)
            {
                log.LogInformation($"{certificate.SubjectName} - {certificate.ExpirationDate}");
            }

            // 更新対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            // App Service を取得
            var sites = await activity.GetSites();

            // サイト単位で証明書の更新を行う
            foreach (var site in sites)
            {
                // 期限切れが近い証明書がバインドされているか確認
                var boundCertificates = certificates.Where(x => site.HostNameSslStates.Any(xs => xs.Thumbprint == x.Thumbprint))
                                        .ToArray();

                // 対象となる証明書が存在しない場合はスキップ
                if (boundCertificates.Length == 0)
                {
                    continue;
                }

                try
                {
                    // 証明書の更新処理を開始
                    await context.CallSubOrchestratorAsync(nameof(RenewSiteCertificates), (site, boundCertificates));
                }
                catch (Exception ex)
                {
                    // 失敗した場合はログに詳細を書き出して続きを実行する
                    log.LogError($"Failed sub orchestration with Certificates = {string.Join(",", boundCertificates.Select(x => x.Thumbprint))}");
                    log.LogError(ex.Message);
                }
            }
        }
Exemple #20
0
        public async Task <List <string> > RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var outputs = new List <string>();

            var activity = context.CreateActivityProxy <IHelloActivity>();

            // Replace "hello" with the name of your Durable Activity Function.
            outputs.Add(await activity.SayHello("Tokyo"));
            outputs.Add(await activity.SayHello("Seattle"));
            outputs.Add(await activity.SayHello("London"));

            // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
            return(outputs);
        }
Exemple #21
0
        public async Task <IReadOnlyList <ResourceGroupItem> > Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <ISharedActivity>();

            try
            {
                var resourceGroups = await activity.GetResourceGroups();

                return(resourceGroups.Select(x => new ResourceGroupItem {
                    Name = x.Name
                }).ToArray());
            }
            catch
            {
                return(Array.Empty <ResourceGroupItem>());
            }
        }
        public async Task RenewCertificates([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetCertificates(context.CurrentUtcDateTime);

            foreach (var certificate in certificates)
            {
                log.LogInformation($"{certificate.SubjectName} - {certificate.ExpirationDate}");
            }

            // 更新対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            // App Service を取得
            var sites = await activity.GetSites();

            var tasks = new List <Task>();

            // サイト単位で証明書の更新を行う
            foreach (var site in sites)
            {
                // 期限切れが近い証明書がバインドされているか確認
                var boundCertificates = certificates.Where(x => site.HostNameSslStates.Any(xs => xs.Thumbprint == x.Thumbprint))
                                        .ToArray();

                // 対象となる証明書が存在しない場合はスキップ
                if (boundCertificates.Length == 0)
                {
                    continue;
                }

                // 証明書の更新処理を開始
                tasks.Add(context.CallSubOrchestratorAsync(nameof(RenewSiteCertificates), (site, boundCertificates)));
            }

            // サブオーケストレーターの完了を待つ
            await Task.WhenAll(tasks);
        }
        public async Task <List <string> > RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <IHelloActivity>();

            var input = new[] { "Tokyo", "Seattle", "London" };

            var tasks = new Task <string> [input.Length];

            for (int i = 0; i < input.Length; i++)
            {
                tasks[i] = activity.SayHello(input[i]);
            }

            await Task.WhenAll(tasks);

            return(tasks.Select(x => x.Result).ToList());
        }
        public async Task PurgeCertificates([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetExpiringCertificates(context.CurrentUtcDateTime);

            foreach (var certificate in certificates)
            {
                log.LogInformation($"{certificate.SubjectName} - {certificate.ExpirationDate}");
            }

            // 対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            var resourceGroups = await activity.GetResourceGroups();

            foreach (var resourceGroup in resourceGroups)
            {
                // App Service を取得
                var sites = await activity.GetSites((resourceGroup.Name, false));

                // App Service にバインド済み証明書のサムプリントを取得
                var boundCertificates = sites.SelectMany(x => x.HostNameSslStates.Select(xs => xs.Thumbprint))
                                        .ToArray();

                var tasks = new List <Task>();

                // バインドされていない証明書を削除
                foreach (var certificate in certificates.Where(x => !boundCertificates.Contains(x.Thumbprint)))
                {
                    tasks.Add(activity.DeleteCertificate(certificate));
                }

                // アクティビティの完了を待つ
                await Task.WhenAll(tasks);
            }
        }
Exemple #25
0
        public async Task Orchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedActivity>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetExpiringCertificates(context.CurrentUtcDateTime);

            // 更新対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            // スロットリング対策として 120 秒以内でジッターを追加する
            var jitter = (uint)context.NewGuid().GetHashCode() % 120;

            await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(jitter), CancellationToken.None);

            // 証明書の更新を行う
            foreach (var certificate in certificates)
            {
                log.LogInformation($"{certificate.Id} - {certificate.ExpiresOn}");

                try
                {
                    // 証明書の更新処理を開始
                    var certificatePolicyItem = await activity.GetCertificatePolicy(certificate.Name);

                    await context.CallSubOrchestratorWithRetryAsync(nameof(SharedOrchestrator.IssueCertificate), _retryOptions, certificatePolicyItem);
                }
                catch (Exception ex)
                {
                    // 失敗した場合はログに詳細を書き出して続きを実行する
                    log.LogError($"Failed sub orchestration with DNS names = {string.Join(",", certificate.DnsNames)}");
                    log.LogError(ex.Message);
                }
            }
        }
        public async Task RenewCertificates([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 期限切れまで 30 日以内の証明書を取得する
            var certificates = await activity.GetCertificates(context.CurrentUtcDateTime);

            // 更新対象となる証明書がない場合は終わる
            if (certificates.Count == 0)
            {
                log.LogInformation("Certificates are not found");

                return;
            }

            // 証明書の更新を行う
            foreach (var certificate in certificates)
            {
                log.LogInformation($"{certificate.Id} - {certificate.Attributes.Expires}");

                // 証明書の更新処理を開始
                await context.CallSubOrchestratorAsync(nameof(SharedFunctions.IssueCertificate), certificate.Policy.X509CertificateProperties.SubjectAlternativeNames.DnsNames);
            }
        }
        public async Task IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var dnsNames = context.GetInput <string[]>();

            var activity = context.CreateActivityProxy <ISharedFunctions>();

            // 前提条件をチェック
            await activity.Dns01Precondition(dnsNames);

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(dnsNames);

            // ACME Challenge を実行
            var challengeResults = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

            // DNS Provider が指定した分だけ遅延させる
            await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(_dnsProvider.PropagationSeconds), CancellationToken.None);

            // DNS で正しくレコードが引けるか確認
            await activity.CheckDnsChallenge(challengeResults);

            // ACME Answer を実行
            await activity.AnswerChallenges(challengeResults);

            // Order のステータスが ready になるまで 60 秒待機
            await activity.CheckIsReady((orderDetails, challengeResults));

            // 証明書を作成し Key Vault に保存
            var certificate = await activity.FinalizeOrder((dnsNames, orderDetails));

            // 作成した DNS レコードを削除
            await activity.CleanupDnsChallenge(challengeResults);

            // 証明書の更新が完了後に Webhook を送信する
            await activity.SendCompletedEvent((certificate.Name, certificate.ExpiresOn, dnsNames));
        }
        public async Task AddCertificate([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            var request = context.GetInput <AddCertificateRequest>();

            var activity = context.CreateActivityProxy <ISharedFunctions>();

            var site = await activity.GetSite((request.ResourceGroupName, request.AppName, request.SlotName));

            if (site == null)
            {
                log.LogError($"{request.AppName} is not found");
                return;
            }

            var hostNameSslStates = site.HostNameSslStates
                                    .Where(x => request.Domains.Contains(x.Name))
                                    .ToArray();

            if (hostNameSslStates.Length != request.Domains.Length)
            {
                foreach (var hostName in request.Domains.Except(hostNameSslStates.Select(x => x.Name)))
                {
                    log.LogError($"{hostName} is not found");
                }
                return;
            }

            // ワイルドカード、コンテナ、Linux の場合は DNS-01 を利用する
            var useDns01Auth = request.Domains.Any(x => x.StartsWith("*")) || site.Kind.Contains("container") || site.Kind.Contains("linux");

            // 前提条件をチェック
            if (useDns01Auth)
            {
                await activity.Dns01Precondition(request.Domains);
            }
            else
            {
                await activity.Http01Precondition(site);
            }

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(request.Domains);

            IList <AcmeChallengeResult> challengeResults;

            // ACME Challenge を実行
            if (useDns01Auth)
            {
                // DNS-01 を使う
                challengeResults = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

                // Azure DNS で正しくレコードが引けるか確認
                await activity.CheckDnsChallenge(challengeResults);
            }
            else
            {
                // HTTP-01 を使う
                challengeResults = await activity.Http01Authorization((site, orderDetails.Payload.Authorizations));

                // HTTP で正しくアクセスできるか確認
                await activity.CheckHttpChallenge(challengeResults);
            }

            // ACME Answer を実行
            await activity.AnswerChallenges(challengeResults);

            // Order のステータスが ready になるまで 60 秒待機
            await activity.CheckIsReady(orderDetails);

            // Order の最終処理を実行し PFX を作成
            var(thumbprint, pfxBlob) = await activity.FinalizeOrder((request.Domains, orderDetails));

            await activity.UpdateCertificate((site, $"{request.Domains[0]}-{thumbprint}", pfxBlob));

            foreach (var hostNameSslState in hostNameSslStates)
            {
                hostNameSslState.Thumbprint = thumbprint;
                hostNameSslState.SslState   = request.UseIpBasedSsl ?? false ? SslState.IpBasedEnabled : SslState.SniEnabled;
                hostNameSslState.ToUpdate   = true;
            }

            await activity.UpdateSiteBinding(site);

            // クリーンアップ処理を実行
            await activity.CleanupVirtualApplication(site);
        }
Exemple #29
0
        public Task <IList <CertificateItem> > GetCertificates([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var activity = context.CreateActivityProxy <ISharedFunctions>();

            return(activity.GetAllCertificates());
        }
        public async Task <Certificate> IssueCertificate([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var(site, dnsNames, forceDns01Challenge) = context.GetInput <(Site, string[], bool)>();

            var activity = context.CreateActivityProxy <ISharedActivity>();

            // ワイルドカード、コンテナ、Linux の場合は DNS-01 を利用する
            var useDns01Auth = forceDns01Challenge || dnsNames.Any(x => x.StartsWith("*")) || site.Kind.Contains("container") || site.Kind.Contains("linux");

            // 前提条件をチェック
            if (useDns01Auth)
            {
                await activity.Dns01Precondition(dnsNames);
            }
            else
            {
                await activity.Http01Precondition(site);
            }

            // 新しく ACME Order を作成する
            var orderDetails = await activity.Order(dnsNames);

            // 既に確認済みの場合は Challenge をスキップする
            if (orderDetails.Payload.Status != "ready")
            {
                // 複数の Authorizations を処理する
                IReadOnlyList <AcmeChallengeResult> challengeResults;

                // ACME Challenge を実行
                if (useDns01Auth)
                {
                    challengeResults = await activity.Dns01Authorization(orderDetails.Payload.Authorizations);

                    // DNS レコードの変更が伝搬するまで 10 秒遅延させる
                    await context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(10), CancellationToken.None);

                    // Azure DNS で正しくレコードが引けるか確認
                    await activity.CheckDnsChallenge(challengeResults);
                }
                else
                {
                    challengeResults = await activity.Http01Authorization((site, orderDetails.Payload.Authorizations));

                    // HTTP で正しくアクセスできるか確認
                    await activity.CheckHttpChallenge(challengeResults);
                }

                // ACME Answer を実行
                await activity.AnswerChallenges(challengeResults);

                // Order のステータスが ready になるまで 60 秒待機
                await activity.CheckIsReady((orderDetails, challengeResults));

                if (useDns01Auth)
                {
                    // 作成した DNS レコードを削除
                    await activity.CleanupDnsChallenge(challengeResults);
                }
            }

            // CSR を作成し Finalize を実行
            var(finalize, rsaParameters) = await activity.FinalizeOrder((dnsNames, orderDetails));

            // Finalize の時点でステータスが valid の時点はスキップ
            if (finalize.Payload.Status != "valid")
            {
                // Finalize 後のステータスが valid になるまで 60 秒待機
                finalize = await activity.CheckIsValid(finalize);
            }

            // 証明書をダウンロードし App Service へアップロード
            var certificate = await activity.UploadCertificate((site, dnsNames[0], forceDns01Challenge, finalize, rsaParameters));