Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SOAP with service fabric - Https and Http binding

I'm currently developing a service fabric app that will expose a soap listener that will be consumed by another app

I keep getting an error saying

Could not find a base address that matches scheme https for the endpoint with binding CustomBinding. Registered base address schemes are []

here is the CreateServiceInstanceListener method

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        var serviceInstanceListers = new List<ServiceInstanceListener>()
        {
            new ServiceInstanceListener(context =>
            {
                return CreateSoapListener(context);
            })
            ,
            new ServiceInstanceListener(context =>
            {
                return CreateSoapHTTPSListener(context);
            }),
        };
        return serviceInstanceListers;
    }


    private static ICommunicationListener CreateSoapHTTPSListener(StatelessServiceContext context)
    {
        string host = context.NodeContext.IPAddressOrFQDN;
        var endpointConfig = context.CodePackageActivationContext.GetEndpoint("SecureServiceEndpoint");
        int port = endpointConfig.Port;
        string scheme = endpointConfig.Protocol.ToString();

        string uri = string.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}/MyService/", scheme, host, port);
        var listener = new WcfCommunicationListener<IServiceInterface>(
            serviceContext: context,
            wcfServiceObject: new Service(),
            listenerBinding: new BasicHttpsBinding(BasicHttpsSecurityMode.Transport),
            address: new EndpointAddress(uri)
        );

        // Check to see if the service host already has a ServiceMetadataBehavior
        ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
        {
            smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            smb.HttpsGetEnabled = true;
            smb.HttpsGetUrl = new Uri(uri);

            listener.ServiceHost.Description.Behaviors.Add(smb);
        }
        return listener;
    }

    private static ICommunicationListener CreateSoapListener(StatelessServiceContext context)
    {
        string host = context.NodeContext.IPAddressOrFQDN;
        var endpointConfig = context.CodePackageActivationContext.GetEndpoint("ServiceEndpoint");
        int port = endpointConfig.Port;
        string scheme = endpointConfig.Protocol.ToString();

        string uri = string.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}/MyService/", scheme, host, port);
        var listener = new WcfCommunicationListener<IServiceInterface>(
            serviceContext: context,
            wcfServiceObject: new Service(),
            listenerBinding: new BasicHttpBinding(BasicHttpSecurityMode.None),
            address: new EndpointAddress(uri)
        );

        // Check to see if the service host already has a ServiceMetadataBehavior
        ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
        {
            smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            smb.HttpGetEnabled = true;
            smb.HttpGetUrl = new Uri(uri);

            listener.ServiceHost.Description.Behaviors.Add(smb);
        }
        return listener;
    }

and here is the app.config (sorry if there are useless entries, I copied it from an existing WCF app)

    <?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
  </startup>

  <system.web>
    <customErrors mode="On"></customErrors>
    <compilation debug="true" targetFramework="4.6.2"/>
    <httpModules>
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/>
    </httpModules>
    <machineKey decryption="AES" decryptionKey="decryptionkey" validation="SHA1" validationKey="validationkey"/>
  </system.web>
  <system.serviceModel>
    <diagnostics wmiProviderEnabled="true">
      <messageLogging logEntireMessage="true" logKnownPii="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true"/>
      <endToEndTracing propagateActivity="true" activityTracing="true" messageFlowTracing="true"/>
    </diagnostics>
    <bindings>
      <customBinding>
        <binding name="HubBinding">
          <security defaultAlgorithmSuite="Basic256Sha256Rsa15" allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificateDuplex" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
          <textMessageEncoding messageVersion="Default"/>
          <httpsTransport maxReceivedMessageSize="1073741824"/>
        </binding>
        <binding name="AuthorityCustomBinding">
          <security defaultAlgorithmSuite="Basic256Sha256Rsa15" allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificateDuplex" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
          <textMessageEncoding messageVersion="Default"/>
          <httpsTransport maxReceivedMessageSize="1073741824"/>
        </binding>
        <binding name="CustomBinding_IServiceInterface">
          <security/>
          <textMessageEncoding/>
          <httpsTransport/>
        </binding>

      </customBinding>
    </bindings>
    <services>
      <service name="MyApp.ProductServiceManufacturer" behaviorConfiguration="ManufacturerBehaviour">
        <endpoint address="" name="ManufacturerProductService" binding="customBinding" bindingConfiguration="HubBinding" contract="MyApp.IProductServiceV20161"/>
      </service>

    </services>
    <client>
      <endpoint address="https://serverurl:8088/IServiceInterface/Service.svc" behaviorConfiguration="HubManufacturerBehavior" binding="customBinding" bindingConfiguration="AuthorityCustomBinding" contract="Service.IServiceInterface" name="CustomBinding_IProductServiceManufacturerV20161">
        <identity>
          <dns value="ServerCert"/>
        </identity>
      </endpoint>

    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="HubManufacturerBehavior">
          <clientCredentials>
            <clientCertificate findValue="XXXXXX" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
            <serviceCertificate>
              <defaultCertificate findValue="XXXXXX" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
        <behavior name="MyApp.ReportingServiceManufacturerAspNetAjaxBehavior">
          <enableWebScript/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ManufacturerBehaviour">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceCredentials>
            <serviceCertificate findValue="XXXXXX" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
          </serviceCredentials>
          <serviceSecurityAudit auditLogLocation="Application" suppressAuditFailure="true" serviceAuthorizationAuditLevel="Failure" messageAuthenticationAuditLevel="Failure"/>
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
    <extensions>
      <bindingElementExtensions>
        <add name="securityBindingElementExtension" type="MyApp.BindingExtensions.SecurityBindingElementExtension, MyApp"/>
      </bindingElementExtensions>
    </extensions>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="http"/>
      <add binding="customBinding" scheme="https"/>
    </protocolMapping>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="TelemetryCorrelationHttpModule"/>
      <add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="integratedMode,managedHandler"/>
      <remove name="ApplicationInsightsWebTracking"/>
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/>
    </modules>


    <directoryBrowse enabled="true"/>
    <validation validateIntegratedModeConfiguration="false"/>
  </system.webServer>
</configuration>

What am I doing wrong or what does the code miss Any help would be appreciated since I never did WCF before. By the way, the WCF app works with the same configuration when deployed on a server but if you are wandering why I'm doing it with service fabric it's not up to me :)

UPDATE Considering LoekD's answer I updated my CreateSoapHTTPSListener method ad here is what t looks like :

private static ICommunicationListener CreateSoapHTTPSListener(StatelessServiceContext context)
    {
        string host = context.NodeContext.IPAddressOrFQDN;
        var endpointConfig = context.CodePackageActivationContext.GetEndpoint("SecureServiceEndpoint");
        int port = endpointConfig.Port;
        string scheme = endpointConfig.Protocol.ToString();
        var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
        binding.MaxReceivedMessageSize = 1073741824;

        string uri = ConfigurationManager.AppSettings.Get("ProductManufacturerService");
        Tools.TraceMessage(uri);
        var listener = new WcfCommunicationListener<IProductServiceV20161>(
            serviceContext: context,
            wcfServiceObject: new ProductServiceManufacturer(),
            listenerBinding: binding,
            address: new EndpointAddress(uri)
        );
        listener.ServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ServiceCertificateThumbprint"));
        listener.ServiceHost.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ClientCertificateThumbprint"));

        // Check to see if the service host already has a ServiceMetadataBehavior
        ServiceMetadataBehavior smb = listener.ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
        {
            smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            smb.HttpsGetEnabled = true;
            smb.HttpGetEnabled = false;
            smb.HttpsGetUrl = new Uri(uri);
            listener.ServiceHost.Description.Behaviors.Add(smb);
        }
        return listener;
    }

Then I got an error saying that :

The Service contains multiple ServiceEndpoints with different ContractDescriptions which each have Name='IProductServiceV20161' and Namespace='namespaceurl/'

I guess it's because there are two definitions of the service endpoint one in the app.config file and the other in the .cs file I commented the endpoint tag in the app.config and it worked. However, the wsdl file I'm getting is missing some entries compared to the file I get with the WCF app.

UPDATE 2 : How to specify the endpoint identity for the service? and is it possible to use a custom BindingElementExtensionElement class?

like image 774
Kira Avatar asked Mar 27 '18 12:03

Kira


2 Answers

Make sure the endpoint in the service manifest is declared as type 'HTTPS' and to add the SSL certificate to the WCF servicehost.

Can you try changing this:

listenerBinding: new BasicHttpsBinding(BasicHttpsSecurityMode.Transport)

into this:

listenerBinding: binding

where binding is defined as:

var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport)
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

More info here.

And configure the service host with your SSL certificate:

listener.ServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "Certificate Thumbprint Here");
like image 59
LoekD Avatar answered Oct 23 '22 07:10

LoekD


I am going to assume that you masked the thumbprint of your certificate with those X's and also changed the server name to serverurl for the client endpoint address.

With those assumptions, I am going to use the values that I see in your client endpoint to show you what appears to missing in your service endpoint configuration.

There are two options on setting the base address to your service. First option works well for one service endpoint and it is a simple fix in your configuration; just add the address to your service. Like this:

  <service name="MyApp.ProductServiceManufacturer" behaviorConfiguration="ManufacturerBehaviour">
    <endpoint address="https://serverurl:8088/IServiceInterface/Service.svc" name="ManufacturerProductService" binding="customBinding" bindingConfiguration="HubBinding" contract="MyApp.IProductServiceV20161"/>
  </service>

Second option, which works well for multiple host headers or even if you multiple services and want to use config transformations for environment deployments. This solution requires you to add the base address(es) individually and then only reference the *.svc in the endpoint address. Like this:

  <service name="MyApp.ProductServiceManufacturer" behaviorConfiguration="ManufacturerBehaviour">
    <endpoint address="Service.svc" name="ManufacturerProductService" binding="customBinding" bindingConfiguration="HubBinding" contract="MyApp.IProductServiceV20161"/>
    <host>
      <baseAddresses>
        <add baseAddress="https://serverurl:8088/IServiceInterface" />
      </baseAddresses>
    </host>
  </service>

Give either of these a try and happy coding.

-TwistedStem

like image 30
Larry Dukek Avatar answered Oct 23 '22 07:10

Larry Dukek