Skip to main content

Accessing Certificates in Service Fabric Hosted Windows Containers

Azure Service Fabric is a great platform for container orchestration. It provides a full suite of features to ensure that your container is held up by the five pillars of software quality -- ensuring scalability, availability, resiliency, management, and security. Assuming your containerized application may need access to certificates to handle encryption, decryption, signing, or verification, Service Fabric even provides a built-in way to expose certificates installed in the LocalMachine store to the container by using a ContainerHostPolicy. You can also explicitly provide certificate files as part of the Data Package. Both approaches are documented well in the use a certificate in a container topic in the docs. What if you need more control over the certificates? What if they're not installed on the node and you need to dynamically make them available to your container at the time of service startup? What actually needs to happen in the setupentrypoint.sh script?

This post shows how you can utilize SetupEntryPoint scripts to manage acquiring certificates and making them available to your Service Fabric hosted container. It will depend on the LocalMachine certificate store to provide the initial certificate, but it could also be a certificate file pulled from Azure Key Vault or some other certificate store. This code was originally created using Visual Studio 15.7.4 (taking advantage of the new container tooling) and the Microsoft Azure Service Fabric SDK version 3.1.269.9494.

Create a Web Application for Containerization

With the container tooling available in the Service Fabric tools (Visual Studio 15.7 or greater), creating a Service Fabric hosted container is really simple. I started with an ASP.NET Core Web Application with a controller that handled encryption and decryption actions. I broke out the encryption and decryption into a separate service so that the certificate would only have to be loaded once. When finished, the controller remains very light:

The encryption and decryption service CertStringCryptoService is mainly responsible for loading a certificate and storing its public and private keys for encryption and decryption respectively. Since it will be hosted in a container, the code depends on environment variables to be configured at runtime. In this case, it will need to know the path to the certificate PFX file and the path to a file containing the password to access the PFX. It will then use the retrieved keys for encryption and decryption.

With this, we have a solution that can be run and tested as-is if the environment variables have been set. The two used are:

  • Custom_PfxFileName
  • Custom_PfxPasswordFileName
There is a third environment variable, Fabric_Folder_App_Work, required for the solution to work. This environment variable is automatically set by the Service Fabric runtime; however, you would need to explicitly set it to be the base path in which the PFX and PFX password files would be found for this to work outside of a Service Fabric hosted container.

Make it Service Fabric Ready

How do we containerize our web application and setup a Service Fabric application to host and orchestrate the solution? Easy. Right-click on your ASP.NET Core Application project, and select Add > Container Orchestrator Support. Select Service Fabric from the dialog drop-down and confirm with OK. You now have a Service Fabric application that is configured to host your web application in a container.

Let's look at what really happened when we added orchestrator support. First, the only change to our existing ASP.NET Core Web Application project is that a PackageRoot directory and a Dockerfile are added. The PackageRoot directory contains Service Fabric service artifacts that build out the service package such as the Config directory representing the Config package and the ServiceManifest.xml that declaratively defines the service type, version, and any other service-specific metadata. The Dockerfile allows Docker to build out the image for your ASP.NET Core Web Application and is used by the build and deploy tasks.

Second, there's now a Service Fabric Application project in your solution. This declaratively defines the application type, version, and provides information about the services that make up that application. We'll mainly be interested in the ApplicationManifest.xml under the ApplicationPackageRoot.

Configure the SetupEntryPoint and Scripts

The SetupEntryPoint is a great place to execute any kind of service setup that might require elevated permissions or access on the node. More information can be found in the documentation here.

We'll need to create the Code directory under our service / ASP.NET Core Web Application project's PackageRoot directory. This location will hold our setup scripts as they will now be part of the service fabric service code package. The SetupEntryPoint executable host requires either .EXE, .BAT, or .CMD files; however, we want to run a PowerShell script. To do that, simply create a CMD or BAT file with nothing more than a line to invoke the PowerShell executable with our PowerShell script.

Our PowerShell script will be doing the work. The first thing it does is randomly generate a password that will be used to secure the exported PFX. It also depends on a new environment variable -- Custom_CertThumbprint -- that will help it find the correct certificate in the LocalMachine store. Once it's found the certificate, it will create a file containing the password and export a PFX secured with that password to the locations built from both the Service Fabric work directory and provided Custom_PfxFileName and Custom_PfxPasswordFileName environment variables.

To let Service Fabric know that we have setup scripts for this particular service, we'll need to update the ServiceManifest.xml by adding a SetupEntryPoint element under the CodePackage element. It will tell Service Fabric to start with the setup.cmd program and to run out of the CodePackage. This is important since our setup script expects our setup.ps1 script to be in the same directory as we're running. You can also use a ConsoleRedirection element for debugging only.

Service Fabric uses the ApplicationManifest.xml to control how services are run -- including the SetupEntryPoint. To run our setup scripts with elevated permissions, we'll need to update the Application Manifest. We'll do two things -- 1) define a SetupAdminUser in the Administrators group, and 2) add a RunAsPolicy element to our service to run the setup as the SetupAdminUser. This Principals element can be added to the end of the manifest, just after the DefaultServices element.

This RunAsPolicy element will need to be added to the Policies under the ServiceManifestImport for our encrypting / decrypting service. In this case, there's only one, but if there is more than one service in your application be sure to add the policy to the correct one. This policy specifies only the Setup entry point execute as this user, so the actual container host will continue to execute as the default user -- likely Network Service. We want to avoid increasing the privileges of the container host, so this is exactly what we want, but you could run the container host with administrator privileges as well by changing the EntrypPointType to Main or All for both to run elevated.

Test it Out

With this in place, the code we wrote in our encryption and decryption service will now be able to pull from a known location at runtime -- even when being hosted within the container on Service Fabric. Let's test it out to be sure. To test, just publish the Service Fabric application to a Service Fabric cluster that supports Windows containers.

Since the controller was written to expect HTTP POST actions with bodies containing strings to be either encoded or decoded, you can use your favorite HTTP client to test. Just replace the cluster URL with the correct URL hosting your application. Don't forget that you may need to update your load balancer to allow the port over which your service endpoint has been configured to pass through to your node(s). Also, make sure the certificate for which you're providing a thumbprint is actually installed in the local machine store on all of the nodes.

Encryption
Decryption

Summary

If you're only in need of certificates known to already be installed on the node, you should continue to use the guidance to import a certificate file through the CertificateRef element in the ContainerHostPolicies in your ApplicationManifest.xml. However, this blog covers an approach that allows greater control and flexibility at the time of service setup so that you can have runtime access to certificate files within your container. A complete working example is available on the AwkwardIndustries GitHub site in the servicefabric-containers-crypto repository.

Happy hacking!

Comments

Popular posts from this blog

Microsoft Flow: Expose Public APIs via Custom Connector

I love fitness trackers. I was excited to get the new Fitbit Alta HR, but some of my eagerness waned when I remembered why I hadn't used a Fitbit since my old Fitbit One -- I hated how it didn't integrate with any of my other applications I used to track my information. Fitbit does support some integration with partner apps, but just not with any apps that I happen to prefer and use. I'm an Android user. I've started to look at Google Fit as a hub for all of my fitness data, but I'm not completely sold on that yet. I've been using Nike+ Run Club since I jumped on the Nike bandwagon years ago and purchased the Nike+ iPod Sport Kit (back when I carried an iPod). I was so excited to have a sensor nestled in my shoe and a coach speaking to me as I was running! Fast-forward to now, and although my phone is the main sensor for everything, I still prefer to use the Nike+ applications to track my runs and training. Nike+ synchronizes data to Google Fit, but not to Fi…

Self-Hosting a Bot in Service Fabric with Katana

I recently participated in a small hackathon with some Microsoft ninjas where we were presented with the problem, How do you host a bot in Service Fabric? It seemed like an easy problem that had probably already been solved, but it wasn't as straightforward as we thought it might be. At its core from a technology perspective, a bot is really just a Web API. We didn't want to actually host IIS on Service Fabric, so we knew we would want to self-host. If you use the built-in Stateless Service ASP.NET Core template, you'll get a great Web API implementation that's wired up with a KestrelCommunictionListener for Service Fabric. But what if you want to use Katana -- Microsoft's .NET Framework OWIN implementation? Katana is a flexible set of components for building and hosting OWIN-based web applications on .NET Framework. Microsoft's Katana Project on GitHub Although ASP.NET Core has incorporated most of the Katana project's functionality, the project will st…