Skip to main content

Catalog Zones

Catalog Zones (RFC 9432) provide automated zone provisioning between DNS servers. A catalog zone contains a list of member zones that should be served, eliminating the need to manually synchronize zone configuration across primary and secondary servers.

Overview

Since version 4.9.0, NSD supports Catalog Zones version “2” as specified in RFC 9432. NSD can function as both:
  • Catalog Consumer: Automatically provisions zones from a catalog
  • Catalog Producer: Maintains a catalog that consumers use
NSD is limited to processing a single catalog consumer zone. Multiple producer zones are supported.

Catalog Zone Concepts

Member Zones

Member zones are the actual DNS zones (like example.com) that are listed in the catalog. The catalog zone itself (like catalog.invalid) only contains metadata about which zones should be served.

Group Properties

Member zones can have a group property that references a pattern name. This allows different zones to use different configuration patterns (transfer settings, access controls, etc.).

Catalog Member ID

Each member zone gets a unique identifier in the catalog, displayed as a subdomain like:
a5b75379.zones.catalog1.invalid.

Consumer Configuration

Basic Consumer Setup

1
Configure Member Pattern
2
Define a pattern for member zones to use:
3
pattern:
    name: "member-zone-config"
    # Primary server for member zones
    request-xfr: 198.51.100.1 NOKEY
    allow-notify: 198.51.100.1 NOKEY
4
Configure Catalog Zone
5
Set up the catalog zone as a consumer:
6
zone:
    name: "catalog1.invalid"
    catalog: consumer
    catalog-member-pattern: "member-zone-config"
    
    # Transfer catalog from primary
    request-xfr: 192.0.2.1 NOKEY
    allow-notify: 192.0.2.1 NOKEY
    
    # Hide catalog contents
    allow-query: BLOCKED
7
Reload Configuration
8
nsd-control reconfig
Security: Catalog zones contain a list of all zones served by your nameserver. Use allow-query: BLOCKED to prevent exposing this information. Consider using TLS for catalog transfers.

Advanced Consumer Configuration

# Define TSIG key
key:
    name: tsig-key.name
    algorithm: hmac-sha256
    secret: "SXMgdGhpcyBhIHNlY3JldCBvciBqdXN0IHRleHQ/Pz8="

# Member pattern
pattern:
    name: "member-zone-config"
    request-xfr: 198.51.100.1 NOKEY
    allow-notify: 198.51.100.1 NOKEY

# Catalog consumer zone with TSIG
zone:
    name: "catalog1.invalid"
    catalog: consumer
    catalog-member-pattern: "member-zone-config"
    
    # Secure catalog transfer with TSIG
    request-xfr: 192.0.2.1 tsig-key.name
    allow-notify: 192.0.2.1 tsig-key.name
    
    allow-query: BLOCKED

Group Property Support

From the source documentation, NSD supports RFC 9432 group properties. Member zones can specify which pattern to use:
; In catalog1.invalid zone file
; Member with default pattern
a5b75379.zones.catalog1.invalid. 0 IN PTR example.net.

; Member with specific group
96143f7d.zones.catalog1.invalid. 0 IN PTR example.org.
96143f7d.zones.catalog1.invalid. 0 IN TXT "group=dnssec-zones"
If the group property is:
  • Missing: Uses catalog-member-pattern
  • Invalid (pattern doesn’t exist): Uses catalog-member-pattern
  • Valid (matches a pattern name): Uses that pattern

Producer Configuration

Basic Producer Setup

1
Define Patterns for Members
2
pattern:
    name: "group0"
    catalog-producer-zone: "catalog1.invalid"

pattern:
    name: "group1"
    catalog-producer-zone: "catalog1.invalid"
3
Configure Catalog Producer Zone
4
zone:
    name: "catalog1.invalid"
    catalog: producer
    
    # Enable IXFR for efficient updates
    store-ixfr: yes
    
    # Provide transfers to consumers
    provide-xfr: 203.0.113.1 NOKEY
    notify: 203.0.113.1 NOKEY
    
    # Hide catalog contents
    allow-query: BLOCKED
5
Add Member Zones
6
# Add zones to the catalog via nsd-control
nsd-control addzone example.net group0
nsd-control addzone example.org group1

Advanced Producer Configuration

server:
    # Enable TLS service
    interface: 192.0.2.1@853
    tls-port: 853
    tls-service-key: "primary.example.key.pem"
    tls-service-pem: "primary.example.cert.pem"

# Member patterns
pattern:
    name: "group0"
    catalog-producer-zone: "catalog1.invalid"

pattern:
    name: "group1"
    catalog-producer-zone: "catalog1.invalid"

# TSIG key
key:
    name: tsig-key.name
    algorithm: hmac-sha256
    secret: "SXMgdGhpcyBhIHNlY3JldCBvciBqdXN0IHRleHQ/Pz8="

# Catalog producer with TLS
zone:
    name: "catalog1.invalid"
    catalog: producer
    
    store-ixfr: yes
    provide-xfr: 203.0.113.1@853 tsig-key.name
    notify: 203.0.113.1 tsig-key.name
    
    allow-query: BLOCKED

Managing Member Zones

Adding Member Zones

# Add zone with default group (group0)
nsd-control addzone example.net group0

# Add zone with specific group
nsd-control addzone example.org group1

# Output: ok

Removing Member Zones

# Remove zone from catalog
nsd-control delzone example.net

# The zone is removed from the catalog
# and from consumer servers on next sync

Listing Member Zones

# View all zones including member status
nsd-control zonestatus
Output example:
zone:   catalog1.invalid
    catalog: producer (serial: 1708341939, # members: 2)
    state: primary

zone:   example.net
    pattern: group0
    catalog-member-id: a5b75379.zones.catalog1.invalid.
    state: primary

zone:   example.org
    pattern: group1
    catalog-member-id: 96143f7d.zones.catalog1.invalid.
    state: primary

Monitoring and Status

Check Catalog Status

# Check consumer catalog status
nsd-control zonestatus catalog1.invalid
Output for consumer:
zone:   catalog1.invalid
    catalog: consumer (serial: 1708341939, # members: 2)
    state: ok
    served-serial: "1708341939 since 2024-02-19T15:19:44"
    commit-serial: "1708341939 since 2024-02-19T15:19:44"
    wait: "3461 sec between attempts"
Output for producer:
zone:   catalog1.invalid
    catalog: producer (serial: 1708341939, # members: 2)
    state: primary

Check Member Status

# View specific member zone
nsd-control zonestatus example.net
Output:
zone:   example.net
    pattern: member-zone-config
    catalog-member-id: a5b75379.zones.catalog1.invalid.
    state: ok
    served-serial: "2024013019 since 2024-02-19T14:25:43"
    commit-serial: "2024013019 since 2024-02-19T14:25:43"
    wait: "7195 sec between attempts"

Invalid Catalog Diagnosis

If a catalog zone is invalid, nsd-control zonestatus shows the reason. Common reasons:
  • Invalid catalog zone format
  • Broken member zone references
  • Missing NSEC3PARAM records (if DNSSEC-signed)
  • Syntax errors in catalog zone file

Zone List Persistence

From the documentation: Member zones are stored in the zone list file (configured with zonelistfile option).
# View zone list
cat /var/db/nsd/zone.list
Output:
# NSD zone list
# name pattern catalog-member-id
cat example.net group0 a5b75379
cat example.org group1 96143f7d
Format:
  • cat: Indicates catalog member zone
  • zone name: The member zone name
  • pattern: Pattern used for this member
  • member-id: Unique catalog member identifier (8 hex characters)

Catalog Zone Content

For producers, NSD automatically generates catalog zone content. The zone is reconstructed from the zone list file on startup.

Catalog Zone Format (RFC 9432)

; Catalog Zone: catalog1.invalid
$ORIGIN catalog1.invalid.
$TTL 3600

@   IN  SOA ns1.example.com. admin.example.com. (
            2024030801 ; serial
            3600       ; refresh
            600        ; retry
            604800     ; expire
            86400 )    ; minimum

    IN  NS  ns1.example.com.

; Catalog version
version IN  TXT "2"

; Member zones
a5b75379.zones  IN  PTR  example.net.
96143f7d.zones  IN  PTR  example.org.

; Group properties
96143f7d.zones  IN  TXT  "group=group1"
Important: For producer zones:
  • zonefile option is used for writing only, never reading
  • Content is reconstructed from the zone list file
  • Don’t manually edit producer zone files

Security Considerations

Privacy Protection

Catalog zones expose your nameserver’s zone list. RFC 9432 recommends:
1
Block Public Queries
2
zone:
    name: "catalog1.invalid"
    catalog: consumer  # or producer
    
    # Block all queries
    allow-query: 0.0.0.0/0 BLOCKED
    allow-query: ::0/0 BLOCKED
3
Use Encrypted Transfers
4
# TLS authentication
tls-auth:
    name: catalog-server
    auth-domain-name: catalog.example.com

# Catalog with XFR-over-TLS
zone:
    name: "catalog1.invalid"
    catalog: consumer
    catalog-member-pattern: "members"
    
    # Encrypted transfer
    request-xfr: 192.0.2.1@853 NOKEY catalog-server
    allow-notify: 192.0.2.1 NOKEY
    
    allow-query: BLOCKED
5
Restrict Network Access
6
Use firewalls to limit access to catalog transfer ports:
7
# iptables example - allow only from specific IPs
iptables -A INPUT -p tcp --dport 853 -s 192.0.2.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 853 -j DROP

Troubleshooting

Symptom: Catalog consumer doesn’t show member zonesDiagnosis:
# Check catalog status
nsd-control zonestatus catalog1.invalid

# Look for errors in log
journalctl -u nsd | grep catalog
Common Causes:
  • Catalog zone not transferred yet
  • Invalid catalog zone format
  • Pattern name in group doesn’t match any defined pattern
  • Network/firewall issues preventing transfer
Solution:
# Force zone transfer
nsd-control force_transfer catalog1.invalid

# Verify catalog zone contents
dig @localhost catalog1.invalid AXFR
Symptom: Member zones use wrong pattern or fail to loadDiagnosis:
nsd-control zonestatus example.org
# Check which pattern is being used
Cause: The group property references a pattern that doesn’t existSolution:
# Ensure pattern exists and matches group name
pattern:
    name: "group1"  # Must match group property exactly
    catalog-producer-zone: "catalog1.invalid"
Symptom: Changes to member zones don’t propagateCause: For producers, NSD automatically updates the serial when zones are added/removed via nsd-controlSolution:
# Check current serial
nsd-control zonestatus catalog1.invalid

# Force notify to consumers
nsd-control notify catalog1.invalid
Symptom: Catalog processing disabled, log shows warningCause: NSD only supports one catalog consumer zoneSolution: Remove or comment out extra consumer zone configurations
# Only one zone should have:
# catalog: consumer

Performance Considerations

Memory Usage

  • Per catalog zone: ~1-2 KB base overhead
  • Per member entry: ~200-500 bytes
  • Large catalogs: 10,000 members ≈ 2-5 MB

Update Performance

Catalog updates use IXFR when possible:
zone:
    name: "catalog1.invalid"
    catalog: producer
    
    # Enable IXFR for efficient updates
    store-ixfr: yes
    ixfr-number: 10
    ixfr-size: 10485760  # 10 MB
  • Members per catalog: < 10,000 for optimal performance
  • Update frequency: Batch changes to reduce serial increments
  • Consumer refresh: Use appropriate SOA refresh/retry timers

Migration Strategies

From Manual Configuration to Catalog

1
Set Up Producer
2
# Configure catalog producer on primary
# Add catalog patterns and zone
3
Add Existing Zones to Catalog
4
# Add all current zones to catalog
nsd-control addzone zone1.example.com members
nsd-control addzone zone2.example.com members
# ... repeat for all zones
5
Configure Consumers
6
# On secondary servers, add catalog consumer configuration
# Remove individual zone configurations
# Reload NSD
nsd-control reconfig
7
Verify Synchronization
8
# Check all servers have same zones
for server in primary secondary1 secondary2; do
    echo "=== $server ==="
    ssh $server "nsd-control zonestatus | grep 'zone:' | wc -l"
done

Build docs developers (and LLMs) love