tl;dr
- Azure Container Apps は現時点ではカスタムドメインの設定に証明書が必須
- Cloudflare オリジン証明書というオリジンと Cloudflare の間の通信で使用する証明書を発行できる仕組みがある
- オリジン証明書を Container Apps に登録して、Cloudflare をフロントに置くことでドメインの証明書管理を Cloudflare にまかせる
Azure Container Apps にカスタムドメインを登録時、証明書が必要
Azure の Azure Container Apps (Preview) ではカスタムドメインを設定して、外部からのアクセスを受け付けるようにできます。
詳しくはしばやんさんのブログを参照していただきたいのですが、現時点ではカスタムドメインの設定時に HTTPS 向けの証明書のアップロードが必要です。
つまりドメインに対する証明書を何らかの方法で作成/用意する必要があり、しばやんさんの記事では Acmebot を用意して Let’s Encrypt で証明書を作成、更新していく方法が紹介されています。
今回触っていた環境は個人で適当に立てているサイトなので動かすものを増やしたくないなと思っていたのですが、元々 Cloudflare を前に置いていたので Cloudflare が自動で発行、更新する証明書を使うようにすれば丸投げできそうな気がしてきました。が、しかし Container Apps は HTTPS 必須なのかカスタムドメインの登録時にどうにしても証明書が必要です。
オリジン証明書を作る
Container Apps のカスタムドメイン設定に必要な証明書をどうするか、というところで Cloudflare にはオリジン証明書を作るという機能があるのでこれを使います。
オリジン証明書は Cloudflare とオリジン、この場合だと Container Apps の通信でのみ使用する目的の証明書で今回の用途にはぴったりです。デフォルトでは15年という長い期限で作成され、個人のサイトならそっちが先に朽ちるか Managed Certificates がくると思うのでよさそうです(今回はある程度放っておきたいのが目的)。
オリジン証明書は Cloudflare の管理画面の SSL/TLS → オリジンサーバー → オリジン証明書 で発行できます。発行すると証明書と秘密鍵がでてきますので PEM 形式で手元に保存します。特に秘密鍵はページを閉じると確認できなくなるのでしっかり保存しておきます。
オリジン証明書を pfx 形式 (PKCS#12) に変換する
次に発行した証明書と秘密鍵を Container Apps に登録します。ところが登録時にパスワードを求められたりしてそのままでは取り込めないので OpenSSL で pfx 形式 (PKCS#12) に変換します。
cat certificate.pem private.pem > origin.pem
openssl pkcs12 -export -in origin.pem -out origin.pfx
オリジン証明書を Container Apps に登録する
pfx 形式の証明書を作成したら Container Apps に登録します。Azure Portal の コンテナー アプリ環境 → (コンテナーアプリ) → 証明書 で “証明書の追加” で追加を行えます。
注意点として現時点では “証明書名” に大文字アルファベットを含む文字列を指定すると “この証明書名は既に使用されています。別の証明書名をお試しください。” という謎のエラーが出るので小文字で入力してください。
カスタムドメインの設定
証明書が作成出来たら後はカスタムドメインの設定を行い、証明書として先ほど登録したものを選択すれば完了です。
まとめ
Container Apps が Managed Certificates にはやく対応してほしいですね。
Source Generator を触っていて気づいたのですが Visual Studio 2019 version 16.9 の段階でいくつか API が追加されていました。
追加されたものは次のようなあるといいよねといった API が増えている感じです。
GeneratorInitializationContext.RegisterForPostInitialization
メソッド
ISyntaxContextReceiver
インターフェース
Visual Studio 2019 であれば 16.9 以降、NuGet パッケージであれば Microsoft.CodeAnalysis.CSharp 3.9.0 を参照すれば使用できます。
GeneratorInitializationContext.RegisterForPostInitialization
メソッド
GeneratorInitializationContext.RegisterForPostInitialization
メソッドは Source Generator が読み込まれて初期化された後に呼び出されるコールバックを登録できます。
これは Source Generator で必要となる属性用のコードを追加したいパターンに役立ちます。例えば次のようなコードがサンプルにあります。
[Generator]
public class AutoNotifyGenerator : ISourceGenerator
{
private const string attributeText = @"
using System;
namespace AutoNotify
{
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
[System.Diagnostics.Conditional(""AutoNotifyGenerator_DEBUG"")]
sealed class AutoNotifyAttribute : Attribute
{
public AutoNotifyAttribute()
{
}
public string PropertyName { get; set; }
}
}
";
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForPostInitialization((i) => i.AddSource("AutoNotifyAttribute", attributeText));
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}
...
}
今までソースで指定したい属性をどうするかという問題があった(一度生成させてその属性を使うのか、手で追加するのかとか)のがこれで解決できそうです。
ISyntaxContextReceiver
インターフェース
コンパイラーがソースコードを処理する際に Source Generator を呼び出す ISyntaxReceiver
インターフェースがあります。
例えばクラス定義に対するコードジェネレートを行いたいときには自分でシンタックスツリーを走査して集めるのではなく、ツリーの走査はコンパイラーに任せて、 ClassDeclarationSyntax を集めておいて最後に処理するといった使い方です。
ISyntaxReceiver
インターフェースは OnVisitSyntaxNode
メソッドのみをもち引数として SyntaxNode
を受ける形でしたが、 ISyntaxContextReceiver
では GeneratorSyntaxContext
を受け取る形になります。
このコンテキストオブジェクトにはセマンティックモデルが含まれているので、Receiver で必要なものをかき集めるときに、コードの解析された情報にアクセスできるようになります。
例えば属性はセマンティックモデルを通してシンボルを取得して、GetAttributes で取得できるので、Source Generator 用の属性がついていることを Receiver が集める段階で確認できます。こちらもサンプルコードが例としてわかりやすいです。
注意
16.9 で追加された API なので古い Visual Studio や .NET SDK では動かない可能性があります(試していない)。
Rider 2021.1.2 ではビルドは問題ないのですが Rider 側が対応していないようでエディター上では正しく動作しなかったりしたので、2021年5月頭時点では使うにはちょっと早いかもしれません。