Introduction

The rise in popularity of JavaScript has resulted in many changes, and the face of web development today is very different. In this article, we will be learning how to protect NodeJS applications at runtime using Accuknox’s open-source tooling. Accuknox hardens and protects workloads using Linux Security Modules (LSM) such as AppArmor and SELinux as well as eBPF for observability and networking enforcement.

NPM packages can contain supply chain threats

NodeJS applications use NPM packages..and these get installed with every Node.js installation by default NPM packages are known to have supply chain issues with malicious npm packages available

Attackers typically compromise the supply chain with their own malicious packages filled with backdoor code to the public repositories and wait for others to implement them in their applications. As we’ve seen time and time again, these threats can spread quietly for months or years without being noticed, allowing the attackers access to your workloads and sensitive data.

Setting up a NodeJS app to demonstrate runtime security

In this blog, we will demonstrate how to protect your Node.js application against such threats by implementing runtime security tools from AccuKnox. These will analyze the application and generate policies that can be enforced by Linux Security Modules (LSMs) like AppArmor and SELinux.

We’ll deploy a Node.js application to a Kubernetes environment and make use of AccuKnox opensource tools to generate zero trust runtime security policies, apply them to the workload and make a comparison on the states before and after installing AccuKnox agents.

The scenario's purpose is to demonstrate how AccuKnox opensource tools can be used to implement zero trust in an environment.

Let's create a Kubernetes cluster

gcloud container clusters create sample-cluster --zone us-central1-c 

We will deploy a simple application that servers on port 8000 and two endpoints /api/getServices and /api/checkSite which checks for a running service from the input and checks for the validity of a URL respectively.

Feel free to use the below deployment file to deploy the application to your k8s environment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-app
  labels:
    app: nodejs-app
spec:
  selector:
    matchLabels:
      app: nodejs-app
  template:
    metadata:
      labels:
        app: nodejs-app
    spec:
      containers:
        - name: nodejs-app
          image: knoxuser/nodejs-poc
          ports:
            - containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nodejs-app-svc
  name: nodejs-app-svc
spec:
  ports:
    - name: "80"
      port: 80
      targetPort: 8000
  selector:
    app: nodejs-app
  type: LoadBalancer
Nodejs-app

You can also deploy the same file from accuknox/samples GitHub repository by copy-pasting the below command:

kubectl apply -f https://raw.githubusercontent.com/accuknox/samples/main/nodejs-poc/k8s.yaml

This will create a deployment nodejs-app and a service nodejs-app-svc. We will take a look at whether the application is running and we have an external IP.

kubectl get po,svc

NAME                              READY   STATUS    RESTARTS   AGE
pod/nodejs-app-7f67bc45dc-jscb6   1/1     Running   0          85s

NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/kubernetes       ClusterIP      10.44.0.1      <none>        443/TCP        6h27m
service/nodejs-app-svc   LoadBalancer   10.44.12.105   34.135.54.3   80:30318/TCP   85s
Deployment NodeJs

We have our application running and exposed to the public internet via port 80 with IP 34.135.54.3

For the complete code please refer to the accuknox/samples GitHub page

Run-time protection using AccuKnox Open-source tools

Accuknox enables the ability to protect your workloads at run-time. Accuknox enables this by allowing you to configure policies (or auto-discover them) for application and network behavior using KubeArmor, Cilium and Auto Policy Discovery tools

KubeArmor

KubeArmor, is open-source software that enables you to protect your cloud workload at run-time.

The problem that KubeArmor solves is that it can prevent cloud workloads from executing malicious activity at runtime.  Malicious activity can be any activity that the workload was not designed for or is not supposed to do.

Cilium

Cilium, is an open-source project to provide eBPF-based networking, security, and observability for cloud-native environments such as Kubernetes clusters and other container orchestration platforms.

Auto Policy Discovery for your Node.js Application

Even though writing KubeArmor and CIlium (System and Network) policies are not a big challenge AccuKnox opensource has it simplified one step further by introducing a new CLI tool for Auto Discovered Policies. The Auto-Discovery module helps users by identifying the flow and generating policies based on it.

Discovering policies has never been better with Auto Discovery. In two simple commands, you can set up and generate policies without having any trouble.

We will use AccuKnox Auto Discovered Policies to generate zero-trust runtime security policies to secure our workload.

The auto-discovered zero trust runtime security policies can be generated using two commands. We will have to deploy Cilium and KubeArmor to the cluster and use a MySQL pod to store the discovered policies from where they can be downloaded with a single command.

First, we will use the below command to install all prerequisites.

curl -s https://raw.githubusercontent.com/accuknox/tools/main/install.sh | bash

Once the command is run successfully it will install the following components to your cluster:

  • KubeArmor protection engine
  • Cilium CNI
  • Auto policy discovery engine
  • MySQL database to keep discovered policies
  • Hubble Relay and KubeArmor Relay

Once this is down we can invoke the second script file which will download the auto-discovered policies from the MySQL database and store them locally. For this we will issue the below command:

curl -s https://raw.githubusercontent.com/accuknox/tools/main/get_discovered_yamls.sh | bash

You should be able to see the following output.

{
  "res": "ok"
}
Got 59 cilium policies in file cilium_policies.yaml
{
  "res": "ok"
}
Got 1 kubearmor policies in file kubearmor_policies_default_default_nodejs-app_bycnubnu.yaml
Got 1 kubearmor policies in file kubearmor_policies_default_explorer_knoxautopolicy_iwakqnyr.yaml
Got 1 kubearmor policies in file kubearmor_policies_default_explorer_mysql_zvbbfzqy.yaml
Cilium Policies

In mere seconds after installing executing auto policy discovery tool, it generated 59 Cilium policies and 3 curated KubeArmor policies.

Let us take a look at some of the autodiscovery policies

CIlium Policy #1

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: autopol-egress-gyipqtvnnrwyzmg
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      app: nodejs-app
  egress:
  - toEndpoints:
    - matchLabels:
        k8s-app: kube-dns
        k8s:io.kubernetes.pod.namespace: kube-system
    toPorts:
    - ports:
      - port: "53"
        protocol: UDP
Cilium Network Policy

Cilium Policy #2

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: autopol-egress-uhqpnyxadmphepg
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      app: nodejs-app
  egress:
  - toPorts:
    - ports:
      - port: "443"
        protocol: TCP
      - port: "80"
        protocol: TCP
Cilium Network Policy -2

From Policy #1 and #2, we can see that there is only external communication from the Node.js pod via ports 53, 80, and 443, and port 53 is used to communicate to kube-dns only.

If we apply this policy it will make sure that the necessary communication via ports 53,80 and 443 are only happening thereby lowering the chances of any network-based attacks or communication happening from the Node.js pod to the external world.

KubeArmor Policy #1

apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
  name: autopol-default-nodejs-app
  namespace: default
spec:
  severity: 1
  selector:
    matchLabels:
      app: nodejs-app
  process:
    matchPaths:
    - path: /bin/bash
    - path: /bin/cat
      fromSource:
      - path: /etc/init.d/procps
----------------------snip---------------------
    matchDirectories:
    - dir: /bin/
      fromSource:
      - path: /bin/sh
  file:
    matchPaths:
    - path: /bin/egrep
      fromSource:
      - path: /bin/grep
      - path: /usr/sbin/grep
----------------------snip---------------------
    matchDirectories:
    - dir: /
      fromSource:
      - path: /bin/bash
----------------------snip---------------------
    - dir: /usr/src/app/node_modules/
      fromSource:
      - path: /usr/local/bin/node
  network:
    matchProtocols:
    - protocol: raw
      fromSource:
      - path: /usr/bin/curl
----------------------snip---------------------
    - protocol: udp
      fromSource:
      - path: /usr/bin/curl
  action: Allow
---
Kubearmor policy

The curated KubeArmor policy is a lengthy one that allows all system calls which are happing in a healthy environment ergo denying all other system calls from happening due to any external factors.

Let us apply all these auto-generated policies and safeguard our workload.

ls -la | awk '{print $9}'
.
..
cilium_policies.yaml
kubearmor_policies_default_default_nodejs-app_bycnubnu.yaml
kubearmor_policies_default_explorer_knoxautopolicy_iwakqnyr.yaml
kubearmor_policies_default_explorer_mysql_zvbbfzqy.yaml
Auto-generated policy

We will apply the cilium_policies.yaml and kubearmor_policies_default_default_nodejs-app_bycnubnu.yaml since both are related to the application nodejs-app deployed on default namespace.

kubectl apply -f cilium_policies.yaml -f kubearmor_policies_default_default_nodejs-app_bycnubnu.yaml

ciliumnetworkpolicy.cilium.io/autopol-egress-gyipqtvnnrwyzmg created
ciliumnetworkpolicy.cilium.io/autopol-egress-uhqpnyxadmphepg created
kubearmorpolicy.security.kubearmor.com/autopol-default-nodejs-app created
Cilium policy

Note: Policy name will change according to the environment

The Policies in action

It is time to verify whether we were able to achieve zero trust by using the auto-discovered policies generated by AccuKnox opensource tools. To test this we will scan the application with some popular scanners.

Before that let us verify that the policies are applied correctly to the cluster

kubectl get cnp,ksp -A

NAMESPACE   NAME                                                           AGE
default     ciliumnetworkpolicy.cilium.io/autopol-egress-gyipqtvnnrwyzmg   26m
default     ciliumnetworkpolicy.cilium.io/autopol-egress-uhqpnyxadmphepg   25m

NAMESPACE   NAME                                                                AGE
default     kubearmorpolicy.security.kubearmor.com/autopol-default-nodejs-app   14m
Policies

Since our deployed application checks system information, there may be a chance that the application is vulnerable to 2021 Node.js vulnerabilities. One particular interest would be CVE-2021-21315 since it was a flaw targeted toward the Node.js package which uses system information checks.

The flaw, which has been assigned the number CVE-2021-21315, affects the "system information" npm component, which receives roughly 800,000 weekly downloads and has surpassed 34 million downloads since its debut.

Initiating the Attack scenario

We will initiate the attack by sending crafted requests to the application. Go to the application IP and give a query as name[]=$(echo -e 'test' > pwn.txt) and watch the pod to see if any new files are created under the working directory.

The complete request would look something like http://35.239.3.12/api/getServices?name[]=$(echo -e 'test' > pwn.txt)

kubectl exec -it $(kubectl get po -lapp=nodejs-app -o name | cut -d / -f 2) -- watch ls -la

Every 2.0s: ls -la                                                                                               nodejs-app-7f67bc45dc-jscb6: Tue Feb 22 20:31:35 2022

total 44
drwxr-xr-x 1 root root  4096 Feb 21 15:17 .
drwxr-xr-x 1 root root  4096 Feb 21 15:13 ..
-rw-r--r-- 1 root root   517 Feb 21 15:09 index.js
drwxr-xr-x 1 root root  4096 Feb 21 15:09 node_modules
-rw-r--r-- 1 root root 14563 Feb 21 15:09 package-lock.json
-rw-r--r-- 1 root root   321 Feb 21 15:09 package.json
Attack scenario

The auto-discovered policies applied earlier are preventing the pod from creating new files under the working directory since it is not normal or safe behavior.

We will delete the KubeArmor and CIlium policy and rerun the exploit and compare the state.

kubectl delete cnp --all    
   
ciliumnetworkpolicy.cilium.io "autopol-egress-gyipqtvnnrwyzmg" deleted
ciliumnetworkpolicy.cilium.io "autopol-egress-uhqpnyxadmphepg" deleted
Kubearmor and cilium policy
kubectl delete ksp --all 

kubearmorpolicy.security.kubearmor.com "autopol-default-nodejs-app" deleted

We will again go to the application IP and give a query as name[]=$(echo -e 'test' > pwn.txt) and watch the pod to see if any new files are created under the working directory.

kubectl exec -it $(kubectl get po -lapp=nodejs-app -o name | cut -d / -f 2) -- watch ls -la

Every 2.0s: ls -la                                                                                               nodejs-app-7f67bc45dc-jscb6: Tue Feb 22 20:40:43 2022

total 52
drwxr-xr-x 1 root root  4096 Feb 22 20:47 .
drwxr-xr-x 1 root root  4096 Feb 21 15:13 ..
-rw-r--r-- 1 root root   517 Feb 21 15:09 index.js
drwxr-xr-x 1 root root  4096 Feb 21 15:09 node_modules
-rw-r--r-- 1 root root 14563 Feb 21 15:09 package-lock.json
-rw-r--r-- 1 root root   321 Feb 21 15:09 package.json
-rw-r--r-- 1 root root     8 Feb 22 20:47 pwn.txt
Working directory

Let's examine the content  pwn.txt as well

kubectl exec -it $(kubectl get po -lapp=nodejs-app -o name | cut -d / -f 2) -- cat pwn.txt

-e test
pwn,txt

We could see that the attack happened after we deleted the policies which were auto-discovered in a safe environment. This means applying the auto-discovered policies ensured that the workload had been protected at runtime.

Accuknox's policy templates repository

Accuknox's policy templates is an open-source repo that also contains a wide range of attack prevention techniques including MITRE, as well as hardening techniques for your workloads. Please visit

NodeJs cover animate

GitHub - kubearmor/policy-templates: Community curated list of System and Network policy templates for the KubeArmor and Cilium to download and apply policy templates.

Conclusion

Achieving zero trust is the next big thing everyone is trying to accomplish. With AccuKnox an organization can achieve zero trust and stay there with relative ease by making use of opensource tools