Gathering detailed insights and metrics for @relaycorp/dnssec
Gathering detailed insights and metrics for @relaycorp/dnssec
Gathering detailed insights and metrics for @relaycorp/dnssec
Gathering detailed insights and metrics for @relaycorp/dnssec
npm install @relaycorp/dnssec
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (97.06%)
JavaScript (2.94%)
Total Downloads
10,301
Last Day
2
Last Week
9
Last Month
39
Last Year
1,306
14 Stars
216 Commits
3 Forks
2 Watching
8 Branches
3 Contributors
Latest Version
1.12.0
Package Id
@relaycorp/dnssec@1.12.0
Unpacked Size
162.93 kB
Size
41.04 kB
File Count
138
NPM Version
10.5.2
Node Version
20.13.1
Publised On
06 Sept 2024
Cumulative downloads
Total Downloads
Last day
100%
2
Compared to previous day
Last week
50%
9
Compared to previous week
Last month
-76.1%
39
Compared to previous month
Last year
-83.1%
1,306
Compared to previous year
@relaycorp/dnssec
This is a resolver-agnostic DNSSEC verification library for Node.js that allows you to use any transport you want: UDP, DNS-over-TLS (DoT), DNS-over-HTTPS (DoH), etc.
The latest version can be installed from NPM:
1npm install @relaycorp/dnssec
You need to write a thin integration with your preferred resolver, keeping the following in mind:
RRSIG
records).Buffer
using DNS wire format (as defined in RFC 1035), or else you'll have to initialise a Message
that contains all the relevant parts of the response (see below).For example, this is how you could use dohdec's DNS-over-HTTPS resolver with Cloudflare to retrieve A
records for a particular domain name:
1// main.js 2import { dnssecLookUp, Question, SecurityStatus } from '@relaycorp/dnssec'; 3import { DNSoverHTTPS } from 'dohdec'; 4 5const doh = new DNSoverHTTPS({ url: 'https://cloudflare-dns.com/dns-query' }); 6 7async function getARecord(domain) { 8 return await dnssecLookUp(new Question(domain, 'A'), async (question) => 9 doh.lookup(question.name, { 10 rrtype: question.getTypeName(), 11 json: false, // Request DNS message in wire format 12 decode: false, // Don't parse the DNS message 13 dnssec: true, // Retrieve RRSIG records 14 dnssecCheckingDisabled: true, // Disable server-side DNSSEC validation 15 }), 16 ); 17} 18 19const [domainName] = process.argv.slice(2); 20const result = await getARecord(domainName); 21if (result.status === SecurityStatus.SECURE) { 22 console.log(`${domainName}/A =`, result.result); 23} else { 24 const reason = result.reasonChain.join(', '); 25 console.error(`DNSSEC verification for ${domain}/A failed: ${reason}`); 26}
And here's what requesting a valid A
record would look like with the script above:
$ node main.js example.com
example.com/A = RrSet {
name: 'example.com.',
classId: 1,
type: 1,
ttl: 83076,
records: [
DnsRecord {
ttl: 83076,
name: 'example.com.',
typeId: 1,
classId: 1,
dataSerialised: <Buffer 5d b8 d8 22>,
dataFields: '93.184.216.34'
}
]
}
When DNSSEC validation succeeds, you get a VerifiedRrSet
object with the following properties:
status
: Set to SecurityStatus.SECURE
.result
: An RRset containing one or more records (DnsRecord
instances). Each record exposes its data in both serialised and deserialised forms in the dataSerialised
and dataFields
properties, respectively. dataFields
is an object whose structure is determined by dns-packet
.As this is primarily a DNSSEC library, we treat DNS and DNSSEC errors differently:
However, errors are thrown upon attempting to parse malformed RDATA values for DNSSEC records -- we use a third-party library that parses the DNS message eagerly.
By default, DNSSEC signatures MUST be valid at the time the dnssecLookUp()
function is called, but this can be customised by passing a Date
or IDatePeriod
object.
An IDatePeriod
object is useful when you just want signatures to be valid at any point within a given time period. For example, if you want to tolerate clock drift, you could accept signatures valid in the past hour:
1import { IDatePeriod, dnssecLookUp } from '@relaycorp/dnssec'; 2import { subHours } from 'date-fns'; 3 4const now = new Date(); 5const datePeriod: IDatePeriod = { start: subHours(now, 1), end: now }; 6dnssecLookUp(QUESTION, RESOLVER, { dateOrPeriod: datePeriod });
By default, the root DNSKEY
(s) are verified against a local copy of IANA's trust anchors. This can be customised with the trustAnchors
option; e.g.:
1import { 2 dnssecLookUp, 3 DnssecAlgorithm, 4 DigestType, 5 TrustAnchor, 6} from '@relaycorp/dnssec'; 7 8const customTrustAnchor: TrustAnchor = { 9 algorithm: DnssecAlgorithm.RSASHA256, 10 digest: Buffer.from('the digest'), 11 digestType: DigestType.SHA256, 12 keyTag: 42, 13}; 14dnssecLookUp(QUESTION, RESOLVER, { trustAnchors: [customTrustAnchor] });
If your DNS lookup library parses responses eagerly and doesn't give you access to the original response in wire format, you will have to convert their messages to Message
instances. Refer to our API docs to learn how to initialise Message
s.
To facilitate the simulation of the various outcomes of DNSSEC validation, we provide the MockChain
utility so that you can pass a custom resolver and trust anchor to dnssecLookUp()
. This is particularly useful in unit tests where you aren't able to mock this module (e.g., Jest doesn't support mocking our ESM as of this writing).
The following example shows how to generate a verified RRset:
1import { 2 dnssecLookUp, 3 DnsRecord, 4 MockChain, 5 RrSet, 6 SecurityStatus, 7} from '@relaycorp/dnssec'; 8 9const RECORD = new DnsRecord( 10 `example.com.`, 11 'TXT', 12 DnsClass.IN, 13 42, 14 'The data', 15); 16const QUESTION = RECORD.makeQuestion(); 17const RRSET = RrSet.init(QUESTION, [RECORD]); 18 19test('Generating a SECURE result', async () => { 20 const mockChain = await MockChain.generate(RECORD.name); 21 22 const { resolver, trustAnchors } = mockChain.generateFixture( 23 RRSET, 24 SecurityStatus.SECURE, 25 ); 26 27 const result = await dnssecLookUp(QUESTION, resolver, { trustAnchors }); 28 expect(result).toStrictEqual({ 29 status: SecurityStatus.SECURE, 30 result: RRSET, 31 }); 32});
The API documentation is available on docs.relaycorp.tech.
This library implements all the relevant RFCs defining DNSSEC, except for the following functionality that we didn't need, but for which you're welcome to propose PRs:
NSEC
and NSEC3
records).3
) because it's too insecure and hardly used.The following DNSSEC algorithms are unsupported, and we probably won't accept PRs for them:
12
) due to lack of support in Node.js, and its lack of popularity and security doesn't seem to justify integrating a third party NPM package supporting it (assuming a suitable one exists).253
and 254
).As surprising as it may sound, there's no (reliable) way to do DNSSEC verification in Node.js in 2022, so when you see a JS app or library that claims DNSSEC support, chances are they're just blindly trusting a resolver like Cloudflare or Google -- which, admittedly, is sufficient in many cases and even desirable for performance reasons.
The Node.js team considered adding DNSSEC support but ruled it out due to lack of support in their upstream DNS library. As a consequence, two libraries have tried to fill the vacuum:
We love contributions! If you haven't contributed to a Relaycorp project before, please take a minute to read our guidelines first.
No vulnerabilities found.
No security vulnerabilities found.