Skip to content

feat(AutoTLS): enabled by default with 1h RegistrationDelay #10724

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

Merged
merged 9 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions cmd/ipfs/kubo/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,16 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
// Private setups should not use public AutoTLS infrastructure
// as it will leak their existence and PeerID identity to CA
// and they will show up at https://crt.sh/?q=libp2p.direct
// Below ensures we hard fail if someone tries to enable both
if cfg.AutoTLS.Enabled.WithDefault(config.DefaultAutoTLSEnabled) {
return errors.New("private networking (swarm.key / LIBP2P_FORCE_PNET) does not work with AutoTLS.Enabled=true, update config to remove this message")
enableAutoTLS := cfg.AutoTLS.Enabled.WithDefault(config.DefaultAutoTLSEnabled)
if enableAutoTLS {
if cfg.AutoTLS.Enabled != config.Default {
// hard fail if someone tries to explicitly enable both
return errors.New("private networking (swarm.key / LIBP2P_FORCE_PNET) does not work with AutoTLS.Enabled=true, update config to remove this message")
} else {
// print error and disable autotls if user runs on default settings
log.Error("private networking (swarm.key / LIBP2P_FORCE_PNET) is not compatible with AutoTLS. Set AutoTLS.Enabled=false in config to remove this message.")
cfg.AutoTLS.Enabled = config.False
}
}
}

Expand Down
22 changes: 15 additions & 7 deletions config/autotls.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package config

import p2pforge "github.com/ipshipyard/p2p-forge/client"
import (
"time"

p2pforge "github.com/ipshipyard/p2p-forge/client"
)

// AutoTLS includes optional configuration of p2p-forge client of service
// for obtaining a domain and TLS certificate to improve connectivity for web
Expand All @@ -21,6 +25,9 @@ type AutoTLS struct {
// Optional Authorization token, used with private/test instances of p2p-forge
RegistrationToken *OptionalString `json:",omitempty"`

// Optional registration delay used when AutoTLS.Enabled is not explicitly set to true in config
RegistrationDelay *OptionalDuration `json:",omitempty"`

// Optional override of CA ACME API used by p2p-forge system
CAEndpoint *OptionalString `json:",omitempty"`

Expand All @@ -29,10 +36,11 @@ type AutoTLS struct {
}

const (
DefaultAutoTLSEnabled = false // experimental, opt-in for now (https://github.com/ipfs/kubo/pull/10521)
DefaultDomainSuffix = p2pforge.DefaultForgeDomain
DefaultRegistrationEndpoint = p2pforge.DefaultForgeEndpoint
DefaultCAEndpoint = p2pforge.DefaultCAEndpoint
DefaultAutoWSS = true // requires AutoTLS.Enabled
DefaultAutoTLSShortAddrs = true // requires AutoTLS.Enabled
DefaultAutoTLSEnabled = true // with DefaultAutoTLSRegistrationDelay, unless explicitly enabled in config
DefaultDomainSuffix = p2pforge.DefaultForgeDomain
DefaultRegistrationEndpoint = p2pforge.DefaultForgeEndpoint
DefaultCAEndpoint = p2pforge.DefaultCAEndpoint
DefaultAutoWSS = true // requires AutoTLS.Enabled
DefaultAutoTLSShortAddrs = true // requires AutoTLS.Enabled
DefaultAutoTLSRegistrationDelay = 1 * time.Hour
)
2 changes: 2 additions & 0 deletions config/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ is useful when using the daemon in test environments.`,

c.Bootstrap = []string{}
c.Discovery.MDNS.Enabled = false
c.AutoTLS.Enabled = False
return nil
},
},
Expand All @@ -104,6 +105,7 @@ Inverse profile of the test profile.`,

c.Swarm.DisableNatPortMap = false
c.Discovery.MDNS.Enabled = true
c.AutoTLS.Enabled = Default
return nil
},
},
Expand Down
1 change: 1 addition & 0 deletions core/coreapi/test/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (NodeProvider) MakeAPISwarm(t *testing.T, ctx context.Context, fullIdentity
c.Addresses.Swarm = []string{fmt.Sprintf("/ip4/18.0.%d.1/tcp/4001", i)}
c.Identity = ident
c.Experimental.FilestoreEnabled = true
c.AutoTLS.Enabled = config.False // disable so no /ws listener is added

ds := syncds.MutexWrap(datastore.NewMapDatastore())
r := &repo.Mock{
Expand Down
24 changes: 14 additions & 10 deletions core/node/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
autonat = fx.Provide(libp2p.AutoNATService(cfg.AutoNAT.Throttle, true))
}

enableTCPTransport := cfg.Swarm.Transports.Network.TCP.WithDefault(true)
enableWebsocketTransport := cfg.Swarm.Transports.Network.Websocket.WithDefault(true)
enableRelayTransport := cfg.Swarm.Transports.Network.Relay.WithDefault(true) // nolint
enableRelayService := cfg.Swarm.RelayService.Enabled.WithDefault(enableRelayTransport)
enableRelayClient := cfg.Swarm.RelayClient.Enabled.WithDefault(enableRelayTransport)
Expand All @@ -129,14 +131,8 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
}
}

if enableAutoTLS {
if !cfg.Swarm.Transports.Network.TCP.WithDefault(true) {
logger.Fatal("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.TCP to be true as well.")
}
if !cfg.Swarm.Transports.Network.Websocket.WithDefault(true) {
logger.Fatal("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.Websocket to be true as well.")
}

switch {
case enableAutoTLS && enableTCPTransport && enableWebsocketTransport:
// AutoTLS for Secure WebSockets: ensure WSS listeners are in place (manual or automatic)
wssWildcard := fmt.Sprintf("/tls/sni/*.%s/ws", cfg.AutoTLS.DomainSuffix.WithDefault(config.DefaultDomainSuffix))
wssWildcardPresent := false
Expand Down Expand Up @@ -170,7 +166,8 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
// if no manual /ws listener was set by the user
if enableAutoWSS && !wssWildcardPresent && !customWsPresent {
if len(tcpListeners) == 0 {
logger.Fatal("Invalid configuration: AutoTLS.AutoWSS=true requires at least one /tcp listener present in Addresses.Swarm, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls")
logger.Error("Invalid configuration, AutoTLS will be disabled: AutoTLS.AutoWSS=true requires at least one /tcp listener present in Addresses.Swarm, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls")
enableAutoTLS = false
}
for _, tcpListener := range tcpListeners {
wssListener := tcpListener + wssWildcard
Expand All @@ -180,8 +177,15 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
}

if !wssWildcardPresent && !enableAutoWSS {
logger.Fatal(fmt.Sprintf("Invalid configuration: AutoTLS.Enabled=true requires a /tcp listener ending with %q to be present in Addresses.Swarm or AutoTLS.AutoWSS=true, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls", wssWildcard))
logger.Error(fmt.Sprintf("Invalid configuration, AutoTLS will be disabled: AutoTLS.Enabled=true requires a /tcp listener ending with %q to be present in Addresses.Swarm or AutoTLS.AutoWSS=true, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls", wssWildcard))
enableAutoTLS = false
}
case enableAutoTLS && !enableTCPTransport:
logger.Error("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.TCP to be true as well. AutoTLS will be disabled.")
enableAutoTLS = false
case enableAutoTLS && !enableWebsocketTransport:
logger.Error("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.Websocket to be true as well. AutoTLS will be disabled.")
enableAutoTLS = false
}

// Gather all the options
Expand Down
9 changes: 9 additions & 0 deletions core/node/libp2p/addrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"

logging "github.com/ipfs/go-log"
version "github.com/ipfs/kubo"
Expand Down Expand Up @@ -145,11 +146,19 @@ func P2PForgeCertMgr(repoPath string, cfg config.AutoTLS, atlsLog *logging.ZapEv
certmagic.Default.Logger = rawLogger.Named("default_fixme")
certmagic.DefaultACME.Logger = rawLogger.Named("default_acme_client_fixme")

registrationDelay := cfg.RegistrationDelay.WithDefault(config.DefaultAutoTLSRegistrationDelay)
if cfg.Enabled == config.True && cfg.RegistrationDelay.IsDefault() {
// Skip delay if user explicitly enabled AutoTLS.Enabled in config
// and did not set custom AutoTLS.RegistrationDelay
registrationDelay = 0 * time.Second
}

certStorage := &certmagic.FileStorage{Path: storagePath}
certMgr, err := p2pforge.NewP2PForgeCertMgr(
p2pforge.WithLogger(rawLogger.Sugar()),
p2pforge.WithForgeDomain(cfg.DomainSuffix.WithDefault(config.DefaultDomainSuffix)),
p2pforge.WithForgeRegistrationEndpoint(cfg.RegistrationEndpoint.WithDefault(config.DefaultRegistrationEndpoint)),
p2pforge.WithRegistrationDelay(registrationDelay),
p2pforge.WithCAEndpoint(cfg.CAEndpoint.WithDefault(config.DefaultCAEndpoint)),
p2pforge.WithForgeAuth(cfg.RegistrationToken.WithDefault(os.Getenv(p2pforge.ForgeAuthEnv))),
p2pforge.WithUserAgent(version.GetUserAgentVersion()),
Expand Down
29 changes: 26 additions & 3 deletions docs/changelogs/v0.34.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,40 @@

- [Overview](#overview)
- [🔦 Highlights](#-highlights)
- [AutoTLS now enabled by default for nodes with 1 hour uptime](#autotls-now-enabled-by-default-for-nodes-with-1-hour-uptime)
- [RPC and CLI command changes](#rpc-and-cli-command-changes)
- [Bitswap improvements from Boxo](#bitswap-improvements-from-boxo)
- [IPFS_LOG_LEVEL deprecated](#ipfs_log_level-deprecated)
- [Pebble datastore format upgrade](#pebble_datastore_format_update)
- [Badger datastore update](#badger_datastore_update)
- [`IPFS_LOG_LEVEL` deprecated](#ipfs_log_level-deprecated)
- [Pebble datastore format update](#pebble-datastore-format-update)
- [Badger datastore update](#badger-datastore-update)
- [👨‍👩‍👧‍👦 Contributors](#-contributors)

### Overview

### 🔦 Highlights

#### AutoTLS now enabled by default for nodes with 1 hour uptime

Starting now, any publicly dialable Kubo node with a `/tcp` listener that remains online for at least one hour will receive a TLS certificate through the [`AutoTLS`](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls) feature.
This occurs automatically, with no need for manual setup.

To bypass the 1-hour delay and enable AutoTLS immediately, users can explicitly opt-in by running the following commands:

```console
$ ipfs config --json AutoTLS.Enabled true
$ ipfs config --json AutoTLS.RegistrationDelay 0
```

AutoTLS will remain disabled under the following conditions:

- The node already has a manually configured `/ws` (WebSocket) listener
- A private network is in use with a `swarm.key`
- TCP or WebSocket transports are disabled, or there is no `/tcp` listener

To troubleshoot, use `GOLOG_LOG_LEVEL="error,autotls=info`.

For more details, check out the [`AutoTLS` configuration documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls) or dive deeper with [AutoTLS libp2p blog post](https://blog.libp2p.io/autotls/).

#### RPC and CLI command changes

- `ipfs config` is now validating json fields ([#10679](https://github.com/ipfs/kubo/pull/10679)).
Expand Down
63 changes: 29 additions & 34 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ config file at runtime.
- [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix)
- [`AutoTLS.RegistrationEndpoint`](#autotlsregistrationendpoint)
- [`AutoTLS.RegistrationToken`](#autotlsregistrationtoken)
- [`AutoTLS.RegistrationDelay`](#autotlsregistrationdelay)
- [`AutoTLS.CAEndpoint`](#autotlscaendpoint)
- [`Bootstrap`](#bootstrap)
- [`Datastore`](#datastore)
Expand Down Expand Up @@ -463,12 +464,7 @@ Type: `duration` (when `0`/unset, the default value is used)

## `AutoTLS`

> [!CAUTION]
> This is an **EXPERIMENTAL** opt-in feature and should not be used in production yet.
> Feel free to enable it and [report issues](https://github.com/ipfs/kubo/issues/new/choose) if you want to help with testing.
> Track progress in [kubo#10560](https://github.com/ipfs/kubo/issues/10560).

AutoTLS feature enables publicly reachable Kubo nodes (those dialable from the public
The [AutoTLS](https://blog.libp2p.io/autotls/) feature enables publicly reachable Kubo nodes (those dialable from the public
internet) to automatically obtain a wildcard TLS certificate for a DNS name
unique to their PeerID at `*.[PeerID].libp2p.direct`. This enables direct
libp2p connections and retrieval of IPFS content from browsers [Secure Context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)
Expand All @@ -480,11 +476,11 @@ broker enabling peer to obtain a wildcard TLS certificate tied to public key of

By default, the certificates are requested from Let's Encrypt. Origin and rationale for this project can be found in [community.letsencrypt.org discussion](https://community.letsencrypt.org/t/feedback-on-raising-certificates-per-registered-domain-to-enable-peer-to-peer-networking/223003).

<a href="https://ipshipyard.com/"><img align="right" src="https://github.com/user-attachments/assets/39ed3504-bb71-47f6-9bf8-cb9a1698f272" /></a>

> [!NOTE]
> Public good DNS and [p2p-forge] infrastructure at `libp2p.direct` is run by the team at [Interplanetary Shipyard](https://ipshipyard.com).
>
> <a href="https://ipshipyard.com/"><img src="https://github.com/user-attachments/assets/39ed3504-bb71-47f6-9bf8-cb9a1698f272" /></a>

[p2p-forge]: https://github.com/ipshipyard/p2p-forge

Default: `{}`
Expand All @@ -493,50 +489,38 @@ Type: `object`

### `AutoTLS.Enabled`

> [!CAUTION]
> This is an **EXPERIMENTAL** opt-in feature and should not be used in production yet.
> Feel free to enable it and [report issues](https://github.com/ipfs/kubo/issues/new/choose) if you want to help with testing.
> Track progress in [kubo#10560](https://github.com/ipfs/kubo/issues/10560).
Enables the AutoTLS feature to provide DNS and TLS support for [libp2p Secure WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) over a `/tcp` port,
to allow JS clients running in web browser [Secure Context](https://w3c.github.io/webappsec-secure-contexts/) to connect to Kubo directly.

Enables AutoTLS feature to get DNS+TLS for [libp2p Secure WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) on `/tcp` port.
When activated, together with [`AutoTLS.AutoWSS`](#autotlsautowss) (default) or manually including a `/tcp/{port}/tls/sni/*.libp2p.direct/ws` multiaddr in [`Addresses.Swarm`](#addressesswarm)
(with SNI suffix matching [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix)), Kubo retrieves a trusted PKI TLS certificate for `*.{peerid}.libp2p.direct` and configures the `/ws` listener to use it.

If `AutoTLS.AutoWSS` is `true`, or `/tcp/../tls/sni/*.libp2p.direct/ws` [multiaddr] is present in [`Addresses.Swarm`](#addressesswarm)
with SNI segment ending with [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix),
Kubo will obtain and set up a trusted PKI TLS certificate for `*.peerid.libp2p.direct`, making it dialable from web browser's [Secure Contexts](https://w3c.github.io/webappsec-secure-contexts/).
**Note:**

> [!TIP]
> - Most users don't need custom `/ws` config in `Addresses.Swarm`. Try running this with `AutoTLS.AutoWSS=true`: it will reuse preexisting catch-all `/tcp` ports that were already forwarded/safelisted on your firewall.
> - Debugging can be enabled by setting environment variable `GOLOG_LOG_LEVEL="error,autotls=debug,p2p-forge/client=debug"`. Less noisy `GOLOG_LOG_LEVEL="error,autotls=info` may be informative enough.
> - Certificates are stored in `$IPFS_PATH/p2p-forge-certs`. Removing directory and restarting daemon will trigger certificate rotation.

> [!IMPORTANT]
> Caveats:
> - Requires your Kubo node to be publicly dialable.
> - If you want to test this with a node that is behind a NAT and uses manual TCP port forwarding or UPnP (`Swarm.DisableNatPortMap=false`), use `AutoTLS.AutoWSS=true`, or manually
> add catch-all `/ip4/0.0.0.0/tcp/4001/tls/sni/*.libp2p.direct/ws` and `/ip6/::/tcp/4001/tls/sni/*.libp2p.direct/ws` to [`Addresses.Swarm`](#addressesswarm)
> and **wait 5-15 minutes** for libp2p node to set up and learn about own public addresses via [AutoNAT](#autonat).
> - If your node is fresh and just started, the [p2p-forge] client may produce and log ERRORs during this time, but once a publicly dialable addresses are set up, a subsequent retry should be successful.
> - The TLS certificate is used only for [libp2p WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) connections.
> - Right now, this is NOT used for hosting a [Gateway](#gateway) over HTTPS (that use case still requires manual TLS setup on reverse proxy, and your own domain).
- This feature requires a publicly reachable node. If behind NAT, manual port forwarding or UPnP (`Swarm.DisableNatPortMap=false`) is required.
- The first time AutoTLS is used, it may take 5-15 minutes + [`AutoTLS.RegistrationDelay`](#autotlsregistrationdelay) before `/ws` listener is added. Be patient.
- Avoid manual configuration. [`AutoTLS.AutoWSS=true`](#autotlsautowss) should automatically add `/ws` listener to existing, firewall-forwarded `/tcp` ports.
- To troubleshoot, use `GOLOG_LOG_LEVEL="error,autotls=debug` for detailed logs, or `GOLOG_LOG_LEVEL="error,autotls=info` for quieter output.
- Certificates are stored in `$IPFS_PATH/p2p-forge-certs`; deleting this directory and restarting the daemon forces a certificate rotation.
- For now, the TLS cert applies solely to `/ws` libp2p WebSocket connections, not HTTP [`Gateway`](#gateway), which still need separate reverse proxy TLS setup with a custom domain.

Default: `false`
Default: `true`

Type: `flag`

### `AutoTLS.AutoWSS`

Optional. Controls if Kubo should add `/tls/sni/*.libp2p.direct/ws` listener to every pre-existing `/tcp` port IFF no explicit `/ws` is defined in [`Addresses.Swarm`](#addressesswarm) already.

Default: `true` (active only if `AutoTLS.Enabled` is `true` as well)
Default: `true` (if `AutoTLS.Enabled`)

Type: `flag`

### `AutoTLS.ShortAddrs`

Optional. Controls if final AutoTLS listeners are announced under shorter `/dnsX/A.B.C.D.peerid.libp2p.direct/tcp/4001/tls/ws` addresses instead of fully resolved `/ip4/A.B.C.D/tcp/4001/tls/sni/A-B-C-D.peerid.libp2p.direct/tls/ws`.

> [!TIP]
> The main use for AutoTLS is allowing connectivity from Secure Context in a web browser, and DNS lookup needs to happen there anyway, making `/dnsX` a more compact, more interoperable option without obvious downside.
The main use for AutoTLS is allowing connectivity from Secure Context in a web browser, and DNS lookup needs to happen there anyway, making `/dnsX` a more compact, more interoperable option without obvious downside.

Default: `true`

Expand Down Expand Up @@ -574,6 +558,17 @@ Default: `""`

Type: `optionalString`

### `AutoTLS.RegistrationDelay`

An additional delay applied before sending a request to the `RegistrationEndpoint`.

The default delay is bypassed if the user explicitly set `AutoTLS.Enabled=true` in the JSON configuration file.
This ensures that ephemeral nodes using the default configuration do not spam the`AutoTLS.CAEndpoint` with unnecessary ACME requests.

Default: `1h` (or `0` if explicit `AutoTLS.Enabled=true`)

Type: `optionalDuration`

### `AutoTLS.CAEndpoint`

Optional override of CA ACME API used by [p2p-forge] system.
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ require (
github.com/ipld/go-car/v2 v2.14.2 // indirect
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
github.com/ipld/go-ipld-prime v0.21.0 // indirect
github.com/ipshipyard/p2p-forge v0.3.0 // indirect
github.com/ipshipyard/p2p-forge v0.4.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jbenet/goprocess v0.1.4 // indirect
Expand Down
Loading
Loading