Carry on with what we have been doing in the previous post regarding deploying containerized apps to AKS. This post addresses some of the issues and how we are going to solve it. My main goal is to allow the app to have access to the on-prem resources using Windows Authentication for Linux containers, just like we would normally do with our apps running on an intranet network. I briefly mentioned our approach to achieve this using Azure VNet. In case you haven’t seen that post, here is the link.
So now we have the app up and running in AKS but it still isn’t able to talk to the database hosted on the on-prem network.
Two issues I have encountered:
- My Navision instance was configured to use NTLM authentication protocol. This means I can only call its OData/SOAP endpoints with this protocol type when using Windows Authentication.
- Containers are unable to connect to the on-prem SQL Server.
Fixing the NTLM authentication issue in NAV
The app I was deploying is a .Net Core 3.1 console, a Worker Service app to be more specific. I have SEQ logging setup in Azure so I can view the logs from there.
These lines of code allow me to send http requests to the OData endpoints using Windows Authentication. It’s a simple HttpClient
class that was injected to the repository.
services.AddHttpClient<INavODataRepository, NavODataRepository>(http =>
{
http.BaseAddress = new Uri(options.NavODataEndpoint);
http.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
http.DefaultRequestHeaders.Add("OData-Version", "4.0");
http.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()
{
Credentials = new NetworkCredential("username", "password", "your_domain")
});
The issue is that out of the box, Linux containers don’t use NTLM be default but my Navision instance running on the on-prem server was configured to accept NTLM coming from any source. The app in AKS cluster keeps throwing this error message:
Error: GSSAPI operation failed with error - An unsupported mechanism was requested. NTLM authentication requires the GSSAPI plugin 'gss-ntlmssp'
This was a hint so I googled gss-ntlmssp
and found out what I need to do. You have two ways to install this plugin:
- Either include it when you build the docker image, or
- You can install after the image has been deployed to AKS
The first approach is easier, you simply add these lines to the Dockerfile
. Remember to put the them inside the runtime stage. This link talks about multi-stage in Docker image build, and this one for the .Net core build.
RUN apt-get update
RUN apt-get -y install gss-ntlmssp
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base
LABEL maintainer="gpham@example.com"
# Workaround as per https://github.com/dotnet/corefx/issues/28961
RUN apt-get update
RUN apt-get -y install gss-ntlmssp
WORKDIR /app
#ENV NETCORE_ENVIRONMENT Development
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["Diamond.ItemSync.WorkerService/Diamond.ItemSync.WorkerService.csproj", "Diamond.ItemSync.WorkerService/"]
COPY ["Diamond.ItemSync.DataAccess/Diamond.ItemSync.DataAccess.csproj", "Diamond.ItemSync.DataAccess/"]
COPY ["Diamond.ItemSync.Domain/Diamond.ItemSync.Domain.csproj", "Diamond.ItemSync.Domain/"]
RUN dotnet restore "Diamond.ItemSync.WorkerService/Diamond.ItemSync.WorkerService.csproj"
COPY . .
WORKDIR "/src/Diamond.ItemSync.WorkerService"
RUN dotnet build "Diamond.ItemSync.WorkerService.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Diamond.ItemSync.WorkerService.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Diamond.ItemSync.WorkerService.dll"]
If somehow, you can’t add via the Dockerfile, you can login to the Linux container and install the plugin from there. I found using Visual Studio code is the simplest way to achieve this or you can just SSH to the container. Make sure you have Kubernetes extension installed on your VS Code.
With just a few steps, we can now have the app talking back to the on-prem Navision instance through its OData or Soap endpoints.
Fixing the issue: containers are unable to connect to the on-prem SQL Server
Normally, you could get a SQL Connection by having something similar to this:
public IDbConnection CreateOpenConnection()
{
try
{
SqlConnection sqlConnection = new SqlConnection(
"Data Source=SQL-INSTANCE;Initial Catalog=DB_NAME;User Id=readonly;Password=PASS_WORD;");
sqlConnection.Open();
return sqlConnection;
}
catch (System.Exception ex)
{
Log.Error("Failed to open SQL connection. Error {@Error}", ex);
throw ex;
}
}
This readonly user was configured to be authenticated using SQL Authentication (as we can’t pass user credentials through a connection for Windows auth). When I tried to execute this code inside my docker container, I got a time out exception. The issue is this line of code: sqlConnection.Open();
takes too long to response. It just keeps hanging forevever.
Here is why.
- My AKS node was created as a Ubuntu-based VM and my AKS container deployment was set to use Ubuntu but the docker image was built for Debian.
If you pay attention to the Dockerfile:
FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build WORKDIR /src
Both the base and the build images are for Debian docker-image build. This will still work but an issue was raised specific to the SQL connection. To fix this, we just need to pull the 3.1-bionic
base images from Microsoft. These are specific to building docker images for Ubuntu:
FROM mcr.microsoft.com/dotnet/core/runtime:3.1-bionic AS base
WORKDIR /app
ARG BUILD_ENV_ARG=development
ENV DOTNET_ENVIRONMENT=$BUILD_ENV_ARG
RUN echo "Installing gss-ntlmssp"
RUN apt-get update
RUN apt-get -y install gss-ntlmssp
RUN echo "Installing gss-ntlmssp complete"
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
It looks like you are using SQL Auth not Windows auth – User Id=readonly;Password=PASS_WORD;
You were right, that connection string for the IDbConnection was using SQL Auth. We can’t just enable windows auth through connection string using username and password. This is specific to SQL. You’re still able to pass AD credentials to your NAV., like what I did above.