Skip to content

Instantly share code, notes, and snippets.

@tonusoo
Last active October 23, 2026 09:47
Show Gist options
  • Select an option

  • Save tonusoo/7d2b3b80e6bc0a6f0a9b1e6dbec9b8d4 to your computer and use it in GitHub Desktop.

Select an option

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/
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
@tonusoo

tonusoo commented Oct 23, 2026

Copy link
Copy Markdown
Author

Systemd service file for IRRd:

irrd@validator-a:~$ systemctl cat irrd.service
# /etc/systemd/system/irrd.service
[Unit]
Description=IRRd4 Service
Requires=redis-server.service postgresql@17-main.service
After=network.target redis-server.service postgresql@17-main.service

[Service]
Type=simple
User=irrd
Group=irrd

# IRRd needs to bind to WHOIS TCP port number 43
AmbientCapabilities=CAP_NET_BIND_SERVICE

# PID file is /run/irrd/irrd.pid
RuntimeDirectory=irrd

WorkingDirectory=/var/lib/irrd
ExecStart=/var/lib/irrd/irrd-venv/bin/irrd --foreground
Restart=on-failure
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
irrd@validator-a:~$

@tonusoo

tonusoo commented Oct 23, 2026

Copy link
Copy Markdown
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