-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat(AutoTLS): opt-in WSS certs from p2p-forge at libp2p.direct #10521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
ce6d09d
ecfc8d9
fe307da
bfc73d0
7c9842e
bef0a21
06708d8
af543af
946298b
d5994c1
7ae58bc
4889612
a038637
538c9bb
b306f56
9bd8ebb
21b5c88
7eeda1b
99b7757
e6e0b7a
ed7e201
e51d907
73c3fd3
e68493a
bcabbb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package config | ||
|
||
import p2pforge "github.com/ipshipyard/p2p-forge/client" | ||
|
||
// ForgeClient includes optional configuration of p2p-forge client of service | ||
// for obtaining a domain and TLS certificate to improve connectivity for web | ||
// browser clients. More: https://github.com/ipshipyard/p2p-forge#readme | ||
type ForgeClient struct { | ||
// Enables the p2p-forge feature | ||
Enabled Flag `json:",omitempty"` | ||
|
||
// Optional override of the parent domain that will be used | ||
ForgeDomain *OptionalString `json:",omitempty"` | ||
|
||
// Optional override of HTTP API that acts as ACME DNS-01 Challenge broker | ||
ForgeEndpoint *OptionalString `json:",omitempty"` | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Optional Authorization token, used with private/test instances of p2p-forge | ||
ForgeAuth *OptionalString `json:",omitempty"` | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Optional override of CA ACME API used by p2p-forge system | ||
CAEndpoint *OptionalString `json:",omitempty"` | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
const ( | ||
DefaultForgeEnabled = false // experimental, opt-in for now (https://github.com/ipfs/kubo/pull/10521) | ||
DefaultForgeDomain = p2pforge.DefaultForgeDomain | ||
DefaultForgeEndpoint = p2pforge.DefaultForgeEndpoint | ||
DefaultCAEndpoint = p2pforge.DefaultCAEndpoint | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,22 @@ | ||
package libp2p | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
|
||
logging "github.com/ipfs/go-log" | ||
version "github.com/ipfs/kubo" | ||
"github.com/ipfs/kubo/config" | ||
p2pforge "github.com/ipshipyard/p2p-forge/client" | ||
"github.com/libp2p/go-libp2p" | ||
"github.com/libp2p/go-libp2p/core/host" | ||
p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic" | ||
ma "github.com/multiformats/go-multiaddr" | ||
mamask "github.com/whyrusleeping/multiaddr-filter" | ||
|
||
"github.com/caddyserver/certmagic" | ||
"go.uber.org/fx" | ||
) | ||
|
||
func AddrFilters(filters []string) func() (*ma.Filters, Libp2pOpts, error) { | ||
|
@@ -87,12 +97,26 @@ func makeAddrsFactory(announce []string, appendAnnouce []string, noAnnounce []st | |
}, nil | ||
} | ||
|
||
func AddrsFactory(announce []string, appendAnnouce []string, noAnnounce []string) func() (opts Libp2pOpts, err error) { | ||
return func() (opts Libp2pOpts, err error) { | ||
addrsFactory, err := makeAddrsFactory(announce, appendAnnouce, noAnnounce) | ||
func AddrsFactory(announce []string, appendAnnouce []string, noAnnounce []string) interface{} { | ||
return func(params struct { | ||
fx.In | ||
ForgeMgr *p2pforge.P2PForgeCertMgr `optional:"true"` | ||
}, | ||
) (opts Libp2pOpts, err error) { | ||
var addrsFactory p2pbhost.AddrsFactory | ||
announceAddrsFactory, err := makeAddrsFactory(announce, appendAnnouce, noAnnounce) | ||
if err != nil { | ||
return opts, err | ||
} | ||
if params.ForgeMgr == nil { | ||
addrsFactory = announceAddrsFactory | ||
} else { | ||
addrsFactory = func(multiaddrs []ma.Multiaddr) []ma.Multiaddr { | ||
forgeProcessing := params.ForgeMgr.AddressFactory()(multiaddrs) | ||
annouceProcessing := announceAddrsFactory(forgeProcessing) | ||
return annouceProcessing | ||
} | ||
} | ||
opts.Opts = append(opts.Opts, libp2p.AddrsFactory(addrsFactory)) | ||
return | ||
} | ||
|
@@ -107,3 +131,47 @@ func ListenOn(addresses []string) interface{} { | |
} | ||
} | ||
} | ||
|
||
func P2PForgeCertMgr(cfg config.ForgeClient) interface{} { | ||
return func() (*p2pforge.P2PForgeCertMgr, error) { | ||
storagePath, err := config.Path("", "p2p-forge-certs") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
forgeLogger := logging.Logger("p2p-forge/client").Desugar() | ||
// TODO: revisit is below is still needed. | ||
// seems that certmagic is written in a way that logs things using default logger | ||
// before a custom one is set, this is the only way to ensure we don't lose | ||
// early logs such as 'maintenance' and 'obtain' events :-/ | ||
certmagic.Default.Logger = forgeLogger | ||
certmagic.DefaultACME.Logger = forgeLogger | ||
|
||
certMgr, err := p2pforge.NewP2PForgeCertMgr( | ||
p2pforge.WithLogger(forgeLogger.Sugar()), | ||
p2pforge.WithForgeDomain(cfg.ForgeDomain.WithDefault(config.DefaultForgeDomain)), | ||
p2pforge.WithForgeRegistrationEndpoint(cfg.ForgeEndpoint.WithDefault(config.DefaultForgeEndpoint)), | ||
p2pforge.WithCAEndpoint(cfg.CAEndpoint.WithDefault(config.DefaultCAEndpoint)), | ||
p2pforge.WithForgeAuth(cfg.ForgeAuth.WithDefault(os.Getenv(p2pforge.ForgeAuthEnv))), | ||
p2pforge.WithUserAgent(version.GetUserAgentVersion()), | ||
p2pforge.WithCertificateStorage(&certmagic.FileStorage{Path: storagePath})) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Found a bug in cluster logs ("error while checking storage for updated ARI; updating ARI now"):
I suspect this is another race condition where certmanager things (like location Will look into this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential fix in e51d907 and ipshipyard/p2p-forge@f599f48. TLDR is that I'm going to deploy to collab cluster, let it run over night and see if ARI error is gone. Update: so far the fix looks good, renewal check does not produce error anymore:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, the error seems to be gone. Considering this resolved, but keeping this open for visibility. |
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return certMgr, nil | ||
} | ||
} | ||
|
||
func StartP2PForgeClient(lc fx.Lifecycle, certMgr *p2pforge.P2PForgeCertMgr, h host.Host) { | ||
lc.Append(fx.Hook{ | ||
OnStart: func(ctx context.Context) error { | ||
certMgr.ProvideHost(h) | ||
return certMgr.Start() | ||
}, | ||
OnStop: func(ctx context.Context) error { | ||
certMgr.Stop() | ||
return nil | ||
}, | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,6 +117,12 @@ config file at runtime. | |
- [`Swarm.DisableBandwidthMetrics`](#swarmdisablebandwidthmetrics) | ||
- [`Swarm.DisableNatPortMap`](#swarmdisablenatportmap) | ||
- [`Swarm.EnableHolePunching`](#swarmenableholepunching) | ||
- [`Swarm.ForgeClient`](#swarmforgeclient) | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- [`Swarm.ForgeClient.Enabled`](#swarmforgeclientenabled) | ||
- [`Swarm.ForgeClient.ForgeDomain`](#swarmforgeclientforgedomain) | ||
- [`Swarm.ForgeClient.ForgeEndpoint`](#swarmforgeclientforgeendpoint) | ||
- [`Swarm.ForgeClient.ForgeAuth`](#swarmforgeclientforgeauth) | ||
- [`Swarm.ForgeClient.CAEndpoint`](#swarmforgeclientcaendpoint) | ||
- [`Swarm.EnableAutoRelay`](#swarmenableautorelay) | ||
- [`Swarm.RelayClient`](#swarmrelayclient) | ||
- [`Swarm.RelayClient.Enabled`](#swarmrelayclientenabled) | ||
|
@@ -1716,6 +1722,83 @@ Default: `true` | |
|
||
Type: `flag` | ||
|
||
### `Swarm.ForgeClient` | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Configuration options for optional [p2p-forge] client that provides Kubo with DNS+TLS+WebSockets multiaddrs that allow for direct connections from JS running in web browsers. | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The default settings use instance at `libp2p.direct` and TLS certificates from Let's Encrypt. | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Origin and rationale for this project can be found in [community.letsencrypt.org discussion]. | ||
lidel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
In short, [p2p-forge] provides a publicly diallable Kubo with a domain name for their PeerID (`peerid.libp2p.direct`), | ||
and acts as [ACME DNS-01 Challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) | ||
broker to allow Kubo to get a wildcard TLS cert for that domain (`*.peerid.libp2p.direct`). | ||
|
||
> [!NOTE] | ||
> Public good infrastructure at `libp2p.direct` is run by the team at [Interplanetary Shipyard](https://ipshipyard.com). | ||
> | ||
> <a href="http://ipshipyard.com/"><img src="https://github.com/user-attachments/assets/39ed3504-bb71-47f6-9bf8-cb9a1698f272" /></a> | ||
|
||
[p2p-forge]: https://github.com/ipshipyard/p2p-forge | ||
[community.letsencrypt.org discussion]: https://community.letsencrypt.org/t/feedback-on-raising-certificates-per-registered-domain-to-enable-peer-to-peer-networking/223003 | ||
|
||
Default: `{}` | ||
|
||
Type: `object` | ||
|
||
#### `Swarm.ForgeClient.Enabled` | ||
|
||
> [!CAUTION] | ||
> This is an EXPERIMENTAL feature and should not be used in production yet. | ||
2color marked this conversation as resolved.
Show resolved
Hide resolved
|
||
> Feel free to enable it and report issues if you want to help with testing. | ||
|
||
Enables **EXPERIMENTAL** [p2p-forge] client. This feature works only if your Kubo node is publicly diallable. | ||
|
||
If enabled, it will detect when `.../tls/sni/.../ws` is present in [`Addresses.Swarm`](#addressesswarm) | ||
and SNI is matching `Swarm.ForgeClient.ForgeDomain`, and set up a trusted TLS certificate matching the domain name used in Secure WebSockets (WSS) listener. | ||
|
||
If you want to test this, add `/ip4/0.0.0.0/tcp/4082/tls/sni/*.libp2p.direct/ws` to [`Addresses.Swarm`](#addressesswarm). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is port Can you use the same port as the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not yet, we need libp2p/go-libp2p#2984 for that. The idea is that TCP port sharing will land in go-libp2p before we enable AutoTLS by default. |
||
|
||
Debugging can be enabled by setting environment variable `GOLOG_LOG_LEVEL="error,p2p-forge/client=debug"` | ||
|
||
Default: `false` | ||
|
||
Type: `flag` | ||
|
||
#### `Swarm.ForgeClient.ForgeDomain` | ||
|
||
Optional override of the parent domain that will be used in DNS+TLS+WebSockets multiaddrs generated by [p2p-forge] client. | ||
Do not change this unless you self-host [p2p-forge]. | ||
|
||
Default: `libp2p.direct` (public good run by [Interplanetary Shipyard](https://ipshipyard.com)) | ||
|
||
Type: `optionalString` | ||
|
||
#### `Swarm.ForgeClient.ForgeEndpoint` | ||
|
||
Optional override of [p2p-forge] HTTP registration API. | ||
Do not change this unless you self-host [p2p-forge]. | ||
|
||
Default: `https://registration.libp2p.direct` (public good run by [Interplanetary Shipyard](https://ipshipyard.com)) | ||
|
||
Type: `optionalString` | ||
|
||
#### `Swarm.ForgeClient.ForgeAuth` | ||
|
||
Optional value for `Forge-Authorization` token sent with request to `ForgeEndpoint` | ||
(useful for private/self-hosted/test instances of [p2p-forge]). | ||
|
||
Default: `""` | ||
|
||
Type: `optionalString` | ||
|
||
#### `Swarm.ForgeClient.CAEndpoint` | ||
|
||
Optional override of CA ACME API used by [p2p-forge] system. | ||
|
||
Default: [certmagic.LetsEncryptProductionCA](https://pkg.go.dev/github.com/caddyserver/certmagic#pkg-constants) (see [community.letsencrypt.org discussion]) | ||
|
||
Type: `optionalString` | ||
|
||
### `Swarm.EnableAutoRelay` | ||
|
||
**REMOVED** | ||
|
@@ -1835,7 +1918,7 @@ Type: `optionalInteger` | |
|
||
#### `Swarm.RelayService.MaxReservationsPerPeer` | ||
|
||
**REMOVED in kubo 0.32 due to removal from go-libp2p v0.37** | ||
**REMOVED in kubo 0.32 due to [go-libp2p#2974](https://github.com/libp2p/go-libp2p/pull/2974)** | ||
|
||
#### `Swarm.RelayService.MaxReservationsPerIP` | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.