NGINX Modern Apps > Class 5 - Application Security and Observability with NAP Source | Edit on
Module 1: NGINX App Protect 5 OSS in a VM¶
We will install NGINX App Protect on NGINX OSS to protect the JuiceShop application. The JuiceShop application has already been installed in the UDF deployment for you.
Install NGINX OSS and NGINX App Protect 5 Module¶
Open a Web Shell session to the NAP WAF VM UDF component.
Alternatively, you can open the Visual Studio Code access method:
Once Visual Studio Code loads, to open the shell, press
CTRL + ~.Install prerequisite packages:
apt-get update && apt-get install -y apt-transport-https lsb-release ca-certificates wget gnupg2 ubuntu-keyring
Download and add the NGINX signing key:
wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key | gpg --dearmor \ | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Download the apt configuration to
/etc/apt/apt.conf.d:wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx
Add the NGINX Open Source repository:
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \ | tee /etc/apt/sources.list.d/nginx.list
Set up repository pinning to prefer our packages over distribution-provided ones:
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | tee /etc/apt/preferences.d/99nginx
Add the NGINX App Protect WAF 5 repository that is local to your UDF deployment:
tee /etc/apt/sources.list.d/nginx-app-protect.list > /dev/null <<EOF deb [trusted=yes] https://repositories.f5demos.com/debian jammy nginx-plus EOF
Install NGINX OSS and the NGINX App Protect WAF 5 package.
sudo apt-get update; apt-get install -y nginx=1.25.5-1~jammy app-protect-module-oss nginx -t
Set NGINX to start on system boot:
systemctl enable --now nginx systemctl status nginx
Installation Check¶
Check the NGINX binary version to ensure that it was installed correctly as a dependency:
nginx -v
You should see the following output:
nginx version: nginx/1.25.5
Policy Compilation¶
NGINX App Protect WAF 5 includes support for all existing security features of NGINX App Protect WAF. For detailed information on these supported security features, see the NGINX App Protect WAF Configuration Guide.
A key distinction with NGINX App Protect WAF 5 is the compilation of security policies and logging profiles into bundle files prior to deployment. This pre-compilation step significantly speeds up the data plane deployment process. Ideally, this process would be automated and your policy’s source of truth is Git. In this lab, we will be using GitLab to build the App Protect policy and log profiles into a tarball, and deploy it to the target systems when the policy has been updated in source control.
NGINX Configuration¶
In your existing Web Shell session to the NAP WAF VM, open the
/etc/nginx/nginx.conffile in your preferred editor. For example:nano /etc/nginx/nginx.conf
You can also edit it with Visual Studio Code if you are using the access method for it:
code /etc/nginx/nginx.conf
At the top of the
nginx.conffile, insert the following directive to load the NGINX App Protect WAF 5 module:load_module modules/ngx_http_app_protect_module.so;
Configure the Enforcer address in the
httpcontext of thenginx.conffile by adding theapp_protect_enforcer_addressdirective to instruct the App Protect module to connect to the enforcer container that we will later deploy with Docker Compose:app_protect_enforcer_address 127.0.0.1:50000;
The file should now look similar to this excerpt:
load_module modules/ngx_http_app_protect_module.so; user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { app_protect_enforcer_address 127.0.0.1:50000; include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; ... }
Save the file (e.g.
CTRL+O, Enterin nano orCTRL+Sin Visual Studio Code), and close the editor if using the Web Shell access method.Open the
/etc/nginx/conf.d/default.conffile in your preferred editor.For example:
nano /etc/nginx/conf.d/default.conf
Or:
code /etc/nginx/conf.d/default.conf
Update the first 2 lines in the
servercontext in thedefault.confwith the following:listen 443 ssl; server_name nap-vm.f5demos.com;
Un-comment the
access_logline so it looks like the following:access_log /var/log/nginx/host.access.log main;
Below the line in the previous step, add the following references to the lab’s TLS certificate and key:
ssl_certificate /etc/ssl/certs/wildcard.f5demos.com.crt.pem; ssl_certificate_key /etc/ssl/private/wildcard.f5demos.com.key.pem;
Enable NGINX App Protect WAF by adding the following line below the ones in the previous step:
app_protect_enable on;
Below the line in the previous step, set the
app_protect_policy_filedirective to the tarball that GitLab has already deployed for you the first time:app_protect_policy_file "/etc/app_protect/bundles/nap5_policy.tgz";
Below the line in the previous step, add the
app_protect_security_log_enabledirective to indicate that we will want to log security events:app_protect_security_log_enable on;
Below the line in the previous step, add the
app_protect_security_logdirective to indicate which log profile bundle we will use, as well as where to send the logs. GitLab has already deployed the log profile tarball for you for you the first time. The logs will be sent to the LogStash syslog ingestion container running in the Dashboard VM:app_protect_security_log "/etc/app_protect/bundles/nap5_log_profile.tgz" syslog:server=dashboard.f5demos.com:8515;
In the
location /block, remove the 2 existing lines.Inside this
locationblock, use theproxy_passdirective to proxy incoming traffic to the JuiceShop application:proxy_pass http://appserver.f5demos.com:3000/;
The file should now look similar to this excerpt:
server { listen 443 ssl; server_name nap-vm.f5demos.com; access_log /var/log/nginx/host.access.log main; ssl_certificate /etc/ssl/certs/wildcard.f5demos.com.crt.pem; ssl_certificate_key /etc/ssl/private/wildcard.f5demos.com.key.pem; app_protect_enable on; app_protect_policy_file "/etc/app_protect/bundles/nap5_policy.tgz"; app_protect_security_log_enable on; app_protect_security_log "/etc/app_protect/bundles/nap5_log_profile.tgz" syslog:server=dashboard.f5demos.com:8515; location / { proxy_pass http://appserver.f5demos.com:3000/; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } ... }
Save the file, and close the editor if using the Web Shell access method.
With the NGINX App Protect WAF 5 dynamic module installed, you can now proceed to install the Docker containers required by the new architecture.
Deploy App Protect WAF 5 Docker Containers¶
Log in to the local Harbor image registry:
docker login repositories.f5demos.com:8443
If prompted, use
adminandHarbor12345as the credentials.Next, we need to set up the directories with the correct ownership for NGINX App Protect WAF 5 services, where
101:101are the default UID/GID for thesystemd-resolveuser in Ubuntu that the App Protect WAF containers will use to access the runtime config on the host.Create Directories:
mkdir -p /opt/app_protect/config /opt/app_protect/bd_config
Set Ownership:
chown -R 101:101 /opt/app_protect/
Examine the Docker Compose file (
~/nap5/docker-compose.yaml) in your editor.You will note that a
waf-enforcerandwaf-config-mgrcontainer will be deployed. They will be pulled from the Harbor image registry in the local deployment. Additionally, note that various volumes are used in order to facilitate runtime config and bundle sharing between the NGINX App Protect WAF module and the App Protect WAF containers.In the NAP WAF VM Web Shell session, start the Docker containers using Docker Compose:
cd ~/nap5 docker compose up -d
You should see output similar to the following:
[+] Running 11/11 ✔ waf-enforcer 4 layers [⣿⣿⣿⣿] 0B/0B Pulled 3.0s ✔ c10e5867c864 Pull complete .3s ✔ 610bc942f22e Pull complete .3s ✔ 3be51ed0c879 Pull complete .1s ✔ 1d00429e5679 Pull complete .5s ✔ waf-config-mgr 5 layers [⣿⣿⣿⣿⣿] 0B/0B Pulled 1.4s ✔ f5ce2c949773 Pull complete .1s ✔ 94794f16c736 Pull complete .1s ✔ 4ca9a728d9c3 Pull complete .3s ✔ a79fde38653f Pull complete .4s ✔ 4f4fb700ef54 Pull complete .2s [+] Running 2/3 ⠸ Network nap5_waf_network Created 1.3s ✔ Container waf-enforcer Started 0.8s ✔ Container waf-config-mgr Started
Confirm that the App Protect WAF Docker containers are running:
docker ps
You should see output similar to:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cc878708af8a repositories.f5demos.com:8443/nap/waf-config-mgr:5.3.0 "/service/config-mgr…" 21 seconds ago Up 20 seconds waf-config-mgr d1f660623bb7 repositories.f5demos.com:8443/nap/waf-enforcer:5.3.0 "/start-enforcer" 21 seconds ago Up 20 seconds 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp waf-enforcer
Restart (or Start) NGINX to reconfigure it with all the config updates we made earlier:
systemctl restart nginx
Note: Unlike in NGINX Plus, the NGINX OSS must be restarted in order to load the updated configuration. This disrupts existing connections and prevents the establishment of new connections while restarting. This is one of the reasons we recommend NGINX Plus for its non-disruptive behavior during configuration reloads.
Test the JuiceShop Application¶
In UDF, open the Firefox access method of the JumpHost VM. This will open the Lab Links page in the first tab.
Click the NAP 5 VM link.
You should see the JuiceShop page:
Perform a Cross-Site Scripting (XSS) attack by modifying the url to
https://nap-vm.f5demos.com/p=<script>
and hit enter.
You should see a WAF block page:
You can also open the browser dev tools (
CTRL + SHIFT + I) and analyze any other requests.
Note down all of the support IDs in each response before moving onto the next step.
View Security Events of the JuiceShop Application¶
In order to view events generated from App Protect WAF, we will be using Kostas Skenderidis’ excellent dashboard for App Protect.
In the lab’s Firefox, click on the Lab Links tab.
Click the Grafana Dashboard link.
If you are prompted to log in, use
adminandN@pWaf5as the credentials.Click the Dashboard icon, and Browse as pictured below:
Click the General link, then click the NGINX NAP Main Dashboard.
You will be presented with the main dashboard in this solution. You should see at least one attacker and attack indicated. Additionally, you will see a violations list below. Feel free to drill into the data and explore the other dashboards while you are here.
Drilling down into events¶
Go back to the Dashboard icon, and Browse as before:
Click the General link, then click the NGINX NAP SupportID.
Click the box next to the SupportID field.
Paste in the Support ID from the other tab.
Look at the box containing the violations in this request.
Scroll down to the box containing the signatures in this request.
You can then repeat this for the other Support ID(s) in the requests that you analyzed earlier in the browser devtools.
The Bundle Compilation Process¶
NGINX App Protect WAF 5 offers default security policies and logging profiles that work out of the box for general use cases. However, for more specific requirements, you may need to create bundles based on your custom security policies and logging profiles. This customization ensures that the deployment is finely tuned to your unique security and operational environment. This lab makes use of a custom policy and log profile. We will examine these files.
Log into GitLab¶
In the lab’s Firefox, click on the Lab Links tab.
Click the GitLab link.
Log into GitLab with
root/GitL@bF5.
Click the nap5 project link as pictured below:
You will be presented with a list of files in the nap5 git repository, not necessarily in this order:
nap5_policy.json- The App Protect WAF security policy for our demo applications.nap5_log_profile.json- The App Protect WAF log profile to be used for the logging of security events..gitlab-ci.yml- The configuration for the GitLab CI/CD pipeline that will compile the JSON App Protect WAF policy and JSON log profile file into bundles and deploy them to the App Protect WAF deployments’ host file systems.updated_policy_contents.txt- Simply a text file containing JSON that we will use to updatenap5_policy.jsonfile’s contents in the next steps.
Click the
nap5_policy.jsonfile. This is the policy file that has been compiles into a tarball, and deployed to the App Protect WAF 5 enforcer that we were testing in the previous section. This is a simple App Protect policy file with few modifications. Note that this policy is in blocking mode.Click the button to go back to the repository:
Once back in the repository, click on the
nap5_log_profile.jsonfile. This is a typical JSON log profile, with the addition of a specific log format specified that the Security Grafana Dashboard requires. This is the log profile currently in use.Click the button to go back to the repository, then click on the
.gitlab-ci.ymlfile. This is the spec file of the CI/CD pipeline that the nap repository uses. This build pipeline has 2 stages:buildanddeploy.The
buildstage uses a Docker image in the local Harbor image registry that contains a waf compiler that has been provided for you. In the build stage, theapcompileutility contained in this container image will compile policies and log profiles into bundles contained in.tgzarchive files (tarballs).The
deploystage uses a simple alpine image to copy the 2 bundles created in the build stage, and deploy them to both the NAP WAF VM and K3s Kubernetes cluster (used later in this lab). Once these are deployed, thewaf-config-mgrcontainers are restarted to apply the latest changes to the waf enforcer container.Note: As long as the name of the policy name or bundle files’ names have not changed, a restart of NGINX is not necessary in order to make policy or log profile changes effective.
Update the Security Policy¶
Let’s say our attack from earlier represented legitimate behavior for that specific application. We can modify the security policy to allow that behavior, though it should be noted this specific attack would be a poor example of legitimate behavior in practice.
Click the button to go back to the repository, then click on the
updated_policy_contents.txtfile. This is a JSON file that we will use to update thenap5_policy.jsonfile.Select the contents of the file, and press Ctrl+C to store in Firefox’s VNC clipboard buffer.
Click the button to go back to the repository, then click on the
nap5_policy.jsonfile.Click the Edit button, then click Edit single file. The policy file will open in an editor.
Highlight all the contents of the file, and press Ctrl+V to paste in the value.
Update the commit message describing this change.
Click the Commit changes button.
GitLab will display a message indicating that the changes to the file have been committed.
Click Build then Pipelines in the left navigation.
The first item at the top list is the pipeline that has been automatically initiated due to your change. Click the status icon as pictured below:
You will be taken to a page that displays the status of the pipeline instance. Within one minute, you should see a status page similar to the image below signifying that the build and deploy stages have completed successfully.
In Firefox, click the tab that contains the request containing XSS that was blocked.
Click refresh to reload the page. Notice that the request now goes through, since we allowed the earlier behavior that had been blocked.
This is the end of the VM based lab for NGINX App Protect.