In this entry we are going to focus on creating the deployment files for our Nginx backend for hosting static websites to act as our frontend for our C5’s. Like before let’s make a checklist of what we want this to do:
- Nginx backend
- Nginx running not as root but in a security context of a user
- Sync the website from a Git repo rather than hosting it on the webserver”
Creating the website repo #
First, go to your organization
Create the repository, keep it public
Click on teams
Give our Operators Permission by clicking View
Now we are going to add some test files.
On our jumpbox we are going to create this structure and push it to this repo:
mkdir hostedsites
mkdir hostedsites/www.example.com
echo "<HTML><H1>This is our test page</H1></HTML>" > hostedsites/www.example.com/index.html
cd hostedsites
git init
git checkout -b main
git add .
git commit -m "first commit"
git remote add origin [email protected]:RedCorner/hostedsites.git
git push -u origin main
Building our base_deployment.yaml #
This will build our Nginx and sidecar deployment.
This deployment should be a bit easier since the Nginx container is already a common container but by default runs as root. Using our deployment file and a new config we can deploy this as a container in user context. We also want a GitOps-style workflow, that we can store then in a repository have the webserver fetch the static sites so we will add a side container of the another common container, git-sync.
Let’s start again with our base_deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-gitsync
labels:
app: nginx-gitsync
spec:
replicas: 1
selector:
matchLabels:
app: nginx-gitsync
template:
metadata:
labels:
app: nginx-gitsync
spec:
securityContext:
runAsUser: 1000
fsGroup: 1000
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 8080
name: httpport
args: ["nginx", "-g", "daemon off;"]
volumeMounts:
- name: webroot
mountPath: /usr/share/nginx/html
- name: nginx-cache
mountPath: /var/cache/nginx
- name: nginx-run
mountPath: /run
- name: nginx-conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
- name: git-sync
image: registry.k8s.io/git-sync/git-sync:v4.2.3
volumeMounts:
- name: webroot
mountPath: /git
env:
- name: GITSYNC_REPO
value: "https://gitea.dev.th3redc0rner.com/RedCorner/hostedsites" # <-- update to your repo
- name: GITSYNC_REF
value: "main"
- name: GITSYNC_ROOT
value: "/git"
- name: GITSYNC_LINK
value: "website"
- name: GITSYNC_PERIOD
value: "30s"
volumes:
- name: webroot
emptyDir: {}
- name: nginx-cache
emptyDir: {}
- name: nginx-run
emptyDir: {}
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: nginx.conf
path: nginx.conf
Lets’ break down this deployment file. It contains the Nginx webserver a side container for syncing the websites to an emptyDir.
Currently it’s pointed to our internal webhosting and the Nginx config can be modified to point to the root of the web frontend we want.
The Nginx config is customized to for the root of nginx to be our custom domain name and to listen on port 8080.
configmap.yaml #
This will represent the Nginx config file. Additionally it will put our website domain as the root of the Nginx web server.
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
data:
nginx.conf: |
events {}
http {
server {
listen 8080;
root /usr/share/nginx/html/website/www.example.com;
location / {
index index.html;
}
}
}
nginxwebservice.yaml #
The web service is places this is in a clusterIP so that it can be accessed by services only internal to our k8s cluster.
apiVersion: v1
kind: Service
metadata:
name: nginx-gitsync
labels:
app: nginx-gitsync
spec:
selector:
app: nginx-gitsync
ports:
- name: http
port: 80
targetPort: httpport
type: ClusterIP
Deploying and initial test #
Now let’s deploy all these files one by one to make sure we have no errors:
kubectl apply -f base_deployment.yaml
kubectl apply -f configmap.yaml
kubectl apply -f nginxwebservice.yaml
kubectl get pods
If we have no errors the output should be:
NAME READY STATUS RESTARTS AGE
adaptix-deployment-7fb9fb7945-9ldcl 1/1 Running 0 15h
nginx-gitsync-5dcb5776bd-wdzlq 2/2 Running 0 3m12s
Now to test if we are serving up the correct files:
kubectl port-forward services/nginx-gitsync 80:8080
In a separate terminal session from our jumpshost:
curl localhost:8080
We should see the output:
<HTML><H1>This is our test page</H1></HTML>
Adding a listener for AdaptixC2 #
Now we are going to create the listener for our agents in AdaptixC2 for the rest of the testing.
ssh [email protected] -L 0.0.0.0:4321:192.168.200.186:4321
Then we connect to our AdaptixC2 instance
Open the listener and right mouse click
Then create the listener with URI being /uri
For testing ,I copied the 2 snakeoil certificates that come with Kali to test the https backend.
Adding the ingress files and testing both deployments together #
Additionally 2 ingress files will be need for www.example.com .
http-website-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webroot
spec:
ingressClassName: nginx
rules:
- host: "www.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: nginx-gitsync
port:
number: 80
This will be the catch all and show our webserver on nginx. One thing to take notice of is that the annotations shows the backend to be http.
adaptix-c2-ingress.yaml :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: adatpixc2-https
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: "www.example.com"
http:
paths:
- pathType: Prefix
path: "/uri"
backend:
service:
name: opsadaptixwebservice
port:
number: 443
This points to any URI beginning with the prefix of /uri for example
https://www.example.com/uri/test
Will go to our the AdaptixC2. Also notice that this annotation tells the ingress controller the backend is https. The backend has to be https to generate https payloads.
Finally our jumpbox host we edit the /etc/host to include www.example.com pointing to one of our nodes in the cluster.
For example:
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
192.168.200.186 www.example.com
Finally lets test both ingress rules and make sure they route correctly.
Using curling on https://www.example.com
curl -k https://www.example.com
If correctly configured will output:
<HTML><H1>This is our test page</H1></HTML>
If use curl on C2 URI.
curl -k https://www.example.com/uri/test
The output should be this:
admin1@devadmin1:~/dev/nginx-gitsync$ curl -k https://www.example.com/uri/test
<!DOCTYPE html>
<html>
<head>
<title>ERROR 404 - Nothing Found</title>
</head>
<body>
<h1 class="cover-heading">ERROR 404 - PAGE NOT FOUND</h1>
</div>
</div>
</div>
</body>
</html>
This is AdaptixC2’s 404 page. Which means everything is being directed correctly.
I have created these resources on GitHub at:
https://github.com/TheR3dC0rner/adaptixdeploymentbuild
Next blog we are going to take these declarative deployments and turn them into deployable helm charts that we can deploy with GitOps as the third part of this helm series.
By outlining the needed parts and having a functional declarative version makes it easier to see what items need variables.