netTCP 绑定 Soap 安全协商失败
Posted
技术标签:
【中文标题】netTCP 绑定 Soap 安全协商失败【英文标题】:netTCP binding Soap Security Negotiation Failed 【发布时间】:2012-03-29 19:17:16 【问题描述】:我正在编写一个 WCF 服务,需要模拟和会话。
当我尝试在本地机器上调用它时没问题,但在远程机器上它总是失败并出现这样的错误:
安全支持提供程序接口 (SSPI) 身份验证失败。服务器可能未在身份为“主机/主机名”的帐户中运行。如果服务器在服务帐户(例如网络服务)中运行,请将帐户的 ServicePrincipalName 指定为服务器的 EndpointAddress 中的标识。如果服务器在用户帐户中运行,请将帐户的 UserPrincipalName 指定为服务器的 EndpointAddress 中的标识。
如果我提供了一个 upn,它会抛出一个身份失败异常。
这是我的配置:
服务器配置(APP):
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="default">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization impersonateCallerForAllOperations="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="DataService.netTcpBinding">
<readerQuotas maxArrayLength="65535" maxBytesPerRead="2147483647" maxStringContentLength="2147483647"/>
<reliableSession enabled="true" inactivityTimeout="24:00:00" ordered="true"/>
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Windows" />
<transport clientCredentialType="Windows"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
<services>
<service behaviorConfiguration="default" name="DataService.DataService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="DataService.netTcpBinding"
name="DataService.DataService" contract="DataService.IDataService"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://address:4504/"/>
<add baseAddress="net.tcp://address:4503/"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
客户端配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="DataService.DataService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10"
maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
maxReceivedMessageSize="65536">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="24.00:00:00"
enabled="true" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" algorithmSuite="Default" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://address:4503/" binding="netTcpBinding"
bindingConfiguration="DataService.DataService"
contract="ataService.IDataService" name="DataService.DataService">
<identity>
<dns value="DOMAIN"/>
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:还有一个肮脏的 hack,如 here、here 和 here 发布的那样, 并分析了here。
您可以提供一个虚拟的服务主体名称 (SPN)。在这种情况下,WCF 不会失败, 但回退到不验证主体的 NTLM 进行身份验证。
所以,配置:
<identity>
<servicePrincipalName value="dummy" >
</identity>
以编程方式
EndpointIdentity identity = EndpointIdentity.CreateSpnIdentity("dummy");
使用 ChannelFactory:
Uri uri = new Uri("net.tcp://<myServer>:<myPort>/myServiceAddress");
ChannelFactory channelFactory = new ChannelFactory<IMyContract>(new NetTcpBinding());
channelFactory.CreateChannel(new EndpointAddress(uri, identity)
也可以。
【讨论】:
【参考方案2】:Windows 服务使用用户主体名称或服务主体名称 (documentation) 注册自己。引用该链接:“如果服务在 LocalSystem、LocalService 或 NetworkService 帐户下运行,则默认情况下会以 host/ 的形式生成服务主体名称 (SPN),因为这些帐户可以访问计算机的SPN 数据。如果服务在不同的帐户下运行,Windows Communication Foundation (WCF) 会以 @ 的形式生成一个 UPN。" 实际上,此引用与您的错误消息所述的内容非常相似。所以看来……
a) 如果服务在本地服务帐户或类似的标准帐户下运行,那么您需要调整您的 client 配置文件以具有此功能,其中实际服务器的名称是“地址”并且端点在 4503 端口上运行:
<identity>
<servicePrincipalName value="host/address:4503" />
</identity>
b) 或者,如果您在专用服务帐户下运行(我们在域“MyDomain”上将其称为“ServiceAccount”),那么您需要
<identity>
<userPrincipalName value="ServiceAccount@MyDomain" />
</identity>
请注意,您可能需要在两种情况下都使用完全限定域名,包括森林和树级别。对于私有 LAN/WAN 内的简单域,这意味着 address.MyDomain.local 和 ServiceAccount@MyDomain.local。如果您的域位于名为 MyTree 的树中,那么它将是 ServiceAccount@MyDomain.MyTree.local;如果这是在名为 MyForest 的森林中,那么它将是 Serviceaccount@MyDomain.MyTree.MyForest.local(对于 ServicePrincipalName 类似)。需要完全限定名称when you are using Kerberos 进行身份验证。
【讨论】:
这是正确的。我还要补充一点,在使用 IIS 托管的 Web 服务时,标识应该与服务所在的应用程序池标识相匹配。这在 IIS 的某些版本(可能是 7?)中已从 NetworkService(您将使用NetworkService@MACHINE-NAME
的用户原则)更改为 AppPoolIdentity(您将使用 host/MACHINE-NAME
的服务原则)。以上是关于netTCP 绑定 Soap 安全协商失败的主要内容,如果未能解决你的问题,请参考以下文章
Nginx反代,后端一个IP绑定多个SSL证书,导致连接失败之解决方法:HTTPS和SNI扩展