util.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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 cmd
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "os/signal"
  21. "runtime"
  22. "syscall"
  23. "time"
  24. "github.com/ipfs/go-log"
  25. "github.com/libp2p/go-libp2p/core/crypto"
  26. "github.com/libp2p/go-libp2p/core/peer"
  27. rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
  28. "github.com/mudler/edgevpn/internal"
  29. "github.com/mudler/edgevpn/pkg/config"
  30. "github.com/multiformats/go-multiaddr"
  31. "github.com/mudler/edgevpn/pkg/logger"
  32. node "github.com/mudler/edgevpn/pkg/node"
  33. "github.com/mudler/edgevpn/pkg/vpn"
  34. "github.com/urfave/cli/v2"
  35. )
  36. var CommonFlags []cli.Flag = []cli.Flag{
  37. &cli.StringFlag{
  38. Name: "config",
  39. Usage: "Specify a path to a edgevpn config file",
  40. EnvVars: []string{"EDGEVPNCONFIG"},
  41. },
  42. &cli.StringSliceFlag{
  43. Name: "listen-maddrs",
  44. Usage: "Override default 0.0.0.0 listen multiaddresses",
  45. EnvVars: []string{"EDGEVPNLISTENMADDRS"},
  46. },
  47. &cli.StringSliceFlag{
  48. Name: "dht-announce-maddrs",
  49. Usage: "Override listen-maddrs on DHT announce",
  50. EnvVars: []string{"EDGEVPNDHTANNOUNCEMADDRS"},
  51. },
  52. &cli.StringFlag{
  53. Name: "timeout",
  54. Usage: "Specify a default timeout for connection stream",
  55. EnvVars: []string{"EDGEVPNTIMEOUT"},
  56. Value: "15s",
  57. },
  58. &cli.IntFlag{
  59. Name: "mtu",
  60. Usage: "Specify a mtu",
  61. EnvVars: []string{"EDGEVPNMTU"},
  62. Value: 1200,
  63. },
  64. &cli.BoolFlag{
  65. Name: "bootstrap-iface",
  66. Usage: "Setup interface on startup (need privileges)",
  67. EnvVars: []string{"EDGEVPNBOOTSTRAPIFACE"},
  68. Value: true,
  69. },
  70. &cli.IntFlag{
  71. Name: "packet-mtu",
  72. Usage: "Specify a mtu",
  73. EnvVars: []string{"EDGEVPNPACKETMTU"},
  74. Value: 1420,
  75. },
  76. &cli.IntFlag{
  77. Name: "channel-buffer-size",
  78. Usage: "Specify a channel buffer size",
  79. EnvVars: []string{"EDGEVPNCHANNELBUFFERSIZE"},
  80. Value: 0,
  81. },
  82. &cli.IntFlag{
  83. Name: "discovery-interval",
  84. Usage: "DHT discovery interval time",
  85. EnvVars: []string{"EDGEVPNDHTINTERVAL"},
  86. Value: 720,
  87. },
  88. &cli.IntFlag{
  89. Name: "ledger-announce-interval",
  90. Usage: "Ledger announce interval time",
  91. EnvVars: []string{"EDGEVPNLEDGERINTERVAL"},
  92. Value: 10,
  93. },
  94. &cli.StringFlag{
  95. Name: "autorelay-discovery-interval",
  96. Usage: "Autorelay discovery interval",
  97. EnvVars: []string{"EDGEVPNAUTORELAYDISCOVERYINTERVAL"},
  98. Value: "5m",
  99. },
  100. &cli.BoolFlag{
  101. Name: "autorelay-static-only",
  102. Usage: "Use only defined static relays",
  103. EnvVars: []string{"EDGEVPNAUTORELAYSTATICONLY"},
  104. },
  105. &cli.IntFlag{
  106. Name: "ledger-synchronization-interval",
  107. Usage: "Ledger synchronization interval time",
  108. EnvVars: []string{"EDGEVPNLEDGERSYNCINTERVAL"},
  109. Value: 10,
  110. },
  111. &cli.IntFlag{
  112. Name: "nat-ratelimit-global",
  113. Usage: "Rate limit global requests",
  114. EnvVars: []string{"EDGEVPNNATRATELIMITGLOBAL"},
  115. Value: 10,
  116. },
  117. &cli.IntFlag{
  118. Name: "nat-ratelimit-peer",
  119. Usage: "Rate limit perr requests",
  120. EnvVars: []string{"EDGEVPNNATRATELIMITPEER"},
  121. Value: 10,
  122. },
  123. &cli.IntFlag{
  124. Name: "nat-ratelimit-interval",
  125. Usage: "Rate limit interval",
  126. EnvVars: []string{"EDGEVPNNATRATELIMITINTERVAL"},
  127. Value: 60,
  128. },
  129. &cli.BoolFlag{
  130. Name: "nat-ratelimit",
  131. Usage: "Changes the default rate limiting configured in helping other peers determine their reachability status",
  132. EnvVars: []string{"EDGEVPNNATRATELIMIT"},
  133. Value: true,
  134. },
  135. &cli.IntFlag{
  136. Name: "max-connections",
  137. Usage: "Max connections",
  138. EnvVars: []string{"EDGEVPNMAXCONNS"},
  139. Value: 0,
  140. },
  141. &cli.StringFlag{
  142. Name: "ledger-state",
  143. Usage: "Specify a ledger state directory",
  144. EnvVars: []string{"EDGEVPNLEDGERSTATE"},
  145. },
  146. &cli.BoolFlag{
  147. Name: "mdns",
  148. Usage: "Enable mDNS for peer discovery",
  149. EnvVars: []string{"EDGEVPNMDNS"},
  150. Value: true,
  151. },
  152. &cli.BoolFlag{
  153. Name: "autorelay",
  154. Usage: "Automatically act as a relay if the node can accept inbound connections",
  155. EnvVars: []string{"EDGEVPNAUTORELAY"},
  156. Value: true,
  157. },
  158. &cli.IntFlag{
  159. Name: "concurrency",
  160. Usage: "Number of concurrent requests to serve",
  161. Value: runtime.NumCPU(),
  162. },
  163. &cli.BoolFlag{
  164. Name: "holepunch",
  165. Usage: "Automatically try holepunching when possible",
  166. EnvVars: []string{"EDGEVPNHOLEPUNCH"},
  167. Value: true,
  168. },
  169. &cli.BoolFlag{
  170. Name: "natservice",
  171. Usage: "Tries to determine reachability status of nodes",
  172. EnvVars: []string{"EDGEVPNNATSERVICE"},
  173. Value: true,
  174. },
  175. &cli.BoolFlag{
  176. Name: "natmap",
  177. Usage: "Tries to open a port in the firewall via upnp",
  178. EnvVars: []string{"EDGEVPNNATMAP"},
  179. Value: true,
  180. },
  181. &cli.BoolFlag{
  182. Name: "dht",
  183. Usage: "Enable DHT for peer discovery",
  184. EnvVars: []string{"EDGEVPNDHT"},
  185. Value: true,
  186. },
  187. &cli.BoolFlag{
  188. Name: "low-profile",
  189. Usage: "Enable low profile. Lowers connections usage",
  190. EnvVars: []string{"EDGEVPNLOWPROFILE"},
  191. Value: true,
  192. },
  193. &cli.IntFlag{
  194. Name: "aliveness-healthcheck-interval",
  195. Usage: "Healthcheck interval",
  196. EnvVars: []string{"HEALTHCHECKINTERVAL"},
  197. Value: 120,
  198. },
  199. &cli.IntFlag{
  200. Name: "aliveness-healthcheck-scrub-interval",
  201. Usage: "Healthcheck scrub interval",
  202. EnvVars: []string{"HEALTHCHECKSCRUBINTERVAL"},
  203. Value: 600,
  204. },
  205. &cli.IntFlag{
  206. Name: "aliveness-healthcheck-max-interval",
  207. Usage: "Healthcheck max interval. Threshold after a node is determined offline",
  208. EnvVars: []string{"HEALTHCHECKMAXINTERVAL"},
  209. Value: 900,
  210. },
  211. &cli.StringFlag{
  212. Name: "log-level",
  213. Usage: "Specify loglevel",
  214. EnvVars: []string{"EDGEVPNLOGLEVEL"},
  215. Value: "info",
  216. },
  217. &cli.StringFlag{
  218. Name: "libp2p-log-level",
  219. Usage: "Specify libp2p loglevel",
  220. EnvVars: []string{"EDGEVPNLIBP2PLOGLEVEL"},
  221. Value: "fatal",
  222. },
  223. &cli.StringSliceFlag{
  224. Name: "discovery-bootstrap-peers",
  225. Usage: "List of discovery peers to use",
  226. EnvVars: []string{"EDGEVPNBOOTSTRAPPEERS"},
  227. },
  228. &cli.IntFlag{
  229. Name: "connection-high-water",
  230. Usage: "max number of connection allowed",
  231. EnvVars: []string{"EDGEVPN_CONNECTION_HIGH_WATER"},
  232. Value: 0,
  233. },
  234. &cli.IntFlag{
  235. Name: "connection-low-water",
  236. Usage: "low number of connection allowed",
  237. EnvVars: []string{"EDGEVPN_CONNECTION_LOW_WATER"},
  238. Value: 0,
  239. },
  240. &cli.StringSliceFlag{
  241. Name: "autorelay-static-peer",
  242. Usage: "List of autorelay static peers to use",
  243. EnvVars: []string{"EDGEVPNAUTORELAYPEERS"},
  244. },
  245. &cli.StringSliceFlag{
  246. Name: "blacklist",
  247. Usage: "List of peers/cidr to gate",
  248. EnvVars: []string{"EDGEVPNBLACKLIST"},
  249. },
  250. &cli.StringFlag{
  251. Name: "token",
  252. Usage: "Specify an edgevpn token in place of a config file",
  253. EnvVars: []string{"EDGEVPNTOKEN"},
  254. },
  255. &cli.BoolFlag{
  256. Name: "limit-enable",
  257. Usage: "Enable resource management",
  258. EnvVars: []string{"LIMITENABLE"},
  259. },
  260. &cli.StringFlag{
  261. Name: "limit-file",
  262. Usage: "Specify a resource limit config (json)",
  263. EnvVars: []string{"LIMITFILE"},
  264. },
  265. &cli.StringFlag{
  266. Name: "limit-scope",
  267. Usage: "Specify a limit scope",
  268. EnvVars: []string{"LIMITSCOPE"},
  269. Value: "system",
  270. },
  271. &cli.IntFlag{
  272. Name: "limit-config-streams",
  273. Usage: "Streams resource limit configuration",
  274. EnvVars: []string{"LIMITCONFIGSTREAMS"},
  275. Value: 200,
  276. },
  277. &cli.IntFlag{
  278. Name: "limit-config-streams-inbound",
  279. Usage: "Inbound streams resource limit configuration",
  280. EnvVars: []string{"LIMITCONFIGSTREAMSINBOUND"},
  281. Value: 30,
  282. },
  283. &cli.IntFlag{
  284. Name: "limit-config-streams-outbound",
  285. Usage: "Outbound streams resource limit configuration",
  286. EnvVars: []string{"LIMITCONFIGSTREAMSOUTBOUND"},
  287. Value: 30,
  288. },
  289. &cli.IntFlag{
  290. Name: "limit-config-conn",
  291. Usage: "Connections resource limit configuration",
  292. EnvVars: []string{"LIMITCONFIGCONNS"},
  293. Value: 200,
  294. },
  295. &cli.IntFlag{
  296. Name: "limit-config-conn-inbound",
  297. Usage: "Inbound connections resource limit configuration",
  298. EnvVars: []string{"LIMITCONFIGCONNSINBOUND"},
  299. Value: 30,
  300. },
  301. &cli.IntFlag{
  302. Name: "limit-config-conn-outbound",
  303. Usage: "Outbound connections resource limit configuration",
  304. EnvVars: []string{"LIMITCONFIGCONNSOUTBOUND"},
  305. Value: 30,
  306. },
  307. &cli.IntFlag{
  308. Name: "limit-config-fd",
  309. Usage: "Max fd resource limit configuration",
  310. EnvVars: []string{"LIMITCONFIGFD"},
  311. Value: 30,
  312. },
  313. &cli.BoolFlag{
  314. Name: "peerguard",
  315. Usage: "Enable peerguard. (Experimental)",
  316. EnvVars: []string{"PEERGUARD"},
  317. },
  318. &cli.BoolFlag{
  319. Name: "privkey-cache",
  320. Usage: "Enable privkey caching. (Experimental)",
  321. EnvVars: []string{"EDGEVPNPRIVKEYCACHE"},
  322. },
  323. &cli.StringFlag{
  324. Name: "privkey-cache-dir",
  325. Usage: "Specify a directory used to store the generated privkey",
  326. EnvVars: []string{"EDGEVPNPRIVKEYCACHEDIR"},
  327. Value: stateDir(),
  328. },
  329. &cli.StringSliceFlag{
  330. Name: "static-peertable",
  331. Usage: "List of static peers to use (in `ip:peerid` format)",
  332. EnvVars: []string{"EDGEVPNSTATICPEERTABLE"},
  333. },
  334. &cli.StringSliceFlag{
  335. Name: "whitelist",
  336. Usage: "List of peers in the whitelist",
  337. EnvVars: []string{"EDGEVPNWHITELIST"},
  338. },
  339. &cli.BoolFlag{
  340. Name: "peergate",
  341. Usage: "Enable peergating. (Experimental)",
  342. EnvVars: []string{"PEERGATE"},
  343. },
  344. &cli.BoolFlag{
  345. Name: "peergate-autoclean",
  346. Usage: "Enable peergating autoclean. (Experimental)",
  347. EnvVars: []string{"PEERGATE_AUTOCLEAN"},
  348. },
  349. &cli.BoolFlag{
  350. Name: "peergate-relaxed",
  351. Usage: "Enable peergating relaxation. (Experimental)",
  352. EnvVars: []string{"PEERGATE_RELAXED"},
  353. },
  354. &cli.StringFlag{
  355. Name: "peergate-auth",
  356. Usage: "Peergate auth",
  357. EnvVars: []string{"PEERGATE_AUTH"},
  358. Value: "",
  359. },
  360. &cli.IntFlag{
  361. Name: "peergate-interval",
  362. Usage: "Peergater interval time",
  363. EnvVars: []string{"EDGEVPNPEERGATEINTERVAL"},
  364. Value: 120,
  365. },
  366. }
  367. func stateDir() string {
  368. baseDir := ".edgevpn"
  369. home, _ := os.UserHomeDir()
  370. if home != "" {
  371. return filepath.Join(home, baseDir)
  372. }
  373. dir, _ := os.Getwd()
  374. return filepath.Join(dir, baseDir)
  375. }
  376. func displayStart(ll *logger.Logger) {
  377. ll.Info(Copyright)
  378. ll.Infof("Version: %s commit: %s", internal.Version, internal.Commit)
  379. }
  380. func stringsToMultiAddr(peers []string) []multiaddr.Multiaddr {
  381. res := []multiaddr.Multiaddr{}
  382. for _, p := range peers {
  383. addr, err := multiaddr.NewMultiaddr(p)
  384. if err != nil {
  385. continue
  386. }
  387. res = append(res, addr)
  388. }
  389. return res
  390. }
  391. // ConfigFromContext returns a config object from a cli context
  392. func ConfigFromContext(c *cli.Context) *config.Config {
  393. var limitConfig *rcmgr.PartialLimitConfig
  394. autorelayInterval, err := time.ParseDuration(c.String("autorelay-discovery-interval"))
  395. if err != nil {
  396. autorelayInterval = 0
  397. }
  398. // Authproviders are supposed to be passed as a json object
  399. pa := c.String("peergate-auth")
  400. d := map[string]map[string]interface{}{}
  401. json.Unmarshal([]byte(pa), &d)
  402. return &config.Config{
  403. NetworkConfig: c.String("config"),
  404. NetworkToken: c.String("token"),
  405. ListenMaddrs: (c.StringSlice("listen-maddrs")),
  406. DHTAnnounceMaddrs: stringsToMultiAddr(c.StringSlice("dht-announce-maddrs")),
  407. Address: c.String("address"),
  408. Router: c.String("router"),
  409. Interface: c.String("interface"),
  410. Libp2pLogLevel: c.String("libp2p-log-level"),
  411. LogLevel: c.String("log-level"),
  412. LowProfile: c.Bool("low-profile"),
  413. Blacklist: c.StringSlice("blacklist"),
  414. Concurrency: c.Int("concurrency"),
  415. FrameTimeout: c.String("timeout"),
  416. ChannelBufferSize: c.Int("channel-buffer-size"),
  417. InterfaceMTU: c.Int("mtu"),
  418. PacketMTU: c.Int("packet-mtu"),
  419. BootstrapIface: c.Bool("bootstrap-iface"),
  420. Whitelist: stringsToMultiAddr(c.StringSlice("whitelist")),
  421. Ledger: config.Ledger{
  422. StateDir: c.String("ledger-state"),
  423. AnnounceInterval: time.Duration(c.Int("ledger-announce-interval")) * time.Second,
  424. SyncInterval: time.Duration(c.Int("ledger-synchronization-interval")) * time.Second,
  425. },
  426. NAT: config.NAT{
  427. Service: c.Bool("natservice"),
  428. Map: c.Bool("natmap"),
  429. RateLimit: c.Bool("nat-ratelimit"),
  430. RateLimitGlobal: c.Int("nat-ratelimit-global"),
  431. RateLimitPeer: c.Int("nat-ratelimit-peer"),
  432. RateLimitInterval: time.Duration(c.Int("nat-ratelimit-interval")) * time.Second,
  433. },
  434. Discovery: config.Discovery{
  435. BootstrapPeers: c.StringSlice("discovery-bootstrap-peers"),
  436. DHT: c.Bool("dht"),
  437. MDNS: c.Bool("mdns"),
  438. Interval: time.Duration(c.Int("discovery-interval")) * time.Second,
  439. },
  440. Connection: config.Connection{
  441. AutoRelay: c.Bool("autorelay"),
  442. MaxConnections: c.Int("max-connections"),
  443. HolePunch: c.Bool("holepunch"),
  444. StaticRelays: c.StringSlice("autorelay-static-peer"),
  445. AutoRelayDiscoveryInterval: autorelayInterval,
  446. OnlyStaticRelays: c.Bool("autorelay-static-only"),
  447. HighWater: c.Int("connection-high-water"),
  448. LowWater: c.Int("connection-low-water"),
  449. },
  450. Limit: config.ResourceLimit{
  451. Enable: c.Bool("limit-enable"),
  452. FileLimit: c.String("limit-file"),
  453. Scope: c.String("limit-scope"),
  454. MaxConns: c.Int("max-connections"), // Turn to 0 to use other way of limiting. Files take precedence
  455. LimitConfig: limitConfig,
  456. },
  457. PeerGuard: config.PeerGuard{
  458. Enable: c.Bool("peerguard"),
  459. PeerGate: c.Bool("peergate"),
  460. Relaxed: c.Bool("peergate-relaxed"),
  461. Autocleanup: c.Bool("peergate-autoclean"),
  462. SyncInterval: time.Duration(c.Int("peergate-interval")) * time.Second,
  463. AuthProviders: d,
  464. },
  465. }
  466. }
  467. func cliToOpts(c *cli.Context) ([]node.Option, []vpn.Option, *logger.Logger) {
  468. nc := ConfigFromContext(c)
  469. lvl, err := log.LevelFromString(nc.LogLevel)
  470. if err != nil {
  471. lvl = log.LevelError
  472. }
  473. llger := logger.New(lvl)
  474. checkErr := func(e error) {
  475. if err != nil {
  476. llger.Fatal(err.Error())
  477. }
  478. }
  479. // Check if we have any privkey identity cached already
  480. if c.Bool("privkey-cache") {
  481. keyFile := filepath.Join(c.String("privkey-cache-dir"), "privkey")
  482. dat, err := os.ReadFile(keyFile)
  483. if err == nil && len(dat) > 0 {
  484. llger.Info("Reading key from", keyFile)
  485. nc.Privkey = dat
  486. } else {
  487. // generate, write
  488. llger.Info("Generating private key and saving it locally for later use in", keyFile)
  489. privkey, err := node.GenPrivKey(0)
  490. checkErr(err)
  491. r, err := crypto.MarshalPrivateKey(privkey)
  492. checkErr(err)
  493. err = os.MkdirAll(c.String("privkey-cache-dir"), 0600)
  494. checkErr(err)
  495. err = os.WriteFile(keyFile, r, 0600)
  496. checkErr(err)
  497. nc.Privkey = r
  498. }
  499. }
  500. for _, pt := range c.StringSlice("static-peertable") {
  501. dat := strings.Split(pt, ":")
  502. if len(dat) != 2 {
  503. checkErr(fmt.Errorf("wrong format for peertable entries. Want a list of ip/peerid separated by `:`. e.g. 10.1.0.1:... "))
  504. }
  505. if nc.Connection.PeerTable == nil {
  506. nc.Connection.PeerTable = make(map[string]peer.ID)
  507. }
  508. nc.Connection.PeerTable[dat[0]] = peer.ID(dat[1])
  509. }
  510. nodeOpts, vpnOpts, err := nc.ToOpts(llger)
  511. if err != nil {
  512. llger.Fatal(err.Error())
  513. }
  514. return nodeOpts, vpnOpts, llger
  515. }
  516. func handleStopSignals() {
  517. s := make(chan os.Signal, 10)
  518. signal.Notify(s, os.Interrupt, syscall.SIGTERM)
  519. for range s {
  520. os.Exit(0)
  521. }
  522. }