api.go 17 KB

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