Last active
October 23, 2026 09:47
-
-
Save tonusoo/7d2b3b80e6bc0a6f0a9b1e6dbec9b8d4 to your computer and use it in GitHub Desktop.
Local Internet Routing Registry (IRR) server setup for route filtering || tested on IRRd version 4.5.3 || NANOG mailing list thread: https://lists.nanog.org/archives/list/nanog@lists.nanog.org/thread/QYURU4X3Y5H3F7EAX3EEGEX4YOTOVZ63/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| irrd: | |
| # For performance reasons, use an Unix domain socket | |
| # for communicating with both the PostgreSQL (socket | |
| # is /run/postgresql/.s.PGSQL.5432) and Redis. | |
| # For PostgreSQL only the peer authentication is | |
| # allowed. | |
| database_url: 'postgresql:///irrd' | |
| redis_url: 'unix:///run/redis/redis-server.sock' | |
| # In Filesystem Hierarchy Standard 3.0, /var/run | |
| # is replaced by /run. | |
| piddir: /run/irrd/ | |
| access_lists: | |
| http_database_status: | |
| - '::1' | |
| - '127.0.0.1' | |
| - '192.168.104.0/24' | |
| server: | |
| # HTTP interface is behind the Apache | |
| # reverse proxy which also handles the | |
| # TLS termination. | |
| http: | |
| status_access_list: http_database_status | |
| interface: '::0' | |
| port: 8080 | |
| url: 'https://validator-a.internal/' | |
| whois: | |
| interface: '::0' | |
| max_connections: 50 | |
| port: 43 | |
| auth: | |
| # If a PGP "key-cert" object is added to the | |
| # database, then the directory below is used | |
| # for storing the public keys database (pubring.kbx). | |
| gnupg_keyring: /var/lib/irrd/gnupg-keyring/ | |
| email: | |
| from: example@example.com | |
| smtp: localhost | |
| notification_header: | | |
| This is to notify you of changes in the {sources_str} database | |
| or object authorisation failures. | |
| You may receive this message because you are listed in | |
| the notify attribute on the changed object(s), or because | |
| you are listed in the mnt-nfy or upd-to attribute on a maintainer | |
| of the object(s). | |
| # IRRd is started with "--foreground" and logging is managed by systemd's journal. | |
| log: | |
| level: DEBUG | |
| rpki: | |
| roa_source: 'file:///var/lib/rpki-client/json' | |
| roa_import_timer: 1800 | |
| pseudo_irr_remarks: | | |
| This AS{asn} route object represents routing data retrieved | |
| from the RPKI. This route object is the result of an automated | |
| RPKI-to-IRR conversion process performed by IRRd. | |
| # "prefixAssertions" entries in the SLURM file are processed as if | |
| # they were ROAs from "roa_source". "prefixFilters" entries in the | |
| # SLURM file are used to filter the ROAs from "roa_source". | |
| # /var/lib/irrd/slurm.json "slurmVersion" 1 file is prepared for | |
| # unforeseen reasons with empty "prefixAssertions" and "prefixFilters" | |
| # arrays. | |
| slurm_source: 'file:///var/lib/irrd/slurm.json' | |
| # Sets the "scopefilter_status" in database for route | |
| # objects to "out_scope_prefix" (prefix is an exact match, | |
| # less specific or more specific) or "out_scope_as" (origin | |
| # is out of scope). This affects all sources including RPKI. | |
| # Bogon prefix and ASN filtering is also done in routers. | |
| scopefilter: | |
| prefixes: | |
| - 0.0.0.0/8 # "This" network | |
| - 10.0.0.0/8 # Private network | |
| - 100.64.0.0/10 # Carrier-grade NAT private network | |
| - 127.0.0.0/8 # Loopback | |
| - 169.254.0.0/16 # Link local addresses | |
| - 172.16.0.0/12 # Private network | |
| - 192.0.0.0/24 # IETF Protocol Assignments | |
| - 192.0.2.0/24 # Documentation (TEST-NET-1) | |
| - 192.88.99.0/24 # Deprecated (6to4 Relay Anycast) | |
| - 192.168.0.0/16 # Private network | |
| - 198.18.0.0/15 # RFC 2544 benchmarking | |
| - 198.51.100.0/24 # Documentation (TEST-NET-2) | |
| - 203.0.113.0/24 # Documentation (TEST-NET-3) | |
| - 224.0.0.0/4 # Multicast | |
| - 240.0.0.0/4 # Reserved addresses | |
| - ::/8 # loopback, unspecified, v4-mapped | |
| - 64:ff9b::/96 # RFC 6052 IPv4-IPv6 Translation | |
| - 64:ff9b:1::/48 # RFC 8215 IPv4-IPv6 Translation | |
| - 100::/64 # Discard-Only Address Block | |
| - 2001::/32 # Teredo tunneling | |
| - 2001:2::/48 # Benchmarking | |
| - 2001:3::/32 # Automatic Multicast Tunneling | |
| - 2001:10::/28 # Deprecated (previously ORCHID) | |
| - 2001:20::/28 # ORCHIDv2 | |
| - 2001:30::/28 # Drone Remote ID Protocol Entity Tags (DETs) Prefix | |
| - 2001:db8::/32 # Documentation | |
| - 2002::/16 # 6to4 prefix | |
| - 3ffe::/16 # used for the 6bone but was returned | |
| - 3fff::/20 # Documentation | |
| - 5f00::/16 # Segment Routing (SRv6) | |
| - fc00::/7 # Unique Local Unicast | |
| - fe80::/10 # Link Local Unicast | |
| - fec0::/10 # RFC 3879 deprecated site-local unicast prefix | |
| - ff00::/8 # Multicast | |
| asns: | |
| - 0 # Reserved. RFC 7607 | |
| - 23456 # AS_TRANS. RFC 6793 | |
| - 64496-64511 # Reserved for use in documentation and sample code. RFC 5398 | |
| - 64512-65534 # Reserved for Private Use. RFC 6996 | |
| - 65535 # Reserved. RFC 7300 | |
| - 65536-65551 # Reserved for use in documentation and sample code. RFC 5398 | |
| - 65552-131071 # Reserved. | |
| - 4200000000-4294967294 # Reserved for Private Use. RFC 6996 | |
| - 4294967295 # Reserved. RFC 7300 | |
| # List of sources ordered in search priority. This does not | |
| # matter for route objects, but in case of as-set objects | |
| # the first one found is returned. | |
| sources_default: | |
| - RPKI | |
| # Authoritative IRR databases operated by RIRs and | |
| # enforcing that new as-set objects are hierarchical. | |
| - APNIC | |
| - LACNIC | |
| - RIPE | |
| # Authoritative IRR databases operated by RIRs and not | |
| # enforcing that new as-set objects are hierarchical. | |
| # AFRINIC "Require newly created AS-SETs to have | |
| # hierarchical names" proposal status: | |
| # https://afrinic.net/afpub-2026-asn-001-draft02 | |
| - AFRINIC | |
| # ARIN "Consultation on Enforcing Hierarchical Naming for AS-SETs": | |
| # https://www.arin.net/participate/community/acsp/consultations/2025/2025-1/ | |
| - ARIN | |
| # Authoritative IRR databases operated by NIRs. | |
| # IDNIC appears to no longer be operational. On the | |
| # other hand, according to value of the "last_update" | |
| # attribute seen in the output of "nc rr.ntt.net 43 <<< | |
| # '!JIDNIC'", NTT seems to receive updates from IDNIC. | |
| # However, those may be changes in the RPKI status of | |
| # an object (rpki_status in db) where source is IDNIC | |
| # as such updates will also update the "last_update" | |
| # attribute. | |
| - REGISTROBR | |
| - JPIRR | |
| # Non-authoritative IRR databases operated by private | |
| # companies, NGOs or RIRs. | |
| - TC | |
| - RADB | |
| - ALTDB | |
| - CANARIE | |
| - NTTCOM | |
| - LEVEL3 | |
| - BELL | |
| - BBOI | |
| - RIPE-NONAUTH | |
| # For non-authoritative IRR databases include only the ones which | |
| # are mirrored by rr.ntt.net (default IRR database of bgpq4). Those | |
| # can be seen with "nc rr.ntt.net 43 <<< '!j-*'". | |
| # Combined route or route6 objects of AFRINIC, APNIC, ARIN, LACNIC, | |
| # RIPE and REGISTROBR contain no duplicates. | |
| sources: | |
| # APNIC does not provide public NRTM access. It's required to sign the | |
| # "APNIC WHOIS data acceptable use agreement" for NRTM access. | |
| # According to https://blog.apnic.net/2025/02/07/apnic-products-2024-in-review/ | |
| # APNIC IRR is technically ready to provide NRTMv4. | |
| # rr.ntt.net seems to have NRTM session with APNIC IRR. | |
| APNIC: | |
| # APNIC provides split files. | |
| import_source: | |
| - 'https://ftp.apnic.net/apnic/whois/apnic.db.route.gz' | |
| - 'https://ftp.apnic.net/apnic/whois/apnic.db.route6.gz' | |
| - 'https://ftp.apnic.net/apnic/whois/apnic.db.as-set.gz' | |
| # APNIC publishes APNIC.CURRENTSERIAL and split files | |
| # once a day. Import route object dumps once an hour, | |
| # minimizing possible synchronization lag. | |
| import_timer: 3600 | |
| # Sanity check. | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 100 | |
| # LACNIC provides public NRTMv3 access. | |
| LACNIC: | |
| keep_journal: true | |
| import_serial_source: 'https://irr.lacnic.net/LACNIC.CURRENTSERIAL' | |
| # In LACNIC's implementation, the "route" and "route6" objects | |
| # are automatically generated based on the ROAs created by | |
| # the organization. In other words, all the "route" and | |
| # "route6" objects in LACNIC IRR have a valid ROA and are | |
| # thus redundant. | |
| import_source: 'https://irr.lacnic.net/lacnic.db.gz' | |
| nrtm_host: irr.lacnic.net | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 100 | |
| # RIPE provides public Near Real Time Mirroring (NRTM) version 4 access. | |
| RIPE: | |
| keep_journal: true | |
| # NRTMv4 Update Notification File contains the URLs for database snapshot | |
| # file and possible delta files. Payload portion can be dumped with: | |
| # curl -s https://nrtm.db.ripe.net/nrtmv4/RIPE/update-notification-file.jose | \ | |
| # cut -d "." -f 2 | basenc --base64url -d | jq | |
| nrtm4_client_notification_file_url: 'https://nrtm.db.ripe.net/nrtmv4/RIPE/update-notification-file.jose' | |
| # Update Notification File is signed by RIPE. PEM encoded ES256 public key is | |
| # published here: https://ftp.ripe.net/ripe/dbase/nrtmv4/nrtmv4_public_key.txt | |
| # IRRd stores this in PostgreSQL "database_status" table and updates the key | |
| # in database when RIPE initiates the key rotation procedure and publishes | |
| # Update Notification File with a new key by setting the value of | |
| # "next_signing_key" field in update-notification-file.jose file. This allows | |
| # key rotation to be processed entirely automatically without changing the | |
| # configuration. According to RIPE database documentation, the key will rotate | |
| # approximately once a year for security reasons. | |
| nrtm4_client_initial_public_key: | | |
| -----BEGIN PUBLIC KEY----- | |
| MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+CLQbYt7qLVBuC/cTeEwKBpVLl2M | |
| TexDGnZzWUWGC4SEBgD7qLDm/k3e6JXI+V3O5gVj4obDlcX7lzm3WkEwgw== | |
| -----END PUBLIC KEY----- | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 100 | |
| # AFRINIC provides NRTMv3. However, users must contact the AFRINIC | |
| # helpdesk to request IP address whitelisting. Procedure is | |
| # documented under "How to mirror the AFRINIC IRR" at | |
| # https://afrinic.net/support#irr-how-tos | |
| AFRINIC: | |
| # RPSL objects change log is in "rpsl_database_journal" table. | |
| keep_journal: true | |
| import_serial_source: 'https://ftp.afrinic.net/pub/dbase/AFRINIC.CURRENTSERIAL' | |
| # AFRINIC and several other IRR databases like RIPE, RADB or ALTDB | |
| # contain objects where the value of the "source" attribute is in | |
| # lowercase or mixed case. For example "afrinic", "Afrinic", | |
| # "AfriNIC", etc. This is not a problem and IRRd imports those | |
| # objects just fine. | |
| import_source: 'https://ftp.afrinic.net/pub/dbase/afrinic.db.gz' | |
| nrtm_host: whois.afrinic.net | |
| nrtm_port: 43003 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 100 | |
| # ARIN provides public NRTMv3 access. | |
| ARIN: | |
| keep_journal: true | |
| import_serial_source: 'https://ftp.arin.net/pub/rr/ARIN.CURRENTSERIAL' | |
| import_source: 'https://ftp.arin.net/pub/rr/arin.db.gz' | |
| # After the initial setup, the IRRd will connect to rr.arin.net:43 | |
| # with 5 minute intervals and executes WHOIS query "-g ARIN:3: | |
| # <serial+1>-LAST" where <serial> number was returned by the server | |
| # during the last query on "%START Version" string. For example, if | |
| # the query was "-g ARIN:3:10972223-LAST" and the server returned | |
| # "%START Version: 3 ARIN 10972223-10972261", then the query | |
| # on the next interval will be "-g ARIN:3:10972262-LAST". | |
| nrtm_host: rr.arin.net | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 100 | |
| # RPSL route objects in registrobr.db file are generated by a script based on | |
| # https://ftp.registro.br/pub/numeracao/origin/nicbr-asn-blk-latest.txt | |
| REGISTROBR: | |
| import_source: 'file:///var/lib/irrd/registrobr.db' | |
| import_timer: 3600 | |
| # Sanity check. | |
| object_class_filter: | |
| - route | |
| - route6 | |
| route_object_preference: 90 | |
| # JPNIC does not provide public NRTM access. | |
| JPIRR: | |
| # Fetch the serial with 5 minute interval and | |
| # if the serial has been incremented, then | |
| # download the dump and make a full import. | |
| import_serial_source: 'https://ftp.nic.ad.jp/jpirr/JPIRR.CURRENTSERIAL' | |
| # JPIRR creates a dump every 30 | |
| # minutes regardless if there has | |
| # been any changes or not. | |
| import_source: 'https://ftp.nic.ad.jp/jpirr/jpirr.db.gz' | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| # JPIRR contains out-of-region route objects. Lower the | |
| # route object preference to make sure, that route objects | |
| # in authoritative NIR or RIR IRR are preferred. Additionally, | |
| # keeping the JPIRR on the same priority as third-party IRRs | |
| # ensures that out-of-region entries in JPIRR do not suppress | |
| # entries in non-authoritative IRRs for non-Japanese space. | |
| # On the other hand, JPIRR removes route and as-set objects | |
| # which have not been updated within last few years in order | |
| # to avoid stale route objects. | |
| route_object_preference: 80 | |
| # TC provides public NRTMv3 access. | |
| TC: | |
| keep_journal: true | |
| import_serial_source: 'https://ftp.bgp.net.br/TC.CURRENTSERIAL' | |
| import_source: 'https://ftp.bgp.net.br/tc.db.gz' | |
| nrtm_host: irr.bgp.net.br | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # RADB provides public NRTMv3 access. | |
| RADB: | |
| keep_journal: true | |
| # RADB does not provide serial and database dump via HTTPS. | |
| import_serial_source: 'ftp://ftp.radb.net/radb/dbase/RADB.CURRENTSERIAL' | |
| import_source: 'ftp://ftp.radb.net/radb/dbase/radb.db.gz' | |
| nrtm_host: nrtm.radb.net | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| # One may think that there is no need to import "route6" | |
| # objects from non-authoritative IRR databases as all | |
| # the IPv6 Global Unicast Address space is allocated | |
| # by RIRs, but it's not uncommon that the "route6" | |
| # object is missing in authoritative IRR database | |
| # and exists in non-authoritative IRR like RADB. | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # ALTDB provides public NRTMv3 access. | |
| ALTDB: | |
| keep_journal: true | |
| # ALTDB does not provide serial and database dump via HTTPS. | |
| import_serial_source: 'ftp://ftp.altdb.net/pub/altdb/ALTDB.CURRENTSERIAL' | |
| import_source: 'ftp://ftp.altdb.net/pub/altdb/altdb.db.gz' | |
| nrtm_host: whois.altdb.net | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # CANARIE provides public NRTMv3 access. | |
| CANARIE: | |
| keep_journal: true | |
| import_serial_source: 'https://whois.canarie.ca/dbase/CURRENTSERIAL' | |
| import_source: 'https://whois.canarie.ca/dbase/canarie.db.gz' | |
| nrtm_host: whois.canarie.ca | |
| nrtm_port: 43003 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # NTTCOM does not provide public NRTM access. | |
| NTTCOM: | |
| import_serial_source: 'https://rr1.ntt.net/nttcomRR/NTTCOM.CURRENTSERIAL' | |
| import_source: 'https://rr1.ntt.net/nttcomRR/nttcom.db.gz' | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # LEVEL3 provides public NRTM access. | |
| LEVEL3: | |
| keep_journal: true | |
| import_serial_source: 'https://rr.level3.net/LEVEL3.CURRENTSERIAL' | |
| import_source: 'https://rr.level3.net/level3.db.gz' | |
| nrtm_host: rr.level3.net | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # BELL does not provide public NRTM access. | |
| BELL: | |
| import_serial_source: 'ftp://whois.in.bell.ca/BELL.CURRENTSERIAL' | |
| import_source: 'ftp://whois.in.bell.ca/bell.db.gz' | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # BBOI provides public NRTMv3 access. | |
| BBOI: | |
| keep_journal: true | |
| import_serial_source: 'ftp://irr.bboi.net/BBOI.CURRENTSERIAL' | |
| import_source: 'ftp://irr.bboi.net/bboi.db.gz' | |
| nrtm_host: irr.bboi.net | |
| nrtm_port: 43 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 | |
| # RIPE-NONAUTH provides public NRTMv3 access. | |
| RIPE-NONAUTH: | |
| keep_journal: true | |
| import_serial_source: 'https://ftp.ripe.net/ripe/dbase/RIPE-NONAUTH.CURRENTSERIAL' | |
| import_source: 'https://ftp.ripe.net/ripe/dbase/ripe-nonauth.db.utf8.gz' | |
| nrtm_host: whois.ripe.net | |
| nrtm_port: 4444 | |
| object_class_filter: | |
| - route | |
| - route6 | |
| - as-set | |
| route_object_preference: 80 |
Author
Author
REGISTROBR source generator:
irrd@validator-a:~$ cat /usr/local/bin/registrobr-db-generator.bash
#!/usr/bin/env bash
dir="/var/lib/irrd"
csum=$(curl -fs https://ftp.registro.br/pub/numeracao/origin/nicbr-asn-blk-latest.txt.sha256)
csum="${csum##* }"
data=$(curl -fs https://ftp.registro.br/pub/numeracao/origin/nicbr-asn-blk-latest.txt)
data_csum=$(sha256sum <<< "$data")
data_csum="${data_csum%% *}"
if [[ "$data_csum" != "$csum" ]]; then
echo "nicbr-asn-blk-latest.txt SHA256 checksum is invalid. Abort"
exit 1
fi
> "$dir"/registrobr.db.tmp
while IFS="|" read -r asn descr owner_id prefixes; do
[[ -z "$prefixes" ]] && continue
#echo "$asn $prefixes"
IFS='|' read -ra prefix_array <<< "$prefixes"
for prefix in "${prefix_array[@]}"; do
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
if [[ "$prefix" == *:* ]]; then
obj_type="route6"
else
obj_type="route"
fi
cat >> "$dir"/registrobr.db.tmp <<- EOF
${obj_type}: ${prefix}
descr: ${descr}
remarks: registro.br ownerid: ${owner_id}
remarks: This route object represents routing data retrieved from Registro.BR
remarks: The original data can be found here: https://registro.br/2/whois?qr=${prefix}
remarks: This route object is the result of an automated WHOIS-to-IRR conversion process.
origin: ${asn}
mnt-by: MAINT-DUMMY
last-modified: ${ts}
source: REGISTROBR
EOF
done
done <<< "$data"
mv "$dir"/registrobr.db.tmp "$dir"/registrobr.db
irrd@validator-a:~$
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Systemd service file for IRRd: