# Firewall & WAF Allowlisting

If your site is protected by a firewall, WAF, or bot-management platform that blocks or challenges traffic by IP, you may need to allowlist Duku's egress IPs.

This lets Duku agents reach your application reliably.

### Egress IP feed

Duku publishes its current egress IPs at two stable URLs:

| URL                               | Format                              | Use it for                                    |
| --------------------------------- | ----------------------------------- | --------------------------------------------- |
| `https://ips.duku.ai/egress.json` | JSON, AWS-style envelope            | Programmatic consumption and change detection |
| `https://ips.duku.ai/egress.txt`  | Plain text, one IP per line, sorted | Firewall UIs and simple shell pipelines       |

The feed is served over HTTPS from CloudFront with a 5-minute edge cache.

### JSON format

```json
{
  "syncToken": "9f2e3a…",
  "version": "1",
  "prefixes": [
    {
      "ip_prefix": "1.2.3.4/32",
      "region": "eu-west-1",
      "environment": "prod",
      "service": "chrome-worker",
      "purpose": "egress"
    }
  ]
}
```

| Field                    | Meaning                                                                     |
| ------------------------ | --------------------------------------------------------------------------- |
| `syncToken`              | Stable hash of the current prefix set. It changes only when the IPs change. |
| `version`                | Schema version. This changes only for breaking JSON layout changes.         |
| `prefixes[].ip_prefix`   | IPv4 CIDR. Today all entries are `/32`.                                     |
| `prefixes[].region`      | AWS region the traffic egresses from, such as `eu-west-1`.                  |
| `prefixes[].environment` | `prod` or `staging`. Most customers only need `prod`.                       |
| `prefixes[].service`     | The Duku service the IP belongs to. Today this is `chrome-worker`.          |
| `prefixes[].purpose`     | `egress` - outbound traffic from Duku agents.                               |

### Quick allowlist

Fetch the plain text feed:

```bash
curl -fsSL https://ips.duku.ai/egress.txt
```

The output is one IP per line. It is sorted and ready to paste into most firewall UIs.

To filter to production only with the JSON feed:

```bash
curl -fsSL https://ips.duku.ai/egress.json \
  | jq -r '.prefixes[] | select(.environment == "prod") | .ip_prefix' \
  | awk -F/ '{print $1}'
```

### Cloudflare

#### Recommended: Skip rule on user agent

Duku agents send a `User-Agent` header that contains `Duku/1.0`.

The simplest setup is a Skip rule on User Agent. This avoids maintaining an IP list and survives IP changes automatically.

{% hint style="warning" %}
A Skip rule cannot override an IP Access Rule. If your block is defined in **Security → WAF → Tools**, you also need an IP allowlist.
{% endhint %}

#### IP allowlist with a Custom List

If your block is IP-based, create a Cloudflare Custom List of type IP.

1. Open **Manage Account → Configurations → Lists**.
2. Click **Create new list**.
3. Choose **IP**.
4. Populate the list from `https://ips.duku.ai/egress.txt`.

Reference the list in your IP Access Rule or WAF Custom Rule:

```
(ip.src in $duku_egress)
```

To keep the list current, schedule a small Cloudflare Worker or CI job that fetches the feed and updates the Rules Lists API.

```javascript
const r = await fetch("https://ips.duku.ai/egress.txt");
const ips = (await r.text()).trim().split("\n");

await fetch(
  `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/rules/lists/${LIST_ID}/items`,
  {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(ips.map((ip) => ({ ip })))
  }
);
```

Run this daily. The feed changes rarely.

### AWS WAF

Use a managed IP set:

```bash
# One-time create
aws wafv2 create-ip-set \
  --name duku-egress \
  --scope REGIONAL \
  --ip-address-version IPV4 \
  --addresses $(curl -fsSL https://ips.duku.ai/egress.txt | sed 's|$|/32|' | paste -sd, -)

# Refresh on a schedule
aws wafv2 update-ip-set \
  --name duku-egress \
  --id <ip-set-id> \
  --scope REGIONAL \
  --lock-token <current-lock-token> \
  --addresses $(curl -fsSL https://ips.duku.ai/egress.txt | sed 's|$|/32|' | paste -sd, -)
```

Then reference the IP set in a WAF rule with the **Allow** action.

### Other platforms

The feed works with any platform that can ingest an IP list over HTTPS.

Common patterns:

* `nginx` - write the IPs into an `allow` block and reload.
* `iptables` or `nftables` - fetch the list and update your rules.
* DataDome, Akamai, and Imperva - paste the list into the platform allowlist UI.

Example:

```bash
for ip in $(curl -fsSL https://ips.duku.ai/egress.txt); do
  iptables -A INPUT -s "$ip" -j ACCEPT
done
```

### Stability and change detection

The IP set is stable. It changes only when Duku's underlying networking changes.

In practice, changes are rare.

Use `syncToken` for cheap change detection. It changes if and only if the IP set changes.

Duku sends advance notice by email before planned changes.

The feed is highly available, served from CloudFront, and the URLs stay stable.

### Need help?

If allowlisting does not resolve the block, contact your Duku account team.

We can usually identify whether the block is IP-based, user-agent-based, or behavioural, and recommend the right configuration.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.duku.ai/integrations/firewall-and-waf-allowlisting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
