Skip to content

Add a Rule Source

A rule does not have to have its list written into your configuration. metadata.sources points it at a list that lives somewhere else — a file on disk, a URL, a feed someone else publishes — and turns whatever shape it arrives in into the rules the plugin expects.

This page is four worked examples. For every option there is, see Rule Sources.

A file of addresses

The simplest thing that works. One address or CIDR per line, # comments ignored:

plugins:
  - plugin: "Kanopi\\Firewall\\Plugins\\IpAddress"
    response: block
    enable: true
    metadata:
      sources:
        - name: tor-exits
          upstream: "{config_dir}/lists/tor-exits.txt"
          validate: cidr
    config:
      - 203.0.113.7      # local additions still land last

validate: cidr is worth the one line: it rejects entries that are not addresses, so a list that arrives as an HTML error page cannot quietly become a rule.

A remote list, refreshed on its own schedule

Swap the path for a URL and give it a ttl:

        - name: tor-exits
          upstream: https://example.org/v1/tor-exits.txt
          ttl: 21600          # six hours
          validate: cidr

Do this next, not later

A cold or expired cache means a visitor pays for the fetch. Refresh out of band and take the request path offline — see Sync Rule Sources, which is a five-line deploy step.

A JSON document you did not design

Cloud providers publish ranges as structured documents with everything in one file. Narrow it rather than consuming all of it:

- plugin: "Kanopi\\Firewall\\Plugins\\IpAddress"
  response: challenge
  enable: true
  metadata:
    sources:
      - name: cloud-ec2-us
        upstream: https://example.org/v1/ranges.json
        format: json
        select: "{prefixes,ipv6_prefixes}.*"
        where:
          - "service:EC2"
          - "region@starts_with:us-"
        template: "{value[ip_prefix|ipv6_prefix]}"
        validate: cidr
        max_delta: 0.25
        ttl: 21600

max_delta: 0.25 refuses a refresh that changes more than a quarter of the list. A feed that breaks upstream usually breaks large, and that is the shape of it.

The same three stages work on CSV:

- plugin: "Kanopi\\Firewall\\Plugins\\Asn"
  response: challenge
  enable: true
  metadata:
    reader:
      type: reader
      db: /usr/local/share/GeoIP/GeoLite2-ASN.mmdb
    sources:
      - name: hosting-asns
        upstream: "{config_dir}/lists/hosting-asns.csv"
        format: csv
        where:
          - "category:hosting"
        template: "asn:{value[asn]}"

Several lists into one rule

Sources contribute in declaration order, and inline config: is appended after all of them — so a deployment can always add an entry without editing a shared list:

- plugin: "Kanopi\\Firewall\\Plugins\\IpAddress"
  response: allow
  weight: -200
  enable: true
  metadata:
    sources:
      - "{config_dir}/lists/circleci.txt"
      - "{config_dir}/lists/uptimerobot.txt"
      - "{config_dir}/lists/github-actions.txt"
  config:
    - 127.0.0.1
    - 10.0.0.0/8

One list, three different answers

A source carries data, not policy. The same list can allow, challenge or block depending on what the deployment wants from it — so you rarely need three lists:

plugins:
  # Trusted automation — straight through, nothing else runs
  - plugin: "Kanopi\\Firewall\\Plugins\\IpAddress"
    response: allow
    weight: -200
    enable: true
    metadata:
      sources:
        - name: uptimerobot
          upstream: "{config_dir}/lists/uptimerobot.txt"
          validate: cidr
          required: true

  # Bulk cloud egress — plausible, but prove it
  - plugin: "Kanopi\\Firewall\\Plugins\\IpAddress"
    response: challenge
    weight: 0
    enable: true
    metadata:
      sources:
        - name: cloud-egress
          upstream: "{config_dir}/lists/cloud-egress.txt"
          validate: cidr

  # Known bad — gone
  - plugin: "Kanopi\\Firewall\\Plugins\\IpAddress"
    response: block
    weight: 10
    enable: true
    metadata:
      sources:
        - name: tor-exits
          upstream: "{config_dir}/lists/tor-exits.txt"
          validate: cidr

Then what

Keep fetches off the request path Sync Rule Sources
A private feed needing a token Authentication
Every option, format and guardrail Rule Sources
Check what a source resolved to firewall-sources config.yml --dry-run