NetTCP, связанное с согласованием безопасности с мылом

Я пишу службу WCF, требующую олицетворения и сеанса.

Это нормально, когда я пытался вызвать его на моем локальном компьютере, но на удаленном компьютере он всегда терпел неудачу с такой ошибкой:

Ошибка аутентификации поставщика поддержки безопасности (SSPI). Возможно, сервер не работает в учетной записи с идентификатором "host/hostname". Если сервер работает в учетной записи службы (например, Network Service), укажите учетную запись 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

Службы Windows регистрируются либо с именем участника-пользователя, либо с именем участника службы (документация). Чтобы процитировать из этой ссылки: "Если служба запущена под учетной записью LocalSystem, LocalService или NetworkService, имя участника-службы службы (SPN) генерируется по умолчанию в форме хоста /, поскольку эти учетные записи имеют доступ к данным SPN компьютера. Если служба работает под другой учетной записью, Windows Communication Foundation (WCF) создает UPN в форме @." Фактически, эта цитата довольно похожа на то, что указано в сообщении об ошибке. Так кажется, что...

a) если служба запущена под учетной записью Local Service или аналогичной стандартной учетной записью, вам необходимо настроить файл конфигурации клиента, чтобы иметь это, где фактическое имя сервера является "адресом", а конечная точка работает на порту 4503

<identity>
     <servicePrincipalName value="host/address:4503" />
</identity>

b) поочередно, если вы работаете под специализированной учетной записью службы (позвоните ей "ServiceAccount" в домене "MyDomain" ), тогда вы хотите

<identity>
     <userPrincipalName value="[email protected]" />
</identity>

Обратите внимание, что в обоих случаях вам может понадобиться полное доменное имя, включая уровни "Лес" и "Дерево". Для простого домена внутри вашей частной LAN/WAN это будет означать адрес .MyDomain.local и [email protected] Если ваш домен находится в дереве с именем MyTree, это будет [email protected]; если это в лесу под названием MyForest, то это будет [email protected](и аналогично для ServicePrincipalName). Полноценное имя необходимо при использовании Kerberos для аутентификации.

Ответ 2

Существует также грязный хак, как указано здесь, здесь и здесь, и проанализировал здесь.

Вы можете предоставить фиктивное имя участника службы (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)

также будет работать.