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.