Skip to content

Commit

Permalink
feat: add auto-confirm option to auto-tls (#2875)
Browse files Browse the repository at this point in the history
Adds an `autoConfirmAddress` to trust that the `libp2p.direct` DNS
addresses have been configured correctly without waiting for
autonat to verify. Defaults to `false`.
  • Loading branch information
achingbrain authored Dec 6, 2024
1 parent d51c21f commit 2625cc3
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 4 deletions.
14 changes: 11 additions & 3 deletions packages/auto-tls/src/auto-tls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { base36 } from 'multiformats/bases/base36'
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
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'
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'
import { DomainMapper } from './domain-mapper.js'
import { createCsr, importFromPem, loadOrCreateKey, supportedAddressesFilter } from './utils.js'
import type { AutoTLSComponents, AutoTLSInit, AutoTLS as AutoTLSInterface } from './index.js'
Expand Down Expand Up @@ -60,9 +60,10 @@ export class AutoTLS implements AutoTLSInterface {
private readonly email
private readonly domain
private readonly domainMapper: DomainMapper
private readonly autoConfirmAddress: boolean

constructor (components: AutoTLSComponents, init: AutoTLSInit = {}) {
this.log = components.logger.forComponent('libp2p:certificate-manager')
this.log = components.logger.forComponent('libp2p:auto-tls')
this.addressManager = components.addressManager
this.privateKey = components.privateKey
this.peerId = components.peerId
Expand All @@ -80,6 +81,7 @@ export class AutoTLS implements AutoTLSInterface {
this.certificatePrivateKeyName = init.certificatePrivateKeyName ?? DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME
this.certificatePrivateKeyBits = init.certificatePrivateKeyBits ?? DEFAULT_CERTIFICATE_PRIVATE_KEY_BITS
this.certificateDatastoreKey = init.certificateDatastoreKey ?? DEFAULT_CERTIFICATE_DATASTORE_KEY
this.autoConfirmAddress = init.autoConfirmAddress ?? DEFAULT_AUTO_CONFIRM_ADDRESS
this.clientAuth = new ClientAuth(this.privateKey)
this.started = false
this.fetching = false
Expand All @@ -100,10 +102,16 @@ export class AutoTLS implements AutoTLSInterface {
]

get [serviceDependencies] (): string[] {
return [
const dependencies = [
'@libp2p/identify',
'@libp2p/keychain'
]

if (!this.autoConfirmAddress) {
dependencies.push('@libp2p/autonat')
}

return dependencies
}

async start (): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions packages/auto-tls/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export const DEFAULT_ACCOUNT_PRIVATE_KEY_BITS = 2048
export const DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME = 'auto-tls-certificate-private-key'
export const DEFAULT_CERTIFICATE_PRIVATE_KEY_BITS = 2048
export const DEFAULT_CERTIFICATE_DATASTORE_KEY = '/libp2p/auto-tls/certificate'
export const DEFAULT_AUTO_CONFIRM_ADDRESS = false
24 changes: 23 additions & 1 deletion packages/auto-tls/src/domain-mapper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { isIPv4, isIPv6 } from '@chainsafe/is-ip'
import { multiaddr } from '@multiformats/multiaddr'
import { getPublicIps } from './utils.js'
import type { ComponentLogger, Libp2pEvents, Logger, TypedEventTarget } from '@libp2p/interface'
import type { AddressManager } from '@libp2p/interface-internal'

const MAX_DATE = 8_640_000_000_000_000

export interface DomainMapperComponents {
logger: ComponentLogger
events: TypedEventTarget<Libp2pEvents>
Expand All @@ -11,6 +14,7 @@ export interface DomainMapperComponents {

export interface DomainMapperInit {
domain: string
autoConfirmAddress?: boolean
}

export class DomainMapper {
Expand All @@ -19,13 +23,15 @@ export class DomainMapper {
private readonly events: TypedEventTarget<Libp2pEvents>
private readonly mappedAddresses: Set<string>
private readonly domain: string
private readonly autoConfirmAddress: boolean
private hasCertificate: boolean

constructor (components: DomainMapperComponents, init: DomainMapperInit) {
this.log = components.logger.forComponent('libp2p:certificate-manager:domain-mapper')
this.log = components.logger.forComponent('libp2p:auto-tls:domain-mapper')
this.addressManager = components.addressManager
this.events = components.events
this.domain = init.domain
this.autoConfirmAddress = init.autoConfirmAddress ?? false

this.mappedAddresses = new Set()
this.hasCertificate = false
Expand Down Expand Up @@ -116,13 +122,29 @@ export class DomainMapper {
this.log.trace('mapping IP %s to domain %s', ip, domain)
this.addressManager.addDNSMapping(domain, [ip])
this.mappedAddresses.add(ip)

if (this.autoConfirmAddress) {
const ma = multiaddr(`/dns4/${domain}`)
this.log('auto-confirming IP address %a', ma)
this.addressManager.confirmObservedAddr(ma, {
ttl: MAX_DATE - Date.now()
})
}
})

addedIp6.forEach(ip => {
const domain = this.toDomain(ip, 6)
this.log.trace('mapping IP %s to domain %s', ip, domain)
this.addressManager.addDNSMapping(domain, [ip])
this.mappedAddresses.add(ip)

if (this.autoConfirmAddress) {
const ma = multiaddr(`/dns6/${domain}`)
this.log('auto-confirming IP address %a', ma)
this.addressManager.confirmObservedAddr(ma, {
ttl: MAX_DATE - Date.now()
})
}
})
}

Expand Down
11 changes: 11 additions & 0 deletions packages/auto-tls/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@ export interface AutoTLSInit {
* @default 2048
*/
certificatePrivateKeyBits?: number

/**
* Any mapped addresses are added to the observed address list. These
* addresses require additional verification by the `@libp2p/autonat` protocol
* or similar before they are trusted.
*
* To skip this verification and trust them immediately pass `true` here
*
* @default false
*/
autoConfirmAddress?: boolean
}

export interface AutoTLS {
Expand Down
41 changes: 41 additions & 0 deletions packages/auto-tls/test/domain-mapper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,47 @@ describe('domain-mapper', () => {
])).to.be.true()
})

it('should auto-confirm DNS mapping', async () => {
await stop(mapper)
mapper = new DomainMapper(components, {
domain: 'example.com',
autoConfirmAddress: true
})
await start(mapper)

const ip4 = '81.12.12.9'
const domain = '81-12-12-9.example.com'

components.addressManager.getAddressesWithMetadata.returns([{
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'),
verified: true,
expires: Infinity,
type: 'transport'
}, {
multiaddr: multiaddr('/ip4/192.168.1.234/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN'),
verified: true,
expires: Infinity,
type: 'transport'
}, {
multiaddr: multiaddr(`/ip4/${ip4}/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN`),
verified: true,
expires: Infinity,
type: 'ip-mapping'
}])

components.events.safeDispatchEvent('certificate:provision', {
detail: {
key: importFromPem(PRIVATE_KEY_PEM),
cert: CERT
}
})

expect(components.addressManager.addDNSMapping.calledWith(domain, [
ip4
])).to.be.true()
expect(components.addressManager.confirmObservedAddr.calledWith(multiaddr(`/dns4/${domain}`))).to.be.true()
})

it('should update domain mapping on self peer update', () => {
const ip4v1 = '81.12.12.9'
const ip6v1 = '2001:4860:4860::8889'
Expand Down

0 comments on commit 2625cc3

Please sign in to comment.