@@ -2,14 +2,28 @@ package main
2
2
3
3
import (
4
4
"context"
5
+ "crypto/ecdsa"
6
+ "crypto/elliptic"
5
7
"crypto/rand"
6
8
"crypto/sha256"
9
+ "crypto/tls"
10
+ "crypto/x509"
11
+ "crypto/x509/pkix"
7
12
"encoding/base64"
13
+ "encoding/pem"
8
14
"errors"
9
15
"fmt"
16
+ libp2pws "github.com/libp2p/go-libp2p/p2p/transport/websocket"
17
+ "github.com/multiformats/go-multiaddr"
18
+ madns "github.com/multiformats/go-multiaddr-dns"
19
+ "log"
20
+ "math/big"
10
21
"net"
22
+ "net/http"
11
23
"os"
24
+ "strings"
12
25
"testing"
26
+ "time"
13
27
14
28
"github.com/coredns/caddy"
15
29
"github.com/coredns/coredns/core/dnsserver"
@@ -23,6 +37,11 @@ import (
23
37
_ "github.com/coredns/coredns/core/plugin" // Load all managed plugins in github.com/coredns/coredns.
24
38
_ "github.com/ipshipyard/p2p-forge/acme"
25
39
_ "github.com/ipshipyard/p2p-forge/ipparser"
40
+
41
+ pebbleCA "github.com/letsencrypt/pebble/v2/ca"
42
+ pebbleDB "github.com/letsencrypt/pebble/v2/db"
43
+ pebbleVA "github.com/letsencrypt/pebble/v2/va"
44
+ pebbleWFE "github.com/letsencrypt/pebble/v2/wfe"
26
45
)
27
46
28
47
const forge = "libp2p.direct"
@@ -332,6 +351,153 @@ func TestIPv6Lookup(t *testing.T) {
332
351
}
333
352
}
334
353
354
+ func TestLibp2pACMEE2E (t * testing.T ) {
355
+ db := pebbleDB .NewMemoryStore ()
356
+ logger := log .New (os .Stdout , "" , 0 )
357
+ ca := pebbleCA .New (logger , db , "" , 0 , 1 , 0 )
358
+ va := pebbleVA .New (logger , 0 , 0 , false , dnsServerAddress , db )
359
+
360
+ wfeImpl := pebbleWFE .New (logger , db , va , ca , false , false , 3 , 5 )
361
+ muxHandler := wfeImpl .Handler ()
362
+
363
+ acmeHTTPListener , err := net .Listen ("tcp" , "127.0.0.1:0" )
364
+ if err != nil {
365
+ t .Fatal (err )
366
+ }
367
+ defer acmeHTTPListener .Close ()
368
+
369
+ // Generate the self-signed certificate and private key
370
+ certPEM , privPEM , err := generateSelfSignedCert ("127.0.0.1" )
371
+ if err != nil {
372
+ log .Fatalf ("Failed to generate self-signed certificate: %v" , err )
373
+ }
374
+
375
+ // Load the certificate and key into tls.Certificate
376
+ cert , err := tls .X509KeyPair (certPEM , privPEM )
377
+ if err != nil {
378
+ log .Fatalf ("Failed to load key pair: %v" , err )
379
+ }
380
+
381
+ // Create a TLS configuration with the certificate
382
+ tlsConfig := & tls.Config {
383
+ Certificates : []tls.Certificate {cert },
384
+ }
385
+
386
+ // Wrap the listener with TLS
387
+ acmeHTTPListener = tls .NewListener (acmeHTTPListener , tlsConfig )
388
+
389
+ go func () {
390
+ http .Serve (acmeHTTPListener , muxHandler )
391
+ }()
392
+
393
+ ctx , cancel := context .WithCancel (context .Background ())
394
+ defer cancel ()
395
+
396
+ cas := x509 .NewCertPool ()
397
+ cas .AppendCertsFromPEM (certPEM )
398
+
399
+ acmeEndpoint := fmt .Sprintf ("https://%s%s" , acmeHTTPListener .Addr (), pebbleWFE .DirectoryPath )
400
+ certLoaded := make (chan bool , 1 )
401
+ h , err := client .NewHostWithP2PForge (forge , fmt .Sprintf ("http://127.0.0.1:%d" , httpPort ), acmeEndpoint , "foo@bar.com" , cas , func () {
402
+ certLoaded <- true
403
+ })
404
+ if err != nil {
405
+ t .Fatal (err )
406
+ }
407
+
408
+ cp := x509 .NewCertPool ()
409
+ cp .AddCert (ca .GetRootCert (0 ).Cert )
410
+ tlsCfgWithTestCA := & tls.Config {RootCAs : cp }
411
+
412
+ localDnsResolver , err := madns .NewResolver (madns .WithDefaultResolver (& net.Resolver {
413
+ PreferGo : true ,
414
+ Dial : func (ctx context.Context , network , address string ) (net.Conn , error ) {
415
+ d := net.Dialer {
416
+ Timeout : time .Second * 5 , // Set a timeout for the connection
417
+ }
418
+ return d .DialContext (ctx , network , dnsServerAddress )
419
+ },
420
+ }))
421
+ if err != nil {
422
+ t .Fatal (err )
423
+ }
424
+ customResolver , err := madns .NewResolver (madns .WithDomainResolver ("libp2p.direct." , localDnsResolver ))
425
+ if err != nil {
426
+ t .Fatal (err )
427
+ }
428
+
429
+ h2 , err := libp2p .New (libp2p .Transport (libp2pws .New , libp2pws .WithTLSClientConfig (tlsCfgWithTestCA )),
430
+ libp2p .MultiaddrResolver (customResolver ))
431
+ if err != nil {
432
+ t .Fatal (err )
433
+ }
434
+
435
+ var dialAddr multiaddr.Multiaddr
436
+ hAddrs := h .Addrs ()
437
+ for _ , addr := range hAddrs {
438
+ as := addr .String ()
439
+ if strings .Contains (as , "p2p-circuit" ) {
440
+ continue
441
+ }
442
+ if strings .Contains (as , "libp2p.direct/ws" ) {
443
+ dialAddr = addr
444
+ break
445
+ }
446
+ }
447
+ if dialAddr == nil {
448
+ t .Fatalf ("no valid wss addresses: %v" , hAddrs )
449
+ }
450
+
451
+ select {
452
+ case <- certLoaded :
453
+ case <- time .After (time .Second * 30 ):
454
+ t .Fatal ("timed out waiting for certificate" )
455
+ }
456
+
457
+ if err := h2 .Connect (ctx , peer.AddrInfo {ID : h .ID (), Addrs : []multiaddr.Multiaddr {dialAddr }}); err != nil {
458
+ t .Fatal (err )
459
+ }
460
+ }
461
+
462
+ func generateSelfSignedCert (ipAddr string ) ([]byte , []byte , error ) {
463
+ priv , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
464
+ if err != nil {
465
+ return nil , nil , err
466
+ }
467
+
468
+ serialNumber , err := rand .Int (rand .Reader , new (big.Int ).Lsh (big .NewInt (1 ), 128 ))
469
+ if err != nil {
470
+ return nil , nil , err
471
+ }
472
+
473
+ template := x509.Certificate {
474
+ SerialNumber : serialNumber ,
475
+ Subject : pkix.Name {
476
+ Organization : []string {"My Organization" },
477
+ },
478
+ NotBefore : time .Now (),
479
+ NotAfter : time .Now ().Add (365 * 24 * time .Hour ), // Valid for 1 year
480
+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature ,
481
+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth },
482
+ BasicConstraintsValid : true ,
483
+ IPAddresses : []net.IP {net .ParseIP (ipAddr )},
484
+ }
485
+
486
+ certDER , err := x509 .CreateCertificate (rand .Reader , & template , & template , & priv .PublicKey , priv )
487
+ if err != nil {
488
+ return nil , nil , err
489
+ }
490
+
491
+ certPEM := pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : certDER })
492
+ privDER , err := x509 .MarshalECPrivateKey (priv )
493
+ if err != nil {
494
+ return nil , nil , err
495
+ }
496
+ privPEM := pem .EncodeToMemory (& pem.Block {Type : "EC PRIVATE KEY" , Bytes : privDER })
497
+
498
+ return certPEM , privPEM , nil
499
+ }
500
+
335
501
// Input implements the caddy.Input interface and acts as an easy way to use a string as a Corefile.
336
502
type Input struct {
337
503
corefile []byte
0 commit comments