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

# 概要

.NET 8のプロセス間通信にgRPCのパイプ通信を使う際に、そのパイプにユーザーアクセス許可（ACL）を与える方法についてです。

[以前の記事](https://qiita.com/spc_ksudoh/items/b998a0decc772b0e4785)で、.NET 8のプロセス間通信にはgRPCのパイプ通信が良さそうだという話と、その実装方法を書きました。その時にはパイプのACLを与える方法として別途Win32APIを使う方法を書いたのですが、もっと簡単にC#だけで実現できたので、その方法をこの記事に書きます。

# 最初に結論まとめ

次のように、パイプの待ち受けを行うIWebHostBuilderに対して、IWebHostBuilder.UseNamedPipesメソッドで使うパイプの設定を与えます。そこで、ACLの設定も行います。

```csharp
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616143/a448b647-d03e-4c28-8d56-8f4d3cac5bef.png)

この利用シーンについて詳しくは、[以前の記事「.NET 8のプロセス間通信には、gRPCのパイプ通信が優秀なようです」](https://qiita.com/spc_ksudoh/items/760a41a73c825eb70e70)に書きました。

Windowsサービス(LocalSystem権限)からパイプを作ると、デフォルトではユーザー権限の書き込み不可になります。しかし上の図のような用途では、ユーザーセッションのアプリはユーザー権限で起動しているため、書き込みを許可する必要があります。

Windowsサービス側（gRPCサーバー側）でのパイプの作り方は、GenericHostを使う方法を[以前の記事](https://qiita.com/spc_ksudoh/items/b998a0decc772b0e4785)で書きました。

ポイントとなるコードだけ引用すると、次の通りです。

```csharp
IHost host = Host.CreateDefaultBuilder(args)
//～略～
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.ConfigureKestrel(options =>
        {
            options.ListenNamedPipe("gRPCWinServiceSamplePipeName", listenOptions =>
            {
                listenOptions.Protocols = HttpProtocols.Http2;
            });
        });
```

このままではユーザー権限書き込み不可です。これに対して、Kestrelが使用するパイプの設定を与える処理を足すことで、ユーザー権限書き込み可にできます。そのコードも足すと、次のようになります。


```csharp
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を使う方法よりも、今回紹介した方法の方がより良いようです。良いものはどんどん取り込んで使っていきましょう！