Skip to content

Commit 2625cc3

Browse files
authored
feat: add auto-confirm option to auto-tls (#2875)
Adds an `autoConfirmAddress` to trust that the `libp2p.direct` DNS addresses have been configured correctly without waiting for autonat to verify. Defaults to `false`.
1 parent d51c21f commit 2625cc3

File tree

5 files changed

+87
-4
lines changed

5 files changed

+87
-4
lines changed

packages/auto-tls/src/auto-tls.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { base36 } from 'multiformats/bases/base36'
1010
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
1111
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
1212
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
13-
import { DEFAULT_ACCOUNT_PRIVATE_KEY_BITS, DEFAULT_ACCOUNT_PRIVATE_KEY_NAME, DEFAULT_ACME_DIRECTORY, DEFAULT_CERTIFICATE_DATASTORE_KEY, DEFAULT_CERTIFICATE_PRIVATE_KEY_BITS, DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME, DEFAULT_FORGE_DOMAIN, DEFAULT_FORGE_ENDPOINT, DEFAULT_PROVISION_DELAY, DEFAULT_PROVISION_REQUEST_TIMEOUT, DEFAULT_PROVISION_TIMEOUT, DEFAULT_RENEWAL_THRESHOLD } from './constants.js'
13+
import { DEFAULT_ACCOUNT_PRIVATE_KEY_BITS, DEFAULT_ACCOUNT_PRIVATE_KEY_NAME, DEFAULT_ACME_DIRECTORY, DEFAULT_AUTO_CONFIRM_ADDRESS, DEFAULT_CERTIFICATE_DATASTORE_KEY, DEFAULT_CERTIFICATE_PRIVATE_KEY_BITS, DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME, DEFAULT_FORGE_DOMAIN, DEFAULT_FORGE_ENDPOINT, DEFAULT_PROVISION_DELAY, DEFAULT_PROVISION_REQUEST_TIMEOUT, DEFAULT_PROVISION_TIMEOUT, DEFAULT_RENEWAL_THRESHOLD } from './constants.js'
1414
import { DomainMapper } from './domain-mapper.js'
1515
import { createCsr, importFromPem, loadOrCreateKey, supportedAddressesFilter } from './utils.js'
1616
import type { AutoTLSComponents, AutoTLSInit, AutoTLS as AutoTLSInterface } from './index.js'
@@ -60,9 +60,10 @@ export class AutoTLS implements AutoTLSInterface {
6060
private readonly email
6161
private readonly domain
6262
private readonly domainMapper: DomainMapper
63+
private readonly autoConfirmAddress: boolean
6364

6465
constructor (components: AutoTLSComponents, init: AutoTLSInit = {}) {
65-
this.log = components.logger.forComponent('libp2p:certificate-manager')
66+
this.log = components.logger.forComponent('libp2p:auto-tls')
6667
this.addressManager = components.addressManager
6768
this.privateKey = components.privateKey
6869
this.peerId = components.peerId
@@ -80,6 +81,7 @@ export class AutoTLS implements AutoTLSInterface {
8081
this.certificatePrivateKeyName = init.certificatePrivateKeyName ?? DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME
8182
this.certificatePrivateKeyBits = init.certificatePrivateKeyBits ?? DEFAULT_CERTIFICATE_PRIVATE_KEY_BITS
8283
this.certificateDatastoreKey = init.certificateDatastoreKey ?? DEFAULT_CERTIFICATE_DATASTORE_KEY
84+
this.autoConfirmAddress = init.autoConfirmAddress ?? DEFAULT_AUTO_CONFIRM_ADDRESS
8385
this.clientAuth = new ClientAuth(this.privateKey)
8486
this.started = false
8587
this.fetching = false
@@ -100,10 +102,16 @@ export class AutoTLS implements AutoTLSInterface {
100102
]
101103

102104
get [serviceDependencies] (): string[] {
103-
return [
105+
const dependencies = [
104106
'@libp2p/identify',
105107
'@libp2p/keychain'
106108
]
109+
110+
if (!this.autoConfirmAddress) {
111+
dependencies.push('@libp2p/autonat')
112+
}
113+
114+
return dependencies
107115
}
108116

109117
async start (): Promise<void> {

packages/auto-tls/src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export const DEFAULT_ACCOUNT_PRIVATE_KEY_BITS = 2048
1010
export const DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME = 'auto-tls-certificate-private-key'
1111
export const DEFAULT_CERTIFICATE_PRIVATE_KEY_BITS = 2048
1212
export const DEFAULT_CERTIFICATE_DATASTORE_KEY = '/libp2p/auto-tls/certificate'
13+
export const DEFAULT_AUTO_CONFIRM_ADDRESS = false

packages/auto-tls/src/domain-mapper.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { isIPv4, isIPv6 } from '@chainsafe/is-ip'
2+
import { multiaddr } from '@multiformats/multiaddr'
23
import { getPublicIps } from './utils.js'
34
import type { ComponentLogger, Libp2pEvents, Logger, TypedEventTarget } from '@libp2p/interface'
45
import type { AddressManager } from '@libp2p/interface-internal'
56

7+
const MAX_DATE = 8_640_000_000_000_000
8+
69
export interface DomainMapperComponents {
710
logger: ComponentLogger
811
events: TypedEventTarget<Libp2pEvents>
@@ -11,6 +14,7 @@ export interface DomainMapperComponents {
1114

1215
export interface DomainMapperInit {
1316
domain: string
17+
autoConfirmAddress?: boolean
1418
}
1519

1620
export class DomainMapper {
@@ -19,13 +23,15 @@ export class DomainMapper {
1923
private readonly events: TypedEventTarget<Libp2pEvents>
2024
private readonly mappedAddresses: Set<string>
2125
private readonly domain: string
26+
private readonly autoConfirmAddress: boolean
2227
private hasCertificate: boolean
2328

2429
constructor (components: DomainMapperComponents, init: DomainMapperInit) {
25-
this.log = components.logger.forComponent('libp2p:certificate-manager:domain-mapper')
30+
this.log = components.logger.forComponent('libp2p:auto-tls:domain-mapper')
2631
this.addressManager = components.addressManager
2732
this.events = components.events
2833
this.domain = init.domain
34+
this.autoConfirmAddress = init.autoConfirmAddress ?? false
2935

3036
this.mappedAddresses = new Set()
3137
this.hasCertificate = false
@@ -116,13 +122,29 @@ export class DomainMapper {
116122
this.log.trace('mapping IP %s to domain %s', ip, domain)
117123
this.addressManager.addDNSMapping(domain, [ip])
118124
this.mappedAddresses.add(ip)
125+
126+
if (this.autoConfirmAddress) {
127+
const ma = multiaddr(`/dns4/${domain}`)
128+
this.log('auto-confirming IP address %a', ma)
129+
this.addressManager.confirmObservedAddr(ma, {
130+
ttl: MAX_DATE - Date.now()
131+
})
132+
}
119133
})
120134

121135
addedIp6.forEach(ip => {
122136
const domain = this.toDomain(ip, 6)
123137
this.log.trace('mapping IP %s to domain %s', ip, domain)
124138
this.addressManager.addDNSMapping(domain, [ip])
125139
this.mappedAddresses.add(ip)
140+
141+
if (this.autoConfirmAddress) {
142+
const ma = multiaddr(`/dns6/${domain}`)
143+
this.log('auto-confirming IP address %a', ma)
144+
this.addressManager.confirmObservedAddr(ma, {
145+
ttl: MAX_DATE - Date.now()
146+
})
147+
}
126148
})
127149
}
128150

packages/auto-tls/src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ export interface AutoTLSInit {
168168
* @default 2048
169169
*/
170170
certificatePrivateKeyBits?: number
171+
172+
/**
173+
* Any mapped addresses are added to the observed address list. These
174+
* addresses require additional verification by the `@libp2p/autonat` protocol
175+
* or similar before they are trusted.
176+
*
177+
* To skip this verification and trust them immediately pass `true` here
178+
*
179+
* @default false
180+
*/
181+
autoConfirmAddress?: boolean
171182
}
172183

173184
export interface AutoTLS {

packages/auto-tls/test/domain-mapper.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,47 @@ describe('domain-mapper', () => {
8484
])).to.be.true()
8585
})
8686

87+
it('should auto-confirm DNS mapping', async () => {
88+
await stop(mapper)
89+
mapper = new DomainMapper(components, {
90+
domain: 'example.com',
91+
autoConfirmAddress: true
92+
})
93+
await start(mapper)
94+
95+
const ip4 = '81.12.12.9'
96+
const domain = '81-12-12-9.example.com'
97+
98+
components.addressManager.getAddressesWithMetadata.returns([{
99+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'),
100+
verified: true,
101+
expires: Infinity,
102+
type: 'transport'
103+
}, {
104+
multiaddr: multiaddr('/ip4/192.168.1.234/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'),
105+
verified: true,
106+
expires: Infinity,
107+
type: 'transport'
108+
}, {
109+
multiaddr: multiaddr(`/ip4/${ip4}/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN`),
110+
verified: true,
111+
expires: Infinity,
112+
type: 'ip-mapping'
113+
}])
114+
115+
components.events.safeDispatchEvent('certificate:provision', {
116+
detail: {
117+
key: importFromPem(PRIVATE_KEY_PEM),
118+
cert: CERT
119+
}
120+
})
121+
122+
expect(components.addressManager.addDNSMapping.calledWith(domain, [
123+
ip4
124+
])).to.be.true()
125+
expect(components.addressManager.confirmObservedAddr.calledWith(multiaddr(`/dns4/${domain}`))).to.be.true()
126+
})
127+
87128
it('should update domain mapping on self peer update', () => {
88129
const ip4v1 = '81.12.12.9'
89130
const ip6v1 = '2001:4860:4860::8889'

0 commit comments

Comments
 (0)