config.go 15 KB

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