Skip to content

Commit 1beca46

Browse files
committed
docs: Adds installation guide for NIC and waf
Closes #7416 * Adds installation guide for NIC and waf * users don't need to leave the document * uses the wafv5 json from the examples directory * users are guided to download their key, certificate, and jwt files, and set up license secrets and image pull secrets * users are guided on copying their compiled policy bundle onto the deployment * users are shown how to compile a json into a policy bundle * example applications are deployed and waf is used in those * there are examples of successful and expectedly failed requests
1 parent 9edb29b commit 1beca46

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
---
2+
title: Deploy NIC and WAF to Kubernetes using Helm
3+
toc: true
4+
weight: 500
5+
type: how-to
6+
product: NIC
7+
docs: DOCS-609
8+
---
9+
10+
## How To: Build and Use a Local NGINX App Protect Docker Image to Compile WAF Policies and Integrate with NGINX Plus Ingress Controller
11+
12+
This document outlines how to:
13+
14+
- Set up license secrets to enable deployment into kubernetes
15+
- Use a WAF docker image to compile a policy JSON file into a compiled bundle
16+
- Set up PersistentVolumes that the deployed WAF instance can access to read the bundle from
17+
- Deploy NGINX Plus Ingress Controller with NAP in Kubernetes
18+
- Test example services to validate that the WAF policies work
19+
20+
---
21+
22+
## Step 1: Prepare Secrets and Credentials (From MyF5 Portal)
23+
24+
Download the following files from your [MyF5 Portal](https://my.f5.com):
25+
26+
- `nginx-repo.crt`
27+
- `nginx-repo.key`
28+
- `nginx-jwt.token`
29+
30+
Store these files locally:
31+
32+
```
33+
├── nginx-repo.crt
34+
├── nginx-repo.key
35+
└── nginx-repo.jwt
36+
```
37+
38+
## Step 2: Pull the NGINX App Protect WAF Compiler image
39+
40+
Log into the nginx private registry using your jwt file and the password `none` which you will have to type in when
41+
asked:
42+
43+
```bash
44+
$ docker login private-registry.nginx.com --username=$(cat nginx-repo.jwt)
45+
46+
i Info → A Personal Access Token (PAT) can be used instead.
47+
To create a PAT, visit https://app.docker.com/settings
48+
49+
50+
Password:
51+
Login Succeeded
52+
```
53+
54+
Once that's done, pull the `waf-compiler` image with:
55+
56+
```bash
57+
$ docker pull private-registry.nginx.com/nap/waf-compiler:5.6.0
58+
```
59+
60+
---
61+
62+
## Step 3: Compile WAF Policy from JSON to Bundle
63+
64+
Download the [provided WAF Policy JSON](https://raw.githubusercontent.com/nginx/kubernetes-ingress/main/tests/data/ap-waf-v5/wafv5.json):
65+
66+
```bash
67+
curl -LO https://raw.githubusercontent.com/nginx/kubernetes-ingress/main/tests/data/ap-waf-v5/wafv5.json
68+
```
69+
70+
Use your pulled NAP Docker image (`private-registry.nginx.com/nap/waf-compiler:5.6.0`) to compile the policy bundle:
71+
72+
```bash
73+
# Using your newly created image
74+
docker run --rm \
75+
-v $(pwd):$(pwd) \
76+
private-registry.nginx.com/nap/waf-compiler:5.6.0 \
77+
-p $(pwd)/wafv5.json \
78+
-o $(pwd)/compiled_policy.tgz
79+
```
80+
81+
After this command, your workspace should contain:
82+
83+
```
84+
├── nginx-repo.crt
85+
├── nginx-repo.key
86+
├── nginx-repo.jwt
87+
├── wafv5.json
88+
└── compiled_policy.tgz
89+
```
90+
91+
---
92+
93+
## Step 4: Create the persistent volume and claim to store the policy bundle
94+
95+
Save the following configuration data as `pvc.yml` in the same directory.
96+
97+
```yaml
98+
apiVersion: v1
99+
kind: PersistentVolume
100+
metadata:
101+
name: task-pv-volume
102+
labels:
103+
type: local
104+
spec:
105+
storageClassName: manual
106+
capacity:
107+
storage: 1Gi
108+
accessModes:
109+
- ReadWriteOnce
110+
hostPath:
111+
path: "/tmp/"
112+
113+
---
114+
apiVersion: v1
115+
kind: PersistentVolumeClaim
116+
metadata:
117+
name: task-pv-claim
118+
spec:
119+
storageClassName: manual
120+
accessModes:
121+
- ReadWriteOnce
122+
resources:
123+
requests:
124+
storage: 1Gi
125+
```
126+
127+
This sets up a 1Gi disk and attaches a claim to it that you will reference in the NIC deployment chart.
128+
129+
Create these with:
130+
```bash
131+
kubectl apply -f pvc.yaml
132+
```
133+
134+
Verify that the persistent volume and claim are created:
135+
136+
```bash
137+
# For the persistent volume
138+
kubectl get pv
139+
140+
# For the persistent volume claim
141+
kubectl get pvc
142+
```
143+
144+
## Step 5: Deploy NGINX Plus NIC Controller with NAP Enabled using Helm
145+
146+
Add the official NGINX Helm repository:
147+
```bash
148+
helm repo add nginx-stable https://helm.nginx.com/stable
149+
helm repo update
150+
```
151+
152+
Create Kubernetes Docker and licensing secrets:
153+
```bash
154+
kubectl create secret \
155+
docker-registry regcred \
156+
--docker-server=private-registry.nginx.com \
157+
--docker-username=$(cat nginx-repo.jwt) \
158+
--docker-password=none
159+
160+
kubectl create secret \
161+
generic license-token \
162+
--from-file=license.jwt=./nginx-repo.jwt \
163+
--type=nginx.com/license
164+
```
165+
166+
Install the required CRDs for NGINX Ingress Controller:
167+
168+
```bash
169+
kubectl apply -f https://raw.githubusercontent.com/nginx/kubernetes-ingress/v5.0.0/deploy/crds.yaml
170+
```
171+
172+
Using helm, install NGINX Ingress Controller
173+
174+
```bash
175+
helm upgrade nic nginx-stable/nginx-ingress \
176+
--set controller.image.repository="private-registry.nginx.com/nginx-ic-nap-v5/nginx-plus-ingress" \
177+
--set controller.image.tag="5.0.0-alpine-fips" \
178+
--set controller.nginxplus=true \
179+
--set controller.appprotect.enable=true \
180+
--set controller.appprotect.v5=true \
181+
--set controller.appprotect.volumes[2].persistentVolumeClaim.claimName="task-pv-claim" \
182+
--set controller.serviceAccount.imagePullSecretName="regcred" \
183+
--set controller.volumeMounts[0].name="app-protect-bundles" \
184+
--set controller.volumeMounts[0].mountPath="/etc/app_protect/bundles/" \
185+
--install
186+
```
187+
188+
Verify deployment success:
189+
```bash
190+
kubectl get pods
191+
```
192+
193+
---
194+
195+
## Step 6: Copy the policy bundle into the running instance
196+
197+
Get the name of the pod from the `kubectl get pods` command above.
198+
199+
Copy the file into the `nginx-ingress` container within the pod:
200+
201+
```bash
202+
kubectl cp ./compiled_policy.tgz \
203+
<pod name>:/etc/app_protect/bundles/compiled_policy.tgz \
204+
-c nginx-ingress
205+
```
206+
207+
Replace `<pod name>` with the actual name of the pod, for example:
208+
209+
```bash
210+
kubectl cp ./compiled_policy.tgz \
211+
nic-nginx-ingress-controller-9bd89589d-j925h:/etc/app_protect/bundles/compiled_policy.tgz \
212+
-c nginx-ingress
213+
```
214+
215+
Confirm that the policy file is in the pod. The following command should list `compiled_policy.tgz`.
216+
217+
```bash
218+
kubectl exec --stdin --tty \
219+
-c nginx-ingress \
220+
<pod name> \
221+
-- ls -la /etc/app_protect/bundles
222+
```
223+
224+
## Step 7: Confirm that the WAF policies work
225+
226+
Save the following kubernetes config file as `webapp.yaml`:
227+
228+
```yaml
229+
apiVersion: k8s.nginx.org/v1
230+
kind: VirtualServer
231+
metadata:
232+
name: webapp
233+
spec:
234+
host: webapp.example.com
235+
policies:
236+
- name: waf-policy
237+
upstreams:
238+
- name: webapp
239+
service: webapp-svc
240+
port: 80
241+
routes:
242+
- path: /
243+
action:
244+
pass: webapp
245+
---
246+
apiVersion: k8s.nginx.org/v1
247+
kind: Policy
248+
metadata:
249+
name: waf-policy
250+
spec:
251+
waf:
252+
enable: true
253+
apBundle: "compiled_policy.tgz"
254+
---
255+
apiVersion: apps/v1
256+
kind: Deployment
257+
metadata:
258+
name: webapp
259+
spec:
260+
replicas: 1
261+
selector:
262+
matchLabels:
263+
app: webapp
264+
template:
265+
metadata:
266+
labels:
267+
app: webapp
268+
spec:
269+
containers:
270+
- name: webapp
271+
image: nginxdemos/nginx-hello:plain-text
272+
ports:
273+
- containerPort: 8080
274+
---
275+
apiVersion: v1
276+
kind: Service
277+
metadata:
278+
name: webapp-svc
279+
spec:
280+
ports:
281+
- port: 80
282+
targetPort: 8080
283+
protocol: TCP
284+
name: http
285+
selector:
286+
app: webapp
287+
```
288+
289+
### Save the public IP and PORT in environment variables
290+
291+
Find out what they are with this:
292+
293+
```bash
294+
kubectl get svc
295+
```
296+
Take note of the external IP of the `nic-nginx-ingress-controller` service and the port. Save them in the following
297+
environment variables:
298+
299+
```bash
300+
IC_IP=XXX.YYY.ZZZ.III
301+
IC_HTTP_PORT=<port number>
302+
```
303+
304+
### Validate that the WAF works
305+
306+
Send a valid request to the deployed application:
307+
308+
```bash
309+
curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP http://webapp.example.com:$IC_HTTP_PORT/
310+
```
311+
312+
```text
313+
Server address: 10.92.2.13:8080
314+
Server name: webapp-7b7dfbff54-dtxzt
315+
Date: 18/Apr/2025:19:39:18 +0000
316+
URI: /
317+
Request ID: 4f378a01fb8a36ae27e2c3059d264527
318+
```
319+
320+
And send one that should be rejected
321+
322+
```bash
323+
curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP "http://webapp.example.com:$IC_HTTP_PORT/<script>"
324+
```
325+
326+
```text
327+
<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your
328+
administrator.<br><br>Your support ID is: 11241918873745059631<br><br>
329+
<a href='javascript:history.back();'>[Go Back]</a></body></html>
330+
```
331+
332+
This is mostly the same as the [examples/custom_resources/app-protect-waf-v5](https://github.com/nginx/kubernetes-ingress/tree/main/examples/custom-resources/app-protect-waf-v5)
333+
deployment in a single file with the policy bundle already set.
334+
335+
You now have a fully operational NIC with NAP deployed in your Kubernetes environment. For further details, troubleshooting, or support, refer to the [official NGINX documentation](https://docs.nginx.com) or reach out directly to your F5/NGINX account team.

0 commit comments

Comments
 (0)