Saturday, April 1, 2017

Advanced concepts WCF listener for Service Bus


For basics of creating a WCF listener to a ServiceBus queue/topic, Pl refer -
WCF-ServiceBus Listener




1)      The default protocol used is NetMessaging (not AMQP). NetMessaging is a native protocol available in .NET only. AMQP is an OASIS standard and interoperable, meaning message sent by .NET publisher could by consumed by PHP/Python client.
2)      Unlike Service Bus, ActiveMQ offers a lot of protocols for message interchange like –
a)      Openwire (native protocol for activemq – default)
b)      Mqtt (light weight pub-sub protocol typically used for IoT)
c)      Amqp (cross platform, efficient)
d)  STOMP
3)      With the above approach, a new instance of WCF service is created each time, a message arrives the topic/queue. This was confirmed with the following code –
public interface IService
{       
        [OperationContract(IsOneWay = true, Action = "*"), ReceiveContextEnabled(ManualControl = true)]
        void MessageReader(Message message);
}
public class MyService: IService
{
        private string instanceId = "";
        public MyService()
        {
            instanceId = Guid.NewGuid().ToString();           
        }
        public void MessageReader(Message message)
        {
            var logEntry = new LogEntry { Message = string.Format("Service instance id is {0}",instanceId) };
            Utilities.WriteLogSimple(logEntry);
        }
}

4)      Once message arrives a topic/queue, a lock is applied. All topics/queues have a message lock timeout property, whose default value is 1 min and can be set to 5 mins as maximum value. The processing of the message has to be committed (or rolled back)  within this time or else the lock will be automatically released and the message will go back to the topic/queue, thus increasing the delivery count. Pl note that this redelivery will trigger the wcf processing again .The max delivery count can also be set. Once the message has been redelivered more than the “max delivery count”, it goes to “deadletter queue”.



  5)   With reference to the point above, the following situation can occur and should be handled carefully. Suppose a message arrives a topic, a wcf instance is created and it applies the lock on the message. The processing is message takes more than 1 minute (default lock timeout) and get redelivered to the topic/queue 10 times (default max delivery count). Ultimately all the 10 times, the message gets processed, thus causing inconsistent results. So make sure the message processing timeout (say database execution timeout or service call) is always less than message lock timeout. Also reduce the max delivery count from 10 (default) to a suitable value.




Thursday, March 16, 2017

Transient fault handling and retry strategy



Transient fault handling and retry strategy

While working with webservices in the middleware layer (say a subscriber to a service bus queue), it is wise to use a good retry strategy for transient fault handling for scenarios such as – connectivity issues, timeout issues etc. Retrying immediately will not help much as the chances of the resource being unavailable would be high. The code below explains ExponentialBackoff strategy. Exponentialbackoff retry strategy means - It ll retry after 2seconds, 4seconds, 8seconds.  

Explanation of ExponentialBackoff parameters –

var retryStrategy = new ExponentialBackoff(3, TimeSpan.FromSeconds(2),
                    TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(1));
a)      first param - number of retries
b)      second param - minimum backoff time (say 2seconds). Meaning - time it should wait 2 seconds before the first RETRY or minimum time between any retries.
c)      third param - maximum backoff limit.  In this case - the third retry will happen after about ~ 2 + 4 + 8 = 14 seconds. The third param is there to put a limit on backoff period (which is 20 in this case).
d)      fourth param - delta to add some randomness, otherwise all clients would be retrying simultaenously.
in this case random value is 1 second. So 2,4,8 could be 1,5,7 etc.


Please find the code files and detailed steps below –


1)      Add a nuget package to EnterpriseLibrary.TransientFaultHandling.
2)      Create the following class (to specify errors/exceptions which should be retried) –
public class TransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
    {
        // add the list of exceptions, that you consider transient.
        public bool IsTransient(Exception ex)
        {
            if (ex is WebException)
                return true;
            if (ex is FakeTimeoutException)
                return true;
            return false;
        }
    }

3)      Create a FakeTimeoutException (for demo/testing purpose) –
public class FakeTimeoutException : Exception
    {
        public FakeTimeoutException(string msg) : base(msg)
        {

        }
    }

4)      Create the main class and method as shown below –

class Program
    {
        static void Main(string[] args)
        {
            var retryStrategy = new ExponentialBackoff(3, TimeSpan.FromSeconds(2),
                    TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(1));
            TransientErrorDetectionStrategy errorDetectionStrategy = new TransientErrorDetectionStrategy();

            var retryPolicy = new RetryPolicy(errorDetectionStrategy,retryStrategy);

            retryPolicy.ExecuteAction(() =>
            ExecuteHTTPGET("https://microsoft.sharepoint.com")
            );

        }

        private static void ExecuteHTTPGET(string requestUri)
        {           
            Console.WriteLine(DateTime.Now);
            throw new FakeTimeoutException("fake timeout");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
            request.KeepAlive = false;
            request.Method = "GET";

            HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
            int requestStatus = (int)webResponse.StatusCode;
            webResponse.Close();
        }

5)     Run the program and see the timestamp in the console. Try by switching FakeTimeoutException with SomeOtherException and see the behavior.

Thursday, December 29, 2016

Implementation of WCF transport security using self-signed certificates


For implementation of WCF message security using certificates, Pl refer this post

--------------------------------------------------------------------------------------------------------




Implementation of WCF transport security using self-signed certificates

Transport layer security means using HTTPS protocol (SSL). This will make sure that the HTTP packets travel encrypted through the network. With the below configuration, you will get confidentiality (because of encryption) but YOU ARE NOT AUTHENTICATING the client. Any client can successfully call https://subhasissecureservice.com/Service1.svc as long as the client has the RootCA under its trusted roots folder. (which is generally the case. In an internet-facing scenario, the SSLs are procured from – say – Verisign. All clients PCs have Verisign in their trusted roots folder.)

Note – Transport security are for POINT-TO-POINT, while Message security are for END-TO-END. (Think about it).

Creation of SSL certificate for subhasissecureservice.com (exactly as any other cert)

makecert -n "CN=SubhasisSecureService.com" -ic "SubhasisCA.cer" -iv "SubhasisCAPrivateKey.pvk" -a sha1 -sky exchange -pe -sv "SubhasisSecureServicePrivateKey.pvk" "SubhasisSecureService_SSL.cer"
---private key password of the above is secureservicepvtkeypwd

pvk2pfx -pvk "SubhasisSecureServicePrivateKey.pvk" -spc "SubhasisSecureService_SSL.cer" -pfx "SubhasisSecureService_SSL.pfx" -pi secureservicepvtkeypwd

1.      Install the pfx in the server mmc.
2.      Make sure Root CA is in the server mmc (if not procured/purchased).
3.      Update host file with <IP> tab SubhasisSecureService.com (if DNS server is not used)
4.      Configure IIS to use site/webservice with SSL (edit bindings).
5.      Make sure Root CA is in the client mmc (if not procured/purchased).
6.      Update host file with <IP> tab SubhasisSecureService.com in the client as well. (if DNS server is not used)

Server config file (Note basicHttpBinding is used with SSL certificates)
Key sections with yellow highlight
<system.serviceModel>
    <client />
    <services>
      <service name="CertSecurityDemoService.Service1">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="secureBasicHttpBinding" contract="CertSecurityDemoService.IService1" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>         
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <bindings>
      <basicHttpBinding>
        <binding name="secureBasicHttpBinding" sendTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
Client config file (Simple)
<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IService1">
          <security mode="Transport" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://subhasissecureservice.com/Service1.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
          contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
    </client>
  </system.serviceModel>


Note – In the above config, we have clientCredentialType as “None”, which means we don’t expect to authenticate. Any client can consume the webservice.
The purpose here is to prevent “man-in-the-middle” attack. (i.e. Tampering of message)

--------------------------------------------------------------------------------------------------------

If you want to add additional security like custom username/pwd, certificates etc to validate the client (prevent anonymous access), along with transport layer security (HTTPS), You can do that by slightly modifying the above code –
1)      At the service level, change security mode to “TransportWithMessageCredential” from “Transport” (Note – TransportWithMessageCredential has high overhead) and add <message clientCredentialType="UserName"/>
2)      Add a servicebehavior to custom validate the username/password.
3)      Client config has minimal change. Only securitymode is changed to TransportWithMessageCredential.
4)      Client code should pass the username and password as shown below –
          ServiceReference1.Service1Client cl = new ServiceReference1.Service1Client();
            cl.ClientCredentials.UserName.UserName = "subhasisrout";
            cl.ClientCredentials.UserName.Password = "abcxyz!$@#";
    Console.WriteLine(cl.GetData(5));



Server config

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="usernamevalidatebehavior">
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <serviceCredentials>
            <userNameAuthentication customUserNamePasswordValidatorType="CertSecurityDemoService.CustomUserNameValidator, CertSecurityDemoService" userNamePasswordValidationMode="Custom"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <client/>
    <services>
      <service name="CertSecurityDemoService.Service1" behaviorConfiguration="usernamevalidatebehavior">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="secureBasicHttpBinding" contract="CertSecurityDemoService.IService1" />
      </service>
    </services>
   
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <bindings>
      <basicHttpBinding>
        <binding name="secureBasicHttpBinding" sendTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>


--------------------------------------------------------------------------------------------------------



public class CustomUserNameValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName == null || password == null)
            {
                throw new ArgumentNullException();
            }

            if (!(userName == "subhasisrout" && password == "abcxyz!$@#"))
            {
                throw new System.IdentityModel.Tokens.SecurityTokenException("Bad Username or Password");
            }
        }
    }

--------------------------------------------------------------------------------------------------------

Client Web config

<system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService1">
                    <security mode="TransportWithMessageCredential" />
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://subhasissecureservice.com/Service1.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
        </client>
</system.serviceModel>

--------------------------------------------------------------------------------------------------------

IIS Settings

Anonymous Authentication – Enabled
Basic Authentication – Enabled
Everything else – Disabled