dataproxy.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package api
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "net/http"
  6. "net/http/httputil"
  7. "net/url"
  8. "time"
  9. "github.com/grafana/grafana/pkg/api/cloudwatch"
  10. "github.com/grafana/grafana/pkg/bus"
  11. "github.com/grafana/grafana/pkg/log"
  12. "github.com/grafana/grafana/pkg/metrics"
  13. "github.com/grafana/grafana/pkg/middleware"
  14. m "github.com/grafana/grafana/pkg/models"
  15. "github.com/grafana/grafana/pkg/setting"
  16. "github.com/grafana/grafana/pkg/util"
  17. )
  18. func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
  19. director := func(req *http.Request) {
  20. req.URL.Scheme = targetUrl.Scheme
  21. req.URL.Host = targetUrl.Host
  22. req.Host = targetUrl.Host
  23. reqQueryVals := req.URL.Query()
  24. if ds.Type == m.DS_INFLUXDB_08 {
  25. req.URL.Path = util.JoinUrlFragments(targetUrl.Path, "db/"+ds.Database+"/"+proxyPath)
  26. reqQueryVals.Add("u", ds.User)
  27. reqQueryVals.Add("p", ds.Password)
  28. req.URL.RawQuery = reqQueryVals.Encode()
  29. } else if ds.Type == m.DS_INFLUXDB {
  30. req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
  31. req.URL.RawQuery = reqQueryVals.Encode()
  32. if !ds.BasicAuth {
  33. req.Header.Del("Authorization")
  34. req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.User, ds.Password))
  35. }
  36. } else {
  37. req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
  38. }
  39. if ds.BasicAuth {
  40. req.Header.Del("Authorization")
  41. req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword))
  42. }
  43. dsAuth := req.Header.Get("X-DS-Authorization")
  44. if len(dsAuth) > 0 {
  45. req.Header.Del("X-DS-Authorization")
  46. req.Header.Del("Authorization")
  47. req.Header.Add("Authorization", dsAuth)
  48. }
  49. // clear cookie headers
  50. req.Header.Del("Cookie")
  51. req.Header.Del("Set-Cookie")
  52. }
  53. return &httputil.ReverseProxy{Director: director, FlushInterval: time.Millisecond * 200}
  54. }
  55. func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
  56. query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
  57. if err := bus.Dispatch(&query); err != nil {
  58. return nil, err
  59. }
  60. return query.Result, nil
  61. }
  62. func ProxyDataSourceRequest(c *middleware.Context) {
  63. c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
  64. ds, err := getDatasource(c.ParamsInt64(":id"), c.OrgId)
  65. if err != nil {
  66. c.JsonApiErr(500, "Unable to load datasource meta data", err)
  67. return
  68. }
  69. if ds.Type == m.DS_CLOUDWATCH {
  70. cloudwatch.HandleRequest(c, ds)
  71. return
  72. }
  73. if ds.Type == m.DS_INFLUXDB {
  74. if c.Query("db") != ds.Database {
  75. c.JsonApiErr(403, "Datasource is not configured to allow this database", nil)
  76. return
  77. }
  78. }
  79. targetUrl, _ := url.Parse(ds.Url)
  80. if len(setting.DataProxyWhiteList) > 0 {
  81. if _, exists := setting.DataProxyWhiteList[targetUrl.Host]; !exists {
  82. c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
  83. return
  84. }
  85. }
  86. proxyPath := c.Params("*")
  87. if ds.Type == m.DS_ES {
  88. if c.Req.Request.Method == "DELETE" {
  89. c.JsonApiErr(403, "Deletes not allowed on proxied Elasticsearch datasource", nil)
  90. return
  91. }
  92. if c.Req.Request.Method == "PUT" {
  93. c.JsonApiErr(403, "Puts not allowed on proxied Elasticsearch datasource", nil)
  94. return
  95. }
  96. if c.Req.Request.Method == "POST" && proxyPath != "_msearch" {
  97. c.JsonApiErr(403, "Posts not allowed on proxied Elasticsearch datasource except on /_msearch", nil)
  98. return
  99. }
  100. }
  101. outputToAuditLog(ds.Type, c)
  102. proxy := NewReverseProxy(ds, proxyPath, targetUrl)
  103. proxy.Transport, err = ds.GetHttpTransport()
  104. if err != nil {
  105. c.JsonApiErr(400, "Unable to load TLS certificate", err)
  106. return
  107. }
  108. proxy.ServeHTTP(c.Resp, c.Req.Request)
  109. c.Resp.Header().Del("Set-Cookie")
  110. }
  111. func outputToAuditLog(dataSourceType string, c *middleware.Context) {
  112. auditLogger := log.New("data-proxy-audit", "userid", c.UserId, "orgid", c.OrgId, "username", c.Login)
  113. var body string
  114. if c.Req.Request.Body != nil {
  115. buffer, _ := ioutil.ReadAll(c.Req.Request.Body)
  116. c.Req.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buffer))
  117. body = string(buffer)
  118. }
  119. auditLogger.Info("Proxying incoming request", "datasource", dataSourceType, "uri", c.Req.RequestURI, "method", c.Req.Request.Method, "body", body)
  120. }