📖

.NET 8のプロセス間通信にgRPCのパイプ通信を使う場合に、パイプへのユーザーアクセス許可(ACL)を与える方法

に公開

概要

.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のパイプ通信を使うケースの話になります。

image.png

この利用シーンについて詳しくは、以前の記事「.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のサンプルも更新しておきました。

https://github.com/suusanex/sample_winservice_pipe_duplex_wcf_and_grpc/commit/560acb8828cc798652aa521cb5df43b1be84a54d

まとめ

以前の記事で紹介したWin32APIを使う方法よりも、今回紹介した方法の方がより良いようです。良いものはどんどん取り込んで使っていきましょう!

Discussion