Enable Https Endpoint for Service Fabric Application

In this post, I would like to share with you on hosting a website in secure mode (https) in Service Fabric.

Assume that I have a ReactJs application is running on Service Fabric with HTTP endpoint as below.

Now, to secure this site by applying the SSL and let it’s running on HTTPs endpoint by following steps.

I. Generate SSL Certificate.

Generate SSL certification just for development purpose. If you want to host the application on PRD, you should request the certificates form your company CA provider.

For localhost Service Fabric I’m using below command to generate a certificate using OpenSSL.

  1. Create private key
Openssl.exe genrsa -out localhost.key 2048
  1. Create certification
Openssl.exe req -new -x509 -key localhost.key -out localhost.cert -days 3650 -subj /CN=localhost
  1. Convert cert to pfx
Openssl.exe pkcs12 -export -out localhost.pfx -inkey localhost.key -in localhost.cert
  1. Import pfx file
     Open mmc.exe and import pfx file to Personal of Local Computer
If don’t have OpenSSL, download here.

II. Enable HTTPS for Service Fabric Application

1. Add application parameter.

Add a new parameter named CertThumbprint and value is Thumbprint of the certificate to Local1Node.xml and Local5Node.xml

<Parameters>
...
<Parameter Name="CertThumbrint" Value="72bfba706ae7e0a9946892d9aae42c319d1d6988" />
</Parameters>

We may have difference certificate for difference environments So we can push the Thumbprint value from the Continuous Delivery system.

2. Update the ApplicationManifest.xml

Adding one more CertThumbprint under Parameters section and one more EndpointCertificate name HttpsCert under Certificates section as below.

<ApplicationManifest>
<Parameters>
...
<Parameter Name="CertThumbprint" DefaultValue="" />
</Parameters>

...
<Certificates>
<EndpointCertificate X509FindValue="[CertThumbprint]" Name="HttpsCert" />
</Certificates>
</ApplicationManifest>

3. Update the Policies for Https endpoint

In the same ApplicationManifest add the below EndpointBindingPolicy config into ReactJs under ServiceManifestImport section which CertificateRef reference to the EndpointCertificate name added above and EndpointRef is a new service endpoint for https will be added later.

<ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="ReactJsPkg" ServiceManifestVersion="1.0.0" />
...
<Policies>
<EndpointBindingPolicy EndpointRef="ServiceEndpointHttps" CertificateRef="HttpsCert" />
</Policies>
</ServiceManifestImport>

4. Update ServiceManifest.xml of ReactJs app.

Open ServiceManifest.xml of ReactJs and update the endpoint as below.

<Resources>
<Endpoints>
<Endpoint Protocol="https" Name="ServiceEndpointHttps" Type="Input" CertificateRef="HttpsCert"/>
</Endpoints>
</Resources>

The name of Endpoint name and CertificateRef must be the same with EndpointRef and EndpointCertificate name which added into ApplicationManifest above.

5. Update the CommunicationListener

  • Using HttpSysCommunicationListener

If you are using HttpSysCommunicationListener then using the below setup for the endpoints.

new ServiceInstanceListener(serviceContext =>
//Replace KestrelCommunicationListener by HttpSysCommunicationListener
new HttpSysCommunicationListener(serviceContext, "ServiceEndpointHttps", (url, listener) =>
{
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");
        return new WebHostBuilder()
//Repalce UseKestrel by UseHttpSys
.UseHttpSys()
.ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseReverseProxyIntegration)
.UseUrls(url)
.Build();
}))

Perfect, now run the application and we have a ReacJs is running https.

  • Using KestrelCommunicationListener

With above configuration is not work for as KestrelCommunicationListener is not able to load the certificate from the configuration directly instead we need to add some additional code to load and binding the certificate manually as below.

new ServiceInstanceListener(serviceContext =>
new KestrelCommunicationListener(serviceContext, "ServiceEndpointHttps", (url, listener) =>
{
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");
        var cert = GetCertificateFromStore(serviceContext);
var port =int.Parse( url.Substring(url.IndexOf("+:") + 2));
        return new WebHostBuilder()
.UseKestrel(op=>{
//Get current Ip of PC
var ip = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(i=>i.AddressFamily == AddressFamily.InterNetwork)??IPAddress.Loopback;
op.Listen(ip, port, listenConfig=>listenConfig.UseHttps(cert));
})
.ConfigureServices(
services => services.AddSingleton(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseReverseProxyIntegration)
.UseUrls(url)
.Build();
}),"ServiceEndpointHttps")

The GetCertificateFromStore() method

private X509Certificate2 GetCertificateFromStore(StatelessServiceContext serviceContext)
{
var config = serviceContext.CodePackageActivationContext.GetConfigurationPackageObject("Config");
        //Load Certificate from Store by Thumbprint
//Uncomment this if you want to load cert from Computer store.
//var thumbprint = config.Settings.Sections["MyConfigSection"].Parameters["CertThumbprint"].Value;
//var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
//try
//{
// store.Open(OpenFlags.ReadOnly);
// var certCollection = store.Certificates;
// var currentCerts = certCollection.Find(X509FindType.FindByThumbprint, thumbprint, false);
// return currentCerts.Count == 0 ? null : currentCerts[0];
//}
//finally
//{
// store.Close();
//}
        //Load Certificate from file
return new X509Certificate2(Path.Combine(config.Path, "localhost.pfx"), "localhost");
}
Look into the GetCertificateFromStore you will see that I’m loading the certificate from the Config folder instead of from the Certificate Store. It means you can attach the certificate along with your application and binding it directly to the listener instead of import the certificate to every servers of Service Fabric cluster.

III. Working with Reverse Proxy.

However, the application is not accessible via Reversed Proxy because my local Service Fabric is running in unsecured mode and the Reverse proxy is supporting HTTP protocol.

In order to access the application via Reversed proxy, we need to run the Service Fabric in secure mode.

1. Run Local Service Fabric in a secured mode

In localhost run below PowerShell script to convert Service Fabric to secured mode.

CD "C:\Program Files\Microsoft SDKs\Service Fabric\ClusterSetup\"
.\DevClusterSetup.ps1 -PathToClusterDataRoot "C:\SfDevCluster\Data" -PathToClusterLogRoot "C:\SfDevCluster\Log" -AsSecureCluster

After the done the installation, We will have secured Service Fabric cluster, and The application is accessible via Reversed proxy.

2. Run PRD Service Fabric in secure mode

If you PRD Service Fabric is unsecured then follow steps here to secure it.

Thanks for reading and download source code here.


Originally published at Drunk Coding.

Like what you read? Give Steven Hoang a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.