api.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. "strings"
  24. "time"
  25. acl "github.com/hectane/go-acl"
  26. )
  27. // APISocketName is the default socket name for accessing the API
  28. const APISocketName = "apisocket"
  29. // APIGet makes a query to the API via a Unix domain or windows pipe socket
  30. func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, error) {
  31. client, err := createNamedSocketHTTPClient(basePath, socketName)
  32. if err != nil {
  33. return http.StatusTeapot, err
  34. }
  35. req, err := http.NewRequest("GET", "http://socket"+queryPath, nil)
  36. if err != nil {
  37. return http.StatusTeapot, err
  38. }
  39. req.Header.Add("Authorization", "bearer "+authToken)
  40. resp, err := client.Do(req)
  41. if err != nil {
  42. return http.StatusTeapot, err
  43. }
  44. err = json.NewDecoder(resp.Body).Decode(obj)
  45. return resp.StatusCode, err
  46. }
  47. // APIPost posts a JSON object to the API via a Unix domain or windows pipe socket and reads a response
  48. func APIPost(basePath, socketName, authToken, queryPath string, post, result interface{}) (int, error) {
  49. client, err := createNamedSocketHTTPClient(basePath, socketName)
  50. if err != nil {
  51. return http.StatusTeapot, err
  52. }
  53. var data []byte
  54. if post != nil {
  55. data, err = json.Marshal(post)
  56. if err != nil {
  57. return http.StatusTeapot, err
  58. }
  59. } else {
  60. data = []byte("null")
  61. }
  62. req, err := http.NewRequest("POST", "http://socket"+queryPath, bytes.NewReader(data))
  63. if err != nil {
  64. return http.StatusTeapot, err
  65. }
  66. req.Header.Add("Authorization", "bearer "+authToken)
  67. resp, err := client.Do(req)
  68. if err != nil {
  69. return http.StatusTeapot, err
  70. }
  71. err = json.NewDecoder(resp.Body).Decode(result)
  72. return resp.StatusCode, err
  73. }
  74. // APIStatus is the object returned by API status inquiries
  75. type APIStatus struct {
  76. Address Address
  77. Clock int64
  78. Config LocalConfig
  79. Online bool
  80. Identity *Identity
  81. InterfaceAddresses []net.IP
  82. MappedExternalAddresses []*InetAddress
  83. Version string
  84. VersionMajor int
  85. VersionMinor int
  86. VersionRevision int
  87. VersionBuild int
  88. }
  89. // APINetwork is the object returned by API network inquiries
  90. type APINetwork struct {
  91. Config *NetworkConfig
  92. Settings *NetworkLocalSettings
  93. MulticastSubscriptions []*MulticastGroup
  94. TapDeviceType string
  95. TapDeviceName string
  96. TapDeviceEnabled bool
  97. }
  98. func apiSetStandardHeaders(out http.ResponseWriter) {
  99. now := time.Now().UTC()
  100. h := out.Header()
  101. h.Set("Cache-Control", "no-cache, no-store, must-revalidate")
  102. h.Set("Expires", "0")
  103. h.Set("Pragma", "no-cache")
  104. h.Set("Date", now.Format(time.RFC1123))
  105. }
  106. func apiSendObj(out http.ResponseWriter, req *http.Request, httpStatusCode int, obj interface{}) error {
  107. h := out.Header()
  108. h.Set("Content-Type", "application/json")
  109. if req.Method == http.MethodHead {
  110. out.WriteHeader(httpStatusCode)
  111. return nil
  112. }
  113. var j []byte
  114. var err error
  115. if obj != nil {
  116. j, err = json.Marshal(obj)
  117. if err != nil {
  118. return err
  119. }
  120. }
  121. out.WriteHeader(httpStatusCode)
  122. _, err = out.Write(j)
  123. return err
  124. }
  125. func apiReadObj(out http.ResponseWriter, req *http.Request, dest interface{}) (err error) {
  126. err = json.NewDecoder(req.Body).Decode(&dest)
  127. if err != nil {
  128. apiSendObj(out, req, http.StatusBadRequest, nil)
  129. }
  130. return
  131. }
  132. func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool {
  133. ah := req.Header.Get("Authorization")
  134. if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
  135. return true
  136. }
  137. ah = req.Header.Get("X-ZT1-Auth")
  138. if len(ah) > 0 && strings.TrimSpace(ah) == token {
  139. return true
  140. }
  141. apiSendObj(out, req, http.StatusUnauthorized, nil)
  142. return false
  143. }
  144. // createAPIServer creates and starts an HTTP server for a given node
  145. func createAPIServer(basePath string, node *Node) (*http.Server, error) {
  146. // Read authorization token, automatically generating one if it's missing
  147. var authToken string
  148. authTokenFile := path.Join(basePath, "authtoken.secret")
  149. authTokenB, err := ioutil.ReadFile(authTokenFile)
  150. if err != nil {
  151. var atb [20]byte
  152. _, err = secrand.Read(atb[:])
  153. if err != nil {
  154. return nil, err
  155. }
  156. for i := 0; i < 20; i++ {
  157. atb[i] = byte("abcdefghijklmnopqrstuvwxyz0123456789"[atb[i]%36])
  158. }
  159. err = ioutil.WriteFile(authTokenFile, atb[:], 0600)
  160. if err != nil {
  161. return nil, err
  162. }
  163. acl.Chmod(authTokenFile, 0600)
  164. authToken = string(atb[:])
  165. } else {
  166. authToken = strings.TrimSpace(string(authTokenB))
  167. }
  168. smux := http.NewServeMux()
  169. smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) {
  170. if !apiCheckAuth(out, req, authToken) {
  171. return
  172. }
  173. apiSetStandardHeaders(out)
  174. if req.Method == http.MethodGet || req.Method == http.MethodHead {
  175. apiSendObj(out, req, http.StatusOK, &APIStatus{
  176. Address: node.Address(),
  177. Clock: TimeMs(),
  178. Config: node.LocalConfig(),
  179. Online: node.Online(),
  180. Identity: node.Identity(),
  181. InterfaceAddresses: node.InterfaceAddresses(),
  182. MappedExternalAddresses: nil,
  183. Version: fmt.Sprintf("%d.%d.%d", CoreVersionMajor, CoreVersionMinor, CoreVersionRevision),
  184. VersionMajor: CoreVersionMajor,
  185. VersionMinor: CoreVersionMinor,
  186. VersionRevision: CoreVersionRevision,
  187. VersionBuild: CoreVersionBuild,
  188. })
  189. } else {
  190. out.Header().Set("Allow", "GET, HEAD")
  191. apiSendObj(out, req, http.StatusMethodNotAllowed, nil)
  192. }
  193. })
  194. smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) {
  195. if !apiCheckAuth(out, req, authToken) {
  196. return
  197. }
  198. apiSetStandardHeaders(out)
  199. if req.Method == http.MethodPost || req.Method == http.MethodPut {
  200. var c LocalConfig
  201. if apiReadObj(out, req, &c) == nil {
  202. apiSendObj(out, req, http.StatusOK, node.LocalConfig())
  203. }
  204. } else if req.Method == http.MethodGet || req.Method == http.MethodHead {
  205. apiSendObj(out, req, http.StatusOK, node.LocalConfig())
  206. } else {
  207. out.Header().Set("Allow", "GET, HEAD, PUT, POST")
  208. apiSendObj(out, req, http.StatusMethodNotAllowed, nil)
  209. }
  210. })
  211. smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) {
  212. if !apiCheckAuth(out, req, authToken) {
  213. return
  214. }
  215. apiSetStandardHeaders(out)
  216. var queriedID Address
  217. if len(req.URL.Path) > 6 {
  218. var err error
  219. queriedID, err = NewAddressFromString(req.URL.Path[6:])
  220. if err != nil {
  221. apiSendObj(out, req, http.StatusNotFound, nil)
  222. return
  223. }
  224. }
  225. if req.Method == http.MethodGet || req.Method == http.MethodHead {
  226. peers := node.Peers()
  227. if queriedID != 0 {
  228. p2 := make([]*Peer, 0, len(peers))
  229. for _, p := range peers {
  230. if p.Address == queriedID {
  231. p2 = append(p2, p)
  232. }
  233. }
  234. apiSendObj(out, req, http.StatusOK, p2)
  235. } else {
  236. apiSendObj(out, req, http.StatusOK, peers)
  237. }
  238. } else {
  239. out.Header().Set("Allow", "GET, HEAD")
  240. apiSendObj(out, req, http.StatusMethodNotAllowed, nil)
  241. }
  242. })
  243. smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) {
  244. if !apiCheckAuth(out, req, authToken) {
  245. return
  246. }
  247. apiSetStandardHeaders(out)
  248. var queriedID NetworkID
  249. if len(req.URL.Path) > 9 {
  250. var err error
  251. queriedID, err = NewNetworkIDFromString(req.URL.Path[9:])
  252. if err != nil {
  253. apiSendObj(out, req, http.StatusNotFound, nil)
  254. return
  255. }
  256. }
  257. if req.Method == http.MethodPost || req.Method == http.MethodPut {
  258. if queriedID == 0 {
  259. apiSendObj(out, req, http.StatusBadRequest, nil)
  260. }
  261. } else if req.Method == http.MethodGet || req.Method == http.MethodHead {
  262. if queriedID == 0 { // no queried ID lists all networks
  263. networks := node.Networks()
  264. apiSendObj(out, req, http.StatusOK, networks)
  265. } else {
  266. }
  267. } else {
  268. out.Header().Set("Allow", "GET, HEAD, PUT, POST")
  269. apiSendObj(out, req, http.StatusMethodNotAllowed, nil)
  270. }
  271. })
  272. smux.HandleFunc("/root/", func(out http.ResponseWriter, req *http.Request) {
  273. if !apiCheckAuth(out, req, authToken) {
  274. return
  275. }
  276. apiSetStandardHeaders(out)
  277. var queriedID Address
  278. if len(req.URL.Path) > 6 {
  279. var err error
  280. queriedID, err = NewAddressFromString(req.URL.Path[6:])
  281. if err != nil {
  282. apiSendObj(out, req, http.StatusNotFound, nil)
  283. return
  284. }
  285. }
  286. if req.Method == http.MethodPost || req.Method == http.MethodPut {
  287. if queriedID == 0 {
  288. apiSendObj(out, req, http.StatusBadRequest, nil)
  289. }
  290. } else if req.Method == http.MethodGet || req.Method == http.MethodHead {
  291. roots := node.Roots()
  292. apiSendObj(out, req, http.StatusOK, roots)
  293. } else {
  294. out.Header().Set("Allow", "GET, HEAD, PUT, POST")
  295. apiSendObj(out, req, http.StatusMethodNotAllowed, nil)
  296. }
  297. })
  298. listener, err := createNamedSocketListener(basePath, APISocketName)
  299. if err != nil {
  300. return nil, err
  301. }
  302. httpServer := &http.Server{
  303. MaxHeaderBytes: 4096,
  304. Handler: smux,
  305. IdleTimeout: 10 * time.Second,
  306. ReadTimeout: 10 * time.Second,
  307. WriteTimeout: 600 * time.Second,
  308. }
  309. httpServer.SetKeepAlivesEnabled(true)
  310. go func() {
  311. httpServer.Serve(listener)
  312. listener.Close()
  313. }()
  314. return httpServer, nil
  315. }