# Kubernetes operations improvements (Early access) Type of document: Reference Product: F5 WAF for NGINX --- There are two new features available for Kubernetes through early access: **Security policy orchestration**, which removes the need for compilation by updating existing security policies. The supported security policy formats are JSON, YAML (where the security policy is defined inline within the AppPolicy CR), and Bundle. **Automated signature updates**, which can auto-update security signatures. This extends the WAF compiler capabilities by providing a native Kubernetes operator-based approach for policy orchestration. These features revolve around a _Policy Controller_ which uses the Kubernetes operator pattern to manage the lifecycle of WAF security artifacts. It handles policy distribution at scale by removing manual steps and providing a declarative configuration model with Custom Resource Definitions (CRDs) for policies, logging profiles and signatures. **Note:** These enhancements are only available for Helm-based deployments. ## Before you begin To complete this guide, you will need the following prerequisites: - A [supported operating system](/waf/fundamentals/technical-specifications.md#supported-operating-systems). - [A functional Kubernetes cluster](https://kubernetes.io/docs/setup/) (installed and running). - [kubectl CLI](https://kubernetes.io/docs/tasks/tools/install-kubectl/) configured and connected to your cluster. - [Docker](https://docs.docker.com/engine/install/) (with Docker Compose) installed and running, for pulling and managing container images. - Ensure you have an active F5 WAF for NGINX subscription (purchased or trial) and have downloaded the associated [SSL certificate, private key, and JWT license](#download-your-subscription-credentials) file from the MyF5 Customer Portal. - [Docker registry credentials](#download-your-subscription-credentials) for private-registry.nginx.com, required to pull images. - [Helm](https://helm.sh/docs/intro/install/) installed, required for deployment. ## Default security policy and logging profile F5 WAF for NGINX uses built-in default security policy and logging profile after installation. To use custom policies or logging profiles, update your NGINX configuration file accordingly. ## Download your subscription credentials **Note:** To access private-registry.nginx.com, you will need to download the JWT license file even when using NGINX Open Source as a base image. **Note:** If you are deploying with Helm, you will also need the JWT license for the `dockerConfigJson`. 1. Log in to [MyF5](https://my.f5.com/manage/s/). 1. Go to **My Products & Plans > Subscriptions** to see your active subscriptions. 1. Find your NGINX subscription, and select the **Subscription ID** for details. 1. Download the **SSL Certificate**, **Private Key** and **JSON Web Token** files from the subscription page. **Note:** Starting from [NGINX Plus Release 33](nginx/releases.md#r33), a JWT file is required for each NGINX Plus instance. For more information, see [About Subscription Licenses](/solutions/about-subscription-licenses.md). **Note:** When using the provided values.yaml for Helm, setting the `appprotect.config.nginxJWT` value ensures that your JWT license is automatically copied to `/etc/nginx/license.jwt` inside the NGINX container. No additional manual copying of the file is needed when deploying with the provided YAML configuration. ## Prepare environment variables Set the following environment variables, which point towards your credential files: ```shell export JWT= export NGINX_REGISTRY_TOKEN= export NGINX_CERT=$(cat /path/to/your/nginx-repo.crt | base64 -w 0) export NGINX_KEY=$(cat /path/to/your/nginx-repo.key | base64 -w 0) ``` They will be used to download and apply necessary resources. ## Configure Docker for the F5 Container Registry **Note:** You may be able to skip this step on an existing Kubernetes deployment, where guidance was already given to configure Docker. Create a directory and copy your certificate and key to this directory: ```shell mkdir -p /etc/docker/certs.d/private-registry.nginx.com cp /etc/docker/certs.d/private-registry.nginx.com/client.cert cp /etc/docker/certs.d/private-registry.nginx.com/client.key ``` ## Create a directory and volume for policy bundles Create the directory on the cluster: ```shell sudo mkdir -p /mnt/nap5_bundles_pv_data sudo chown -R 101:101 /mnt/nap5_bundles_pv_data ``` Create the file `pv-hostpath.yaml` with the persistent volume file content: ```yaml apiVersion: v1 kind: PersistentVolume metadata: name: nginx-app-protect-shared-bundles-pv labels: type: local spec: accessModes: - ReadWriteMany capacity: storage: "2Gi" hostPath: path: "/mnt/nap5_bundles_pv_data" persistentVolumeReclaimPolicy: Retain storageClassName: manual ``` Apply the `pv-hostpath.yaml` file to create the new persistent volume for policy bundles: ```shell kubectl apply -f pv-hostpath.yaml ``` **Note:** The volume name defaults to `-bundles-pv`, but can be customized using the `appprotect.storage.pv.name` setting in your `values.yaml` file. If you do this, ensure that all corresponding values for persistent volumes point to the correct names. ### Download and apply CRDs These enhancements require specific CRDs to be applied before deployment. These CRDs define the resources that the Policy Controller manages: - `appolicies.appprotect.f5.com` - Defines WAF security policies - `aplogconfs.appprotect.f5.com` - Manages logging profiles and configurations - `apusersigs.appprotect.f5.com` - Handles user-defined signatures - `apsignatures.appprotect.f5.com` - Manages signature updates and collections To obtain the CRDs, log into the Helm registry and pull the chart, changing the `--version` parameter for your desired version. ```shell helm registry login private-registry.nginx.com helm pull oci://private-registry.nginx.com/nap/nginx-app-protect --version 5.9.0-ea --untar ``` Then change into the directory and apply the CRDs using _kubectl apply_: ```shell cd nginx-app-protect kubectl apply -f crds/ ``` ### Update NGINX configuration To activate these enhancements, NGINX requires configuration to integrate with the Policy Controller. The directive `app_protect_default_config_source` must be set to `"custom-resource"` to enable the features. ```nginx user nginx; worker_processes auto; load_module modules/ngx_http_app_protect_module.so; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { 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 stdout main; sendfile on; keepalive_timeout 65; app_protect_enforcer_address 127.0.0.1:50000; # Enable enhancements app_protect_default_config_source "custom-resource"; app_protect_security_log_enable on; app_protect_security_log my-logging-cr /opt/app_protect/bd_config/s.log; server { listen 80; server_name localhost; location / { app_protect_enable on; # Reference to Custom Resource policy name app_protect_policy_file my-policy-cr; client_max_body_size 0; default_type text/html; proxy_pass http://127.0.0.1/proxy$request_uri; } location /proxy { app_protect_enable off; client_max_body_size 0; default_type text/html; return 200 "Hello! I got your URI request - $request_uri\n"; } } } ``` These are the directives: - `app_protect_default_config_source "custom-resource"` - Enables the Policy Controller integration - `app_protect_policy_file my-policy-cr` - References a Custom Resource policy name instead of bundle file paths - `app_protect_security_log my-logging-cr` - References a Custom Resource logging configuration name ## Update Helm configuration These new enhancements are deployed as part of the F5 WAF for NGINX Helm chart. To enable them, you must configure the Policy Controller settings in your `values.yaml` file: ```yaml # Specify the target namespace for your deployment # Replace with your chosen namespace name (e.g., "nap-plm" or "production") # This must match the namespace you will create in Step 4 or an existing namespace you plan to use namespace: appprotect: ## Enable/Disable F5 WAF for NGINX Deployment enable: true ## The number of replicas of the F5 WAF for NGINX deployment replicas: 1 ## Configure root filesystem as read-only and add volumes for temporary data readOnlyRootFilesystem: false ## The annotations for deployment annotations: {} ## InitContainers for the F5 WAF for NGINX pod initContainers: [] # - name: init-container # image: busybox:latest # command: ['sh', '-c', 'echo this is initial setup!'] nginx: image: ## The image repository of the F5 WAF for NGINX WAF image you built ## This must reference the Docker image you built following the Docker deployment guide ## Replace with your actual registry and update the image name/tag as needed repository: /nginx-app-protect-5 ## The tag of the NGINX image tag: latest ## The pull policy for the NGINX image imagePullPolicy: IfNotPresent ## The resources of the NGINX container. resources: requests: cpu: 10m memory: 16Mi # limits: # cpu: 1 # memory: 1Gi wafConfigMgr: image: ## The image repository of the WAF Config Mgr repository: private-registry.nginx.com/nap/waf-config-mgr ## The tag of the WAF Config Mgr image tag: ## The pull policy for the WAF Config Mgr image imagePullPolicy: IfNotPresent ## The resources of the Waf Config Manager container resources: requests: cpu: 10m memory: 16Mi # limits: # cpu: 500m # memory: 500Mi wafEnforcer: image: ## The image repository of the WAF Enforcer repository: private-registry.nginx.com/nap/waf-enforcer ## The tag of the WAF Enforcer image tag: ## The pull policy for the WAF Enforcer image imagePullPolicy: IfNotPresent ## The environment variable for enforcer port to be set on the WAF Enforcer container env: enforcerPort: "50000" ## The resources of the WAF Enforcer container resources: requests: cpu: 20m memory: 256Mi # limits: # cpu: 1 # memory: 1Gi wafIpIntelligence: enable: false image: ## The image repository of the WAF IP Intelligence repository: private-registry.nginx.com/nap/waf-ip-intelligence ## The tag of the WAF IP Intelligence tag: ## The pull policy for the WAF IP Intelligence imagePullPolicy: IfNotPresent ## The resources of the WAF IP Intelligence container resources: requests: cpu: 10m memory: 256Mi # limits: # cpu: 200m # memory: 1Gi policyController: ## Enable/Disable Policy Controller Deployment enable: true ## Number of replicas for the Policy Controller replicas: 1 ## The image repository of the WAF Policy Controller image: repository: private-registry.nginx.com/nap/waf-policy-controller ## The tag of the WAF Policy Controller tag: ## The pull policy for the WAF Policy Controller imagePullPolicy: IfNotPresent wafCompiler: ## The image repository of the WAF Compiler image: repository: private-registry.nginx.com/nap/waf-compiler ## The tag of the WAF Compiler image tag: ## Save logs before deleting a job or not enableJobLogSaving: false ## The resources of the WAF Policy Controller resources: requests: cpu: 100m memory: 128Mi # limits: # memory: 256Mi # cpu: 250m ## InitContainers for the Policy Controller pod initContainers: [] # - name: init-container # image: busybox:latest # command: ['sh', '-c', 'echo this is initial setup!'] storage: bundlesPath: ## Specifies the name of the volume to be used for storing policy bundles name: app-protect-bundles ## Defines the mount path inside the WAF Config Manager container where the bundles will be stored mountPath: /etc/app_protect/bundles pv: ## PV name that pvc will request ## if empty will be used -shared-bundles-pv name: nginx-app-protect-shared-bundles-pv pvc: ## The storage class to be used for the PersistentVolumeClaim. 'manual' indicates a manually managed storage class bundlesPvc: storageClass: manual ## The amount of storage requested for the PersistentVolumeClaim storageRequest: 2Gi # Not needed as values will be set during helm install # nginxRepo: # ## Used for Policy Controller to pull the security updates from the NGINX repository. # ## The base64-encoded TLS certificate for the NGINX repository. # nginxCrt: "" # ## The base64-encoded TLS key for the NGINX repository. # nginxKey: "" config: ## The name of the ConfigMap used by the NGINX container name: nginx-config ## The annotations of the configmap annotations: {} # Not needed as value will be set during helm install # ## The JWT token license.txt of the ConfigMap for customizing NGINX configuration. # nginxJWT: "" ## The nginx.conf of the ConfigMap for customizing NGINX configuration nginxConf: |- user nginx; worker_processes auto; load_module modules/ngx_http_app_protect_module.so; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } # Uncomment if using mtls # mTLS configuration # stream { # upstream enforcer { # # Replace with the actual F5 WAF for NGINX Enforcer address and port if different # server 127.0.0.1:4431; # } # server { # listen 5000; # proxy_pass enforcer; # proxy_ssl_server_name on; # proxy_timeout 30d; # proxy_ssl on; # proxy_ssl_certificate /etc/ssl/certs/app_protect_client.crt; # proxy_ssl_certificate_key /etc/ssl/certs/app_protect_client.key; # proxy_ssl_trusted_certificate /etc/ssl/certs/app_protect_server_ca.crt; # } # } http { 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 stdout main; sendfile on; keepalive_timeout 65; # Enable enhancements # WAF default config source. For policies from CRDs, use "custom-resource" # Remove this line to use default bundled policies app_protect_default_config_source "custom-resource"; # WAF enforcer address. For mTLS, use port 5000 app_protect_enforcer_address 127.0.0.1:50000; server { listen 80; server_name localhost; location / { app_protect_enable on; app_protect_security_log_enable on; app_protect_security_log log_all stderr; # WAF policy - use Custom Resource name when these enhancements are enabled app_protect_policy_file app_protect_default_policy; client_max_body_size 0; default_type text/html; proxy_pass http://127.0.0.1/proxy$request_uri; } location /proxy { app_protect_enable off; client_max_body_size 0; default_type text/html; return 200 "Hello! I got your URI request - $request_uri\n"; } } # include /etc/nginx/conf.d/*.conf; } ## The default.conf of the ConfigMap for customizing NGINX configuration nginxDefault: {} ## The extra entries of the ConfigMap for customizing NGINX configuration entries: {} ## It is recommended to use your own TLS certificates and keys mTLS: ## The base64-encoded TLS certificate for the F5 WAF for NGINX Enforcer (server) ## Note: It is recommended that you specify your own certificate serverCert: "" ## The base64-encoded TLS key for the F5 WAF for NGINX Enforcer (server) ## Note: It is recommended that you specify your own key serverKey: "" ## The base64-encoded TLS CA certificate for the F5 WAF for NGINX Enforcer (server) ## Note: It is recommended that you specify your own certificate serverCACert: "" ## The base64-encoded TLS certificate for the NGINX (client) ## Note: It is recommended that you specify your own certificate clientCert: "" ## The base64-encoded TLS key for the NGINX (client) ## Note: It is recommended that you specify your own key clientKey: "" ## The base64-encoded TLS CA certificate for the NGINX (client) ## Note: It is recommended that you specify your own certificate clientCACert: "" ## The extra volumes of the NGINX container volumes: [] # - name: extra-conf # configMap: # name: extra-conf ## The extra volumeMounts of the NGINX container volumeMounts: [] # - name: extra-conf # mountPath: /etc/nginx/conf.d/extra.conf # subPath: extra.conf service: nginx: ports: - port: 80 protocol: TCP targetPort: 80 ## The type of service to create. NodePort will expose the service on each Node's IP at a static port. type: NodePort # Not needed as value will be set during helm install # ## This is a base64-encoded string representing the contents of the Docker configuration file (config.json). # ## This file is used by Docker to manage authentication credentials for accessing private Docker registries. # ## By encoding the configuration file in base64, sensitive information such as usernames, passwords, and access tokens are protected from being exposed directly in plain text. # ## You can create this base64-encoded string yourself by encoding your config.json file, or you can create the Kubernetes secret containing these credentials before deployment and not use this value directly in the values.yaml file. # dockerConfigJson: "" ``` ## Configure Docker Create a Docker registry secret: ```shell kubectl create secret docker-registry regcred -n \ --docker-server=private-registry.nginx.com \ --docker-username=$JWT \ --docker-password=none ``` ## Deploy or upgrade the Helm chart Deploy the chart, adding the parameter to enable the Policy Controller: ```shell helm install . \ --namespace \ --create-namespace \ --set appprotect.policyController.enable=true \ --set dockerConfigJson=$NGINX_REGISTRY_TOKEN \ --set appprotect.config.nginxJWT=$JWT \ --set appprotect.nginxRepo.nginxCert=$NGINX_CERT \ --set appprotect.nginxRepo.nginxKey=$NGINX_KEY ``` If you would like to instead upgrade an existing deployment, use this `upgrade` command: ```shell helm upgrade . \ --namespace \ --values /path/to/your/values.yaml \ --set appprotect.policyController.enable=true \ --set dockerConfigJson=$NGINX_REGISTRY_TOKEN \ --set appprotect.config.nginxJWT=$JWT \ --set appprotect.nginxRepo.nginxCrt=$NGINX_CERT \ --set appprotect.nginxRepo.nginxKey=$NGINX_KEY ``` ## Verify the Policy Controller is running Check that all components are deployed successfully using _kubectl get_: ```shell kubectl get pods -n kubectl get crds | grep appprotect.f5.com kubectl get pvc -n kubectl get pv kubectl get all -n ``` If you don't see a persistent volume claim in the namespace, first check that storage configuration in your values file is correct: ```shell helm get values -n ``` You should see a section named _appprotect.storage_ with the parameter _bundlesPvc.storageRequest_. If it's missing, use `helm upgrade` to add it: ```shell helm upgrade . --namespace \ --values /path/to/your/values.yaml \ --set appprotect.policyController.enable=true \ --set dockerConfigJson=$NGINX_REGISTRY_TOKEN \ --set appprotect.config.nginxJWT=$JWT \ --set appprotect.nginxRepo.nginxCrt=$NGINX_CERT \ --set appprotect.nginxRepo.nginxKey=$NGINX_KEY \ --set appprotect.storage.pvc.bundlesPvc.storageClass=manual \ --set appprotect.storage.pvc.bundlesPvc.storageRequest=2Gi ``` If the volume claim exists but shows "Pending", review the binding: ```shell kubectl describe pvc -n kubectl describe pv nginx-app-protect-shared-bundles-pv ``` Ensure the `pv` _storageClassName_ matches the `pvc` requirements. In totality, you should see the following: - **Policy Controller pod**: `1/1 Running` status - **F5 WAF for NGINX pod**: `3/3 Running` status (nginx, waf-config-mgr, waf-enforcer containers) - **All 4 CRDs**: Each CRD should be installed and show creation timestamps - **Service**: The NodePort service should be available with assigned port If you are using the IP intelligence feature, you will have a 4th F5 WAF for NGINX pod (waf-ip-intelligence). ## Update security policies ### Create custom policy resources During installation, you can create policy resources using Kubernetes manifests. Here are two examples, which you can use to create your own: #### APPolicy Create a file named `dataguard-blocking-policy.yaml` with the following content: ```yaml apiVersion: appprotect.f5.com/v1 kind: APPolicy metadata: name: dataguard-blocking spec: policy: name: dataguard_blocking template: name: POLICY_TEMPLATE_NGINX_BASE applicationLanguage: utf-8 enforcementMode: blocking blocking-settings: violations: - name: VIOL_DATA_GUARD alarm: true block: true data-guard: enabled: true maskData: true creditCardNumbers: true usSocialSecurityNumbers: true enforcementMode: ignore-urls-in-list enforcementUrls: [] ``` Apply the policy: ```shell kubectl apply -f dataguard-blocking-policy.yaml -n ``` #### APUserSig Create a file named `apple-usersig.yaml` with the following content: ```yaml apiVersion: appprotect.f5.com/v1 kind: APUserSig metadata: name: apple spec: signatures: - accuracy: medium attackType: name: Brute Force Attack description: Medium accuracy user defined signature with tag (Fruits) name: Apple_medium_acc risk: medium rule: content:"apple"; nocase; signatureType: request systems: - name: Microsoft Windows - name: Unix/Linux tag: Fruits ``` Apply the user signature: ```shell kubectl apply -f apple-usersig.yaml -n ``` ### Check policy status You can check the status of your resources using `kubectl get` or `kubectl describe`. The Policy Controller will show status information including: - Bundle location - Compilation status - Signature update timestamps ```shell kubectl get appolicy dataguard-blocking -n -o yaml ``` ```yaml apiVersion: appprotect.f5.com/v1 kind: APPolicy metadata: name: dataguard-blocking namespace: localenv-plm # ... other metadata fields spec: policy: # ... policy configuration status: bundle: compilerVersion: 11.559.0 location: /etc/app_protect/bundles/dataguard-blocking-policy/dataguard-blocking_policy20250914102339.tgz signatures: attackSignatures: "2025-09-03T08:36:25Z" botSignatures: "2025-09-03T10:50:19Z" threatCampaigns: "2025-09-02T07:28:43Z" state: ready processing: datetime: "2025-09-14T10:23:48Z" isCompiled: true ``` ```shell kubectl describe appolicy dataguard-blocking -n ``` ```text Name: dataguard-blocking Namespace: localenv-plm Labels: Annotations: API Version: appprotect.f5.com/v1 Kind: APPolicy Metadata: Creation Timestamp: 2025-09-10T11:17:07Z Finalizers: appprotect.f5.com/finalizer Generation: 3 # ... other metadata fields Spec: Policy: Application Language: utf-8 Blocking - Settings: Violations: Alarm: true Block: true Name: VIOL_DATA_GUARD Data - Guard: Credit Card Numbers: true Enabled: true Enforcement Mode: ignore-urls-in-list # ... other policy settings Status: Bundle: Compiler Version: 11.559.0 Location: /etc/app_protect/bundles/dataguard-blocking-policy/dataguard-blocking_policy20250914102339.tgz Signatures: Attack Signatures: 2025-09-03T08:36:25Z Bot Signatures: 2025-09-03T10:50:19Z Threat Campaigns: 2025-09-02T07:28:43Z State: ready Processing: Datetime: 2025-09-14T10:23:48Z Is Compiled: true Events: ``` The key information to review is the following: - **`Status.Bundle.State`**: Policy compilation state - `ready` - Policy successfully compiled and available - `processing` - Policy is being compiled - `error` - Compilation failed (check Policy Controller logs) - **`Status.Bundle.Location`**: File path where the compiled policy bundle is stored - **`Status.Bundle.Compiler Version`**: Version of the WAF compiler used for compilation - **`Status.Bundle.Signatures`**: Timestamps showing when security signatures were last updated - `Attack Signatures` - Attack signature update timestamp - `Bot Signatures` - Bot signature update timestamp - `Threat Campaigns` - Threat campaign signature update timestamp - **`Status.Processing.Is Compiled`**: Boolean indicating if compilation completed successfully - **`Status.Processing.Datetime`**: Timestamp of the last compilation attempt - **`Events`**: Shows any Kubernetes events related to the policy (usually none for successful policies) - **`status.processing.isCompiled`**: Boolean indicating if compilation completed successfully - **`status.processing.datetime`**: Timestamp of the last compilation attempt ## Validate your installation ### Apply a policy Apply one of the sample policy Custom Resources to verify your installation is working correctly. For example, using the dataguard policy you created earlier: ```shell kubectl apply -f dataguard-blocking-policy.yaml -n ``` ### Check policy compilation status Verify that the policy has been compiled successfully by checking the Custom Resource status: ```shell kubectl get appolicy -n -o yaml ``` You should see output similar to this, with `state: ready` and no errors: ```yaml status: bundle: compilerVersion: 11.559.0 location: /etc/app_protect/bundles/dataguard-blocking-policy/dataguard-blocking_policy20250904100458.tgz signatures: attackSignatures: "2025-08-28T01:16:06Z" botSignatures: "2025-08-27T11:35:31Z" threatCampaigns: "2025-08-25T09:57:39Z" state: ready processing: datetime: "2025-09-04T10:05:52Z" isCompiled: true ``` ### Review Policy Controller logs Check the Policy Controller logs for expected compilation messages: ```shell kubectl logs -n ``` Successful compilation logs will look similar to this example: ```text 2025-09-04T10:05:52Z INFO Job is completed {"controller": "appolicy", "controllerGroup": "appprotect.f5.com", "controllerKind": "APPolicy", "APPolicy": {"name":"dataguard-blocking","namespace":"localenv-plm"}, "namespace": "localenv-plm", "name": "dataguard-blocking", "reconcileID": "6bab7054-8a8a-411f-8ecc-01399a308ef6", "job": "dataguard-blocking-appolicy-compile"} 2025-09-04T10:05:52Z INFO job state is {"controller": "appolicy", "controllerGroup": "appprotect.f5.com", "controllerKind": "APPolicy", "APPolicy": {"name":"dataguard-blocking","namespace":"localenv-plm"}, "namespace": "localenv-plm", "name": "dataguard-blocking", "reconcileID": "6bab7054-8a8a-411f-8ecc-01399a308ef6", "job": "dataguard-blocking-appolicy-compile", "state": "ready"} 2025-09-04T10:05:52Z INFO bundle state was changed {"controller": "appolicy", "controllerGroup": "appprotect.f5.com", "controllerKind": "APPolicy", "APPolicy": {"name":"dataguard-blocking","namespace":"localenv-plm"}, "namespace": "localenv-plm", "name": "dataguard-blocking", "reconcileID": "6bab7054-8a8a-411f-8ecc-01399a308ef6", "job": "dataguard-blocking-appolicy-compile", "from": "processing", "to": "ready"} ``` ### Verify bundle creation Check that the policy bundle has been created in the shared volume directory: ```shell ls -la /mnt/nap5_bundles_pv_data/dataguard-blocking-policy/ ``` You should see the compiled policy bundle file in the directory structure. ### Test policy enforcement There are a few steps involved in testing that policy bundles are being deployed and enforced correctly. First, identify and confirm the deployment information: ```shell kubectl get all -n ``` Look for the fields _CLUSTER-IP_ and the full deployment name: ```shell NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/localenv-plm-nginx-app-protect-nginx NodePort 10.43.205.101 80:30970/TCP 21h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/localenv-plm-nginx-app-protect-deployment 1/1 1 1 21h ``` Then open your `values.yaml` file in an editor and look for the policy directive: ```yaml app_protect_policy_file app_protect_default_policy ``` Replace _app_protect_default_policy_ with the custom resource name, such as: ```yaml app_protect_policy_file dataguard-blocking; ``` Use `helm upgrade` to apply the new configuration, replacing the name and namespace accordingly: ```shell helm upgrade . \ --namespace \ --values /path/to/your/values.yaml \ --set appprotect.policyController.enable=true \ --set dockerConfigJson=$NGINX_REGISTRY_TOKEN \ --set appprotect.config.nginxJWT=$JWT \ --set appprotect.nginxRepo.nginxCrt=$NGINX_CERT \ --set appprotect.nginxRepo.nginxKey=$NGINX_KEY ``` Restart your Kubernetes deployment to load the new configuration changes: ```shell kubectl rollout restart deployment -n ``` Send a test request to trigger the dataguard policy: ```shell curl "http://[CLUSTER-IP]:80/?a=