2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-30 14:55:17 +00:00

Merge pull request #74 from VersatusHPC/fix/riscv64-net-dns-key-record

fix(xcat-dep): riscv64 ships a Net::DNS without the KEY record
This commit is contained in:
Daniel Hilst
2026-09-29 11:16:26 -03:00
committed by GitHub
8 changed files with 214 additions and 14 deletions
@@ -69,6 +69,7 @@ jobs:
prove -v t/goconserver_cross_build.t
prove -v t/ipxe_xcat_payload.t
prove -v t/mockbuild-all.t
prove -v t/net_dns_rr_types.t
prove -v t/repo-lock-race.t
prove -v t/nfslock.t
prove -v -It/lib t/genesis_openembedded_release.t
+8 -4
View File
@@ -489,10 +489,14 @@ Deliberately not built for riscv64:
Test::XML) are EPEL-only as well, so it can neither be built nor installed without EPEL;
xCAT uses it for HP blade and VirtualBox support only.
Known differences from the EPEL-fed x86_64 repo: perl-Net-DNS is the 0.80 release of
`perl-Net-DNS/Net-DNS.spec`, built pure-perl (`--noxs`) as noarch, where EPEL 10 ships
1.47 -- its spec BuildRequires perl(Net::LibIDN2), which is EPEL-only too. A newer,
XS-free perl-Net-DNS without that BuildRequires is a follow-up. The riscv64 goconserver
perl-Net-DNS is built from `perl-Net-DNS/Net-DNS.spec` at 1.57. xCAT needs the KEY record,
which release 0.80 left to the separate Net::DNS::SEC distribution. 1.57 is newer than the
1.47 that EPEL 10 ships, because 1.56 and 1.57 fix CVE-2026-64193, CVE-2026-64194 and an
unbounded recursion on TSIG. This spec
does not take the EPEL-only perl(Net::LibIDN2) BuildRequires of the EPEL package:
Net::DNS treats Net::LibIDN2 as optional and uses it for internationalised names only.
Known difference from the EPEL-fed x86_64 repo: the riscv64 goconserver
binaries are stripped by the Go linker (`-ldflags "-s -w"`, cross build only) because the
host's brp-strip cannot strip a riscv64 ELF.
+1 -1
View File
@@ -180,7 +180,7 @@ perl-Crypt-Rijndael=1.13
perl-Digest-SHA1=2.13
perl-Expect=1.35
perl-Mail-Sender=0.903
perl-Net-DNS=0.80
perl-Net-DNS=1.57
perl-Net-IP=1.26
perl-Path-Class=0.37
+3 -2
View File
@@ -1,6 +1,7 @@
How to build the perl-Net-DNS
1. cp the Net-DNS-0.80.tar.gz to the rpmbuild/SOURCES/
1. cp the Net-DNS-1.57.tar.gz to the rpmbuild/SOURCES/
2. cp the Net-DNS.spec to the rpmbuild/SPECS/
3. cd rpmbuild/SPECS/
4. rpmbuild -bb Net-DNS.spec (The default buildarch in Net-DNS.spec is x86_64, needs to modify buildarch if the build arch is not x86_64)
4. rpmbuild -bb Net-DNS.spec (Net::DNS is pure perl, so the rpm is noarch and one
build serves every architecture.)
Binary file not shown.
Binary file not shown.
+27 -7
View File
@@ -2,7 +2,7 @@
# - Net::DNS -
# This spec file was automatically generated by cpan2rpm [ver: 2.028]
# The following arguments were used:
# ./Net-DNS-0.80.tar.gz
# ./Net-DNS-1.57.tar.gz
# For more information on cpan2rpm please visit: http://perl.arix.com/
#
@@ -13,8 +13,8 @@
name: perl-Net-DNS
summary: Net-DNS - Perl DNS resolver module
version: 0.80
release: 2
version: 1.57
release: 1
vendor: Olaf Kolkman <olaf@net-dns.org>
packager: Arix International <cpan2rpm@arix.com>
license: Artistic
@@ -23,7 +23,7 @@ url: http://www.cpan.org
buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n)
buildarch: noarch
prefix: %(echo %{_prefix})
source: Net-DNS-0.80.tar.gz
source: Net-DNS-1.57.tar.gz
# cpan2rpm specs carry no BuildRequires; an EL10 buildroot has neither perl nor make, and
# perl-generators is what makes rpm compute the perl(...) Requires.
@@ -34,9 +34,15 @@ BuildRequires: perl(ExtUtils::MakeMaker)
BuildRequires: perl(Digest::HMAC)
BuildRequires: perl(Digest::MD5)
BuildRequires: perl(Digest::SHA)
BuildRequires: perl(IO::Socket::IP)
BuildRequires: perl(MIME::Base64)
BuildRequires: perl(Test::More)
# The perl dependency generator reads "use base OS_CONF" in Net::DNS::Resolver::Base as a module
# name. OS_CONF is a constant that names the platform resolver class at run time, so no package
# provides perl(OS_CONF) and the generated dependency stops dnf from installing the rpm.
%global __requires_exclude ^perl\\(OS_CONF\\)$
%description
Net::DNS is a collection of Perl modules that act as a Domain Name System
(DNS) resolver. It allows the programmer to perform DNS queries that are
@@ -61,8 +67,9 @@ grep -rsl '^#!.*perl' . |
grep -v '.bak$' |xargs --no-run-if-empty \
%__perl -MExtUtils::MakeMaker -e 'MY->fixin(@ARGV)'
CFLAGS="$RPM_OPT_FLAGS"
# --noxs: pure-perl Net::DNS (no compiled dn_expand), so one noarch rpm serves every arch
%{__perl} Makefile.PL --noxs `%{__perl} -MExtUtils::MakeMaker -e ' print qq|PREFIX=%{buildroot}%{_prefix}| if \$ExtUtils::MakeMaker::VERSION =~ /5\.9[1-6]|6\.0[0-5]/ '`
# Net::DNS is pure perl from 1.01 on, so one noarch rpm serves every arch. The mock chroot has
# no network: --noonline-tests keeps the resolver tests from waiting for one.
%{__perl} Makefile.PL --noonline-tests `%{__perl} -MExtUtils::MakeMaker -e ' print qq|PREFIX=%{buildroot}%{_prefix}| if \$ExtUtils::MakeMaker::VERSION =~ /5\.9[1-6]|6\.0[0-5]/ '`
%{__make}
%if %maketest
%{__make} test
@@ -99,7 +106,7 @@ find %{buildroot}%{_prefix} \
%{__perl} -MFile::Find -le '
find({ wanted => \&wanted, no_chdir => 1}, "%{buildroot}");
print "%doc Changes README TODO contrib demo";
print "%doc Changes LICENSE README contrib demo";
for my $x (sort @dirs, @files) {
push @ret, $x unless indirs($x);
}
@@ -139,6 +146,19 @@ find %{buildroot}%{_prefix} \
%defattr(-,root,root)
%changelog
* Tue Sep 29 2026 xCAT build - 1.57-1
- Net::DNS 1.57. 1.47 carries CVE-2026-64193 (code injection via EDNS
EXTENDED-ERROR) and CVE-2026-64194 (deep compression pointer chains),
fixed in 1.56, and unbounded recursion on a misplaced TSIG, fixed in 1.57.
* Sat Sep 05 2026 xCAT build - 1.47-1
- Net::DNS 1.47. Release 0.80 leaves the DNSSEC records to Net::DNS::SEC, so
Net::DNS::RR->new("<key>. IN KEY ...") dies and xCAT makedns fails. 1.47 is
also the release EPEL 10 ships, so every architecture now gets the same one.
- Makefile.PL of 1.47 rejects --noxs (the module carries no XS); pass
--noonline-tests instead, because the mock chroot has no network.
- BuildRequires perl(IO::Socket::IP), which Makefile.PL needs to configure.
* Thu Aug 20 2026 xCAT build - 0.80-2
- Build the pure-perl module (--noxs) as noarch instead of an XS x86_64 rpm.
- BuildRequires for an EL10 buildroot (make, perl-interpreter, perl-generators,
+174
View File
@@ -0,0 +1,174 @@
#!/usr/bin/perl
# The xCAT ddns plugin signs a DDNS update with a key record that it builds itself:
# Net::DNS::RR->new("<keyname>. IN KEY 512 3 <algorithm> <secret>")
# Net::DNS 0.80 leaves the DNSSEC records, KEY included, to the separate Net::DNS::SEC
# distribution, so that call dies with "zone file representation not defined for KEY" and
# makedns returns non-zero. The x86_64 and ppc64le repositories take Net::DNS from EPEL and
# never showed the gap; riscv64 has no EPEL and builds this one, so only that architecture
# shipped a Net::DNS without KEY.
#
# The test drives the Net::DNS that the shipped source tarball contains. It does not read the
# module text: it extracts the tarball, puts its lib first on @INC, loads Net::DNS::RR from
# there, and constructs the records xCAT constructs.
use strict;
use warnings;
use Test::More;
use FindBin qw($RealBin);
use lib "$RealBin/..", "$RealBin/../lib";
use File::Temp qw(tempdir);
use Archive::Tar;
use version;
use MockBuildUtils qw(read_manifest version_matches);
use XCAT::BuildUtils qw(read_lines);
# The records xCAT builds, and the class Net::DNS must return for each. The numbers are the
# algorithm codes of xCAT::DHCP::OmapiPolicy (157 hmac-md5, 163 hmac-sha256, 165 hmac-sha512).
my $SECRET = 'c2VjcmV0';
my @RECORDS = (
{ rr => "xcat_key. IN KEY 512 3 157 $SECRET", type => 'KEY' },
{ rr => "xcat_key. IN KEY 512 3 163 $SECRET", type => 'KEY' },
{ rr => "xcat_key. IN KEY 512 3 165 $SECRET", type => 'KEY' },
);
my $root = "$RealBin/..";
# The spec is the artifact here: it names the version built and the tarball it is built from.
# read_lines dies when the spec is gone.
my @spec = read_lines("$root/perl-Net-DNS/Net-DNS.spec");
my ($version) = map { /^version:\s*(\S+)/i ? $1 : () } @spec;
my ($source) = map { /^source:\s*(\S+)/i ? $1 : () } @spec;
die "the Net::DNS spec declares no version and source; this test covers nothing\n"
unless $version && $source;
# Net::DNS moved the DNSSEC records, KEY included, into the core distribution at release 1.01.
# Below that release the KEY record lives in the separate Net::DNS::SEC distribution, which
# xcat-dep does not build.
my $KEY_FLOOR = '1.01';
# Net::DNS pads the minor field (0.80, 1.01, 1.47), so the decimal form of version.pm orders
# the releases correctly.
sub at_least_floor { return version->parse($_[0]) >= version->parse($KEY_FLOOR) ? 1 : 0 }
ok(at_least_floor($version),
"the spec builds Net::DNS $KEY_FLOOR or newer ($version), so KEY is in the core distribution");
# Every target whose manifest section lists perl-Net-DNS builds it from this one spec, so the
# records must work for all of them. A target that takes Net::DNS from EPEL is not listed.
my %manifest = read_manifest("$root/packages-manifest.conf");
my @targets = grep { exists $manifest{$_}{'perl-Net-DNS'} } sort keys %manifest;
die 'no manifest target builds perl-Net-DNS; this test covers nothing' unless @targets;
# The pin is the second place the version is written down, and mockbuild-all.pl fails the run
# when the built rpm does not match it. A pin below $KEY_FLOOR puts a Net::DNS without KEY back
# into the repositories a service node reads, which have no EPEL copy to outrank it. An operator
# pin (">= 0.80") accepts such a build too, so only an exact version is allowed here.
for my $target (@targets) {
my $pin = $manifest{$target}{'perl-Net-DNS'};
my $exact = $pin =~ /\A\d+(?:\.\d+)+\z/ ? 1 : 0;
ok($exact, "[$target] the perl-Net-DNS pin ($pin) names one exact version");
ok($exact && at_least_floor($pin),
"[$target] the perl-Net-DNS pin ($pin) is $KEY_FLOOR or newer");
ok(version_matches($version, $pin),
"[$target] the perl-Net-DNS pin ($pin) accepts the version the spec builds ($version)");
}
my $tarball = "$root/perl-Net-DNS/$source";
die "$tarball is missing, so the spec cannot build" unless -f $tarball;
# The tarball, the spec and the Buildnote name one release. A second tarball beside the spec is a
# release that nothing builds, and a Buildnote that names it sends a manual build to the wrong one.
my @tarballs = map { s{.*/}{}r } glob("$root/perl-Net-DNS/Net-DNS-*.tar.gz");
is_deeply(\@tarballs, [$source], "perl-Net-DNS/ holds only the tarball the spec builds ($source)");
my @buildnote = read_lines("$root/perl-Net-DNS/Buildnote");
my @named = map { /(Net-DNS-[\d.]+\.tar\.gz)/ ? $1 : () } @buildnote;
is_deeply(\@named, [$source], "the Buildnote names the tarball the spec builds ($source)");
my $tmp = tempdir(CLEANUP => 1);
{
my $tar = Archive::Tar->new;
$tar->read($tarball) or die "Cannot read $tarball: " . Archive::Tar->error;
$tar->setcwd($tmp);
$tar->extract or die "Cannot extract $tarball: " . Archive::Tar->error;
}
my ($libdir) = grep { -d } glob("$tmp/*/lib");
die "$tarball holds no lib/ directory" unless defined $libdir;
# The extracted copy goes first on @INC, so it, and not a Net::DNS installed on the build host,
# answers the calls. The test checks which file each module came from.
unshift @INC, $libdir;
require Net::DNS::RR;
my $loaded = $INC{'Net/DNS/RR.pm'} // '(nothing)';
is(index($loaded, $tmp), 0, 'Net::DNS::RR is loaded from the shipped tarball, not from the host')
or diag("loaded: $loaded");
for my $want (@RECORDS) {
my $rr = eval { Net::DNS::RR->new($want->{rr}) };
ok($rr, "Net::DNS builds '$want->{rr}'") or diag($@);
next unless $rr;
is($rr->type, $want->{type}, "... as a $want->{type} record");
is(ref($rr), "Net::DNS::RR::$want->{type}", "... of class Net::DNS::RR::$want->{type}");
is($rr->key, $SECRET, '... carrying the key material given to it');
}
my $key_module = $INC{'Net/DNS/RR/KEY.pm'} // '(nothing)';
is(index($key_module, $tmp), 0, 'the KEY record class also comes from the shipped tarball')
or diag("loaded: $key_module");
# CVE-2026-64194: a reply whose owner name is a long chain of compression pointers makes the
# decoder recurse once per pointer. Net::DNS 1.56 stops the chain; 1.47 decodes all of it.
# The reply below holds a NULL record whose rdata is 200 pointers, each to the one before it,
# and an A record whose owner name points at the last one.
{
my $reply = pack('n6', 1, 0x8100, 1, 2, 0, 0) . "\x01a\x00" . pack('nn', 1, 1);
my $rdata_at = length($reply) + 12;
my ($chain, $prev) = ('', 12);
for (1 .. 200) {
my $here = $rdata_at + length $chain;
$chain .= pack 'n', 0xC000 | $prev;
$prev = $here;
}
$reply .= pack('nnnNn', 0xC00C, 10, 1, 0, length $chain) . $chain;
$reply .= pack('nnnNn', 0xC000 | $prev, 1, 1, 0, 4) . "\x7f\0\0\1";
require Net::DNS::Packet;
local $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /^Deep recursion/ };
# Packet->new reports a decode error in $@ and returns, as the resolver expects.
Net::DNS::Packet->new(\$reply);
like($@, qr/deep compression recursion/,
'Net::DNS rejects a reply that chains 200 compression pointers (CVE-2026-64194)');
}
# rt.cpan.org #181125, fixed in Net::DNS 1.57: a reply with a TSIG record in the answer section,
# followed by another record, makes the re-encode of that reply recurse without bound. 1.56 and
# 1.47 recurse. The wrapper stops the recursion at 20 levels and records that it did: Net::DNS
# catches the die inside the TSIG encoder, so the re-encode itself still returns.
{
my $reply = pack('n6', 1, 0x8100, 1, 2, 0, 0) . "\x07example\x00" . pack('nn', 1, 1);
my $rdata = "\x0bhmac-sha256\x00" . pack('nNn nnnn', 0, 0, 300, 0, 1, 0, 0);
$reply .= "\x03key\x00" . pack('nnNn', 250, 255, 0, length $rdata) . $rdata;
$reply .= "\x00" . pack('nnNn', 1, 1, 0, 4) . "\x7f\0\0\1";
my $encode = \&Net::DNS::Packet::encode;
my ($depth, $stopped) = (0, 0);
no warnings 'redefine';
local *Net::DNS::Packet::encode = sub {
if (++$depth > 20) {
$stopped = 1;
die "Net::DNS::Packet::encode recursed more than 20 levels deep\n";
}
my $wire = eval { $encode->(@_) };
my $error = $@;
$depth--;
die $error if $error;
return $wire;
};
local $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /misplaced or corrupt TSIG/ };
my $packet = Net::DNS::Packet->new(\$reply);
ok($packet, 'Net::DNS decodes a reply with a misplaced TSIG record') or diag($@);
my $data = $packet && eval { $packet->data };
ok(defined $data, 'Net::DNS re-encodes that reply') or diag($@);
ok(!$stopped, 'the re-encode does not recurse without bound (rt.cpan.org #181125)');
}
done_testing;