Enhancing DevSecOps: Automating Security in Azure Node.js WebApp Containers with GitHub Actions and Trivy

Vikum Jayalath
5 min readSep 3, 2023

In our ever-evolving digital landscape, containerization has emerged as the preferred method for efficient application deployment and management. Microsoft Azure WebApps, in conjunction with containerization technologies such as Docker, offer a versatile and scalable platform for hosting web applications. Nevertheless, as organizations increasingly embrace containerization, the imperative to secure these deployments has grown exponentially.

Enter GitHub Actions, a robust automation and CI/CD platform that seamlessly facilitates the integration of security measures into your Azure WebApp container deployments. By harnessing the capabilities of GitHub Actions and incorporating the powerful Trivy scanner, you can streamline security scans, vulnerability assessments, and deployment processes, all the while ensuring the resilience and protection of your containerized applications against potential threats.

Here is the proposed solution architecture that we plan to implement. From the Azure side, I have already set up an Azure WebApp, an Azure Container Registry (ACR), and a User Managed Identity to facilitate image pulling. For the Demo WebApp we will be using NodeJS.

I’ve established an OpenID Connect (OIDC) connection between Azure and a specific GitHub repository for authentication purposes. For step-by-step instructions on setting up this OIDC connection between Azure and GitHub, please visit the following link.

To provision the WebApp, Container Registry, and Managed Identity using Azure CLI, you can leverage the following code snippet by incorporating your specific configurations:

# Please replace placeholders with your actual configuration values

# Create a Resource Group (if not already created)
az group create --name YourResourceGroupName --location YourLocation

# Create a Web App with Node.js 18 LTS:
az webapp create \
--name YourWebAppName \
--resource-group YourResourceGroupName \
--plan YourAppServicePlanName \
--runtime "NODE|18-lts"

# Create an Azure Container Registry (ACR):
az acr create \
--name YourAcrName \
--resource-group YourResourceGroupName \
--sku Standard

# Configure Managed Identity for ACR:
az webapp identity assign \
--name YourWebAppName \
--resource-group YourResourceGroupName

# Get the web app's identity principal ID
principalId=$(az webapp identity show --name YourWebAppName --resource-group YourResourceGroupName --query principalId --output tsv)

# Grant ACR pull permissions to the web app's identity
az acr update \
--name YourAcrName \
--resource-group YourResourceGroupName \
--assign-identity $principalId

Once Azure Resources have been created and verified, both YAML and DockerFile must be created.

DockerFile (Please change the configuration as required)

FROM node:16-alpine

WORKDIR /

COPY . .

RUN npm install

RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]

YAML File (Please change the configuration as required)

I have included the majority of the values as GitHub secrets, and you have the flexibility to insert your own values as required. However, I want to emphasize the importance of not including any sensitive information within the YAML file.

name: Build & Publish
on:
pull_request:
branches:
- main
types:
- closed

permissions:
id-token: write
contents: read

jobs:
build_deploy:
if: github.event.pull_request.merged == true #run only if merged

name: BUILD & DEPLOY
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "AZ OIDC Login"
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: "Build Image"
uses: docker/build-push-action@v3.2.0
with:
context: .
push: false
tags: ${{ secrets.ACR_NAME }}.azurecr.io/demo-app:${{ github.sha }}

- name: Run Trivy vulnerability Scanner Before Push
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ secrets.ACR_NAME }}.azurecr.io/demo-app:${{ github.sha }}"
format: "table"
exit-code: '1'
ignore-unfixed: true
vuln-type: "os,library"
severity: "CRITICAL,HIGH"

- name: "Login to Azure ACR"
uses: azure/docker-login@v1
with:
login-server: ${{ secrets.ECR_LOGIN_SERVER }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}

- name: "Push Scanned Image to ACR"
run: |
docker push ${{ secrets.ECR_LOGIN_SERVER }}/demo-app:${{ github.sha }}

- name: "Deploy to WebApp"
uses: azure/webapps-deploy@v2
with:
app-name: "demo-app"
images: "${{ secrets.ACR_NAME }}.azurecr.io/demo-app:${{ github.sha }}"

- name: Azure logout
run: |
az logout

Explanation of the YAML

Trigger: This workflow is triggered when a pull request to the main branch is closed (merged or closed without merging). It will only run when the pull request is closed.

Permissions: This section specifies the OIDC permissions required for this workflow. It has two permissions:

id-token: It grants write access.

contents: It grants read access to the contents of the repository.

Job: build_deploy: This job is named “BUILD & DEPLOY” and will run on an Ubuntu latest runner.

Steps:

a. actions/checkout@v2: This step checks out the code from the repository.

b. AZ OIDC Login: This step logs into Azure using Azure OIDC (OpenID Connect) authentication. It uses secrets for client-id, tenant-id, and subscription-id.

c. Build FE Image: This step builds a Docker image for a front-end application. It uses the docker/build-push-action@v3.2.0 action and sets the context to the current directory. It does not push the image but tags it with a reference that includes the GitHub SHA.

d. Run Trivy vulnerability Scanner Before Push: This step scans the Docker image for vulnerabilities using Trivy. It specifies the image reference to scan, the output format as a table, and to ignore unfixed vulnerabilities. It scans for vulnerabilities in the operating system and libraries with a severity of CRITICAL or HIGH.

e. Login to Azure ACR: This step logs into the Azure Container Registry (ACR) using the provided credentials (username and password).

f. Push Scanned Image to ACR: This step pushes the Docker image (built in step c) to the Azure Container Registry.

g. Deploy to WebApp: This step deploys the Docker image to an Azure Web App named “demo-app.” It specifies the image reference to deploy from the Azure Container Registry.

h. Azure logout: This step logs out from the Azure CLI.

In the dynamic world of containerized application deployment, security is non-negotiable. GitHub Actions, in tandem with the formidable Trivy scanner, empowers organizations to fortify their Azure WebApp container deployments seamlessly.

This potent combination streamlines security scans, vulnerability assessments, and deployment processes, ensuring the robust protection of your applications in an ever-evolving digital landscape. It’s the ultimate assurance for resilient, secure, and efficient containerized applications.

--

--

🌐 Cloud Solutions Architect | ☁️ DevOps Specialist | 🔒 Security Enthusiast