api.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. * Copyright (C)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. package zerotier
  14. import (
  15. "bytes"
  16. secrand "crypto/rand"
  17. "encoding/json"
  18. "fmt"
  19. "io/ioutil"
  20. "net"
  21. "net/http"
  22. "path"
  23. "runtime"
  24. "strconv"
  25. "strings"
  26. "time"
  27. "github.com/hectane/go-acl"
  28. )
  29. // APISocketName is the default socket name for accessing the API
  30. const APISocketName = "apisocket"
  31. // APIGet makes a query to the API via a Unix domain or windows pipe socket
  32. func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, int64, error) {
  33. client, err := createNamedSocketHTTPClient(basePath, socketName)
  34. if err != nil {
  35. return http.StatusTeapot, 0, err
  36. }
  37. req, err := http.NewRequest("GET", "http://socket"+queryPath, nil)
  38. if err != nil {
  39. return http.StatusTeapot, 0, err
  40. }
  41. req.Header.Add("Authorization", "bearer "+authToken)
  42. resp, err := client.Do(req)
  43. if err != nil {
  44. return http.StatusTeapot, 0, err
  45. }
  46. err = json.NewDecoder(resp.Body).Decode(obj)
  47. ts := resp.Header.Get("X-ZT-Clock")
  48. t := int64(0)
  49. if len(ts) > 0 {
  50. t, _ = strconv.ParseInt(ts, 10, 64)
  51. }
  52. return resp.StatusCode, t, err
  53. }
  54. // APIPost posts a JSON object to the API via a Unix domain or windows pipe socket and reads a response
  55. func APIPost(basePath, socketName, authToken, queryPath string, post, result interface{}) (int, int64, error) {
  56. client, err := createNamedSocketHTTPClient(basePath, socketName)
  57. if err != nil {
  58. return http.StatusTeapot, 0, err
  59. }
  60. var data []byte
  61. if post != nil {
  62. data, err = json.Marshal(post)
  63. if err != nil {
  64. return http.StatusTeapot, 0, err
  65. }
  66. } else {
  67. data = []byte("null")
  68. }
  69. req, err := http.NewRequest("POST", "http://socket"+queryPath, bytes.NewReader(data))
  70. if err != nil {
  71. return http.StatusTeapot, 0, err
  72. }
  73. req.Header.Add("Content-Type", "application/json")
  74. req.Header.Add("Authorization", "bearer "+authToken)
  75. resp, err := client.Do(req)
  76. if err != nil {
  77. return http.StatusTeapot, 0, err
  78. }
  79. ts := resp.Header.Get("X-ZT-Clock")
  80. t := int64(0)
  81. if len(ts) > 0 {
  82. t, _ = strconv.ParseInt(ts, 10, 64)
  83. }
  84. if result != nil {
  85. err = json.NewDecoder(resp.Body).Decode(result)
  86. return resp.StatusCode, t, err
  87. }
  88. return resp.StatusCode, t, nil
  89. }
  90. // APIDelete posts DELETE to a path and fills result with the outcome (if any) if result is non-nil
  91. func APIDelete(basePath, socketName, authToken, queryPath string, result interface{}) (int, int64, error) {
  92. client, err := createNamedSocketHTTPClient(basePath, socketName)
  93. if err != nil {
  94. return http.StatusTeapot, 0, err
  95. }
  96. req, err := http.NewRequest("DELETE", "http://socket"+queryPath, nil)
  97. if err != nil {
  98. return http.StatusTeapot, 0, err
  99. }
  100. req.Header.Add("Authorization", "bearer "+authToken)
  101. resp, err := client.Do(req)
  102. if err != nil {
  103. return http.StatusTeapot, 0, err
  104. }
  105. ts := resp.Header.Get("X-ZT-Clock")
  106. t := int64(0)
  107. if len(ts) > 0 {
  108. t, _ = strconv.ParseInt(ts, 10, 64)
  109. }
  110. if result != nil {
  111. err = json.NewDecoder(resp.Body).Decode(result)
  112. return resp.StatusCode, t, err
  113. }
  114. return resp.StatusCode, t, nil
  115. }
  116. // APIStatus is the object returned by API status inquiries
  117. type APIStatus struct {
  118. Address Address `json:"address"`
  119. Clock int64 `json:"clock"`
  120. StartupTime int64 `json:"startupTime"`
  121. Config *LocalConfig `json:"config"`
  122. Online bool `json:"online"`
  123. PeerCount int `json:"peerCount"`
  124. PathCount int `json:"pathCount"`
  125. Identity *Identity `json:"identity"`
  126. InterfaceAddresses []net.IP `json:"interfaceAddresses,omitempty"`
  127. MappedExternalAddresses []*InetAddress `json:"mappedExternalAddresses,omitempty"`
  128. Version string `json:"version"`
  129. VersionMajor int `json:"versionMajor"`
  130. VersionMinor int `json:"versionMinor"`
  131. VersionRevision int `json:"versionRevision"`
  132. VersionBuild int `json:"versionBuild"`
  133. OS string `json:"os"`
  134. Architecture string `json:"architecture"`
  135. Concurrency int `json:"concurrency"`
  136. Runtime string `json:"runtimeVersion"`
  137. }
  138. // APINetwork is the object returned by API network inquiries
  139. type APINetwork struct {
  140. ID NetworkID `json:"id"`
  141. Config NetworkConfig `json:"config"`
  142. Settings *NetworkLocalSettings `json:"settings,omitempty"`
  143. ControllerFingerprint *Fingerprint `json:"controllerFingerprint,omitempty"`
  144. MulticastSubscriptions []*MulticastGroup `json:"multicastSubscriptions,omitempty"`
  145. PortType string `json:"portType"`
  146. PortName string `json:"portName"`
  147. PortEnabled bool `json:"portEnabled"`
  148. PortErrorCode int `json:"portErrorCode"`
  149. PortError string `json:"portError"`
  150. }
  151. func apiNetworkFromNetwork(n *Network) *APINetwork {
  152. var nn APINetwork
  153. nn.ID = n.ID()
  154. nn.Config = n.Config()
  155. ls := n.LocalSettings()
  156. nn.Settings = &ls
  157. nn.MulticastSubscriptions = n.MulticastSubscriptions()
  158. nn.PortType = n.Tap().Type()
  159. nn.PortName = n.Tap().DeviceName()
  160. nn.PortEnabled = n.Tap().Enabled()
  161. ec, errStr := n.Tap().Error()
  162. nn.PortErrorCode = ec
  163. nn.PortError = errStr
  164. return &nn
  165. }
  166. func apiSetStandardHeaders(out http.ResponseWriter) {
  167. h := out.Header()
  168. h.Set("Cache-Control", "no-cache, no-store, must-revalidate")
  169. h.Set("Expires", "0")
  170. h.Set("Pragma", "no-cache")
  171. t := time.Now().UTC()
  172. h.Set("Date", t.Format(time.RFC1123))
  173. h.Set("X-ZT-Clock", strconv.FormatInt(t.UnixNano()/int64(1000000), 10))
  174. }
  175. func apiSendObj(out http.ResponseWriter, req *http.Request, httpStatusCode int, obj interface{}) error {
  176. h := out.Header()
  177. h.Set("Content-Type", "application/json")
  178. if req.Method == http.MethodHead {
  179. out.WriteHeader(httpStatusCode)
  180. return nil
  181. }
  182. var j []byte
  183. var err error
  184. if obj != nil {
  185. j, err = json.Marshal(obj)
  186. if err != nil {
  187. return err
  188. }
  189. }
  190. out.WriteHeader(httpStatusCode)
  191. _, err = out.Write(j)
  192. return err
  193. }
  194. func apiReadObj(out http.ResponseWriter, req *http.Request, dest interface{}) (err error) {
  195. err = json.NewDecoder(req.Body).Decode(&dest)
  196. if err != nil {
  197. _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid JSON: " + err.Error()})
  198. }
  199. return
  200. }
  201. func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool {
  202. ah := req.Header.Get("Authorization")
  203. if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
  204. return true
  205. }
  206. ah = req.Header.Get("X-ZT1-Auth")
  207. if len(ah) > 0 && strings.TrimSpace(ah) == token {
  208. return true
  209. }
  210. _ = apiSendObj(out, req, http.StatusUnauthorized, &APIErr{"authorization token not found or incorrect (checked X-ZT1-Auth and Authorization headers)"})
  211. return false
  212. }
  213. // createAPIServer creates and starts an HTTP server for a given node
  214. func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, error) {
  215. // Read authorization token, automatically generating one if it's missing
  216. var authToken string
  217. authTokenFile := path.Join(basePath, "authtoken.secret")
  218. authTokenB, err := ioutil.ReadFile(authTokenFile)
  219. if err != nil {
  220. var atb [20]byte
  221. _, err = secrand.Read(atb[:])
  222. if err != nil {
  223. return nil, nil, err
  224. }
  225. for i := 0; i < 20; i++ {
  226. atb[i] = "abcdefghijklmnopqrstuvwxyz0123456789"[atb[i]%36]
  227. }
  228. err = ioutil.WriteFile(authTokenFile, atb[:], 0600)
  229. if err != nil {
  230. return nil, nil, err
  231. }
  232. _ = acl.Chmod(authTokenFile, 0600)
  233. authToken = string(atb[:])
  234. } else {
  235. authToken = strings.TrimSpace(string(authTokenB))
  236. }
  237. smux := http.NewServeMux()
  238. smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) {
  239. defer func() {
  240. e := recover()
  241. if e != nil {
  242. _ = apiSendObj(out, req, http.StatusInternalServerError, nil)
  243. }
  244. }()
  245. if !apiCheckAuth(out, req, authToken) {
  246. return
  247. }
  248. apiSetStandardHeaders(out)
  249. if req.Method == http.MethodGet || req.Method == http.MethodHead {
  250. pathCount := 0
  251. peers := node.Peers()
  252. for _, p := range peers {
  253. pathCount += len(p.Paths)
  254. }
  255. _ = apiSendObj(out, req, http.StatusOK, &APIStatus{
  256. Address: node.Address(),
  257. Clock: TimeMs(),
  258. StartupTime: node.startupTime,
  259. Config: node.LocalConfig(),
  260. Online: node.Online(),
  261. PeerCount: len(peers),
  262. PathCount: pathCount,
  263. Identity: node.Identity(),
  264. InterfaceAddresses: node.InterfaceAddresses(),
  265. MappedExternalAddresses: nil,
  266. Version: fmt.Sprintf("%d.%d.%d", CoreVersionMajor, CoreVersionMinor, CoreVersionRevision),
  267. VersionMajor: CoreVersionMajor,
  268. VersionMinor: CoreVersionMinor,
  269. VersionRevision: CoreVersionRevision,
  270. VersionBuild: CoreVersionBuild,
  271. OS: runtime.GOOS,
  272. Architecture: runtime.GOARCH,
  273. Concurrency: runtime.NumCPU(),
  274. Runtime: runtime.Version(),
  275. })
  276. } else {
  277. out.Header().Set("Allow", "GET, HEAD")
  278. _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"/status is read-only"})
  279. }
  280. })
  281. smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) {
  282. defer func() {
  283. e := recover()
  284. if e != nil {
  285. _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"})
  286. }
  287. }()
  288. if !apiCheckAuth(out, req, authToken) {
  289. return
  290. }
  291. apiSetStandardHeaders(out)
  292. if req.Method == http.MethodPost || req.Method == http.MethodPut {
  293. var c LocalConfig
  294. if apiReadObj(out, req, &c) == nil {
  295. _, err := node.SetLocalConfig(&c)
  296. if err != nil {
  297. _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"error applying local config: " + err.Error()})
  298. } else {
  299. lc := node.LocalConfig()
  300. _ = apiSendObj(out, req, http.StatusOK, &lc)
  301. }
  302. }
  303. } else if req.Method == http.MethodGet || req.Method == http.MethodHead {
  304. _ = apiSendObj(out, req, http.StatusOK, node.LocalConfig())
  305. } else {
  306. out.Header().Set("Allow", "GET, HEAD, PUT, POST")
  307. _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method})
  308. }
  309. })
  310. smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) {
  311. var err error
  312. defer func() {
  313. e := recover()
  314. if e != nil {
  315. _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"})
  316. }
  317. }()
  318. if !apiCheckAuth(out, req, authToken) {
  319. return
  320. }
  321. apiSetStandardHeaders(out)
  322. var queriedStr string
  323. var queriedID Address
  324. var queriedFP *Fingerprint
  325. if len(req.URL.Path) > 6 {
  326. queriedStr = req.URL.Path[6:]
  327. if len(queriedStr) == AddressStringLength {
  328. queriedID, err = NewAddressFromString(queriedStr)
  329. if err != nil {
  330. _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"})
  331. return
  332. }
  333. } else {
  334. queriedFP, err = NewFingerprintFromString(queriedStr)
  335. if err != nil {
  336. _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"})
  337. return
  338. }
  339. }
  340. }
  341. var peer *Peer
  342. peers := node.Peers()
  343. if queriedFP != nil {
  344. for _, p := range peers {
  345. if p.Fingerprint.Equals(queriedFP) {
  346. peer = p
  347. break
  348. }
  349. }
  350. } else if queriedID != 0 {
  351. for _, p := range peers {
  352. if p.Address == queriedID {
  353. peer = p
  354. break
  355. }
  356. }
  357. }
  358. if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodPost || req.Method == http.MethodPut {
  359. if peer != nil {
  360. _ = apiSendObj(out, req, http.StatusOK, peer)
  361. } else if len(queriedStr) > 0 {
  362. _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"})
  363. } else {
  364. _ = apiSendObj(out, req, http.StatusOK, peers)
  365. }
  366. } else {
  367. out.Header().Set("Allow", "GET, HEAD")
  368. _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method"})
  369. }
  370. })
  371. smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) {
  372. defer func() {
  373. e := recover()
  374. if e != nil {
  375. _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"})
  376. }
  377. }()
  378. if !apiCheckAuth(out, req, authToken) {
  379. return
  380. }
  381. apiSetStandardHeaders(out)
  382. var queriedID NetworkID
  383. if len(req.URL.Path) > 9 {
  384. var err error
  385. queriedID, err = NewNetworkIDFromString(req.URL.Path[9:])
  386. if err != nil {
  387. _ = apiSendObj(out, req, http.StatusNotFound, nil)
  388. return
  389. }
  390. }
  391. if req.Method == http.MethodDelete {
  392. if queriedID == 0 {
  393. _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only specific networks can be deleted"})
  394. return
  395. }
  396. networks := node.Networks()
  397. for _, nw := range networks {
  398. if nw.id == queriedID {
  399. _ = node.Leave(queriedID)
  400. _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(nw))
  401. return
  402. }
  403. }
  404. _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
  405. } else if req.Method == http.MethodPost || req.Method == http.MethodPut {
  406. if queriedID == 0 {
  407. _ = apiSendObj(out, req, http.StatusBadRequest, nil)
  408. return
  409. }
  410. var nw APINetwork
  411. if apiReadObj(out, req, &nw) == nil {
  412. n := node.Network(nw.ID)
  413. if n == nil {
  414. if nw.ControllerFingerprint != nil && nw.ControllerFingerprint.Address != nw.ID.Controller() {
  415. _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"fingerprint's address does not match what should be the controller's address"})
  416. } else {
  417. n, err := node.Join(nw.ID, nw.ControllerFingerprint, nw.Settings, nil)
  418. if err != nil {
  419. _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only individual networks can be added or modified with POST/PUT"})
  420. } else {
  421. _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
  422. }
  423. }
  424. } else {
  425. if nw.Settings != nil {
  426. n.SetLocalSettings(nw.Settings)
  427. }
  428. _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
  429. }
  430. }
  431. } else if req.Method == http.MethodGet || req.Method == http.MethodHead {
  432. networks := node.Networks()
  433. if queriedID == 0 { // no queried ID lists all networks
  434. nws := make([]*APINetwork, 0, len(networks))
  435. for _, nw := range networks {
  436. nws = append(nws, apiNetworkFromNetwork(nw))
  437. }
  438. _ = apiSendObj(out, req, http.StatusOK, nws)
  439. return
  440. }
  441. for _, nw := range networks {
  442. if nw.ID() == queriedID {
  443. _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(nw))
  444. return
  445. }
  446. }
  447. _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
  448. } else {
  449. out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
  450. _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method})
  451. }
  452. })
  453. listener, err := createNamedSocketListener(basePath, APISocketName)
  454. if err != nil {
  455. return nil, nil, err
  456. }
  457. httpServer := &http.Server{
  458. MaxHeaderBytes: 4096,
  459. Handler: smux,
  460. IdleTimeout: 10 * time.Second,
  461. ReadTimeout: 10 * time.Second,
  462. WriteTimeout: 600 * time.Second,
  463. }
  464. httpServer.SetKeepAlivesEnabled(true)
  465. go func() {
  466. _ = httpServer.Serve(listener)
  467. _ = listener.Close()
  468. }()
  469. var tcpHttpServer *http.Server
  470. tcpBindAddr := node.LocalConfig().Settings.APITCPBindAddress
  471. if tcpBindAddr != nil {
  472. tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{
  473. IP: tcpBindAddr.IP,
  474. Port: tcpBindAddr.Port,
  475. })
  476. if err != nil {
  477. node.infoLog.Printf("ERROR: unable to start API HTTP server at TCP bind address %s: %s (named socket listener startd, continuing anyway)", tcpBindAddr.String(), err.Error())
  478. } else {
  479. tcpHttpServer = &http.Server{
  480. MaxHeaderBytes: 4096,
  481. Handler: smux,
  482. IdleTimeout: 10 * time.Second,
  483. ReadTimeout: 10 * time.Second,
  484. WriteTimeout: 600 * time.Second,
  485. }
  486. tcpHttpServer.SetKeepAlivesEnabled(true)
  487. go func() {
  488. _ = tcpHttpServer.Serve(tcpListener)
  489. _ = tcpListener.Close()
  490. }()
  491. }
  492. }
  493. return httpServer, tcpHttpServer, nil
  494. }