Samuel K. Addison

Securing Containerized FastAPI Web App on Azure

Azure containers Security

Starting a new project is always thrilling, especially when you get to explore new technologies. Recently, I had the chance to work with containers for a personal project hosted on Azure. It was an exciting opportunity to dive into this technology that's transforming how we develop, deploy, and manage applications. Containers offer a lightweight and efficient way to run software in isolated environments, but they also come with important security considerations. In this blog post, I'll share my experience integrating containers into my project and the security measures I implemented to protect my workflow.

What are containers?

According to Docker,

A container is a standardized unit of software that packages up code and all its dependencies, ensuring that the application runs quickly and reliably across different computing environments. A Docker container image is a lightweight, standalone, executable package that includes everything needed to run an application: code, runtime, system tools, system libraries, and settings.

I chose containers for my project because they had a smoother and more efficient setup as compared to virtual machines (VMs). Due to this, I could simply package the application together with related dependencies and deployment became much easier. Besides, I wanted to improve on my container deployment skills in the cloud, and using Docker containers on Azure provided an excellent opportunity to achieve this.

Setting Up Containers On Azure

To move my FastAPI-based REST API to the cloud and make it accessible to everyone, I decided to use Azure services for deployment and Docker for containerization. Here's a detailed explanation of how I configured containers on Azure:

Step 1: Build the Docker Container

📋

                        # Use the official Python image from the Docker Hub
                        FROM python:3.11-slim
                        
                        # Set the working directory
                        WORKDIR /code

                        # Copy the requirements file
                        COPY requirements.txt .

                        # Install the dependencies
                        RUN pip install --no-cache-dir --upgrade -r requirements.txt

                        # Copy the rest of the application code
                        COPY . .

                        # Run test on the app
                        RUN python -m pytest tests

                        # Expose the port the app runs on
                        EXPOSE 3100

                        # Command to run the application
                        CMD ["gunicorn", "delivery_fee_calculator.main:app"]
                    

Step 2: Setting Up Azure Container Registry (ACR)

To store and manage my Docker images, I set up an Azure Container Registry (ACR). Here are the steps I followed:

  1. I created an ACR instance using the Azure CLI.
  2. 📋
    
                                az acr create --resource-group myResourceGroup --name myACR --sku Basic
                            
  3. I logged in to my ACR instance.
  4. 📋
    
                                az acr login --name myACR
                            
  5. I tagged my local Docker image and pushed it to ACR.
  6. 📋
    
                                docker tag playground-fastapi myACR.azurecr.io/playground-fastapi:latest
                                docker push myACR.azurecr.io/playground-fastapi:latest
                            

Step 3: Setting Up Azure Service Principal

To automate deployment and manage access, I created an Azure Service Principal:

A lil talk about Service Principal: Service Principals are vital for developers aiming to securely manage and automate access to Azure resources. Instead of using a developer's account or creating individual entities, Service Principals offer a dedicated identity for applications and automated tools, keeping sensitive credentials safe. This method improves security by following the principle of least privilege, allowing developers to assign specific roles and permissions that restrict access to only necessary resources. Additionally, Service Principals simplify the automation process, making it easier to handle deployments and maintain consistency across environments. Regularly rotating credentials and monitoring activity help developers uphold strong security practices while seamlessly integrating with Azure services.

📋

                        az ad sp create-for-rbac --name myServicePrincipal --role contributor --scopes /subscriptions/{subscription-id}/resourceGroups/myResourceGroup
                    

Step 4: Deploying the Container to Azure Web App

Finally, I deployed the Docker container to Azure, making it accessible to everyone. It can be viewed here.

Implementing Security Measures

To protect sensitive data, I avoided hardcoding passwords and API keys in the source code. Instead, I used GitHub Actions secrets to store and manage these values. This kept sensitive information encrypted and only accessible by the necessary GitHub workflows. By setting up environment variables in GitHub, I kept credentials out of the version control system, lowering the risk of accidental exposure. I also used Azure Key Vault for managing secrets. It offers a secure way to store and access secrets, keys, and certificates. Integrating Key Vault with my deployment process ensured that sensitive information was retrieved safely at runtime. For container security, I built Docker images from official base images and kept them updated with the latest security patches. Regular scans of the images helped identify and address potential security issues early in development.

Challenges and solutions

While deploying my FastAPI project to Azure using containers, I faced several challenges that required debugging and problem-solving. One major issue was authentication when pushing the tagged Docker image to Azure Container Registry (ACR). I received an "unauthorized: authentication required" error because I hadn't logged into the registry before the push. Despite the service principal having the AcrPush role, access was denied. To fix this, I logged into the registry using the Azure CLI before pushing. The Azure Docker Login GitHub Action also helped manage this process smoothly.

Another challenge occurred during deployment to Azure App Service. I encountered an error: "The client {client_id} does not have authorization to perform action 'Microsoft.Web/sites/config/list/action'." This was because the service principal lacked the necessary role to modify the app service configuration. Initially, it had the AcrPush role, which was insufficient. I updated the service principal's role to Contributor, granting the needed permissions for deployment tasks like updating app settings and deploying the container image. I also dealt with image-building and network-related challenges. Ensuring Docker images were correctly built and tagged, and adhered to best practices, required careful attention. Network configuration, especially setting up private endpoints for secure access, added complexity that needed to be addressed for a secure and reliable deployment.

By systematically addressing these challenges and using Azure’s tools and best practices, I successfully deployed my FastAPI project to Azure. These experiences enhanced my understanding of Azure's capabilities and improved my skills in managing containerized applications and cloud deployments. This version is more concise and avoids cliches, focusing on the key points and solutions.

Conclusion

Deploying my FastAPI project to Azure using containers was eye-opening. It taught me a lot about cloud services and containerization. I learned how to secure deployments, set up CI/CD pipelines, and tackle technical hurdles along the way. Azure's tools were a big help. I used them to create a solid, scalable setup for my app. The process wasn't always smooth, but each problem I solved improved my skills. This project gave me hands-on experience with cloud development. It's definitely changed how I'll approach future projects. I feel much more confident working with containers and cloud services now. Overall, it was a challenging but rewarding experience. I'm excited to apply what I've learned to my next cloud-based project.

References