config.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. Copyright © 2021-2022 Ettore Di Giacinto <[email protected]>
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package config
  14. import (
  15. "fmt"
  16. "math/bits"
  17. "os"
  18. "runtime"
  19. "strings"
  20. "time"
  21. "github.com/ipfs/go-log"
  22. "github.com/libp2p/go-libp2p"
  23. dht "github.com/libp2p/go-libp2p-kad-dht"
  24. "github.com/libp2p/go-libp2p/core/network"
  25. "github.com/libp2p/go-libp2p/core/peer"
  26. "github.com/libp2p/go-libp2p/p2p/host/autorelay"
  27. rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
  28. connmanager "github.com/libp2p/go-libp2p/p2p/net/connmgr"
  29. "github.com/mudler/edgevpn/pkg/blockchain"
  30. "github.com/mudler/edgevpn/pkg/crypto"
  31. "github.com/mudler/edgevpn/pkg/discovery"
  32. "github.com/mudler/edgevpn/pkg/logger"
  33. "github.com/mudler/edgevpn/pkg/node"
  34. "github.com/mudler/edgevpn/pkg/trustzone"
  35. "github.com/mudler/edgevpn/pkg/trustzone/authprovider/ecdsa"
  36. "github.com/mudler/edgevpn/pkg/vpn"
  37. "github.com/mudler/water"
  38. "github.com/multiformats/go-multiaddr"
  39. "github.com/peterbourgon/diskv"
  40. )
  41. // Config is the config struct for the node and the default EdgeVPN services
  42. // It is used to generate opts for the node and the services before start.
  43. type Config struct {
  44. NetworkConfig, NetworkToken string
  45. Address string
  46. ListenMaddrs []string
  47. DHTAnnounceMaddrs []multiaddr.Multiaddr
  48. Router string
  49. Interface string
  50. Libp2pLogLevel, LogLevel string
  51. LowProfile, BootstrapIface bool
  52. Blacklist []string
  53. Concurrency int
  54. FrameTimeout string
  55. ChannelBufferSize, InterfaceMTU, PacketMTU int
  56. NAT NAT
  57. Connection Connection
  58. Discovery Discovery
  59. Ledger Ledger
  60. Limit ResourceLimit
  61. Privkey []byte
  62. // PeerGuard (experimental)
  63. // enable peerguardian and add specific auth options
  64. PeerGuard PeerGuard
  65. Whitelist []multiaddr.Multiaddr
  66. }
  67. type PeerGuard struct {
  68. Enable bool
  69. Relaxed bool
  70. Autocleanup bool
  71. PeerGate bool
  72. // AuthProviders in the freemap form:
  73. // ecdsa:
  74. // private_key: "foo_bar"
  75. AuthProviders map[string]map[string]interface{}
  76. SyncInterval time.Duration
  77. }
  78. type ResourceLimit struct {
  79. FileLimit string
  80. LimitConfig *rcmgr.PartialLimitConfig
  81. Scope string
  82. MaxConns int
  83. StaticMin int64
  84. StaticMax int64
  85. Enable bool
  86. }
  87. // Ledger is the ledger configuration structure
  88. type Ledger struct {
  89. AnnounceInterval, SyncInterval time.Duration
  90. StateDir string
  91. }
  92. // Discovery allows to enable/disable discovery and
  93. // set bootstrap peers
  94. type Discovery struct {
  95. DHT, MDNS bool
  96. BootstrapPeers []string
  97. Interval time.Duration
  98. }
  99. // Connection is the configuration section
  100. // relative to the connection services
  101. type Connection struct {
  102. HolePunch bool
  103. AutoRelay bool
  104. AutoRelayDiscoveryInterval time.Duration
  105. StaticRelays []string
  106. OnlyStaticRelays bool
  107. PeerTable map[string]peer.ID
  108. MaxConnections int
  109. LowWater int
  110. HighWater int
  111. }
  112. // NAT is the structure relative to NAT configuration settings
  113. // It allows to enable/disable the service and NAT mapping, and rate limiting too.
  114. type NAT struct {
  115. Service bool
  116. Map bool
  117. RateLimit bool
  118. RateLimitGlobal, RateLimitPeer int
  119. RateLimitInterval time.Duration
  120. }
  121. // Validate returns error if the configuration is not valid
  122. func (c Config) Validate() error {
  123. if c.NetworkConfig == "" &&
  124. c.NetworkToken == "" {
  125. return fmt.Errorf("EDGEVPNCONFIG or EDGEVPNTOKEN not supplied. At least a config file is required")
  126. }
  127. return nil
  128. }
  129. func peers2List(peers []string) discovery.AddrList {
  130. addrsList := discovery.AddrList{}
  131. for _, p := range peers {
  132. addrsList.Set(p)
  133. }
  134. return addrsList
  135. }
  136. func peers2AddrInfo(peers []string) []peer.AddrInfo {
  137. addrsList := []peer.AddrInfo{}
  138. for _, p := range peers {
  139. pi, err := peer.AddrInfoFromString(p)
  140. if err == nil {
  141. addrsList = append(addrsList, *pi)
  142. }
  143. }
  144. return addrsList
  145. }
  146. var infiniteResourceLimits = rcmgr.InfiniteLimits.ToPartialLimitConfig().System
  147. // ToOpts returns node and vpn options from a configuration
  148. func (c Config) ToOpts(l *logger.Logger) ([]node.Option, []vpn.Option, error) {
  149. if err := c.Validate(); err != nil {
  150. return nil, nil, err
  151. }
  152. config := c.NetworkConfig
  153. address := c.Address
  154. router := c.Router
  155. iface := c.Interface
  156. logLevel := c.LogLevel
  157. libp2plogLevel := c.Libp2pLogLevel
  158. dhtE, mDNS := c.Discovery.DHT, c.Discovery.MDNS
  159. ledgerState := c.Ledger.StateDir
  160. peers := c.Discovery.BootstrapPeers
  161. lvl, err := log.LevelFromString(logLevel)
  162. if err != nil {
  163. lvl = log.LevelError
  164. }
  165. llger := logger.New(lvl)
  166. libp2plvl, err := log.LevelFromString(libp2plogLevel)
  167. if err != nil {
  168. libp2plvl = log.LevelFatal
  169. }
  170. token := c.NetworkToken
  171. addrsList := peers2List(peers)
  172. dhtOpts := []dht.Option{}
  173. if c.LowProfile {
  174. dhtOpts = append(dhtOpts, dht.BucketSize(20))
  175. }
  176. if len(c.DHTAnnounceMaddrs) > 0 {
  177. dhtOpts = append(dhtOpts, dht.AddressFilter(
  178. func(m []multiaddr.Multiaddr) []multiaddr.Multiaddr {
  179. return c.DHTAnnounceMaddrs
  180. },
  181. ),
  182. )
  183. }
  184. d := discovery.NewDHT(dhtOpts...)
  185. m := &discovery.MDNS{}
  186. opts := []node.Option{
  187. node.ListenAddresses(c.ListenMaddrs...),
  188. node.WithDiscoveryInterval(c.Discovery.Interval),
  189. node.WithLedgerAnnounceTime(c.Ledger.AnnounceInterval),
  190. node.WithLedgerInterval(c.Ledger.SyncInterval),
  191. node.Logger(llger),
  192. node.WithDiscoveryBootstrapPeers(addrsList),
  193. node.WithBlacklist(c.Blacklist...),
  194. node.LibP2PLogLevel(libp2plvl),
  195. node.WithInterfaceAddress(address),
  196. node.WithSealer(&crypto.AESSealer{}),
  197. node.FromBase64(mDNS, dhtE, token, d, m),
  198. node.FromYaml(mDNS, dhtE, config, d, m),
  199. }
  200. for ip, peer := range c.Connection.PeerTable {
  201. opts = append(opts, node.WithStaticPeer(ip, peer))
  202. }
  203. if len(c.Privkey) > 0 {
  204. opts = append(opts, node.WithPrivKey(c.Privkey))
  205. }
  206. vpnOpts := []vpn.Option{
  207. vpn.WithConcurrency(c.Concurrency),
  208. vpn.WithInterfaceAddress(address),
  209. vpn.WithLedgerAnnounceTime(c.Ledger.AnnounceInterval),
  210. vpn.Logger(llger),
  211. vpn.WithTimeout(c.FrameTimeout),
  212. vpn.WithInterfaceType(water.TUN),
  213. vpn.NetLinkBootstrap(c.BootstrapIface),
  214. vpn.WithChannelBufferSize(c.ChannelBufferSize),
  215. vpn.WithInterfaceMTU(c.InterfaceMTU),
  216. vpn.WithPacketMTU(c.PacketMTU),
  217. vpn.WithRouterAddress(router),
  218. vpn.WithInterfaceName(iface),
  219. }
  220. libp2pOpts := []libp2p.Option{libp2p.UserAgent("edgevpn")}
  221. // AutoRelay section configuration
  222. if c.Connection.AutoRelay {
  223. relayOpts := []autorelay.Option{}
  224. staticRelays := c.Connection.StaticRelays
  225. if c.Connection.AutoRelayDiscoveryInterval == 0 {
  226. c.Connection.AutoRelayDiscoveryInterval = 5 * time.Minute
  227. }
  228. // If no relays are specified and no discovery interval, then just use default static relays (to be deprecated)
  229. relayOpts = append(relayOpts, autorelay.WithPeerSource(d.FindClosePeers(llger, c.Connection.OnlyStaticRelays, staticRelays...)))
  230. libp2pOpts = append(libp2pOpts,
  231. libp2p.EnableAutoRelay(relayOpts...))
  232. }
  233. if c.NAT.RateLimit {
  234. libp2pOpts = append(libp2pOpts, libp2p.AutoNATServiceRateLimit(
  235. c.NAT.RateLimitGlobal,
  236. c.NAT.RateLimitPeer,
  237. c.NAT.RateLimitInterval,
  238. ))
  239. }
  240. if c.Connection.LowWater != 0 && c.Connection.HighWater != 0 {
  241. llger.Infof("connmanager water limits low: %d high: %d", c.Connection.LowWater, c.Connection.HighWater)
  242. cm, err := connmanager.NewConnManager(
  243. c.Connection.LowWater,
  244. c.Connection.HighWater,
  245. connmanager.WithGracePeriod(80*time.Second),
  246. )
  247. if err != nil {
  248. llger.Fatal("could not create connection manager")
  249. }
  250. libp2pOpts = append(libp2pOpts, libp2p.ConnectionManager(cm))
  251. } else {
  252. llger.Infof("connmanager disabled")
  253. }
  254. if !c.Limit.Enable || runtime.GOOS == "darwin" {
  255. llger.Info("go-libp2p resource manager protection disabled")
  256. libp2pOpts = append(libp2pOpts, libp2p.ResourceManager(&network.NullResourceManager{}))
  257. } else {
  258. llger.Info("go-libp2p resource manager protection enabled")
  259. var limiter rcmgr.Limiter
  260. if c.Limit.FileLimit != "" {
  261. limitFile, err := os.Open(c.Limit.FileLimit)
  262. if err != nil {
  263. return opts, vpnOpts, err
  264. }
  265. defer limitFile.Close()
  266. l, err := rcmgr.NewDefaultLimiterFromJSON(limitFile)
  267. if err != nil {
  268. return opts, vpnOpts, err
  269. }
  270. limiter = l
  271. } else if c.Limit.MaxConns == -1 {
  272. llger.Infof("max connections: unlimited")
  273. scalingLimits := rcmgr.DefaultLimits
  274. // Add limits around included libp2p protocols
  275. libp2p.SetDefaultServiceLimits(&scalingLimits)
  276. // Turn the scaling limits into a concrete set of limits using `.AutoScale`. This
  277. // scales the limits proportional to your system memory.
  278. scaledDefaultLimits := scalingLimits.AutoScale()
  279. // Tweak certain settings
  280. cfg := rcmgr.PartialLimitConfig{
  281. System: rcmgr.ResourceLimits{
  282. Memory: rcmgr.Unlimited64,
  283. FD: rcmgr.Unlimited,
  284. Conns: rcmgr.Unlimited,
  285. ConnsInbound: rcmgr.Unlimited,
  286. ConnsOutbound: rcmgr.Unlimited,
  287. Streams: rcmgr.Unlimited,
  288. StreamsOutbound: rcmgr.Unlimited,
  289. StreamsInbound: rcmgr.Unlimited,
  290. },
  291. // Transient connections won't cause any memory to be accounted for by the resource manager/accountant.
  292. // Only established connections do.
  293. // As a result, we can't rely on System.Memory to protect us from a bunch of transient connection being opened.
  294. // 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.
  295. Transient: rcmgr.ResourceLimits{
  296. Memory: rcmgr.Unlimited64,
  297. FD: rcmgr.Unlimited,
  298. Conns: rcmgr.Unlimited,
  299. ConnsInbound: rcmgr.Unlimited,
  300. ConnsOutbound: rcmgr.Unlimited,
  301. Streams: rcmgr.Unlimited,
  302. StreamsInbound: rcmgr.Unlimited,
  303. StreamsOutbound: rcmgr.Unlimited,
  304. },
  305. // Lets get out of the way of the allow list functionality.
  306. // If someone specified "Swarm.ResourceMgr.Allowlist" we should let it go through.
  307. AllowlistedSystem: infiniteResourceLimits,
  308. AllowlistedTransient: infiniteResourceLimits,
  309. // Keep it simple by not having Service, ServicePeer, Protocol, ProtocolPeer, Conn, or Stream limits.
  310. ServiceDefault: infiniteResourceLimits,
  311. ServicePeerDefault: infiniteResourceLimits,
  312. ProtocolDefault: infiniteResourceLimits,
  313. ProtocolPeerDefault: infiniteResourceLimits,
  314. Conn: infiniteResourceLimits,
  315. Stream: infiniteResourceLimits,
  316. // Limit the resources consumed by a peer.
  317. // This doesn't protect us against intentional DoS attacks since an attacker can easily spin up multiple peers.
  318. // We specify this limit against unintentional DoS attacks (e.g., a peer has a bug and is sending too much traffic intentionally).
  319. // In that case we want to keep that peer's resource consumption contained.
  320. // To keep this simple, we only constrain inbound connections and streams.
  321. PeerDefault: rcmgr.ResourceLimits{
  322. Memory: rcmgr.Unlimited64,
  323. FD: rcmgr.Unlimited,
  324. Conns: rcmgr.Unlimited,
  325. ConnsInbound: rcmgr.DefaultLimit,
  326. ConnsOutbound: rcmgr.Unlimited,
  327. Streams: rcmgr.Unlimited,
  328. StreamsInbound: rcmgr.DefaultLimit,
  329. StreamsOutbound: rcmgr.Unlimited,
  330. },
  331. }
  332. // Create our limits by using our cfg and replacing the default values with values from `scaledDefaultLimits`
  333. limits := cfg.Build(scaledDefaultLimits)
  334. // The resource manager expects a limiter, se we create one from our limits.
  335. limiter = rcmgr.NewFixedLimiter(limits)
  336. } else if c.Limit.MaxConns != 0 {
  337. min := int64(1 << 30)
  338. max := int64(4 << 30)
  339. if c.Limit.StaticMin != 0 {
  340. min = c.Limit.StaticMin
  341. }
  342. if c.Limit.StaticMax != 0 {
  343. max = c.Limit.StaticMax
  344. }
  345. maxconns := int(c.Limit.MaxConns)
  346. defaultLimits := rcmgr.DefaultLimits.Scale(min+max/2, logScale(2*maxconns))
  347. llger.Infof("max connections: %d", c.Limit.MaxConns)
  348. limiter = rcmgr.NewFixedLimiter(defaultLimits)
  349. } else {
  350. llger.Infof("max connections: defaults limits")
  351. defaults := rcmgr.DefaultLimits
  352. def := &defaults
  353. libp2p.SetDefaultServiceLimits(def)
  354. limiter = rcmgr.NewFixedLimiter(def.AutoScale())
  355. }
  356. rc, err := rcmgr.NewResourceManager(limiter, rcmgr.WithAllowlistedMultiaddrs(c.Whitelist))
  357. if err != nil {
  358. llger.Fatal("could not create resource manager")
  359. }
  360. libp2pOpts = append(libp2pOpts, libp2p.ResourceManager(rc))
  361. }
  362. if c.Connection.HolePunch {
  363. libp2pOpts = append(libp2pOpts, libp2p.EnableHolePunching())
  364. }
  365. if c.NAT.Service {
  366. libp2pOpts = append(libp2pOpts, libp2p.EnableNATService())
  367. }
  368. if c.NAT.Map {
  369. libp2pOpts = append(libp2pOpts, libp2p.NATPortMap())
  370. }
  371. opts = append(opts, node.WithLibp2pOptions(libp2pOpts...))
  372. if ledgerState != "" {
  373. opts = append(opts, node.WithStore(blockchain.NewDiskStore(diskv.New(diskv.Options{
  374. BasePath: ledgerState,
  375. CacheSizeMax: uint64(50), // 50MB
  376. }))))
  377. } else {
  378. opts = append(opts, node.WithStore(&blockchain.MemoryStore{}))
  379. }
  380. if c.PeerGuard.Enable {
  381. pg := trustzone.NewPeerGater(c.PeerGuard.Relaxed)
  382. dur := c.PeerGuard.SyncInterval
  383. // Build up the authproviders for the peerguardian
  384. aps := []trustzone.AuthProvider{}
  385. for ap, providerOpts := range c.PeerGuard.AuthProviders {
  386. a, err := authProvider(llger, ap, providerOpts)
  387. if err != nil {
  388. return opts, vpnOpts, fmt.Errorf("invalid authprovider: %w", err)
  389. }
  390. aps = append(aps, a)
  391. }
  392. pguardian := trustzone.NewPeerGuardian(llger, aps...)
  393. opts = append(opts,
  394. node.WithNetworkService(
  395. pg.UpdaterService(dur),
  396. pguardian.Challenger(dur, c.PeerGuard.Autocleanup),
  397. ),
  398. node.EnableGenericHub,
  399. node.GenericChannelHandlers(pguardian.ReceiveMessage),
  400. )
  401. // We always pass a PeerGater such will be registered to the API if necessary
  402. opts = append(opts, node.WithPeerGater(pg))
  403. // IF it's not enabled, we just disable it right away.
  404. if !c.PeerGuard.PeerGate {
  405. pg.Disable()
  406. }
  407. }
  408. return opts, vpnOpts, nil
  409. }
  410. func authProvider(ll log.StandardLogger, s string, opts map[string]interface{}) (trustzone.AuthProvider, error) {
  411. switch strings.ToLower(s) {
  412. case "ecdsa":
  413. pk, exists := opts["private_key"]
  414. if !exists {
  415. return nil, fmt.Errorf("No private key provided")
  416. }
  417. return ecdsa.ECDSA521Provider(ll, fmt.Sprint(pk))
  418. }
  419. return nil, fmt.Errorf("not supported")
  420. }
  421. func logScale(val int) int {
  422. bitlen := bits.Len(uint(val))
  423. return 1 << bitlen
  424. }