概要
.NET 8のプロセス間通信にgRPCのパイプ通信を使う際に、そのパイプにユーザーアクセス許可(ACL)を与える方法についてです。
以前の記事で、.NET 8のプロセス間通信にはgRPCのパイプ通信が良さそうだという話と、その実装方法を書きました。その時にはパイプのACLを与える方法として別途Win32APIを使う方法を書いたのですが、もっと簡単にC#だけで実現できたので、その方法をこの記事に書きます。
最初に結論まとめ
次のように、パイプの待ち受けを行うIWebHostBuilderに対して、IWebHostBuilder.UseNamedPipesメソッドで使うパイプの設定を与えます。そこで、ACLの設定も行います。
IHost host = Host.CreateDefaultBuilder(args)
//~略~
.ConfigureWebHostDefaults(webBuilder =>
{
var ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
webBuilder.UseNamedPipes(option =>
{
option.PipeSecurity = ps;
option.CurrentUserOnly = false;
});
//~略~
説明
次の画像のように、Windowsサービスとユーザーセッションのプロセス間通信の用途で、gRPCのパイプ通信を使うケースの話になります。
この利用シーンについて詳しくは、以前の記事「.NET 8のプロセス間通信には、gRPCのパイプ通信が優秀なようです」に書きました。
Windowsサービス(LocalSystem権限)からパイプを作ると、デフォルトではユーザー権限の書き込み不可になります。しかし上の図のような用途では、ユーザーセッションのアプリはユーザー権限で起動しているため、書き込みを許可する必要があります。
Windowsサービス側(gRPCサーバー側)でのパイプの作り方は、GenericHostを使う方法を以前の記事で書きました。
ポイントとなるコードだけ引用すると、次の通りです。
IHost host = Host.CreateDefaultBuilder(args)
//~略~
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
options.ListenNamedPipe("gRPCWinServiceSamplePipeName", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
});
このままではユーザー権限書き込み不可です。これに対して、Kestrelが使用するパイプの設定を与える処理を足すことで、ユーザー権限書き込み可にできます。そのコードも足すと、次のようになります。
IHost host = Host.CreateDefaultBuilder(args)
//~略~
.ConfigureWebHostDefaults(webBuilder =>
{
var ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
webBuilder.UseNamedPipes(option =>
{
option.PipeSecurity = ps;
option.CurrentUserOnly = false;
});
webBuilder.ConfigureKestrel(options =>
{
options.ListenNamedPipe("gRPCWinServiceSamplePipeName", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
});
//~略~
このコードでは、AuthenticatedUserからの読み書きを許可するPipeSecurityを作成し、それをUseNamedPipesメソッドのパラメータに渡しています。また、パイプを作成したのとは異なるユーザーからのアクセスを許可するため、CurrentUserOnly = false
も設定しています。
これだけで、パイプへのユーザーアクセス許可(ACL)を与えることができます。Win32APIを使ってむりやり実現する方法よりだいぶシンプルですし、こちらの方が正しいやり方に見えます。今後はこの方法を使っていこうと思います。
GitHubのサンプルも更新しておきました。
まとめ
以前の記事で紹介したWin32APIを使う方法よりも、今回紹介した方法の方がより良いようです。良いものはどんどん取り込んで使っていきましょう!