使用 LXD REST API,ClientWebSocket 在 .Net Core 上抛出“AuthenticationException”,但在 .Net Framework 上,它运行良好

Posted

技术标签:

【中文标题】使用 LXD REST API,ClientWebSocket 在 .Net Core 上抛出“AuthenticationException”,但在 .Net Framework 上,它运行良好【英文标题】:Using LXD REST API, ClientWebSocket throws 'AuthenticationException' on .Net Core, but on .Net Framework, it works well 【发布时间】:2019-02-09 08:30:58 【问题描述】:

我正在将 LXD.NET 移植到 .Net Standard 2.0。通过 HTTPS 实现的 REST API 可以很好地与 .Net Core 和 .Net Framework 配合使用。 但是使用 WebSocket API(例如 1.0/containers/<name>/exec ),ClientWebSocket 仅在 .Net Core 上引发身份验证异常。 (在 .Net Framework 上,它不会抛出并且运行良好。

LXD 在 Ubuntu 18.04 上运行。我在两个客户端上进行了测试,一个在 Windows 10 上运行,另一个在 Linux 上运行(运行 LXD 的同一台 linux 计算机)。

所有源代码都是Here(https://github.com/GnicoJP/lxd-dotnet-websocket-test)。

重现步骤(服务器端)

LXD 设置

$ sudo apt install lxc lxd  
$ sudo lxd init  

我将 LXD 配置为可通过网络使用。

$ sudo lxc launch images:alpine/3.8 &lt;container_name&gt;  

我发布了一张图片。

SSL 密钥/证书生成

$ openssl genrsa 2048 > client.key  
$ openssl req -new -key client.key > client.csr  
$ openssl x509 -days 3650 -req -signkey client.key < client.csr > client.crt  
$ openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12

将 client.p12 带到客户端 PC。

我注册了 SSL 证书

$ sudo lxc config trust add client.key

源代码

入口点是Here。我正在调用 exec API。(wait-for-websocket 是真的) 解析 Exec API 的结果是 Here。我觉得 WebSocket 的 URI 没有错。 打开 ClientWebSocket 是Here。在 .Net Framework 上,WebSocket 在有/没有证书的情况下都能很好地工作。

抛出的异常

System.AggregateException
HResult=0x80131500
Message=One or more errors occurred. (Unable to connect to the remote server)
Source=System.Private.CoreLib
Stack Trace:
    at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) in Task.cs:line 2840
    at System.Threading.Tasks.Task.Wait() in Task.cs:line 2706
    at lxd_dotnet_websocket_test.Program.Main(String[] args) in lxd-dotnet-websocket-test\Program.cs:line 20

Inner Exception 1:
System.Net.WebSockets.WebSocketException: Unable to connect to the remote server
HResult=-2147467259
Stack Trace:
    at System.Net.WebSockets.WebSocketHandle.ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)
    at System.Net.WebSockets.ClientWebSocket.ConnectAsyncCore(Uri uri, CancellationToken cancellationToken)
    at LXD.ClientWebSocketExtensions.CreateAndConnectAsync(String url, API API) in .\libs\LXD\Util\ClientWebSocketExtensions.cs:line 19
    at LXD.Domain.Container.ContainerExecResult.ContainerExecResultWithWebSockets.TestCreate(API API, ContainerExec exec, JToken response, String operationUrl) in .\libs\LXD\Domain\Container.cs:line 228

Inner Exception 2:
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
HResult=-2146233087
Stack Trace:
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
    at System.Threading.Tasks.ValueTask`1.get_Result()
    at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    at System.Threading.Tasks.ValueTask`1.get_Result()
    at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
    at System.Threading.Tasks.ValueTask`1.get_Result()
    at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    at System.Net.WebSockets.WebSocketHandle.ConnectAsyncCore(Uri uri, CancellationToken cancellationToken, ClientWebSocketOptions options)

Inner Exception 3:
AuthenticationException: The remote certificate is invalid according to the validation procedure.
HResult=-2146233087
Stack Trace:
    at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
    at System.Net.Security.SslState.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
    at System.Net.Security.SslState.ThrowIfExceptional()
    at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
    at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
    at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
    at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__47_1(IAsyncResult iar)
    at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)

我是 *** 的初学者。如果我的问题或我的英语不好,请告诉我。

【问题讨论】:

您是否总是使用与服务器相同的机器(和相同的帐户)运行?从错误消息中显示“远程证书无效”。您是否以管理员权限运行该服务?我通常建议使用像 wireshark 或 fiddler 这样的嗅探器,并比较工作应用程序和非工作应用程序中的 HTTP 标头。 HTTP 使用 TCP 作为传输层。 TCP 数据报的最大大小约为 1500 字节。所以一个 HTTP 由一个或多个 TCP 消息组成。如果 TLS 失败,它将显示在 TCP 数据报上。在 TCP [FIN] 中表示连接关闭。 感谢您的回复。我没有在同一台机器上运行。但是,如果我用 .Net Framework 编译它,它运行良好(即使我从不同的机器访问。)。我想知道为什么使用 .Net Core 访问 WebSocket API 会失败。我会尝试使用 Wireshark,谢谢。 LXD 以管理员权限运行。我不认为它是由权限错误引起的,因为使用其他库(pylxd 等)创建的其他程序将无法访问与使用 .NetCore 构建的 websocket 相同的 websocket。 我不认为问题出在代码上。我相信问题是服务器。坏服务器上的 Net Core 版本可能不同。在很多情况下,Net 库都是调用 windows dll 的包装器。因此,如果 Windows 库不同或 Net 库不同(在不同的机器上),代码并不总是有效。这就是为什么我建议发布应用程序并安装在更新 Windows dll 的部署机器上。在这种情况下,错误消息表明证书有问题。证书日期可能已过期。 使用 .Net Core 和 .Net Framework 构建的程序在同一台计算机、相同的 Windows 环境、相同的证书上运行。 (如果之前的评论,“我没有在同一台机器上运行。”让你感到困惑,对不起。这意味着服务器和客户端是不同的机器。)如果证书无效,我想知道 .Net Framework 不会抛出例外。很抱歉不知道,谢谢您的回复。 【参考方案1】:

.Net Framework 不检查 WebSocket 中使用的证书是否可信, 但是.Net Core 可以。我无法从异常中得到解决方法,所以我调查了by using PerfView。

如何解决

我安装了 LXD 的服务器证书。 将 server.crt 文件 (/var/lxd/server.crt) 带到客户端 PC,然后作为受信任的根证书安装。 请阅读如何安装:Windows、Linux

【讨论】:

以上是关于使用 LXD REST API,ClientWebSocket 在 .Net Core 上抛出“AuthenticationException”,但在 .Net Framework 上,它运行良好的主要内容,如果未能解决你的问题,请参考以下文章

“lxd”与 lxc/docker 有何不同?

将 lxc 迁移到 lxd

juju创建lxd容器时如何使用本地镜像(by quqi99)

基于LXD搭建多人共用GPU服务器,简单易用,全网最详细!

Kubernetes 可以在没有 LXD 守护进程的情况下管理 LXC 容器吗?

markdown 通过LXC / LXD运行Minikube