Skip to content

Deploy BlueLock on Azure Container Apps¶

Overview

Build an Azure Container Apps test environment with Terraform, onboard it to AccuKnox as a VM cluster and block a command in a running replica.

Workflow at a Glance¶

How the Deployment Fits Together¶

Azure hides the Kubernetes API server of Container Apps, so you cannot onboard it as a Kubernetes cluster. BlueLock uses a VM cluster: the control plane runs on a VM and the replicas act as worker nodes.

Azure term What it is
Container Apps environment A resource boundary for CPU, memory and networking
Container app Like a ReplicaSet. A replica is a pod with one or more containers

Prerequisites¶

Requirement Used for
Azure access Creating the resource group, virtual network, VM and Container Apps environment
AWS CLI, configured The login to public ECR in Step 2
Terraform Running both scripts
Docker The docker login command in Step 2
git Cloning the BlueLock repository
socat Connecting to the container app in Step 6
AccuKnox account Getting a join token and viewing the results

Step 1. Get a Join Token From AccuKnox¶

In AccuKnox, open Settings, then Manage Cluster, then Onboard. Select the cluster type VM, enter a name and copy the join token from the knoxctl onboard vm cp-node command. Terraform runs that command on the control plane VM, so you only need the token.

Step 2. Sign in to AWS and Public ECR¶

Enter your Access Key ID and Secret Access Key.

aws configure

Log in to public ECR.

aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/k9v9d5v2

Step 3. Create the Base Infrastructure¶

Clone the repository and open azure-container-apps.

git clone https://github.com/accuknox/bluelock.git
cd bluelock/terraform/azure-container-apps

Create the base infrastructure. This takes about 5 minutes.

cd 1-infra
terraform init
terraform apply -var="vm_token=YOUR_JOIN_TOKEN"

The script creates the resource group, virtual network, control plane VM and Container Apps environment. It then installs Docker and knoxctl on the VM and onboards it as the control plane with your token.

Variables you can change

Override a default with -var, for example -var="location=<REGION>".

Variable Default Notes
location Central India Azure region
resource_group_name rg-azure-test Resource group that holds everything
name_prefix bluelock-aca Prefix for resource names
vm_username testuser Login name on the control plane VM
vm_password Has a default Set your own value with -var
vm_token None Required. Your AccuKnox join token

The script prints these outputs: resource_group_name, vnet_name, vm_public_ip, vm_private_ip, container_app_environment_id and container_app_environment_name.

Step 4. Create the Container App¶

Create the container app. The command reads the VM public IP from Step 3.

cd ../2-app
terraform init
terraform apply \
    -var="vm_public_ip=$(cd ../1-infra && terraform output -raw vm_public_ip)"

Terraform creates a container app with 2 replicas. Each runs the bluelock init container, armored-container and kubearmor-vm-adapter.

Variables you can change
Variable Default Notes
resource_group_name rg-azure-test Use the same value as in 1-infra
container_app_environment bluelock-aca-container-app-env Environment to deploy into
container_app_name bluelock-container-app Name of the container app
vm_public_ip None Required. Public IP of the control plane VM
Set up manually instead of using Terraform

Replaces Steps 3 and 4. Create these Azure resources in order.

1. Virtual network

  1. Create a virtual network with 2 subnets. Make one a normal subnet and delegate the other to Microsoft.App/environments, because the Container Apps environment joins this network. See the Azure networking guide for the subnet size.
  2. For example, create default with size /24 and aca-subnet with size /20.

2. Virtual machine

  1. Create an Ubuntu machine in the normal subnet of the virtual network.
  2. Open inbound access to all ports from everywhere, for every protocol, in the VM's network security group. Do this for testing only. Do not do this in production. Open only the ports that the agents need.
  3. Install Docker.
  4. Onboard the VM as the control plane of a VM cluster in AccuKnox.

3. Container app

  1. Create a container app.
  2. When it asks for a Container Apps environment, create a new one. In the Network tab, integrate it with the delegated subnet that you created.
  3. In Containers, select Docker registry and enter the image delusionaloptimist/ka-socat:latest.
  4. In Monitoring, select stream logs without storing.
  5. In Ingress, enable ingress. Allow TCP from everywhere on port 1337 with target port 1337. Leave the other settings unchanged.
  6. Create the container app.

4. Configure the container app

  1. In the left panel, open Export template under Automation and choose Deploy.

    The Azure Export template page for the container app with the Deploy button highlighted

  2. Choose Edit template. Replace the template object in the JSON with the following. Replace <CONTROL_PLANE_IP> in the VM adapter arguments with the public IP of the VM.

    "template": {
        "containers": [
            {
                "image": "public.ecr.aws/k9v9d5v2/accuknox-bluelock/ka-socat-demo:latest",
                "name": "armored-container",
                "command": [
                    "/kubearmor/bluelock",
                    "socat",
                    "TCP-LISTEN:1337,reuseaddr,fork",
                    "EXEC:bash,pty,stderr,setsid,sigint,sane"
                ],
                "args": [],
                "env": [
                    {
                        "name": "CONTAINERNAME",
                        "value": "armored-container"
                    },
                    {
                        "name": "CONTAINERIMAGE",
                        "value": "public.ecr.aws/k9v9d5v2/accuknox-bluelock/ka-socat-demo:latest"
                    },
                    {
                        "name": "K8S",
                        "value": "false"
                    }
                ],
                "resources": {
                    "cpu": 0.5,
                    "memory": "1Gi"
                },
                "probes": [],
                "volumeMounts": [
                    {
                        "volumeName": "kubearmor-dir",
                        "mountPath": "/kubearmor"
                    }
                ]
            },
            {
                "image": "docker.io/accuknox/vm-adapter:v0.1.29",
                "name": "kubearmor-vm-adapter",
                "command": [],
                "args": [
                    "--kubearmor-addr=localhost:32767",
                    "--relay-server-addr=<CONTROL_PLANE_IP>:32768",
                    "--node-state-refresh=4",
                    "--policy-list-enable=true",
                    "--policy-list-relay-frequency=5m0s",
                    "--sia-addr=<CONTROL_PLANE_IP>:32769",
                    "--pea-addr=<CONTROL_PLANE_IP>:32770",
                    "--harden-addr=<CONTROL_PLANE_IP>:32771",
                    "--annotation-topic=annotation",
                    "--annotation-enable=true"
                ],
                "env": [
                    {
                        "name": "test",
                        "value": "true"
                    }
                ],
                "resources": {
                    "cpu": 0.5,
                    "memory": "1Gi"
                },
                "probes": [],
                "volumeMounts": [
                    {
                        "volumeName": "opt-storage",
                        "mountPath": "/opt"
                    }
                ]
            }
        ],
        "initContainers": [
            {
                "image": "public.ecr.aws/k9v9d5v2/accuknox-bluelock:v1.0.0",
                "name": "bluelock",
                "resources": {
                    "cpu": 0.25,
                    "memory": "0.5Gi"
                },
                "volumeMounts": [
                    {
                        "volumeName": "kubearmor-dir",
                        "mountPath": "/kubearmor"
                    }
                ]
            }
        ],
        "scale": {
            "minReplicas": 2,
            "maxReplicas": 2,
            "cooldownPeriod": 300,
            "pollingInterval": 30
        },
        "volumes": [
            {
                "name": "kubearmor-dir",
                "storageType": "EmptyDir"
            },
            {
                "name": "opt-storage",
                "storageType": "EmptyDir"
            }
        ]
    }
    
  3. Save and deploy this revision.

  4. After the deployment finishes, open Log stream in the left panel. It shows the logs of both the armored-container and the kubearmor-vm-adapter. Check that neither shows errors.

    The Azure Log stream page for the kubearmor-vm-adapter container, showing connections to the relay server and no errors

Step 5. Confirm the Nodes Appear in AccuKnox¶

In AccuKnox, the replicas appear as nodes next to the control plane VM.

The AccuKnox cluster panel for the Azure Container Apps cluster, showing the control plane VM and two container app replicas as nodes

Step 6. Test Enforcement With a Block Policy¶

  1. In the Azure portal, copy the container app's application URL. It looks like <container-app-name>.<hostname>.<region>.azurecontainerapps.io.

  2. Connect to the container app.

    socat - TCP:<address>:1337
    

    You cannot choose the replica

    The app has one URL and Azure's load balancing picks the replica. You cannot choose which replica you reach.

  3. Run this. It succeeds because no policy exists yet.

    ls /etc/ssl/certs
    

    A terminal session that lists the files in /etc/ssl/certs on the container app

  4. In AccuKnox, open Runtime Protection, then Policies, and create this policy. ksp-block-file-dir-recursive.yaml holds the same rules. Activate it after you save.

    apiVersion: security.kubearmor.com/v1
    kind: KubeArmorPolicy
    metadata:
      name: bluelock-test-policy
      namespace: container_namespace
    spec:
      action: Block
      file:
        matchDirectories:
        - dir: /etc/ssl/
          recursive: true
        - dir: /etc/pki/
          recursive: true
        - dir: /usr/local/share/ca-certificates/
          recursive: true
      message: Credentials modification denied
      selector:
        matchLabels:
          kubearmor.io/container.name: armored-container
      severity: 1
      tags:
      - MITRE
      - MITRE_T1552_unsecured_credentials
    
  5. Run the command again. It now fails.

    ls /etc/ssl/certs
    

    A terminal session where ls /etc/ssl/certs returns Permission denied on the container app

  6. Open the alert in AccuKnox.

    The AccuKnox alert Credentials modification denied with action Block and result Permission Denied

Step 7. Destroy the Test Environment When You Finish¶

Destroy the container app first, then the base infrastructure. Both folders need the same variables that you passed to terraform apply.

cd bluelock/terraform/azure-container-apps/2-app
terraform destroy \
    -var="vm_public_ip=$(cd ../1-infra && terraform output -raw vm_public_ip)"
cd ../1-infra
terraform destroy -var="vm_token=YOUR_JOIN_TOKEN"