auth_proxy.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package middleware
  2. import (
  3. "fmt"
  4. "net"
  5. "net/mail"
  6. "reflect"
  7. "strings"
  8. "time"
  9. "github.com/grafana/grafana/pkg/bus"
  10. "github.com/grafana/grafana/pkg/log"
  11. "github.com/grafana/grafana/pkg/login"
  12. m "github.com/grafana/grafana/pkg/models"
  13. "github.com/grafana/grafana/pkg/services/session"
  14. "github.com/grafana/grafana/pkg/setting"
  15. )
  16. var (
  17. AUTH_PROXY_SESSION_VAR = "authProxyHeaderValue"
  18. )
  19. func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
  20. if !setting.AuthProxyEnabled {
  21. return false
  22. }
  23. proxyHeaderValue := ctx.Req.Header.Get(setting.AuthProxyHeaderName)
  24. if len(proxyHeaderValue) == 0 {
  25. return false
  26. }
  27. // if auth proxy ip(s) defined, check if request comes from one of those
  28. if err := checkAuthenticationProxy(ctx.Req.RemoteAddr, proxyHeaderValue); err != nil {
  29. ctx.Handle(407, "Proxy authentication required", err)
  30. return true
  31. }
  32. // initialize session
  33. if err := ctx.Session.Start(ctx.Context); err != nil {
  34. log.Error(3, "Failed to start session. error %v", err)
  35. return false
  36. }
  37. defer func() {
  38. if err := ctx.Session.Release(); err != nil {
  39. ctx.Logger.Error("failed to save session data", "error", err)
  40. }
  41. }()
  42. query := &m.GetSignedInUserQuery{OrgId: orgID}
  43. // if this session has already been authenticated by authProxy just load the user
  44. sessProxyValue := ctx.Session.Get(AUTH_PROXY_SESSION_VAR)
  45. if sessProxyValue != nil && sessProxyValue.(string) == proxyHeaderValue && getRequestUserId(ctx) > 0 {
  46. // if we're using ldap, sync user periodically
  47. if setting.LdapEnabled {
  48. syncQuery := &m.LoginUserQuery{
  49. ReqContext: ctx,
  50. Username: proxyHeaderValue,
  51. }
  52. if err := syncGrafanaUserWithLdapUser(syncQuery); err != nil {
  53. if err == login.ErrInvalidCredentials {
  54. ctx.Handle(500, "Unable to authenticate user", err)
  55. return false
  56. }
  57. ctx.Handle(500, "Failed to sync user", err)
  58. return false
  59. }
  60. }
  61. query.UserId = getRequestUserId(ctx)
  62. // if we're using ldap, pass authproxy login name to ldap user sync
  63. } else if setting.LdapEnabled {
  64. ctx.Session.Delete(session.SESS_KEY_LASTLDAPSYNC)
  65. syncQuery := &m.LoginUserQuery{
  66. ReqContext: ctx,
  67. Username: proxyHeaderValue,
  68. }
  69. if err := syncGrafanaUserWithLdapUser(syncQuery); err != nil {
  70. if err == login.ErrInvalidCredentials {
  71. ctx.Handle(500, "Unable to authenticate user", err)
  72. return false
  73. }
  74. ctx.Handle(500, "Failed to sync user", err)
  75. return false
  76. }
  77. if syncQuery.User == nil {
  78. ctx.Handle(500, "Failed to sync user", nil)
  79. return false
  80. }
  81. query.UserId = syncQuery.User.Id
  82. // no ldap, just use the info we have
  83. } else {
  84. extUser := &m.ExternalUserInfo{
  85. AuthModule: "authproxy",
  86. AuthId: proxyHeaderValue,
  87. }
  88. if setting.AuthProxyHeaderProperty == "username" {
  89. extUser.Login = proxyHeaderValue
  90. // only set Email if it can be parsed as an email address
  91. emailAddr, emailErr := mail.ParseAddress(proxyHeaderValue)
  92. if emailErr == nil {
  93. extUser.Email = emailAddr.Address
  94. }
  95. } else if setting.AuthProxyHeaderProperty == "email" {
  96. extUser.Email = proxyHeaderValue
  97. extUser.Login = proxyHeaderValue
  98. } else {
  99. ctx.Handle(500, "Auth proxy header property invalid", nil)
  100. return true
  101. }
  102. for _, field := range []string{"Name", "Email", "Login"} {
  103. if setting.AuthProxyHeaders[field] == "" {
  104. continue
  105. }
  106. if val := ctx.Req.Header.Get(setting.AuthProxyHeaders[field]); val != "" {
  107. reflect.ValueOf(extUser).Elem().FieldByName(field).SetString(val)
  108. }
  109. }
  110. // add/update user in grafana
  111. cmd := &m.UpsertUserCommand{
  112. ReqContext: ctx,
  113. ExternalUser: extUser,
  114. SignupAllowed: setting.AuthProxyAutoSignUp,
  115. }
  116. err := bus.Dispatch(cmd)
  117. if err != nil {
  118. ctx.Handle(500, "Failed to login as user specified in auth proxy header", err)
  119. return true
  120. }
  121. query.UserId = cmd.Result.Id
  122. }
  123. if err := bus.Dispatch(query); err != nil {
  124. ctx.Handle(500, "Failed to find user", err)
  125. return true
  126. }
  127. // Make sure that we cannot share a session between different users!
  128. if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId {
  129. // remove session
  130. if err := ctx.Session.Destory(ctx.Context); err != nil {
  131. log.Error(3, "Failed to destroy session. error: %v", err)
  132. }
  133. // initialize a new session
  134. if err := ctx.Session.Start(ctx.Context); err != nil {
  135. log.Error(3, "Failed to start session. error: %v", err)
  136. }
  137. }
  138. ctx.Session.Set(AUTH_PROXY_SESSION_VAR, proxyHeaderValue)
  139. ctx.SignedInUser = query.Result
  140. ctx.IsSignedIn = true
  141. ctx.Session.Set(session.SESS_KEY_USERID, ctx.UserId)
  142. return true
  143. }
  144. var syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
  145. expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix()
  146. var lastLdapSync int64
  147. if lastLdapSyncInSession := query.ReqContext.Session.Get(session.SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
  148. lastLdapSync = lastLdapSyncInSession.(int64)
  149. }
  150. if lastLdapSync < expireEpoch {
  151. ldapCfg := login.LdapCfg
  152. if len(ldapCfg.Servers) < 1 {
  153. return fmt.Errorf("No LDAP servers available")
  154. }
  155. for _, server := range ldapCfg.Servers {
  156. author := login.NewLdapAuthenticator(server)
  157. if err := author.SyncUser(query); err != nil {
  158. return err
  159. }
  160. }
  161. query.ReqContext.Session.Set(session.SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
  162. }
  163. return nil
  164. }
  165. func getRequestUserId(c *m.ReqContext) int64 {
  166. userID := c.Session.Get(session.SESS_KEY_USERID)
  167. if userID != nil {
  168. return userID.(int64)
  169. }
  170. return 0
  171. }
  172. func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error {
  173. if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 {
  174. return nil
  175. }
  176. proxies := strings.Split(setting.AuthProxyWhitelist, ",")
  177. var proxyObjs []*net.IPNet
  178. for _, proxy := range proxies {
  179. proxyObjs = append(proxyObjs, coerceProxyAddress(proxy))
  180. }
  181. sourceIP, _, _ := net.SplitHostPort(remoteAddr)
  182. sourceObj := net.ParseIP(sourceIP)
  183. for _, proxyObj := range proxyObjs {
  184. if proxyObj.Contains(sourceObj) {
  185. return nil
  186. }
  187. }
  188. return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, sourceIP)
  189. }
  190. func coerceProxyAddress(proxyAddr string) *net.IPNet {
  191. proxyAddr = strings.TrimSpace(proxyAddr)
  192. if !strings.Contains(proxyAddr, "/") {
  193. proxyAddr = strings.Join([]string{proxyAddr, "32"}, "/")
  194. }
  195. _, network, err := net.ParseCIDR(proxyAddr)
  196. if err != nil {
  197. fmt.Println(err)
  198. }
  199. return network
  200. }