Wednesday, June 21, 2017

KeepAlive Url agent SSL Forced authentication issues and quick fix!

Table of Contents

  1. Introduction
  2. Problem Statement: Failed SSL forced Authenticatio
  3. Bit of a background
  4. Quick Fix: Sitecore SSL Keepalive UrlAgent Issue
  5. Miscellaneous Custom UrlAgent for KeepAlive

Introduction


The purpose of this article is bring forward the key issues that one can come across when dealing with keepalive url. I'll not delve deep into what is Keepalive rather I'll focus on problem statement and list of references that can handy. Also I have given my way of approaching to the solution in different scenario. Maybe the solution I have provided may not stand the best recommended practice however it atleast give some foresight to deal with issues like this. The content more or less is about using https :// keepalive url when working with .net 4.5 azure webapp or environment required forced ssl.

Problem Statement: Failed SSL forced Authentication



Bit of a background


The SSL forced authentication will only arise if you have forced https for your website and http is restricted through firewall. Now if you are using .net 6.0 or higher this will work without any issue whereas .net 4.5 and lower version will not enforce TLS protocol by default.You may have to set them explicitly to map to your ssl certificate.
  • .NET 4.6 and above. You don’t need to do any additional work to support TLS 1.2, it’s supported by default.
  • .NET 4.5. TLS 1.2 is supported, but it’s not a default protocol. You need to opt-in to use it.
Refer this http://blogs.perficient.com/microsoft/2016/04/tsl-1-2-and-net-support/

***Poodle Attack


As per my understanding reason to do this to prevent poodle attack. Refer this for more details Security bulletin TechNet Poodle Attack

***SSL Scan


You can scan your site for poodle attack. Scan your site for poodle attack SSL Scan Test

Quick Fix: Sitecore SSL Keepalive UrlAgent Issue


Add this  Global.asax startup Or application Start method for .net 4.5 web app. This explains lot https://stackoverflow.com/questions/28286086/default-securityprotocol-in-net-4-5
 System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 
Add this to your web config Ip restriction to allow access to O.0.0.0 azure webapps




http://www.secqa.com/index.php?qa=2247&qa_1=sitecore-azure-website-forbidden-its-access-permissions-127
https://stackoverflow.com/questions/34389510/sitecore-8-1-azure-website-forbidden-by-its-access-permissions-127-0-0-1/34869927
https://stackoverflow.com/questions/24884098/azure-website-ip-restriction

The above solution will solve your forced ssl authentication issue.

Sitecore support KB must eloborate and refine this one to reduce number of support request. https://kb.sitecore.net/articles/376036 [Check out 4th Scenario Covered]

Someone even face this for mongo integration. http://techqa.info/programming/question/39699800/Intermittent--Authentication-failed-because-the-remote-party-has-closed-the-transport-stream-----Mongo-C-

Miscellaneous Custom UrlAgent for KeepAlive


If you are facing some other issues you will always have an options to create custom URLagent like this one below. I tried to create one with adding above solution. You can even create generic version to get azure host using custom solution making use of sitecore webutil. For e.g calling out of box webtil methods. for e.g GetHostIPAddress
Making use of above method we can create custom urlagent and add it to our solution.For testing purpose we reduce the interval to 2 minutes. Sitecore.config

using Sitecore.Diagnostics;
using Sitecore.Web;
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace MyProject
{
    public class SslUrlAgent
    {

        private bool _logActivity = true;
        private readonly string _url;

        public bool LogActivity
        {
            get
            {
                return this._logActivity;
            }
            set
            {
                this._logActivity = value;
            }
        }


        public SslUrlAgent(string url)
        {
            Assert.ArgumentNotNull((object)url, "url");
            this._url = url;
        }

    
        public void Run()
        {
            try
            {
                string fullUrl = WebUtil.GetFullUrl(this._url);
                this.LogInfo("Scheduling.UrlAgent started. Url: " + fullUrl);
                this.LogInfo("Scheduling.UrlAgent done (received: " + (object)ExecuteSSLWithCertificatePolicyWebPage(fullUrl).Length + " bytes)");
            }
            catch (Exception ex)
            {
                Log.Error("Exception in UrlAgent (url: " + this._url + ")", ex, (object)this);
            }
        }
     
        private bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
        {
            // If the certificate is a valid, signed certificate, return true.
            if (error == System.Net.Security.SslPolicyErrors.None)
            {
                return true;
            }

            LogInfo("ICertificatePolicy: " + error.ToString());

            return false;
        }
        private string ExecuteSSLWithCertificatePolicyWebPage(string url)
        {
            System.Net.ServicePointManager.ServerCertificateValidationCallback += (send, certificate, chain, sslPolicyErrors) => { return true; };
            using (WebClient webClient = new WebClient())
            {
                // System.Net.ServicePointManager.ServerCertificateValidationCallback += (send, certificate, chain, sslPolicyErrors) => { return true; };
                // ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;                          
                return Encoding.UTF8.GetString(webClient.DownloadData(url));
            }         
        }
      
        private void LogInfo(string message)
        {
            if (!this.LogActivity)
                return;
            Log.Info(message, (object)this);
        }
    }
}

 
Some useful webutil method for reference. I used dotPeek to get the implementation of these method.
    public static string GetFullUrl(string url)
    {
      Assert.ArgumentNotNull((object) url, "url");
      return WebUtil.GetFullUrl(url, WebUtil.GetServerUrl());
    }


 
    public static string GetFullUrl(string url, string serverUrl)
    {
      Assert.ArgumentNotNull((object) url, "url");
      Assert.ArgumentNotNull((object) serverUrl, "serverUrl");
      if (url.IndexOf("://", StringComparison.InvariantCulture) < 0)
        return FileUtil.MakePath(serverUrl, url, '/');
      return url;
    }


 
    public static string GetHostIPAddress()
    {
      HttpContext current = HttpContext.Current;
      try
      {
        if (current != null)
        {
          if (current.Request != null)
          {
            Uri url = current.Request.Url;
            if (url.HostNameType == UriHostNameType.Dns)
            {
              IPHostEntry hostEntry = Dns.GetHostEntry(url.Host);
              if (hostEntry.AddressList.Length >= 0)
                return hostEntry.AddressList[0].ToString();
            }
          }
        }
      }
      catch (SocketException ex)
      {
        Log.SingleWarn(string.Format("Cannot resolve the IP address of the web server with the specified hostname: {0}.", (object) current.Request.Url.Host), (object) typeof (WebUtil));
      }
      return string.Empty;
    }

 
 
    public static string GetHostName()
    {
      HttpContext current = HttpContext.Current;
      if (current != null && current.Request != null)
        return current.Request.Url.Host;
      return string.Empty;
    }

 
 
    public static string GetIISName()
    {
      HttpServerUtility server = WebUtil.GetServer();
      if (server != null)
        return SecurityUtil.ComputeHash(server.MapPath("/"));
      return string.Empty;
    }




With above implementation if you have any webproxy issue or anything you can try to resolve using custom urlagent approach as given above. Hope this article help you to some extend. Thanks.



Monday, May 1, 2017

Domain Name Service Cheat Sheet

Domain Name Service


First look for domain name to IP mapping is done through Host file and if not exist it look up in server domain cache. If the mapping is not available in host and aswell as in DNS cache it will eventually look up into DNS server.
  1. Host- This is host file C:\Windows\System32\drivers\etc\hosts
  2. DNS Cache
  3. DNS Server



Sr.No
DNS Attribute
Remark
1
Ipconfig/flushdns
Flush DNS Cache
2
Ipconfig/displaydns
Display DNS Details
3
Forward DNS Lookup
Internet domain name to find an IP address
4
Reverse DNS Lookup
Internet IP address to find a domain name.
5
Root Hint

6
Authorative DNS Response
Contains Address reference in Enterprise DNS server
7
Non Authorative DNS Response
Available either in non enterprise root dns server
8
Cache DNS Path
C:\Windows\System32\dns\cache.DNS
9
Wireshark
To intercept all network protocal
10
Addr ARPA-.in-addr.arpa (inverse address)
Translate IP address to a hostname
11
PTR-record Reverse DNS
Translaate Hostnmae to IP address
12
Cname - Canonical Name
The ALIAS record maps a name to another name, but in turns it can coexist with other records on that name
13
Load balancing
Netmasking Ordering
Identify default gateway Ip and subnet of incoming request and based on zone/Subnet it allows the access to server.
14
Round Robin
Based on availability and if server is not busy it will access and route request to the given server
15
FQDN
Fully Qualified Domain Name
16
DNS Resolution

Thursday, April 20, 2017

Rules Based Engine using Tuples

Dynamic Rules Defined Using Tuple

  1. Input: Define Rules Case Conditions
  2. Output :Define Results based on Rules case conditions

       

          
   
    class Program
    {
        static void Main(string[] args)
        {
            var options = GenerateRulesBasedResults(true, false, false, false, true);
            foreach (var option in options)
            {
                Console.WriteLine (option.ToString());
                /* Ford
                 * BMW
                 * Mercedes
                 */
            }
            Console.Read();
        }
        public static List<CarSale> GenerateRulesBasedResults(bool isCruiseControl, bool isAutomatic, 
                                                               bool isUnderWarranty, bool isOnSale, bool isOnRoadOffSale)
        {
            List<CarSale> options;
            Results.TryGetValue(Tuple.Create(isCruiseControl, isAutomatic, isUnderWarranty, isOnSale, isOnRoadOffSale), out options);
            return options;
        }

        //I/P Rule Case Condition variant
        private static readonly Tuple<bool, bool, bool, bool, bool> RuleCaseCondition1 = Tuple.Create(true, false, false, false, true);
        private static readonly Tuple<bool, bool, bool, bool, bool> RuleCaseCondition2 = Tuple.Create(false, true, false, false, true);       

        //O/P Rule based results.
        private static readonly Dictionary<Tuple<bool, bool, bool, bool, bool>, List<CarSale>> Results =
            new Dictionary<Tuple<bool, bool, bool, bool, bool>, List<CarSale>>
        {
                { RuleCaseCondition1, new List<CarSale> {   CarSale.Ford , CarSale.BMW, CarSale.Mecedes} },

                { RuleCaseCondition2, new List<CarSale> {   CarSale.Suzuki , CarSale.Mazda} },
        };

        public enum CarSale
        {
            Honda,
            BMW,
            Mazda,
            Suzuki,
            Mecedes,
            Wolkswagen,
            Ford
        }

  

  
       
 

Tuesday, April 11, 2017

Sitecore MVC best practice Avoid Multiple types were found that match the controller

Scenario


Pre- production cleanup and performance improvement. In terms of security and performance optimization, sitecore logs, iis logs, http sys logs or event logs are best place to look upon. It give detailed behavour of application in terms of errors, redundant calls and other user specific details that are security threats. Having said that keeping production environment clean is good for application overall health. Below is the one of the example that we may ignore. This can be viewed in event logs mostly.


Event Log

Multiple types were found that match the controller named 'Authentication'. This can happen if the route that services this request ('sitecore/shell/api/sitecore/{controller}/{action}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter. The request for 'Authentication' has found the following matching controllers: Sitecore.Controllers.AuthenticationController My.MVC.Areas.MyAPI.Controllers.Api.AuthenticationController at System.Web.Mvc.DefaultControllerFactory.GetControllerTypeWithinNamespaces(RouteBase route, String controllerName, HashSet`1 namespaces) at System.Web.Mvc.DefaultControllerFactory.GetControllerType(RequestContext requestContext, String controllerName) at System.Web.Mvc.DefaultControllerFactory.System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, String controllerName) at Sitecore.Mvc.Controllers.SitecoreControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, String controllerName) at System.Web.Mvc.MvcRouteHandler.GetSessionStateBehavior(RequestContext requestContext) at System.Web.Mvc.MvcRouteHandler.GetHttpHandler(RequestContext requestContext) at System.Web.Mvc.MvcRouteHandler.System.Web.Routing.IRouteHandler.GetHttpHandler(RequestContext requestContext) at Sitecore.Mvc.Routing.RouteHandlerWrapper.GetHttpHandler(RequestContext requestContext) at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) at System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e) at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

BottomLine

Never use sitecore or .net intrinsic names for controller

Sunday, April 9, 2017

Add Sitecore AD Membership Provider at runtime

Before you refer this blog, refer this blogpost
https://santoshpoojari.blogspot.com.au/2017/03/sitecore-active-directorysitecoreadmemb.html

I have taken more generic approach to add this. I have refined the above method with c# generic constraint.


Things to be taken care.      

We cannot use below Providers add due: Exception Details: System.NotSupportedException: Collection is read-only.

//Membership.Providers.Add(provider);
Therefore we should use utility class that give extension method AddTo.

http://stackoverflow.com/questions/1432508/programmatically-adding-a-membership-provider

Generic Membership Provider

  
 public static void InitialiseProviders <T>(T provider, string providerName, NameValueCollection providerConfiguration) where T : MembershipProvider
        {
            provider.Initialize(providerName, providerConfiguration);
            provider.AddTo(Membership.Providers);
            //We cannot use below Providers add due: Exception Details: System.NotSupportedException: Collection is read-only.
            //Membership.Providers.Add(provider);
        }

Utility Extension Method

public static class ProviderUtil
{
    static private FieldInfo providerCollectionReadOnlyField;

    static ProviderUtil()
    {
        Type t = typeof(ProviderCollection);
        providerCollectionReadOnlyField = t.GetField("_ReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
    }

    static public void AddTo(this ProviderBase provider, ProviderCollection pc)
    {
        bool prevValue = (bool)providerCollectionReadOnlyField.GetValue(pc);
        if (prevValue)
            providerCollectionReadOnlyField.SetValue(pc, false);

        pc.Add(provider);

        if (prevValue)
            providerCollectionReadOnlyField.SetValue(pc, true);
    }
}


Initialize the LDap AD Configuration and call the generic method

 public static NameValueCollection LdapSitecoreProviderConfiguration
        {
            get {

                 NameValueCollection config = new NameValueCollection();                                 
                 config["type"] = "LightLDAP.SitecoreADMembershipProvider";
                 config["name"] = "SomeProviderName";

                 config["connectionStringName"] = "LDAPConnString";
                 config["applicationName"] = "sitecore";
 
                 config["connectionUsername"] = ConfigurationManager.AppSettings["AppSettingKeys-UserName"];
                 config["connectionPassword"] = ConfigurationManager.AppSettings["AppSettingKeys-Password"];
 
                 config["minRequiredPasswordLength"] = "1";
                 config["minRequiredNonalphanumericCharacters"] = "0";
                 config["requiresQuestionAndAnswer"] = "false";
 
                 config["requiresUniqueEmail"] = "false";
                 config["connectionProtection"] = "secure";
 
                 config["attributeMapUsername"] = "sAMAccountName";
                 config["enableSearchMethods"] = "true";
 
                 config["customFilter"] = "(memberOf=Your org custom filter)";
                 return config;
            }
        }
InitialiseProviders(new SitecoreADMembershipProvider(), "someldapName", xyz.LdapSitecoreProviderConfiguration);

Saturday, April 1, 2017

Devops- VSO and Git!

Azure :Continuous Delivery Preview

Donovan Brown Channel 9 Session

Devops Systematic Integration 
  • Build -CI 
  • Deploy Release -CD
Helped me create first end to end devops CI and CD pipeline using continuous delivery preview.

https://channel9.msdn.com/events/Visual-Studio/Visual-Studio-2017-Launch/WEB-105

I face one issue with nuget restore and it is because of this error causing constant issue.
http://stackoverflow.com/questions/32254439/nuget-packages-are-missing
After configuring continuous delivery with resource group and app service plan. It asks for repository and setup build and release pipeline by itself. In fact zero configuration.

























Options : Multiple configuration To queue parallel build on different agents


Even you can apply above magic directly from powershell script.

Create New: Yo team



Update existing CI and CD:Yo team/pipeline

Build Agents

  • No physical controller
  • Work side by side with XAML
  • Agents are XCOPY deployed and auto updated from server
  • Agent pool and collection assign to machine Agent

Gear Icon

Important Information
  • Download agents
  • Unblock the folder
  • Run ConfigureAgent.Ps1
  • Copy Team foundation server url
  • Give working folder name
  • Run as window service or interactive mode

Note:
  • Agent Capability in relation with Build General Tab -> Demand.
  • Demand can be used to run build on Premise agent or cloud agent.

Monday, March 27, 2017

Solr Basic Authentication : Secure through key vault

If you are using key vault and you want to secure solr basic authentication credential, it is good practice to fetch it from appsettings which will retrieve from key vault server for specific environment at runtime. In order to do that you may have to configure solr configuration programmatically. Below is the useful sitecore stackexchange link that can come handy.



http://sitecore.stackexchange.com/questions/5112/how-to-initialize-solr-end-point-connection-with-basic-authentication-programmat/5145#5145

namespace MyClassLibrary
{
  public class MyBasicAuthHttpWebRequestFactory : IHttpWebRequestFactory
  {


    public MyBasicAuthHttpWebRequestFactory(string username, string password)
    {
      this.username = ConfigurationManager.AppSettings["username"];
      this.password = ConfigurationManager.AppSettings["password"];
    }

    public IHttpWebRequest Create(string url)
    {
      return this.Create(new Uri(url));
    }

    public IHttpWebRequest Create(Uri url)
    {
      HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
      string base64String = Convert.ToBase64String(Encoding.ASCII.GetBytes(this.username + ":" + this.password));
      request.Headers.Add("Authorization", "Basic " + base64String);
      return (IHttpWebRequest) new HttpWebRequestAdapter(request);
    }
  }
}

http://sitecore.stackexchange.com/questions/5062/update-the-sitecore-configs-at-runtime-possible/5076#5076