Inter-Process Communication mit gRPC
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.
Wichtig
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.
Auf demselben Computer ausgeführte Prozesse können so konzipiert werden, dass sie miteinander kommunizieren. Betriebssysteme bieten Technologien für die schnelle und effiziente Inter-Process Communication (IPC). Beliebte Beispiele für IPC-Technologien sind Unix-Domänensockets und Named Pipes.
.NET bietet Unterstützung für die Inter-Process Communication mithilfe von gRPC.
Für die integrierte Unterstützung für Named Pipes in ASP.NET Core ist .NET 8 oder höher erforderlich.
Erste Schritte
IPC-Aufrufe werden von einem Client an einen Server gesendet. Damit die Kommunikation zwischen Apps auf einem Computer mit gRPC funktioniert, muss mindestens eine App einen ASP.NET Core-gRPC-Server hosten.
Ein ASP.NET Core-gRPC-Server wird normalerweise aus der gRPC-Vorlage erstellt. Die von der Vorlage erstellte Projektdatei verwendet Microsoft.NET.SDK.Web
als 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>
Der Microsoft.NET.SDK.Web
SDK-Wert fügt automatisch einen Verweis auf das ASP.NET Core-Framework hinzu. Der Verweis ermöglicht der App die Verwendung von ASP.NET Core-Typen, die zum Hosten eines Servers erforderlich sind.
Es ist auch möglich, vorhandenen Projekten, in denen nicht ASP.NET Core verwendet wird (z. B. Windows-Dienste, WPF-Apps oder WinForms-Apps), einen Server hinzuzufügen. Weitere Informationen finden Sie unter Hosten von gRPC in Projekten ohne ASP.NET Core.
IPC-Transporte (Inter-Process Communication)
gRPC-Aufrufe zwischen einem Client und einem Server auf verschiedenen Computern werden in der Regel über TCP-Sockets gesendet. TCP ist eine gute Wahl für die Kommunikation über ein Netzwerk oder das Internet. IPC-Transporte bieten jedoch Vorteile bei der Kommunikation zwischen Prozessen auf demselben Computer:
- Weniger Aufwand und schnellere Übertragungsgeschwindigkeiten.
- Integration in Betriebssystemsicherheitsfunktionen.
- Verwendet keine TCP-Ports, bei denen es sich um eine begrenzte Ressource handelt.
.NET unterstützt mehrere IPC-Transporte:
- Unix Domain Sockets (UDS) ist eine umfassend unterstützte IPC-Technologie. UDS ist die beste Wahl zum Erstellen plattformübergreifender Apps und kann unter Linux, macOS und Windows 10/Windows Server 2019 oder höher verwendet werden.
- Named Pipes werden von allen Versionen von Windows unterstützt. Named Pipes lassen sich gut in die Windows-Sicherheit integrieren, mit der der Clientzugriff auf die Pipe gesteuert werden kann.
- Zusätzliche IPC-Transporte durch Implementieren von IConnectionListenerFactory und Registrieren der Implementierung beim App-Start.
Je nach Betriebssystem können plattformübergreifende Apps unterschiedliche IPC-Transporte auswählen. Eine App kann das Betriebssystem beim Start überprüfen und den gewünschten Transport für diese Plattform auswählen:
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;
});
});
Sicherheitsüberlegungen
IPC-Apps senden und empfangen RPC-Aufrufe. Die externe Kommunikation ist ein potenzieller Angriffsvektor für IPC-Apps und muss ausreichend geschützt werden.
Schützen der IPC-Server-App gegen unerwartete Aufrufer
Die IPC-Server-App hostet RPC-Dienste, die von anderen Apps aufgerufen werden können. Eingehende Aufrufer sollten authentifiziert werden, um zu verhindern, dass nicht vertrauenswürdige Clients RPC-Aufrufe an den Server durchführen.
Die Transportsicherheit stellt eine Option dar, um einen Server zu schützen. IPC-Transporte wie Unix-Domänensockets und Named Pipes unterstützen das Einschränken des Zugriffs basierend auf Betriebssystemberechtigungen:
- Named Pipes unterstützen das Schützen einer Pipe mit dem Windows-Zugriffssteuerungsmodell. Zugriffsrechte können in .NET konfiguriert werden, wenn ein Server mit der PipeSecurity-Klasse gestartet wird.
- Unix-Domänensockets unterstützen das Schützen eines Sockets mit Dateiberechtigungen.
Eine weitere Möglichkeit zum Schützen eines IPC-Servers besteht darin, die Authentifizierungs- und Autorisierungsfeatures zu verwenden, die in ASP.NET Core integriert ist. Beispielsweise könnte der Server so konfiguriert werden, dass eine Zertifikatauthentifizierung erforderlich ist. RPC-Aufrufe durch Client-Apps ohne das erforderliche Zertifikat schlagen mit der Antwort „Nicht autorisiert“ fehl.
Überprüfen des Servers in der IPC-Client-App
Es ist wichtig, dass die Client-App die Identität des Servers überprüft, den sie aufruft. Die Validierung ist erforderlich, um die App davor zu schützen, dass böswillige Akteur*innen den vertrauenswürdigen Server stoppen, eigene Server ausführen und eingehende Daten von Clients akzeptieren.
Named Pipes unterstützen das Abrufen des Kontos, unter dem ein Server ausgeführt wird. Ein Client kann überprüfen, ob der Server durch das erwartete Konto gestartet wurde:
internal static bool CheckPipeConnectionOwnership(
NamedPipeClientStream pipeStream, SecurityIdentifier expectedOwner)
{
var remotePipeSecurity = pipeStream.GetAccessControl();
var remoteOwner = remotePipeSecurity.GetOwner(typeof(SecurityIdentifier));
return expectedOwner.Equals(remoteOwner);
}
Eine weitere Option zum Überprüfen des Servers ist das Schützen der Serverendpunkte mit HTTPS in ASP.NET Core. Der Client kann den SocketsHttpHandler
so konfigurieren, dass dieser überprüft, ob der Server das erwartete Zertifikat verwendet, wenn die Verbindung hergestellt wird.
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.
}
}
};
Schützen vor einer Rechteausweitung der Named Pipe
Named Pipes unterstützen ein Feature namens Identitätswechsel. Mithilfe des Identitätswechsels kann der Server mit Named Pipes Code mit den Berechtigungen von Clientbenutzer*innen ausführen. Dies ist ein nützliches Feature, das jedoch zulassen kann, dass ein Server mit niedriger Berechtigung die Identität mit einem Aufrufer mit erhöhten Rechten wechseln und dann schädlichen Code ausführen kann.
Clients können vor diesem Angriff schützen, indem sie den Identitätswechsel beim Herstellen einer Verbindung zu einem Server nicht zulassen. Beim Erstellen einer Clientverbindung sollte ein TokenImpersonationLevel-Wert von None
oder Anonymous
verwendet werden, sofern kein anderer Wert für den Server erforderlich ist:
using var pipeClient = new NamedPipeClientStream(
serverName: ".", pipeName: "testpipe", PipeDirection.In, PipeOptions.None, TokenImpersonationLevel.None);
await pipeClient.ConnectAsync();
TokenImpersonationLevel.None
ist der Standardwert in NamedPipeClientStream
-Konstruktoren, die keinen impersonationLevel
-Parameter aufweisen.
Konfigurieren von Client und Server
Client und Server müssen für die Verwendung eines IPC-Transports (Inter-Process Communication) konfiguriert werden. Weitere Informationen zum Konfigurieren von Kestrel und SocketsHttpHandler für die Verwendung von IPC finden Sie hier:
- Prozessübergreifende Kommunikation mit gRPC- und Unix-Domänensockets
- Prozessübergreifende Kommunikation mit gRPC und Named Pipes
Hinweis
Für die integrierte Unterstützung für Named Pipes in ASP.NET Core ist .NET 8 oder höher erforderlich.
Auf demselben Computer ausgeführte Prozesse können so konzipiert werden, dass sie miteinander kommunizieren. Betriebssysteme bieten Technologien für die schnelle und effiziente Inter-Process Communication (IPC). Beliebte Beispiele für IPC-Technologien sind Unix-Domänensockets und Named Pipes.
.NET bietet Unterstützung für die Inter-Process Communication mithilfe von gRPC.
Hinweis
Für die integrierte Unterstützung für Named Pipes in ASP.NET Core ist .NET 8 oder höher erforderlich.
Weitere Informationen finden Sie unter der .NET 8- oder höhen Version dieses Themas
Erste Schritte
gRPC-Aufrufe werden von einem Client an einen Server gesendet. Damit die Kommunikation zwischen Apps auf einem Computer mit gRPC funktioniert, muss mindestens eine App einen ASP.NET Core-gRPC-Server hosten.
ASP.NET Core und gRPC können in einer beliebigen App mit .NET Core 3.1 oder höher gehostet werden, indem sie dem Projekt das Microsoft.AspNetCore.App
-Framework hinzufügen.
<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>
Die obige Projektdatei:
- Fügt einen Frameworkverweis auf
Microsoft.AspNetCore.App
hinzu Der Frameworkverweis ermöglicht es Apps, die nicht unter ASP.NET Core laufen (z. B. Windows-Dienste, WPF-Apps oder WinForms-Apps), ASP.NET Core zu verwenden und einen ASP.NET Core-Server zu hosten. - Fügt einen NuGet-Paketverweis auf
Grpc.AspNetCore
hinzu - Fügt eine
.proto
-Datei hinzu
Konfigurieren von Unix-Domänensockets
gRPC-Aufrufe zwischen einem Client und einem Server auf verschiedenen Computern werden in der Regel über TCP-Sockets gesendet. TCP wurde für die Kommunikation über ein Netzwerk entwickelt. Unix-Domänensockets (UDS) sind eine breit unterstützte IPC-Technologie, die effizienter ist als TCP, wenn der Client und der Server auf demselben Computer sind. .NET bietet integrierte Unterstützung für UDS in Client- und Server-Apps.
Anforderungen:
- .NET 5 oder höher
- Linux, macOS oder Windows 10/Windows Server 2019 oder höher
Serverkonfiguration
Unix-Domänensockets (UDS) werden von Kestrel unterstützt, was in Program.cs
konfiguriert wird:
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;
});
});
});
Für das vorherige Beispiel gilt Folgendes:
- Der Endpunkt von Kestrel wird in
ConfigureKestrel
konfiguriert. - ListenUnixSocket wird aufgerufen, um dem UDS mit dem angegebenen Pfad zu laschen.
- Erstellt einen UDS-Endpunkt, der nicht für die Verwendung von HTTPS konfiguriert ist. Informationen zum Aktivieren von HTTPS finden Sie unter Kestrel-HTTPS-Endpunktkonfiguration.
Clientkonfiguration
GrpcChannel
unterstützt gRPC-Aufrufe für benutzerdefinierte Datentransporte. Wenn ein Kanal erstellt wird, kann er mit einer SocketsHttpHandler
-Klasse konfiguriert werden, die über ein benutzerdefiniertes ConnectCallback
-Objekt verfügt. Der Rückruf ermöglicht es dem Client, Verbindungen für benutzerdefinierte Datentransporte herzustellen und dann HTTP-Anforderungen über diese Datentransporte zu senden.
Beispiel für eine UDS-Verbindungsfactory (Unix Domain Socket):
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;
}
}
}
So wird die benutzerdefinierte Verbindungsfactory zum Erstellen eines Kanals verwendet:
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
});
}
Kanäle, die mithilfe des vorangehenden Codes erstellt werden, senden gRPC-Aufrufe über Unix Domain Sockets (UDS). Unterstützung für andere IPC-Technologien kann mithilfe der Erweiterbarkeit in Kestrel und SocketsHttpHandler
implementiert werden.