Compare commits

...

6 Commits

Author SHA1 Message Date
Gabriel Cruz
e82c61bf53 chore(autotls): wait until multiaddresses are set 2025-07-10 11:37:09 -03:00
Gabriel Cruz
437490efa0 chore(ws): add timeout to autotls wait 2025-07-04 10:02:30 -03:00
Gabriel Cruz
71797fb9ee chore(autotls): start services before transports 2025-07-03 15:50:44 -03:00
Gabriel Cruz
1c54746b83 chore(autotls): add autotls-ws integration test 2025-07-03 15:43:48 -03:00
Gabriel Cruz
eed555437d chore(wstransport): start method returns CancelledError 2025-07-03 14:55:24 -03:00
Gabriel Cruz
1f7e121fcb feat(autotls): make wstransport use autotls 2025-07-03 09:34:36 -03:00
15 changed files with 451 additions and 105 deletions

View File

@@ -0,0 +1,33 @@
import ./service, ./acme/client, ../peeridauth/client
import ../crypto/crypto, ../crypto/rsa, websock/websock
type MockAutotlsService* = ref object of AutotlsService
mockedCert*: TLSCertificate
mockedKey*: TLSPrivateKey
proc new*(
T: typedesc[MockAutotlsService],
rng: ref HmacDrbgContext = newRng(),
config: AutotlsConfig = AutotlsConfig.new(),
): T =
T(
acmeClient: ACMEClient.new(api = ACMEApi.new(acmeServerURL = config.acmeServerURL)),
brokerClient: PeerIDAuthClient.new(),
bearer: Opt.none(BearerToken),
cert: Opt.none(AutotlsCert),
certReady: newAsyncEvent(),
config: config,
rng: rng,
)
proc getTLSPrivkey*(self: MockAutotlsService): TLSPrivateKey =
self.mockedKey
method getCertWhenReady*(
self: MockAutotlsService, timeout: Duration = DefaultWaitTimeout
): Future[TLSCertificate] {.async: (raises: [AutoTLSError, CancelledError]).} =
return self.mockedCert
method setup*(self: MockAutotlsService) {.base, async.} =
discard

View File

@@ -12,7 +12,7 @@
import net, results, json, sequtils
import chronos/apps/http/httpclient, chronos, chronicles, bearssl/rand
import chronos/apps/http/httpclient, chronos, chronicles, bearssl/rand, bearssl/pem
import
./acme/client,
@@ -23,7 +23,9 @@ import
../peerinfo,
../switch,
../utils/heartbeat,
../wire
../wire,
../crypto/crypto,
../crypto/rsa
logScope:
topics = "libp2p autotls"
@@ -39,6 +41,10 @@ const
]
DefaultRenewCheckTime* = 1.hours
DefaultRenewBufferTime = 1.hours
DefaultMaCheckTime = 1.seconds
DefaultIssueRetryTime = 10.seconds
DefaultIssueRetryAttemps = 6
DefaultWaitTimeout* = 3.seconds
AutoTLSBroker* = "registration.libp2p.direct"
AutoTLSDNSServer* = "libp2p.direct"
@@ -61,27 +67,24 @@ type AutotlsConfig* = ref object
ipAddress: Opt[IpAddress]
renewCheckTime*: Duration
renewBufferTime*: Duration
maCheckTime: Duration
issueRetryTime: Duration
issueRetryAttemps: int
type AutotlsService* = ref object of Service
acmeClient: ACMEClient
acmeClient*: ACMEClient
bearer*: Opt[BearerToken]
brokerClient: PeerIDAuthClient
brokerClient*: PeerIDAuthClient
cert*: Opt[AutotlsCert]
certReady*: AsyncEvent
config: AutotlsConfig
config*: AutotlsConfig
managerFut: Future[void]
peerInfo: PeerInfo
rng: ref HmacDrbgContext
rng*: ref HmacDrbgContext
proc new*(T: typedesc[AutotlsCert], cert: TLSCertificate, expiry: Moment): T =
T(cert: cert, expiry: expiry)
proc getCertWhenReady*(
self: AutotlsService
): Future[TLSCertificate] {.async: (raises: [AutoTLSError, CancelledError]).} =
await self.certReady.wait()
return self.cert.get.cert
proc new*(
T: typedesc[AutotlsConfig],
ipAddress: Opt[IpAddress] = NoneIp,
@@ -89,6 +92,9 @@ proc new*(
acmeServerURL: Uri = parseUri(LetsEncryptURL),
renewCheckTime: Duration = DefaultRenewCheckTime,
renewBufferTime: Duration = DefaultRenewBufferTime,
maCheckTime: Duration = DefaultMaCheckTime,
issueRetryTime: Duration = DefaultIssueRetryTime,
issueRetryAttemps: int = DefaultIssueRetryAttemps,
): T =
T(
dnsResolver: DnsResolver.new(nameServers),
@@ -96,6 +102,9 @@ proc new*(
ipAddress: ipAddress,
renewCheckTime: renewCheckTime,
renewBufferTime: renewBufferTime,
maCheckTime: maCheckTime,
issueRetryTime: issueRetryTime,
issueRetryAttemps: issueRetryAttemps,
)
proc new*(
@@ -115,6 +124,117 @@ proc new*(
rng: rng,
)
method getCertWhenReady*(
self: AutotlsService, timeout: Duration = DefaultWaitTimeout
): Future[TLSCertificate] {.base, async: (raises: [AutoTLSError, CancelledError]).} =
# if await self.certReady.wait().withTimeout(timeout):
await self.certReady.wait()
self.certReady.clear()
return self.cert.get.cert
# raise newException(CancelledError, "timed out waiting for cert to be ready")
method getTLSPrivkey*(
self: AutotlsService
): TLSPrivateKey {.base, gcsafe, raises: [AutoTLSError, TLSStreamProtocolError].} =
let derPrivKey =
try:
self.acmeClient.key.seckey.rsakey.getBytes.get
except ValueError as exc:
raise newException(AutoTLSError, "Unable to get TLS private key", exc)
let pemPrivKey: string = pemEncode(derPrivKey, "PRIVATE KEY")
TLSPrivateKey.init(pemPrivKey)
method issueCertificate(
self: AutotlsService
): Future[bool] {.
base, async: (raises: [AutoTLSError, ACMEError, PeerIDAuthError, CancelledError])
.} =
trace "Issuing certificate"
if self.peerInfo.isNil():
error "peerInfo not set"
return false
# generate autotls domain string: "*.{peerID}.libp2p.direct"
let baseDomain =
api.Domain(encodePeerId(self.peerInfo.peerId) & "." & AutoTLSDNSServer)
let domain = api.Domain("*." & baseDomain)
let acmeClient = self.acmeClient
trace "Requesting ACME challenge"
let dns01Challenge = await acmeClient.getChallenge(@[domain])
let keyAuth = acmeClient.genKeyAuthorization(dns01Challenge.dns01.token)
let strMultiaddresses: seq[string] = self.peerInfo.addrs.mapIt($it)
echo strMultiaddresses
let payload = %*{"value": keyAuth, "addresses": strMultiaddresses}
let registrationURL = parseUri("https://" & AutoTLSBroker & "/v1/_acme-challenge")
trace "Sending challenge to AutoTLS broker"
# before this runs we need to make sure mutiaddresses are available
let (bearer, response) =
await self.brokerClient.send(registrationURL, self.peerInfo, payload, self.bearer)
debug "Broker response", status = response.status
if response.status != HttpOk:
error "Failed to authenticate with AutoTLS Broker"
return false
if self.bearer.isNone():
# save bearer token for future
self.bearer = Opt.some(bearer)
trace "Waiting for DNS record to be set"
let dnsSet = await checkDNSRecords(
self.config.dnsResolver, self.config.ipAddress.get(), baseDomain, keyAuth
)
if not dnsSet:
error "DNS records not set"
return false
trace "Notifying challenge completion to ACME and downloading cert"
let certResponse = await acmeClient.getCertificate(domain, dns01Challenge)
trace "Installing certificate"
let newCert =
try:
AutotlsCert.new(
TLSCertificate.init(certResponse.rawCertificate),
asMoment(certResponse.certificateExpiry),
)
except TLSStreamProtocolError:
raise newException(AutoTLSError, "Could not parse downloaded certificates")
self.cert = Opt.some(newCert)
self.certReady.fire()
trace "Certificate installed"
return true
proc issueWithRetries(
self: AutotlsService
): Future[bool] {.async: (raises: [CancelledError]).} =
for _ in 0 ..< self.config.issueRetryAttemps:
try:
if await self.issueCertificate():
return true
await sleepAsync(self.config.issueRetryTime)
except CancelledError as exc:
raise exc
except CatchableError as exc:
error "Failed to issue certificate", err = exc.msg
break
return false
proc manage(self: AutotlsService) {.async: (raises: [CancelledError]).} =
heartbeat "Certificate Management", self.config.renewCheckTime:
if self.cert.isNone():
if not await self.issueWithRetries():
return
# AutotlsService will renew the cert 1h before it expires
let cert = self.cert.get
let waitTime = cert.expiry - Moment.now - self.config.renewBufferTime
if waitTime <= self.config.renewBufferTime:
if not await self.issueWithRetries():
return
method setup*(
self: AutotlsService, switch: Switch
): Future[bool] {.async: (raises: [CancelledError]).} =
@@ -128,89 +248,9 @@ method setup*(
except AutoTLSError as exc:
error "Failed to get public IP address", err = exc.msg
return false
self.managerFut = self.run(switch)
self.managerFut = self.manage()
return hasBeenSetup
method issueCertificate(
self: AutotlsService
) {.base, async: (raises: [AutoTLSError, ACMEError, PeerIDAuthError, CancelledError]).} =
trace "Issuing certificate"
assert not self.peerInfo.isNil(), "Cannot issue new certificate: peerInfo not set"
# generate autotls domain string: "*.{peerID}.libp2p.direct"
let baseDomain =
api.Domain(encodePeerId(self.peerInfo.peerId) & "." & AutoTLSDNSServer)
let domain = api.Domain("*." & baseDomain)
let acmeClient = self.acmeClient
trace "Requesting ACME challenge"
let dns01Challenge = await acmeClient.getChallenge(@[domain])
let keyAuth = acmeClient.genKeyAuthorization(dns01Challenge.dns01.token)
let strMultiaddresses: seq[string] = self.peerInfo.addrs.mapIt($it)
let payload = %*{"value": keyAuth, "addresses": strMultiaddresses}
let registrationURL = parseUri("https://" & AutoTLSBroker & "/v1/_acme-challenge")
trace "Sending challenge to AutoTLS broker"
let (bearer, response) =
await self.brokerClient.send(registrationURL, self.peerInfo, payload, self.bearer)
if self.bearer.isNone():
# save bearer token for future
self.bearer = Opt.some(bearer)
if response.status != HttpOk:
raise newException(
AutoTLSError, "Failed to authenticate with AutoTLS Broker at " & AutoTLSBroker
)
debug "Waiting for DNS record to be set"
let dnsSet = await checkDNSRecords(
self.config.dnsResolver, self.config.ipAddress.get(), baseDomain, keyAuth
)
if not dnsSet:
raise newException(AutoTLSError, "DNS records not set")
debug "Notifying challenge completion to ACME and downloading cert"
let certResponse = await acmeClient.getCertificate(domain, dns01Challenge)
debug "Installing certificate"
let newCert =
try:
AutotlsCert.new(
TLSCertificate.init(certResponse.rawCertificate),
asMoment(certResponse.certificateExpiry),
)
except TLSStreamProtocolError:
raise newException(AutoTLSError, "Could not parse downloaded certificates")
self.cert = Opt.some(newCert)
self.certReady.fire()
debug "Certificate installed"
method run*(
self: AutotlsService, switch: Switch
) {.async: (raises: [CancelledError]).} =
heartbeat "Certificate Management", self.config.renewCheckTime:
if self.cert.isNone():
try:
await self.issueCertificate()
except CancelledError as exc:
raise exc
except CatchableError as exc:
error "Failed to issue certificate", err = exc.msg
break
# AutotlsService will renew the cert 1h before it expires
let cert = self.cert.get
let waitTime = cert.expiry - Moment.now - self.config.renewBufferTime
if waitTime <= self.config.renewBufferTime:
try:
await self.issueCertificate()
except CancelledError as exc:
raise exc
except CatchableError as exc:
error "Failed to renew certificate", err = exc.msg
break
method stop*(
self: AutotlsService, switch: Switch
): Future[bool] {.async: (raises: [CancelledError]).} =

View File

@@ -185,7 +185,7 @@ proc withWsTransport*(
): SwitchBuilder =
b.withTransport(
proc(upgr: Upgrade, privateKey: PrivateKey, autotls: AutotlsService): Transport =
WsTransport.new(upgr, tlsPrivateKey, tlsCertificate, tlsFlags, flags)
WsTransport.new(upgr, tlsPrivateKey, tlsCertificate, autotls, tlsFlags, flags)
)
when defined(libp2p_quic_support):

View File

@@ -302,6 +302,8 @@ proc sendWithoutBearer(
):
raise newException(PeerIDAuthError, "Failed to validate server's signature")
echo $authorizationResponse.response
echo $authorizationResponse.response.status
return (authorizationResponse.bearer, authorizationResponse.response)
proc sendWithBearer(

View File

@@ -31,7 +31,7 @@ type RelayTransport* = ref object of Transport
method start*(
self: RelayTransport, ma: seq[MultiAddress]
) {.async: (raises: [LPError, transport.TransportError]).} =
) {.async: (raises: [LPError, transport.TransportError, CancelledError]).} =
if self.selfRunning:
trace "Relay transport already running"
return

View File

@@ -344,8 +344,11 @@ proc start*(s: Switch) {.public, async: (raises: [CancelledError, LPError]).} =
return
debug "starting switch for peer", peerInfo = s.peerInfo
var startFuts: seq[Future[void]]
for t in s.transports:
echo "here1"
echo s.peerInfo.listenAddrs
let addrs = s.peerInfo.listenAddrs.filterIt(t.handles(it))
s.peerInfo.listenAddrs.keepItIf(it notin addrs)
@@ -365,10 +368,11 @@ proc start*(s: Switch) {.public, async: (raises: [CancelledError, LPError]).} =
s.acceptFuts.add(s.accept(t))
s.peerInfo.listenAddrs &= t.addrs
await s.peerInfo.update()
for service in s.services:
discard await service.setup(s)
await s.peerInfo.update()
await s.ms.start()
s.started = true

View File

@@ -52,7 +52,7 @@ proc listenAddress(self: MemoryTransport, ma: MultiAddress): MultiAddress =
method start*(
self: MemoryTransport, addrs: seq[MultiAddress]
) {.async: (raises: [LPError, transport.TransportError]).} =
) {.async: (raises: [LPError, transport.TransportError, CancelledError]).} =
if self.running:
return

View File

@@ -210,7 +210,7 @@ method handles*(transport: QuicTransport, address: MultiAddress): bool {.raises:
method start*(
self: QuicTransport, addrs: seq[MultiAddress]
) {.async: (raises: [LPError, transport.TransportError]).} =
) {.async: (raises: [LPError, transport.TransportError, CancelledError]).} =
doAssert self.listener.isNil, "start() already called"
#TODO handle multiple addr

View File

@@ -107,7 +107,7 @@ proc new*(
method start*(
self: TcpTransport, addrs: seq[MultiAddress]
): Future[void] {.async: (raises: [LPError, transport.TransportError]).} =
): Future[void] {.async: (raises: [LPError, transport.TransportError, CancelledError]).} =
## Start transport listening to the given addresses - for dial-only transports,
## start with an empty list

View File

@@ -250,7 +250,7 @@ method dial*(
method start*(
self: TorTransport, addrs: seq[MultiAddress]
) {.async: (raises: [LPError, transport.TransportError]).} =
) {.async: (raises: [LPError, transport.TransportError, CancelledError]).} =
## listen on the transport
##

View File

@@ -42,7 +42,7 @@ proc newTransportClosedError*(parent: ref Exception = nil): ref TransportError =
method start*(
self: Transport, addrs: seq[MultiAddress]
) {.base, async: (raises: [LPError, TransportError]).} =
) {.base, async: (raises: [LPError, TransportError, CancelledError]).} =
## start the transport
##

View File

@@ -16,6 +16,7 @@ import results
import chronos, chronicles
import
transport,
../autotls/service,
../errors,
../wire,
../multicodec,
@@ -108,8 +109,9 @@ type WsTransport* = ref object of Transport
acceptFuts: seq[Future[HttpRequest]]
tlsPrivateKey: TLSPrivateKey
tlsCertificate: TLSCertificate
tlsPrivateKey*: TLSPrivateKey
tlsCertificate*: TLSCertificate
autotls: AutotlsService
tlsFlags: set[TLSFlags]
flags: set[ServerFlags]
handshakeTimeout: Duration
@@ -121,7 +123,7 @@ proc secure*(self: WsTransport): bool =
method start*(
self: WsTransport, addrs: seq[MultiAddress]
) {.async: (raises: [LPError, transport.TransportError]).} =
) {.async: (raises: [LPError, transport.TransportError, CancelledError]).} =
## listen on the transport
##
@@ -132,6 +134,20 @@ method start*(
await procCall Transport(self).start(addrs)
trace "Starting WS transport"
# use autotls if possible (and if cert/key not specified)
if not self.secure and not self.autotls.isNil():
try:
echo "cert.isSome() == " & $self.autotls.cert.isSome()
echo "before getCertWhenReady"
self.tlsCertificate = await self.autotls.getCertWhenReady()
echo "after getCertWhenReady"
self.tlsPrivateKey = self.autotls.getTLSPrivkey()
echo "after getTLSPrivkey"
except AutoTLSError as exc:
raise newException(LPError, exc.msg, exc)
except TLSStreamProtocolError as exc:
raise newException(LPError, exc.msg, exc)
self.wsserver = WSServer.new(factories = self.factories, rng = self.rng)
for i, ma in addrs:
@@ -356,6 +372,7 @@ proc new*(
upgrade: Upgrade,
tlsPrivateKey: TLSPrivateKey,
tlsCertificate: TLSCertificate,
autotls: AutotlsService,
tlsFlags: set[TLSFlags] = {},
flags: set[ServerFlags] = {},
factories: openArray[ExtFactory] = [],
@@ -368,6 +385,7 @@ proc new*(
upgrader: upgrade,
tlsPrivateKey: tlsPrivateKey,
tlsCertificate: tlsCertificate,
autotls: autotls,
tlsFlags: tlsFlags,
flags: flags,
factories: @factories,
@@ -389,6 +407,7 @@ proc new*(
upgrade = upgrade,
tlsPrivateKey = nil,
tlsCertificate = nil,
autotls = nil,
flags = flags,
factories = @factories,
rng = rng,

View File

@@ -10,4 +10,4 @@ when defined(linux) and defined(amd64):
# This file may not be copied, modified, or distributed except according to
# those terms.
import testpeeridauth_integration, testautotls_integration
import testpeeridauth_integration, testautotls_integration, testwstransport_integration

View File

@@ -12,6 +12,8 @@
import chronos, stew/byteutils
import
../libp2p/[
autotls/service,
autotls/mockservice,
stream/connection,
transports/transport,
transports/wstransport,
@@ -23,6 +25,98 @@ import
import ./helpers, ./commontransport
const
AutoTLSCertificate =
"""
-----BEGIN CERTIFICATE-----
MIID5DCCA2mgAwIBAgISLHPA5xRAjh3ZwYmBw4ISA+7LMAoGCCqGSM49BAMDMFIx
CzAJBgNVBAYTAlVTMSAwHgYDVQQKExcoU1RBR0lORykgTGV0J3MgRW5jcnlwdDEh
MB8GA1UEAxMYKFNUQUdJTkcpIFBzZXVkbyBQbHVtIEU1MB4XDTI1MDcwMjE3NDQ1
M1oXDTI1MDkzMDE3NDQ1MlowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAh4
VNa76Ycl6RpoZ2qfM7biZzC/nz4G7lArKBMPyMUh0EX6U22VaZdxOvTfglRUxtJa
NMleBtUhDcZd4HCkNZGjggJvMIICazAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFJgN
WGKp9N/2bWsjD2ugSkt/8jlBMB8GA1UdIwQYMBaAFPxG0QFDX7t7pj0waK4RuuC8
bcnTMDYGCCsGAQUFBwEBBCowKDAmBggrBgEFBQcwAoYaaHR0cDovL3N0Zy1lNS5p
LmxlbmNyLm9yZy8wXAYDVR0RAQH/BFIwUIJOKi5rNTFxemk1dXF1NWRoOGRpY3ps
bzJwZDc3djBic2Vkb2tzaXo1dnk4ZXF3MnJwaWpnZ2ozdzlnNm81ejlpcy5saWJw
MnAuZGlyZWN0MBMGA1UdIAQMMAowCAYGZ4EMAQIBMDEGA1UdHwQqMCgwJqAkoCKG
IGh0dHA6Ly9zdGctZTUuYy5sZW5jci5vcmcvNzYuY3JsMIIBDAYKKwYBBAHWeQIE
AgSB/QSB+gD4AHYA3Zk0/KXnJIDJVmh9gTSZCEmySfe1adjHvKs/XMHzbmQAAAGX
zHNiGAAABAMARzBFAiBVH0V4L9LG5ksU+hOvFVgT51WUpStneTVwdSdOGbdm6gIh
ANdv5EQZI1vOVxok4D1lh1Z7hS7nfTG5HCIBbZ19iAKOAH4A2KJiliJSBM2181NJ
WC5O1mWiRRsPJ6i2iWE2s7n8BwgAAAGXzHNnWwAIAAAFAD6beWUEAwBHMEUCIAOv
AXuiujBNXne+BzzivJGbr3F0wKK7xhxIBfr/jGgyAiEA/xpSIMMcxZmH5Q4UlZj+
EH9kAlEXwmI/MFemd7pdH54wCgYIKoZIzj0EAwMDaQAwZgIxAJXdmvcIxiLPx33H
4WgXbDfjzZHiqyn+5t+73GZUDfyVotuiOvoRPpAp0L+WRS33iQIxAPWcAtrmyb7M
/BXH2ccVFRUtAJTxajwBcN9un7GJUIAb9Fbz6EOBGTT7pukEc155Zw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEljCCAn6gAwIBAgIQRzEp1D1mDiVVv4b1zlB56jANBgkqhkiG9w0BAQsFADBm
MQswCQYDVQQGEwJVUzEzMDEGA1UEChMqKFNUQUdJTkcpIEludGVybmV0IFNlY3Vy
aXR5IFJlc2VhcmNoIEdyb3VwMSIwIAYDVQQDExkoU1RBR0lORykgUHJldGVuZCBQ
ZWFyIFgxMB4XDTI0MDMxMzAwMDAwMFoXDTI3MDMxMjIzNTk1OVowUjELMAkGA1UE
BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSEwHwYDVQQD
ExgoU1RBR0lORykgUHNldWRvIFBsdW0gRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
AATljbbcV+mqWZa3g+z0bDOuBpZOtbi48iK9rjLtPdRU0WsgVp53MW3nXFU6qVYV
zEYaYd6PSmec0Tj3R5zEp5/F+cuOjTdh3AkTMzYm1tkflocPBN5APHYZ+76WxZad
q+WjggEAMIH9MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU/EbRAUNfu3um
PTBorhG64LxtydMwHwYDVR0jBBgwFoAUtfNl8v6wCpIf+zx980SgrGMlwxQwNgYI
KwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRwOi8vc3RnLXgxLmkubGVuY3Iu
b3JnLzATBgNVHSAEDDAKMAgGBmeBDAECATArBgNVHR8EJDAiMCCgHqAchhpodHRw
Oi8vc3RnLXgxLmMubGVuY3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAAtCGn4iG
cupruhkCTcoDqSIVTFgVR8JJ3GvGL7SYwIc4Fn0As66nQgnkATIzF5+gFb+CXEQD
qR2Jo+R38OeT7lQ1rNDcaJcbY6hL8cNRku3QlcfdYODZ5pgTVH04gTZUJISZKLjD
kMMcQIDZlF7iYqTvmHbn2ISSKorsJ3QKAvWhHwMoJtocSz3VeDJIep5QtbHnoXh1
/dyDx7sp8RuhC0eO9ElTgDtiA2V6JxigLPzqcnibBBR4bFLGtMNE4EvOOD/Fkd0L
hdGDbAMNd+O06n+b0rgmDvg75IgOV6fpDrdZFoiNfCckOEJh9v10uYt4pTc3B6lf
zI/X3EWP1H4VJmsYuy+OA29jPeP831sAObZtd3RWv0LQPrMfx6FCmy4AaeYEMvul
FrF6OX+JbssE+bn83F+sGEMZu/eVBwwKh3db7+2UduMdTOb8DePE3Aqlg9zofS8X
9fJXrrp+PPrdQyvM3e8DxuioWa9GLG30yD9WD6WTlSiiOrdWGOzisWpW4shFoL8u
0EfmeLVU4JVbauhOYZASQXABNeXewe9lqJWwfqaARYpRjyf+jRibn22H5NVK4Vog
l55Iq1rUgjc8r493NaNrlNwG7va7Ztkch5lJ3oL/FEVlVSK4snTbgb0b5qjQz3SA
i7rA/8QRZvOLnKNtdEUlDZNrzkZwHNluLGw=
-----END CERTIFICATE-----
"""
AutoTLSKey =
"""
-----BEGIN PRIVATE KEY-----
MIIG4wIBAAKCAYEAvwGLPY+4xuaVU4Uw0pFazEM2UT9l6BYOjuK+amdUHNLcj1Mdh3DXTJUIHt1c
79Aa3I4Vi6cFg1Kz14aQvwOYW+bX5MwLeOlotWCSlx4pHb6kv2kevNzmp4RCp0D1bEqts3Gae7ql
V+2ygteeliHjgGBJFdrPK9QbdPtbjhc5lWKDmiTuAwg26qDDsdCjupTPf75TRc8Z1I5JbWzRH26M
wxyZCslIaz7IWXjABpnPU+c5W6D/Iv4wVij/ihQOzGVoq+3luOpxohtycMX3di+tmRbdE7+q8ZXD
MYaZxjls1m+30MPo0DXoEheR1y3TpSDo6ekC71vRUiDQ+MZ8vyO0t5Hedcsq4gc2Ox3Hszqh45Ck
4xSqNnVDtRffWak3RzqZpqEoj09pvXh9THJH5O+9+0echYW2rmckN8w0UXF/82lZYffWcmU2Mhjj
xoNomkk6NZFKoqHCqL5l1oR0WLTAFlI8csSVxsS/tpaIIpaGI33hP3YyE7IRL309GGFRKrT9AgMB
AAECggGBAJ+VxqR0xElKtlDF43jLATXQoj1X3uj+JMO1Jqr4EgrTEnydUPqsiPXvPo2rHc8v7IGC
JPY9YhnKq3/TanRtqIqAYLlE0gD/4wBH47Jm/KthcXyLc6cQWZZ0psvfNi54ZpCaxhvCYgsJCjDP
vixpvA6yY93ip11TJm2i5Wfed7ocSSAs4r+dyWRXVannTCTD2Go+toyI8GfrSeYnGMJON0V9S1D7
w4n3NqWqgaYCNHtBoWaxKPovrmsObhMLlyGnR09TtWbBZ3FjlUMOwWu6B4qX/+dFgK9qynDIPfQ7
HOw2mZFD5pFZhgtLnzhIAbrjDPWtwt67mAvedDzr+3nzp2DKTlP+RgTy++hnWM/0YDZ5zuAtuQFo
q0MeRVK3svTQlTkO38FYMPdDcl2NlET8KA0qI/kDL5Ip6Eb5uB+tEvLAg+4dR6YuVCRVod+JzPEb
U/jxeN5uTlM9GYkm0jg0FWTqxn4vq1j6haMREgXa3Q8eFluRy220buVsE+37FKDA8QKBwQDjtWuc
FpwYdjM7KHKuCuBJBb2mVvcC+BubUFlHNvYBJHqpNXp8Q7qf+miaF59R2cg+Zaa0NtxSL/EVatTr
yOdmhf/87aNkbxgZ7++ImO1pn/iBA0cmI1Kgz3Bzkn1pu9DDaxQ7zqpwwdU1zyuhDjaqowIvifLq
+ujDCqvVGTg0pJaVpP7oNmElQ9i1kNpbGTVQ8IliYONf0AFzZ8vRuJ/Vej3JcFJHL/coQrt27tpI
s8urMD76dcg0qcrYfoh8HwcCgcEA1ry/LuEx9uIpsNZLCi4ZkrhMoadG8ACOasGxj6FE2SE/GZ39
uPNSNViZq503nzNq1db9Pt+Bta+IQej1ppaVV4vmtWXtD2cBJUv8Jpr4xz3A0Gurqmjih94Yro9v
Ig5JrLc0/v4grdhnkJZsKcWtfx+wQfCUJNlN/BsFuIUASfa3xVeomjgicRSsQ73HXYA5KVkQnn4K
RYfBsgKqqe0x69PYi0Qsawngtd4bq1Bvfy0svf6qX0WdOEOXOxWVLgbbAoHANrL68Zjg0GN8dQaH
XdWRARmW8CFN3vG4t/t6JshGGgooSQNms/kVGJ7vh6yLAf99wbdrbzkKfde0Yv+xvB4bsB4aWyi+
qj6hnIFtmfOafFgIOv2NltS/YY/TJIAZDlAmmvra9m7ztHhrfiyQ/3RJn33e5YqOxvGU/l1O37ba
MJMk9TeYYDHH7kq5AQyV13Jbw2C0r+Q0Wmy+HHnflTZzdrWRqBUKPr1/8rTtEWnZF8PQ9gN17XZj
rHrpFk52/NH7AoHAeldKrRDMAJZVnlRYqFIfa8HolujQt4f5m8UCvovox7PzWUrz9N1b5ty1oFqQ
B/mpUm+MFLgOFE8PWE27Ns/wAdLI/Gw3pWDP/EnQPMZqGkmKgrP1N79N4I6ejUVW0ZZGT0qJvQVX
5PO3/V5V/W6MLDMHnmnMXToY/hr/JWNRCNKxXJNWkZaNuNNIWcfTv+d/qZj+qO2yOG7h4eM3DF0A
5hTp+F482DbmeXczWGUZQOGh7hUbR/BHZHjNvnHLbk+lAoHAX+64DM//5Zkex93fAVB02aFzSGie
0RH+2fKShZt3BVAWkPswmauL0LIl/cpIqtKwKDomXGw92EiJORQ4oFxt29lnzNYAZr8Jab3iRwCh
NESce1bIOj4HHTrdTiOzeEgax31D7K1qS4bB8BKOaoEIUaFXdBxoZlEgWP6QjHEyjxgt7w5eMcwI
ssSVAvL2spb2VgtwE2TZ9wQLBDJiz41qddH6TOXFxCwScf7rL3H/hyLHmmu6U1a2Vf6s4wUG6lHW
-----END PRIVATE KEY-----
"""
SecureKey =
"""
-----BEGIN PRIVATE KEY-----
@@ -78,6 +172,7 @@ suite "WebSocket transport":
Upgrade(),
TLSPrivateKey.init(SecureKey),
TLSCertificate.init(SecureCert),
nil, # autotls
{TLSFlags.NoVerifyHost, TLSFlags.NoVerifyServerName},
)
except CatchableError:
@@ -91,6 +186,7 @@ suite "WebSocket transport":
Upgrade(),
TLSPrivateKey.init(SecureKey),
TLSCertificate.init(SecureCert),
nil, # autotls
{TLSFlags.NoVerifyHost},
)
@@ -136,3 +232,72 @@ suite "WebSocket transport":
await closing
await transport1.stop()
asyncTest "autotls certificate is used when manual tlscertificate is not specified":
let ma = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0/tls/ws").tryGet()]
let autotls = MockAutotlsService.new()
autotls.mockedKey = TLSPrivateKey.init(AutoTLSKey)
autotls.mockedCert = TLSCertificate.init(AutoTLSCertificate)
await autotls.setup()
let wstransport = WsTransport.new(
Upgrade(),
nil, # TLSPrivateKey
nil, # TLSCertificate
autotls,
{TLSFlags.NoVerifyHost, TLSFlags.NoVerifyServerName},
)
await wstransport.start(ma)
defer:
await wstransport.stop()
# TLSPrivateKey and TLSCertificate should be set
check wstransport.secure
# autotls should be used
check wstransport.tlsCertificate == await autotls.getCertWhenReady()
asyncTest "manually set tlscertificate is preferred over autotls when both are specified":
let ma = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0/tls/ws").tryGet()]
let autotls = MockAutotlsService.new()
autotls.mockedKey = TLSPrivateKey.init(AutoTLSKey)
autotls.mockedCert = TLSCertificate.init(AutoTLSCertificate)
await autotls.setup()
let secureCert = TLSCertificate.init(SecureCert)
let secureKey = TLSPrivateKey.init(SecureKey)
let wstransport = WsTransport.new(
Upgrade(),
secureKey,
secureCert,
autotls,
{TLSFlags.NoVerifyHost, TLSFlags.NoVerifyServerName},
)
await wstransport.start(ma)
defer:
await wstransport.stop()
# TLSPrivateKey and TLSCertificate should be set
check wstransport.secure
# autotls should be ignored
check wstransport.tlsCertificate == secureCert
check wstransport.tlsPrivateKey == secureKey
asyncTest "wstransport is not secure when both manual tlscertificate and autotls are not specified":
let ma = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0/tls/ws").tryGet()]
let wstransport = WsTransport.new(
Upgrade(),
nil, # TLSPrivateKey
nil, # TLSCertificate
nil, # autotls
{TLSFlags.NoVerifyHost, TLSFlags.NoVerifyServerName},
)
await wstransport.start(ma)
defer:
await wstransport.stop()
# TLSPrivateKey and TLSCertificate should not be set
check not wstransport.secure

View File

@@ -0,0 +1,83 @@
{.used.}
# Nim-Libp2p
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
{.push raises: [].}
import chronos
import chronos/apps/http/httpclient
import
../libp2p/[
stream/connection,
upgrademngrs/upgrade,
autotls/acme/client,
autotls/service,
autotls/utils,
multiaddress,
switch,
builders,
wire,
protocols/ping,
]
from ./helpers import suite, asyncTest, asyncTeardown, checkTrackers, skip, check
suite "WebSocket transport integration":
teardown:
checkTrackers()
asyncTest "autotls certificate is used when manual tlscertificate is not specified":
try:
discard getPublicIPAddress()
except:
skip() # host doesn't have public IPv4 address
return
let pingProtocol = Ping.new(rng = newRng())
let switch1 = SwitchBuilder
.new()
.withRng(newRng())
.withAddress(MultiAddress.init("/ip4/0.0.0.0/tcp/0/wss").tryGet())
.withTcpTransport()
.withWsTransport()
.withAutotls(
config = AutotlsConfig.new(acmeServerURL = parseUri(LetsEncryptURLStaging))
)
.withYamux()
.withNoise()
.build()
# let switch2 = SwitchBuilder
# .new()
# .withRng(newRng())
# .withAddress(MultiAddress.init("/ip4/0.0.0.0/tcp/0/wss").tryGet())
# .withWsTransport()
# .withAutotls(
# config = AutotlsConfig.new(acmeServerURL = parseUri(LetsEncryptURLStaging))
# )
# .withYamux()
# .withNoise()
# .build()
await switch1.start()
# await switch2.start()
# # should succeed
# let conn =
# await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, PingCodec)
# echo "7"
# discard await pingProtocol.ping(conn)
# echo "8"
defer:
# await conn.close()
await switch1.stop()
# await switch2.stop()