mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-22 02:47:48 +08:00
Some checks are pending
CodeQL / codeql (push) Waiting to run
Docker Check / lint (push) Waiting to run
Docker Check / build (push) Waiting to run
Gateway Conformance / gateway-conformance (push) Waiting to run
Gateway Conformance / gateway-conformance-libp2p-experiment (push) Waiting to run
Go Build / go-build (push) Waiting to run
Go Check / go-check (push) Waiting to run
Go Lint / go-lint (push) Waiting to run
Go Test / go-test (push) Waiting to run
Interop / interop-prep (push) Waiting to run
Interop / helia-interop (push) Blocked by required conditions
Interop / ipfs-webui (push) Blocked by required conditions
Sharness / sharness-test (push) Waiting to run
Spell Check / spellcheck (push) Waiting to run
adds Gateway.MaxRangeRequestFileSize configuration to protect against CDN bugs where range requests over certain sizes return entire files instead of requested byte ranges, causing unexpected bandwidth costs. - default: 0 (no limit) - returns 501 Not Implemented for oversized range requests - protects against CDNs like Cloudflare that ignore range requests over 5GiB also introduces OptionalBytes type to reduce code duplication when handling byte-size configuration values, replacing manual string parsing with humanize.ParseBytes. migrates existing byte-size configs to use this new type. Fixes: https://github.com/ipfs/boxo/issues/856
146 lines
6.2 KiB
Go
146 lines
6.2 KiB
Go
package libp2p
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
"github.com/ipfs/kubo/config"
|
|
"github.com/ipfs/kubo/core/node/libp2p/fd"
|
|
"github.com/libp2p/go-libp2p"
|
|
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
|
|
"github.com/pbnjay/memory"
|
|
)
|
|
|
|
var infiniteResourceLimits = rcmgr.InfiniteLimits.ToPartialLimitConfig().System
|
|
|
|
// This file defines implicit limit defaults used when Swarm.ResourceMgr.Enabled
|
|
|
|
// createDefaultLimitConfig creates LimitConfig to pass to libp2p's resource manager.
|
|
// The defaults follow the documentation in docs/libp2p-resource-management.md.
|
|
// Any changes in the logic here should be reflected there.
|
|
func createDefaultLimitConfig(cfg config.SwarmConfig) (limitConfig rcmgr.ConcreteLimitConfig, logMessageForStartup string, err error) {
|
|
maxMemoryDefault := uint64(memory.TotalMemory()) / 2
|
|
maxMemory := cfg.ResourceMgr.MaxMemory.WithDefault(maxMemoryDefault)
|
|
|
|
maxMemoryMB := maxMemory / (1024 * 1024)
|
|
maxFD := int(cfg.ResourceMgr.MaxFileDescriptors.WithDefault(int64(fd.GetNumFDs()) / 2))
|
|
|
|
// At least as of 2023-01-25, it's possible to open a connection that
|
|
// doesn't ask for any memory usage with the libp2p Resource Manager/Accountant
|
|
// (see https://github.com/libp2p/go-libp2p/issues/2010#issuecomment-1404280736).
|
|
// As a result, we can't currently rely on Memory limits to full protect us.
|
|
// Until https://github.com/libp2p/go-libp2p/issues/2010 is addressed,
|
|
// we take a proxy now of restricting to 1 inbound connection per MB.
|
|
// Note: this is more generous than go-libp2p's default autoscaled limits which do
|
|
// 64 connections per 1GB
|
|
// (see https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit_defaults.go#L357 ).
|
|
systemConnsInbound := int(1 * maxMemoryMB)
|
|
|
|
partialLimits := rcmgr.PartialLimitConfig{
|
|
System: rcmgr.ResourceLimits{
|
|
Memory: rcmgr.LimitVal64(maxMemory),
|
|
FD: rcmgr.LimitVal(maxFD),
|
|
|
|
Conns: rcmgr.Unlimited,
|
|
ConnsInbound: rcmgr.LimitVal(systemConnsInbound),
|
|
ConnsOutbound: rcmgr.Unlimited,
|
|
|
|
Streams: rcmgr.Unlimited,
|
|
StreamsOutbound: rcmgr.Unlimited,
|
|
StreamsInbound: rcmgr.Unlimited,
|
|
},
|
|
|
|
// Transient connections won't cause any memory to be accounted for by the resource manager/accountant.
|
|
// Only established connections do.
|
|
// As a result, we can't rely on System.Memory to protect us from a bunch of transient connection being opened.
|
|
// We limit the same values as the System scope, but only allow the Transient scope to take 25% of what is allowed for the System scope.
|
|
Transient: rcmgr.ResourceLimits{
|
|
Memory: rcmgr.LimitVal64(maxMemory / 4),
|
|
FD: rcmgr.LimitVal(maxFD / 4),
|
|
|
|
Conns: rcmgr.Unlimited,
|
|
ConnsInbound: rcmgr.LimitVal(systemConnsInbound / 4),
|
|
ConnsOutbound: rcmgr.Unlimited,
|
|
|
|
Streams: rcmgr.Unlimited,
|
|
StreamsInbound: rcmgr.Unlimited,
|
|
StreamsOutbound: rcmgr.Unlimited,
|
|
},
|
|
|
|
// Lets get out of the way of the allow list functionality.
|
|
// If someone specified "Swarm.ResourceMgr.Allowlist" we should let it go through.
|
|
AllowlistedSystem: infiniteResourceLimits,
|
|
|
|
AllowlistedTransient: infiniteResourceLimits,
|
|
|
|
// Keep it simple by not having Service, ServicePeer, Protocol, ProtocolPeer, Conn, or Stream limits.
|
|
ServiceDefault: infiniteResourceLimits,
|
|
|
|
ServicePeerDefault: infiniteResourceLimits,
|
|
|
|
ProtocolDefault: infiniteResourceLimits,
|
|
|
|
ProtocolPeerDefault: infiniteResourceLimits,
|
|
|
|
Conn: infiniteResourceLimits,
|
|
|
|
Stream: infiniteResourceLimits,
|
|
|
|
// Limit the resources consumed by a peer.
|
|
// This doesn't protect us against intentional DoS attacks since an attacker can easily spin up multiple peers.
|
|
// We specify this limit against unintentional DoS attacks (e.g., a peer has a bug and is sending too much traffic intentionally).
|
|
// In that case we want to keep that peer's resource consumption contained.
|
|
// To keep this simple, we only constrain inbound connections and streams.
|
|
PeerDefault: rcmgr.ResourceLimits{
|
|
Memory: rcmgr.Unlimited64,
|
|
FD: rcmgr.Unlimited,
|
|
Conns: rcmgr.Unlimited,
|
|
ConnsInbound: rcmgr.DefaultLimit,
|
|
ConnsOutbound: rcmgr.Unlimited,
|
|
Streams: rcmgr.Unlimited,
|
|
StreamsInbound: rcmgr.DefaultLimit,
|
|
StreamsOutbound: rcmgr.Unlimited,
|
|
},
|
|
}
|
|
|
|
scalingLimitConfig := rcmgr.DefaultLimits
|
|
libp2p.SetDefaultServiceLimits(&scalingLimitConfig)
|
|
|
|
// Anything set above in partialLimits that had a value of rcmgr.DefaultLimit will be overridden.
|
|
// Anything in scalingLimitConfig that wasn't defined in partialLimits above will be added (e.g., libp2p's default service limits).
|
|
partialLimits = partialLimits.Build(scalingLimitConfig.Scale(int64(maxMemory), maxFD)).ToPartialLimitConfig()
|
|
|
|
// Simple checks to override autoscaling ensuring limits make sense versus the connmgr values.
|
|
// There are ways to break this, but this should catch most problems already.
|
|
// We might improve this in the future.
|
|
// See: https://github.com/ipfs/kubo/issues/9545
|
|
if partialLimits.System.ConnsInbound > rcmgr.DefaultLimit && cfg.ConnMgr.Type.WithDefault(config.DefaultConnMgrType) != "none" {
|
|
maxInboundConns := int64(partialLimits.System.ConnsInbound)
|
|
if connmgrHighWaterTimesTwo := cfg.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater) * 2; maxInboundConns < connmgrHighWaterTimesTwo {
|
|
maxInboundConns = connmgrHighWaterTimesTwo
|
|
}
|
|
|
|
if maxInboundConns < config.DefaultResourceMgrMinInboundConns {
|
|
maxInboundConns = config.DefaultResourceMgrMinInboundConns
|
|
}
|
|
|
|
// Scale System.StreamsInbound as well, but use the existing ratio of StreamsInbound to ConnsInbound
|
|
if partialLimits.System.StreamsInbound > rcmgr.DefaultLimit {
|
|
partialLimits.System.StreamsInbound = rcmgr.LimitVal(maxInboundConns * int64(partialLimits.System.StreamsInbound) / int64(partialLimits.System.ConnsInbound))
|
|
}
|
|
partialLimits.System.ConnsInbound = rcmgr.LimitVal(maxInboundConns)
|
|
}
|
|
|
|
msg := fmt.Sprintf(`
|
|
Computed default go-libp2p Resource Manager limits based on:
|
|
- 'Swarm.ResourceMgr.MaxMemory': %q
|
|
- 'Swarm.ResourceMgr.MaxFileDescriptors': %d
|
|
|
|
These can be inspected with 'ipfs swarm resources'.
|
|
|
|
`, humanize.Bytes(maxMemory), maxFD)
|
|
|
|
// We already have a complete value thus pass in an empty ConcreteLimitConfig.
|
|
return partialLimits.Build(rcmgr.ConcreteLimitConfig{}), msg, nil
|
|
}
|