# Build and use the compiler tool




This document describes how to use the F5 WAF for NGINX compiler, a tool for converting security policies and logging profiles from JSON to a bundle file that F5 WAF can process and apply.

You can use it to get the latest security updates for [Attack signatures](/waf/policies/attack-signatures.md), Threat campaigns and Bot signatures.

The compiler is packaged as a Docker image and can executed using the Docker CLI or as part of a continuous integration/continuous delivery (CI/CD) pipeline.

**Note:** 

If you are using a virtual machine/bare-metal installation, read the [Update F5 WAF for NGINX signatures](/waf/install/update-signatures.md) topic.

If you are using a Helm-based Kubernetes deployment, read the [Kubernetes operations improvements (Early access)](/waf/install/kubernetes-plm.md) topic.

One or more bundle files can be referenced in the NGINX configuration file, and you can configure global settings such as the cookie seed and user-defined signatures.

For more information about policies, read the [Configure policies](/waf/policies/configuration.md) topic.

## Before you begin

To complete this guide, you will need the following prerequisites:

- An active F5 WAF for NGINX subscription. Available from [MyF5](https://my.f5.com/manage/s/) (Purchased or trial).
  - Download the [SSL certificate and private key](#download-your-subscription-credentials) associated with your F5 WAF for NGINX subscription from the MyF5 Customer Portal.
- [Docker registry credentials](#configure-docker-for-the-f5-container-registry) are needed to access private-registry.nginx.com
- [Docker](https://docs.docker.com/get-started/get-docker/)

## Download your subscription credentials 

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** and **Private Key** files from the subscription page.

## Configure Docker for the F5 Container Registry 

Create a directory and copy your certificate and key to this directory:

```shell
mkdir -p /etc/docker/certs.d/private-registry.nginx.com
cp <path-to-your-nginx-repo.crt> /etc/docker/certs.d/private-registry.nginx.com/client.cert
cp <path-to-your-nginx-repo.key> /etc/docker/certs.d/private-registry.nginx.com/client.key
```

## Create the Dockerfile

This example Dockerfile is based on a Debian image.

```dockerfile
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=private-registry.nginx.com/nap/waf-compiler:
FROM ${BASE_IMAGE}

# Installing packages as root
USER root

ENV DEBIAN_FRONTEND="noninteractive"

RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
    --mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
    apt-get update \
    && apt-get install -y \
        apt-transport-https \
        lsb-release \
        ca-certificates \
        wget \
        gnupg2 \
        ubuntu-keyring \
    && wget -qO - https://cs.nginx.com/static/keys/app-protect-security-updates.key | gpg --dearmor | \
    tee /usr/share/keyrings/app-protect-security-updates.gpg >/dev/null \
    && printf "deb [signed-by=/usr/share/keyrings/app-protect-security-updates.gpg] \
    https://pkgs.nginx.com/app-protect-security-updates/ubuntu `lsb_release -cs` nginx-plus\n" | \
    tee /etc/apt/sources.list.d/nginx-app-protect.list \
    && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
    && apt-get update \
    && apt-get install -y \
        app-protect-attack-signatures \
        app-protect-bot-signatures \
        app-protect-threat-campaigns \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# non-root default user (UID 101)
USER nginx
```

**Note:** 

You can can upgrade or downgrade one of the Signatures by specifying a specific version, such as _app-protect-attack-signatures-2020.04.30_.

You can use the Docker registry API to list the available image tags.

Replace `<path-to-your-nginx-repo.key>` with the location of your client key and `<path-to-your-nginx-repo.crt>` with the location of your client certificate.

```shell
curl -s https://private-registry.nginx.com/v2/nap/waf-compiler/tags/list --key <path-to-your-nginx-repo.key> --cert <path-to-your-nginx-repo.crt>
```

## Build the container image

Run the following command to build your image, where `waf-compiler-<version-tag>:custom` is an example of the image tag:

```shell
sudo docker build --no-cache --platform linux/amd64 \
--secret id=nginx-crt,src=nginx-repo.crt \
--secret id=nginx-key,src=nginx-repo.key \
-t waf-compiler-<version-tag>:custom .
```

**Note:** 

Never upload your F5 WAF for NGINX images to a public container registry such as Docker Hub. Doing so violates your license agreement.

## Update security packages in the compiler image

To use newer attack signatures, bot signatures, or threat campaigns with the compiler workflow, rebuild your compiler image with the updated security packages, then recompile your policy bundles.

Updating the compiler image alone does not update a running F5 WAF for NGINX deployment. The Enforcer continues to use the signatures contained in the currently deployed policy bundle until you deploy a newly compiled bundle and apply the updated configuration.

To apply updated signatures from a compiler image:

1. Rebuild the compiler image with the updated security packages:
    - `app-protect-attack-signatures`
    - `app-protect-bot-signatures`
    - `app-protect-threat-campaigns`
2. Recompile all policy bundles with the rebuilt compiler image.
3. Replace or mount the updated bundle in your F5 WAF for NGINX deployment.
4. Apply the updated configuration:
    - If only the F5 WAF for NGINX configuration changed, and `nginx.conf` and its included files did not change, use [apreload](/waf/configure/apreload.md).
    - If `nginx.conf`, any included NGINX file, or any App Protect directive in `nginx.conf` changed, reload NGINX.

A full NGINX restart is not required.

## Using the compiler

This section uses `version-tag` as a placeholder in its examples, following the previous section. Ensure that all input files are accessible to UID 101.

### Compile a security policy

To compile a security policy from a JSON file and create a policy bundle, run the following command:

```shell
docker run --rm \
 -v $(pwd):$(pwd) \
 waf-compiler-<version-tag>:custom \
 -p $(pwd)/policy.json -o $(pwd)/compiled_policy.tgz
```

**Note:** 

Ensure that the output directory is writable, otherwise you may encounter a permission denied error.

To use multiple policy bundles within a single NGINX configuration, you must supply a [global settings](#global-settings) JSON file.

This ensures that all bundles have a common foundation such as cookie seed and user-defined signatures.

An example `global_settings.json` might look as follows:

```json
{
    "waf-settings": {
        "cookie-protection": {
            "seed": "<seed value>"
        }
    }
}
```

To compile a policy with global settings, add the `-g` parameter:

```shell
docker run --rm \
 -v $(pwd):$(pwd) \
 waf-compiler-<version-tag>:custom \
 -g $(pwd)/global_settings.json -p $(pwd)/policy.json -o $(pwd)/compiled_policy.tgz
```

You can incorporate the source of the policy (as `policy.json`) or logging profile (as `logging_profile.json`) into the final bundle using the `-include-source` parameter.

```shell
docker run --rm \
 -v $(pwd):$(pwd) \
 waf-compiler-<version-tag>:custom \
 -include-source -full-export -g $(pwd)/global_settings.json -p $(pwd)/policy.json -o $(pwd)/compiled_policy.tgz
```

This will transform any configuration that relies on external references into an inline configuration within the bundled source.

Additionally, when `-include-source` is combined with `-full-export`, the policy.json within the bundle will contain the entire source policy, including any default settings from the base template.

### Compile a logging profile

To compile a logging profile, execute the command below:

```shell
docker run \
 -v $(pwd):$(pwd) \
 waf-compiler-<version-tag>:custom \
 -l $(pwd)/log_01.json -o $(pwd)/log01.tgz
```

### View bundle information

To view information about a bundle file, such as attack signatures versions, use the following command:

```shell
docker run \
 -v $(pwd):$(pwd) \
 waf-compiler-<version-tag>:custom \
 -dump -bundle $(pwd)/compiled_policy.tgz
```

## Debugging bundle errors

When [configuring policies](/waf/policies/configuration.md), you may run into problems.

There are ways to remediate them based on the context:

| Description             | Solution  |
| ----------------------- | --------  |
| _Expected declarative policy_ | Ensure the JSON of the policy is well-formed | 
| _Policy Bundles version is older than the local version_ | You must recompile all your bundles from scratch when installing security updates. |
| _Policy Bundles version is newer than the local version_ | You must recompile all your bundles from scratch when installing security updates. |
| _Found mixed content of compiled and raw configuration_ | Only pre-compiled bundles can be used in NGINX configuration: compile JSON to bundles first. |
| _Compiler is required, but not installed: Missing /opt/app_protect/bin/config_set_compiler_ | Only pre-compiled bundles can be used in NGINX configuration: compile JSON to bundles first. |
| _Policy Bundles have differing global states_ | Recompile all of your bundles from scratch with your custom compiler. Bundles must be compiled with the same compiler: you cannot mix default and custom bundles. |
| _Policy Bundles have differing cookie seeds_ | Recompile all of your bundles from scratch with your custom compiler. Bundles must be compiled with the same compiler: you cannot mix default and custom bundles. |
| _Duplicate policy name found_ | Don't compile multiple policies with the same name, or one policy to multiple bundles. Each policy can be compiled once but a bundle can be re-used. |
| _Duplicate logging profile name found_ | Don't compile the same logging profile to multiple bundles. Each profile can be compiled once but a bundle can be re-used. |
| _Timeout waiting for enforcer_ | Likely an internal issue: [contact Support](/waf/support.md) |

## Global settings

The global settings allows configuration of the following items:

### cookie-protection

| Name       | Type   | Description |
| ---------- | ------ | ----------- |
| seed       | string | The seed value is used by F5 WAF for NGINX to generate the encryption key for the cookies it creates. These cookies are used for various purposes such as validating the integrity of the cookies generated by the application. Use a random alphanumeric string of at least 20 characters length (but not more than 1000 characters). |

### user-defined-signatures

| Name  | Reference | Type   | Description |
| ----- | --------- | ------ | ----------- |
| $ref  | Yes       | string | Path to the file that contains the user defined signatures. |

#### Example

```json
{
    "waf-settings": {
        "cookie-protection": {
            "seed": "80miIOiSeXfvNBiDJV4t"
        },
        "user-defined-signatures": [
            {
                "$ref": "file:///policies/uds.json"
            }
        ]
    }
}
```

**Note:** 

When deploying multiple scalability instances (Such as Kubernetes deployment replicas), ensure that all policy bundles are compiled with the same global settings and security updates.

## Using the compiler in a CI/CD process

When executing commands inside the compiler container, ensure that you use `/opt/app_protect/bin/apcompile` as the compiler binary.

This is particularly important if you're overriding the default entry point as part of a CI/CD process.

```shell
/opt/app_protect/bin/apcompile -g /path/to/global_settings.json -p /path/to/policy.json -o /path/to/compiled_policy.tgz
```

