Azure, Debugging, Web, WebApi

Debugging a managed identity connection to Azure SQL in Azure App Service using containers

So you’ve been eager to deploy your containerized web application that you’ve been working on for a while, to Azure App Service. You follow the best security practices which means you’ve setup your connection to your Azure SQL database using a managed identity. Upon loading your application, you get a dreadful exception that you can’t connect to the database. You then wonder, is this a database permission problem or some another problem, such as the application cannot connect to the database using its managed identity. This problem is even tougher to diagnose in applications such as Custom Off the Shelf (COTS) that you don’t have the source code.

Rest assured, this happen to the best of us (yes, even me!). In this post, I will give you some techniques to help you determine if the problem is indeed a problem between the web app and the Azure SQL database, through the use of managed identities, without having to modify any code in your application, keeping the debugging strictly on the infrastructure side.

All files for this blog post can be found at https://github.com/DOMZE/AppServiceContainerDebug

Using a Linux container

The steps required can be listed as follow:

  1. Create an image that
    • has a dummy website (static page) hosted using NGINX or your web server of choice
    • contains SSHD that is configured specifically for App Service
    • contains sqlcmd (either installed through binaries or through the compiled binaries)
  2. Push your image to Azure Container Registry or your registry of choice that the App Service can consume
  3. Let the container and web app start
  4. Access the WebSSH option in the Kudu (Advanced Tools) where you should be able to have a shell
  5. Get an access token (AT) for database.windows.net using the Azure Instance Metadata Service (IMDS)
  6. Use that token as credentials using sqlcmd

Creating the container image

Using Docker, you can use the following Dockerfile to create a static website using NGINX, install and configure SSHD for App Service and lastly install sqlcmd.

You can find more information on SSHD in Linux containers for App Service in the documentation. A full article was also created by the Azure team and can be viewed here, but for simplicity I will go straight to the point with a more minimalist example, not using any specific programming languages (such as C# or java or python).

As described in the Azure team article, some files will be needed to create the image, In my case, there is 5:

  • A sshd_config that will configure SSHD
  • A entrypoint.sh file that will start NGINX along with SSHD
  • A static HTML file that will be the index.html for NGINX
  • A helper script to be able to connect to the database using sqlcmd
  • The Dockerfile

sshd_config

As shown in the documentation, here’s a sshd_config file to configure OpenSSH. Create it along side your Dockerfile:

This configuration doesn’t allow external connections to the container. Port 2222 of the container is accessible only within the bridge network of a private virtual network and isn’t accessible to an attacker on the internet.
This information can be important to share to your security teams.

Entrypoint shell script

Create the following entrypoint.sh along side your Dockerfile. Note the (optional) env vars line. As mentioned in the Azure blog post, by default with custom Docker images, when SSH’ing into a container, only a few certain environment variables may be seen when trying to use something like env or printenv.

To create the entrypoint script, you need to understand NGINX Dockerfile. Using the Debian flavor, you can see that the entrypoint is /docker-entrypoint.sh and it’s passing 3 parameters using the CMD instruction.

HTML index file

A simple HTML file can be created. Save it alongside your Dockerfile and name it index.html. Here you can be creative. If you feel adventurous, you can use Generative AI to create yourself a HTML static file.

Dockerfile

To create the image you will use in the web app, create a Dockerfile with the following content. Once saved, run the command to build your image. Note that there isn’t a native flavor for Debian as per the documentation, so I opted to use the go binary instead.

helper script

Here is a helper script that can be used to call all the necessary commands to connect to the database.

  • The first parameter is the server name (from i.e. <servername>.database.windows.net)
  • The second parameter is the database name
  • The third parameter is the client id for the managed identity if you are using a user assigned identity

Last steps…

Once you’ve built, pushed the container to your registry and deployed the container to your app service, you can select SSH in the app service pane and you will be connected to the WebSSH. You can then navigate to /home and call the helper shell script.

Using a Windows container

Similarly to the Linux container instructions, the steps required can be listed as follow:

  1. Create an image that
    • has a dummy website (static page) hosted using IIS or your web server of choice
    • contains sqlcmd through the compiled binaries
  2. Push your image to Azure Container Registry or your registry of choice that the App Service can consume
  3. Let the container and web app start
  4. Open up the Kudu (Advanced Tools) and navigate to the SSH tab. SSH doesn’t need to be installed in Windows image unlike in Linux based images. It is prebaked for you in the platform.
  5. Get an access token (AT) for database.windows.net using the Azure Instance Metadata Service (IMDS)
  6. Use that token as credentials using sqlcmd
If you are building the container on Windows, you may encounter an error such as “Invoke-WebRequest : The remote name could not be resolved”. This happened to me.
It seems there is a problem with Docker when there are multiple networking adapters (Ethernet, Wi-Fi, etc.) present on the host.

You can fix this by following the post in the Docker issue here.

Dockerfile

When building Windows containers, make sure to pick a supported parent image. See the list in the documentation.

Helper script

The helper script is the PowerShell equivalent to the Linux helper script above. Use it with the same parameters, that is, ServerName, Database and optionally a ClientId if you are using a user assigned identity.

Conclusion

With the above, you can keep those images handy in your Container Registry to diagnose managed identity connection problems. Hope this can accelerate your debugging and help you eliminate credentials when connecting to your databases, making your processes more secure.