Komunikasi antarproses dengan gRPC

Proses yang berjalan pada komputer yang sama dapat dirancang untuk berkomunikasi satu sama lain. Sistem operasi menyediakan teknologi untuk memungkinkan komunikasi antarproses (IPC) yang cepat dan efisien. Contoh populer teknologi IPC adalah soket domain Unix dan pipa Bernama.

.NET menyediakan dukungan untuk komunikasi antarproses menggunakan gRPC.

Dukungan bawaan untuk Pipa bernama di ASP.NET Core memerlukan .NET 8 atau yang lebih baru.

Mulai

Panggilan IPC dikirim dari klien ke server. Untuk berkomunikasi antara aplikasi di komputer dengan gRPC, setidaknya satu aplikasi harus menghosting server gRPC ASP.NET Core.

Server gRPC ASP.NET Core biasanya dibuat dari templat gRPC. File proyek yang dibuat oleh templat menggunakan Microsoft.NET.SDK.Web sebagai SDK:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.47.0" />
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
  </ItemGroup>

</Project>

Nilai Microsoft.NET.SDK.Web SDK secara otomatis menambahkan referensi ke kerangka kerja ASP.NET Core. Referensi memungkinkan aplikasi untuk menggunakan jenis ASP.NET Core yang diperlukan untuk menghosting server.

Anda juga dapat menambahkan server ke proyek non-ASP.NET Core yang ada, seperti Windows Services, aplikasi WPF, atau aplikasi WinForms. Lihat Host gRPC di proyek non-ASP.NET Core untuk informasi selengkapnya.

Transportasi komunikasi antar proses (IPC)

Panggilan gRPC antara klien dan server pada komputer yang berbeda biasanya dikirim melalui soket TCP. TCP adalah pilihan yang baik untuk berkomunikasi melalui jaringan atau Internet. Namun, transportasi IPC menawarkan keuntungan saat berkomunikasi antara proses pada komputer yang sama:

  • Lebih sedikit overhead dan kecepatan transfer yang lebih cepat.
  • Integrasi dengan fitur keamanan OS.
  • Tidak menggunakan port TCP, yang merupakan sumber daya terbatas.

.NET mendukung beberapa transportasi IPC:

Bergantung pada OS, aplikasi lintas platform dapat memilih transportasi IPC yang berbeda. Aplikasi dapat memeriksa OS saat startup dan memilih transportasi yang diinginkan untuk platform tersebut:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    if (OperatingSystem.IsWindows())
    {
        serverOptions.ListenNamedPipe("MyPipeName");
    }
    else
    {
        var socketPath = Path.Combine(Path.GetTempPath(), "socket.tmp");
        serverOptions.ListenUnixSocket(socketPath);
    }

    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http2;
    });
});

Pertimbangan keamanan

Aplikasi IPC mengirim dan menerima panggilan RPC. Komunikasi eksternal adalah vektor serangan potensial untuk aplikasi IPC dan harus diamankan dengan benar.

Mengamankan aplikasi server IPC terhadap penelepon tak terduga

Aplikasi server IPC menghosting layanan RPC untuk dihubungi aplikasi lain. Penelepon masuk harus diautentikasi untuk mencegah klien yang tidak tepercaya melakukan panggilan RPC ke server.

Keamanan transportasi adalah salah satu opsi untuk mengamankan server. Transportasi IPC, seperti soket domain Unix dan pipa bernama, mendukung pembatasan akses berdasarkan izin sistem operasi:

  • Pipa bernama mendukung pengamanan pipa dengan model kontrol akses Windows. Hak akses dapat dikonfigurasi di .NET saat server dimulai menggunakan PipeSecurity kelas .
  • Soket domain Unix mendukung pengamanan soket dengan izin file.

Opsi lain untuk mengamankan server IPC adalah menggunakan autentikasi dan otorisasi yang disertakan dalam ASP.NET Core. Misalnya, server dapat dikonfigurasi untuk memerlukan autentikasi sertifikat. Panggilan RPC yang dilakukan oleh aplikasi klien tanpa sertifikat yang diperlukan gagal dengan respons yang tidak sah.

Memvalidasi server di aplikasi klien IPC

Penting bagi aplikasi klien untuk memvalidasi identitas server yang dipanggilnya. Validasi diperlukan untuk melindungi dari aktor jahat agar tidak menghentikan server tepercaya, menjalankannya sendiri, dan menerima data masuk dari klien.

Pipa bernama menyediakan dukungan untuk mendapatkan akun tempat server berjalan. Klien dapat memvalidasi server diluncurkan oleh akun yang diharapkan:

internal static bool CheckPipeConnectionOwnership(
    NamedPipeClientStream pipeStream, SecurityIdentifier expectedOwner)
{
    var remotePipeSecurity = pipeStream.GetAccessControl();
    var remoteOwner = remotePipeSecurity.GetOwner(typeof(SecurityIdentifier));
    return expectedOwner.Equals(remoteOwner);
}

Opsi lain untuk memvalidasi server adalah mengamankan titik akhirnya dengan HTTPS di dalam ASP.NET Core. Klien dapat mengonfigurasi SocketsHttpHandler untuk memvalidasi server menggunakan sertifikat yang diharapkan saat koneksi dibuat.

var socketsHttpHandler = new SocketsHttpHandler()
{
    SslOptions = new SslOptions()
    {
        RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
        {
            if (sslPolicyErrors != SslPolicyErrors.None)
            {
                return false;
            }

            // Validate server cert thumbprint matches the expected thumbprint.
        }
    }
};

Melindungi dari eskalasi hak istimewa pipa bernama

Pipa bernama mendukung fitur yang disebut peniruan identitas. Dengan menggunakan peniruan identitas, server pipa bernama dapat menjalankan kode dengan hak istimewa pengguna klien. Ini adalah fitur yang kuat tetapi dapat memungkinkan server dengan hak istimewa rendah untuk meniru pemanggil hak istimewa tinggi dan kemudian menjalankan kode berbahaya.

Klien dapat melindungi dari serangan ini dengan tidak mengizinkan peniruan saat menyambungkan ke server. Kecuali diperlukan oleh server, TokenImpersonationLevel nilai None atau Anonymous harus digunakan saat membuat koneksi klien:

using var pipeClient = new NamedPipeClientStream(
    serverName: ".", pipeName: "testpipe", PipeDirection.In, PipeOptions.None, TokenImpersonationLevel.None);
await pipeClient.ConnectAsync();

TokenImpersonationLevel.None adalah nilai default dalam NamedPipeClientStream konstruktor yang tidak memiliki impersonationLevel parameter.

Mengonfigurasi klien dan server

Klien dan server harus dikonfigurasi untuk menggunakan transportasi komunikasi antar-proses (IPC). Untuk informasi selengkapnya tentang mengonfigurasi Kestrel dan SocketsHttpHandler menggunakan IPC:

Catatan

Dukungan bawaan untuk Pipa bernama di ASP.NET Core memerlukan .NET 8 atau yang lebih baru.

Proses yang berjalan pada komputer yang sama dapat dirancang untuk berkomunikasi satu sama lain. Sistem operasi menyediakan teknologi untuk memungkinkan komunikasi antarproses (IPC) yang cepat dan efisien. Contoh populer teknologi IPC adalah soket domain Unix dan pipa Bernama.

.NET menyediakan dukungan untuk komunikasi antarproses menggunakan gRPC.

Catatan

Dukungan bawaan untuk Pipa bernama di ASP.NET Core memerlukan .NET 8 atau yang lebih baru.
Untuk informasi selengkapnya, lihat versi .NET 8 atau yang lebih baru dari topik ini

Mulai

Panggilan gRPC dikirim dari klien ke server. Untuk berkomunikasi antara aplikasi di komputer dengan gRPC, setidaknya satu aplikasi harus menghosting server gRPC ASP.NET Core.

ASP.NET Core dan gRPC dapat dihosting Microsoft.AspNetCore.App di aplikasi apa pun menggunakan .NET Core 3.1 atau yang lebih baru dengan menambahkan kerangka kerja ke proyek.

<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Grpc.AspNetCore" Version="2.47.0" />
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
  </ItemGroup>

</Project>

File proyek sebelumnya:

  • Menambahkan referensi kerangka kerja ke Microsoft.AspNetCore.App. Referensi kerangka kerja memungkinkan aplikasi non-ASP.NET Core, seperti Windows Services, aplikasi WPF, atau aplikasi WinForms untuk menggunakan ASP.NET Core dan menghosting server ASP.NET Core.
  • Menambahkan referensi paket NuGet ke Grpc.AspNetCore.
  • .proto Menambahkan file.

Mengonfigurasi soket domain Unix

Panggilan gRPC antara klien dan server pada komputer yang berbeda biasanya dikirim melalui soket TCP. TCP dirancang untuk berkomunikasi di seluruh jaringan. Soket domain Unix (UDS) adalah teknologi IPC yang didukung secara luas yang lebih efisien daripada TCP ketika klien dan server berada di komputer yang sama. .NET menyediakan dukungan bawaan untuk UDS di aplikasi klien dan server.

Persyaratan:

Konfigurasi server

Soket domain Unix (UDS) didukung oleh Kestrel, yang dikonfigurasi dalam Program.cs:

public static readonly string SocketPath = Path.Combine(Path.GetTempPath(), "socket.tmp");

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.ConfigureKestrel(options =>
            {
                if (File.Exists(SocketPath))
                {
                    File.Delete(SocketPath);
                }
                options.ListenUnixSocket(SocketPath, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http2;
                });
            });
        });

Contoh sebelumnya:

  • KestrelMengonfigurasi titik akhir di ConfigureKestrel.
  • ListenUnixSocket Panggilan untuk mendengarkan UDS dengan jalur yang ditentukan.
  • Membuat titik akhir UDS yang tidak dikonfigurasi untuk menggunakan HTTPS. Untuk informasi tentang mengaktifkan HTTPS, lihat Kestrel Konfigurasi titik akhir HTTPS.

Konfigurasi klien

GrpcChannel mendukung melakukan panggilan gRPC melalui transportasi kustom. Saat saluran dibuat, saluran dapat dikonfigurasi dengan SocketsHttpHandler yang memiliki kustom ConnectCallback. Panggilan balik memungkinkan klien untuk membuat koneksi melalui transportasi kustom dan kemudian mengirim permintaan HTTP melalui transportasi tersebut.

Contoh pabrik koneksi soket domain Unix:

public class UnixDomainSocketConnectionFactory
{
    private readonly EndPoint _endPoint;

    public UnixDomainSocketConnectionFactory(EndPoint endPoint)
    {
        _endPoint = endPoint;
    }

    public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
        CancellationToken cancellationToken = default)
    {
        var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

        try
        {
            await socket.ConnectAsync(_endPoint, cancellationToken).ConfigureAwait(false);
            return new NetworkStream(socket, true);
        }
        catch
        {
            socket.Dispose();
            throw;
        }
    }
}

Menggunakan pabrik koneksi kustom untuk membuat saluran:

public static readonly string SocketPath = Path.Combine(Path.GetTempPath(), "socket.tmp");

public static GrpcChannel CreateChannel()
{
    var udsEndPoint = new UnixDomainSocketEndPoint(SocketPath);
    var connectionFactory = new UnixDomainSocketConnectionFactory(udsEndPoint);
    var socketsHttpHandler = new SocketsHttpHandler
    {
        ConnectCallback = connectionFactory.ConnectAsync
    };

    return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
    {
        HttpHandler = socketsHttpHandler
    });
}

Saluran yang dibuat menggunakan kode sebelumnya mengirim panggilan gRPC melalui soket domain Unix. Dukungan untuk teknologi IPC lainnya dapat diimplementasikan menggunakan ekstensibilitas di Kestrel dan SocketsHttpHandler.