Udostępnij za pośrednictwem


Komunikacja między procesami przy użyciu gRPC

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Procesy uruchomione na tym samym komputerze można zaprojektować tak, aby komunikowały się ze sobą. Systemy operacyjne zapewniają technologie umożliwiające szybką i wydajną komunikację między procesami (IPC). Popularne przykłady technologii IPC to gniazda domeny systemu Unix i nazwane potoki.

Platforma .NET zapewnia obsługę komunikacji między procesami przy użyciu biblioteki gRPC.

Wbudowana obsługa nazwanych potoków w programie ASP.NET Core wymaga platformy .NET 8 lub nowszej.

Rozpocznij

Wywołania IPC są wysyłane z klienta do serwera. Aby komunikować się między aplikacjami na maszynie z usługą gRPC, co najmniej jedna aplikacja musi hostować serwer gRPC ASP.NET Core.

Serwer gRPC platformy ASP.NET Core jest zwykle tworzony na podstawie szablonu gRPC. Plik projektu utworzony przez szablon używa Microsoft.NET.SDK.Web go jako zestawu 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>

Microsoft.NET.SDK.Web Wartość zestawu SDK automatycznie dodaje odwołanie do platformy ASP.NET Core. Odwołanie umożliwia aplikacji używanie typów ASP.NET Core wymaganych do hostowania serwera.

Istnieje również możliwość dodania serwera do istniejących projektów non-ASP.NET Core, takich jak usługi systemu Windows, aplikacje WPF lub aplikacje WinForms. Aby uzyskać więcej informacji, zobacz Host gRPC w projektach non-ASP.NET Core.

Transporty komunikacji między procesami (IPC)

Wywołania gRPC między klientem a serwerem na różnych maszynach są zwykle wysyłane za pośrednictwem gniazd TCP. Protokół TCP to dobry wybór do komunikacji między siecią lub Internetem. Jednak transporty IPC oferują zalety podczas komunikacji między procesami na tym samym komputerze:

  • Mniejsze obciążenie i szybkość transferu.
  • Integracja z funkcjami zabezpieczeń systemu operacyjnego.
  • Nie używa portów TCP, które są ograniczonym zasobem.

Platforma .NET obsługuje wiele transportów IPC:

  • Gniazda domeny systemu Unix (UDS) to powszechnie obsługiwana technologia IPC. Usługa UDS jest najlepszym wyborem do tworzenia aplikacji międzyplatformowych i można jej używać w systemach Linux, macOS i Windows 10/Windows Server 2019 lub nowszych.
  • Nazwane potoki są obsługiwane przez wszystkie wersje systemu Windows. Nazwane potoki dobrze integrują się z zabezpieczeniami systemu Windows, które mogą kontrolować dostęp klienta do potoku.
  • Dodatkowe transporty IPC przez zaimplementowanie IConnectionListenerFactory i zarejestrowanie implementacji podczas uruchamiania aplikacji.

W zależności od systemu operacyjnego aplikacje międzyplatformowe mogą wybierać różne transporty IPC. Aplikacja może sprawdzić system operacyjny podczas uruchamiania i wybrać odpowiedni transport dla tej platformy:

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;
    });
});

Zagadnienia dotyczące zabezpieczeń

Aplikacje IPC wysyłają i odbierają wywołania RPC. Komunikacja zewnętrzna jest potencjalnym wektorem ataku dla aplikacji IPC i musi być odpowiednio zabezpieczona.

Zabezpieczanie aplikacji serwera IPC względem nieoczekiwanych wywołań

Aplikacja serwera IPC hostuje usługi RPC dla innych aplikacji do wywołania. Osoby wywołujące przychodzące powinny być uwierzytelniane, aby uniemożliwić niezaufanym klientom wykonywanie wywołań RPC do serwera.

Zabezpieczenia transportu to jedna z opcji zabezpieczania serwera. Transporty IPC, takie jak gniazda domeny systemu Unix i nazwane potoki, obsługują ograniczanie dostępu na podstawie uprawnień systemu operacyjnego:

  • Nazwane potoki obsługują zabezpieczanie potoku za pomocą modelu kontroli dostępu systemu Windows. Prawa dostępu można skonfigurować na platformie .NET, gdy serwer jest uruchamiany przy użyciu PipeSecurity klasy .
  • Gniazda domeny systemu Unix obsługują zabezpieczanie gniazda z uprawnieniami do plików.

Inną opcją zabezpieczania serwera IPC jest użycie uwierzytelniania i autoryzacji wbudowanej w ASP.NET Core. Na przykład serwer można skonfigurować tak, aby wymagał uwierzytelniania certyfikatu. Wywołania RPC wykonywane przez aplikacje klienckie bez wymaganego certyfikatu kończą się niepowodzeniem z nieautoryzowaną odpowiedzią.

Weryfikowanie serwera w aplikacji klienckiej IPC

Ważne jest, aby aplikacja kliencka weryfikowała identity wywoływany serwer. Weryfikacja jest niezbędna do ochrony przed złośliwym aktorem przed zatrzymaniem zaufanego serwera, uruchamianiem własnych i akceptowaniem danych przychodzących od klientów.

Nazwane potoki zapewniają obsługę pobierania konta, w którym działa serwer. Klient może zweryfikować, czy serwer został uruchomiony przez oczekiwane konto:

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

Inną opcją weryfikacji serwera jest zabezpieczenie punktów końcowych za pomocą protokołu HTTPS wewnątrz ASP.NET Core. Klient może skonfigurować SocketsHttpHandler w celu zweryfikowania, czy serwer używa oczekiwanego certyfikatu podczas nawiązywania połączenia.

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.
        }
    }
};

Ochrona przed podwyższeniem uprawnień nazwanych potoków

Nazwane potoki obsługują funkcję o nazwie personifikacja. Przy użyciu personifikacji nazwany serwer potoków może wykonywać kod z uprawnieniami użytkownika klienta. Jest to zaawansowana funkcja, ale może umożliwić serwerowi z niskimi uprawnieniami personifikację obiektu wywołującego wysoki poziom uprawnień, a następnie uruchomić złośliwy kod.

Klient może chronić się przed tym atakiem, nie zezwalając na personifikację podczas nawiązywania połączenia z serwerem. Jeśli nie jest to wymagane przez serwer, TokenImpersonationLevel należy użyć wartości None lub Anonymous podczas tworzenia połączenia klienta:

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

TokenImpersonationLevel.None jest wartością domyślną w NamedPipeClientStream konstruktorach, które nie mają parametru impersonationLevel .

Konfigurowanie klienta i serwera

Klient i serwer muszą być skonfigurowane do korzystania z transportu komunikacji między procesami (IPC). Aby uzyskać więcej informacji na temat konfigurowania Kestrel i SocketsHttpHandler używania protokołu IPC:

Uwaga

Wbudowana obsługa nazwanych potoków w programie ASP.NET Core wymaga platformy .NET 8 lub nowszej.

Procesy uruchomione na tym samym komputerze można zaprojektować tak, aby komunikowały się ze sobą. Systemy operacyjne zapewniają technologie umożliwiające szybką i wydajną komunikację między procesami (IPC). Popularne przykłady technologii IPC to gniazda domeny systemu Unix i nazwane potoki.

Platforma .NET zapewnia obsługę komunikacji między procesami przy użyciu biblioteki gRPC.

Uwaga

Wbudowana obsługa nazwanych potoków w programie ASP.NET Core wymaga platformy .NET 8 lub nowszej.
Aby uzyskać więcej informacji, zobacz platformę .NET 8 lub nowszą wersję tego tematu

Rozpocznij

Wywołania gRPC są wysyłane z klienta do serwera. Aby komunikować się między aplikacjami na maszynie z usługą gRPC, co najmniej jedna aplikacja musi hostować serwer gRPC ASP.NET Core.

ASP.NET Core i gRPC mogą być hostowane w dowolnej aplikacji przy użyciu platformy .NET Core 3.1 lub nowszej, dodając Microsoft.AspNetCore.App strukturę do projektu.

<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>

Poprzedni plik projektu:

  • Dodaje odwołanie do platformy do Microsoft.AspNetCore.Appelementu . Dokumentacja platformy umożliwia korzystanie z aplikacji non-ASP.NET Core, takich jak usługi systemu Windows, aplikacje WPF lub aplikacje WinForms do korzystania z platformy ASP.NET Core i hostowanie serwera ASP.NET Core.
  • Dodaje odwołanie do pakietu NuGet do Grpc.AspNetCoreelementu .
  • .proto Dodaje plik.

Konfigurowanie gniazd domeny systemu Unix

Wywołania gRPC między klientem a serwerem na różnych maszynach są zwykle wysyłane za pośrednictwem gniazd TCP. Protokół TCP został zaprojektowany pod kątem komunikacji między siecią. Gniazda domeny systemu Unix (UDS) to powszechnie obsługiwana technologia IPC, która jest wydajniejsza niż TCP, gdy klient i serwer znajdują się na tej samej maszynie. Platforma .NET zapewnia wbudowaną obsługę usługi UDS w aplikacjach klienta i serwera.

Wymagania:

Konfiguracja serwera

Gniazda domeny systemu Unix (UDS) są obsługiwane przez Kestrelusługę , która jest skonfigurowana w programie 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;
                });
            });
        });

Powyższy przykład:

  • Konfiguruje Kestrelpunkty końcowe w programie ConfigureKestrel.
  • Wywołania ListenUnixSocket do nasłuchiwania usługi UDS z określoną ścieżką.
  • Tworzy punkt końcowy usługi UDS, który nie jest skonfigurowany do używania protokołu HTTPS. Aby uzyskać informacje na temat włączania protokołu HTTPS, zobacz Kestrel Konfiguracja punktu końcowego HTTPS.

Konfiguracja klientów

GrpcChannel obsługuje wykonywanie wywołań gRPC w przypadku transportu niestandardowego. Po utworzeniu kanału można go skonfigurować za pomocą elementu SocketsHttpHandler z niestandardowym ConnectCallbackelementem . Wywołanie zwrotne umożliwia klientowi nawiązywanie połączeń za pośrednictwem transportu niestandardowego, a następnie wysyłanie żądań HTTP za pośrednictwem tego transportu.

Przykład fabryki połączeń gniazd domeny systemu 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;
        }
    }
}

Tworzenie kanału przy użyciu niestandardowej fabryki połączeń:

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
    });
}

Kanały utworzone przy użyciu poprzedniego kodu wysyłają wywołania gRPC za pośrednictwem gniazd domeny systemu Unix. Obsługę innych technologii IPC można zaimplementować przy użyciu rozszerzalności w systemach Kestrel i SocketsHttpHandler.