auth_proxy.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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/infra/remotecache"
  11. "github.com/grafana/grafana/pkg/login"
  12. m "github.com/grafana/grafana/pkg/models"
  13. "github.com/grafana/grafana/pkg/setting"
  14. )
  15. const (
  16. // cachePrefix is a prefix for the cache key
  17. cachePrefix = "auth-proxy-sync-ttl:%s"
  18. )
  19. func initContextWithAuthProxy(store *remotecache.RemoteCache, 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. query := &m.GetSignedInUserQuery{OrgId: orgID}
  33. cacheKey := fmt.Sprintf(cachePrefix, proxyHeaderValue)
  34. userID, err := store.Get(cacheKey)
  35. inCache := err == nil
  36. // load the user if we have them
  37. if inCache {
  38. query.UserId = userID.(int64)
  39. // if we're using ldap, pass authproxy login name to ldap user sync
  40. } else if setting.LdapEnabled {
  41. syncQuery := &m.LoginUserQuery{
  42. ReqContext: ctx,
  43. Username: proxyHeaderValue,
  44. }
  45. if err := syncGrafanaUserWithLdapUser(syncQuery); err != nil {
  46. if err == login.ErrInvalidCredentials {
  47. ctx.Handle(500, "Unable to authenticate user", err)
  48. return false
  49. }
  50. }
  51. if syncQuery.User == nil {
  52. ctx.Handle(500, "Failed to sync user", nil)
  53. return false
  54. }
  55. query.UserId = syncQuery.User.Id
  56. // no ldap, just use the info we have
  57. } else {
  58. extUser := &m.ExternalUserInfo{
  59. AuthModule: "authproxy",
  60. AuthId: proxyHeaderValue,
  61. }
  62. if setting.AuthProxyHeaderProperty == "username" {
  63. extUser.Login = proxyHeaderValue
  64. // only set Email if it can be parsed as an email address
  65. emailAddr, emailErr := mail.ParseAddress(proxyHeaderValue)
  66. if emailErr == nil {
  67. extUser.Email = emailAddr.Address
  68. }
  69. } else if setting.AuthProxyHeaderProperty == "email" {
  70. extUser.Email = proxyHeaderValue
  71. extUser.Login = proxyHeaderValue
  72. } else {
  73. ctx.Handle(500, "Auth proxy header property invalid", nil)
  74. return true
  75. }
  76. for _, field := range []string{"Name", "Email", "Login"} {
  77. if setting.AuthProxyHeaders[field] == "" {
  78. continue
  79. }
  80. if val := ctx.Req.Header.Get(setting.AuthProxyHeaders[field]); val != "" {
  81. reflect.ValueOf(extUser).Elem().FieldByName(field).SetString(val)
  82. }
  83. }
  84. // add/update user in grafana
  85. cmd := &m.UpsertUserCommand{
  86. ReqContext: ctx,
  87. ExternalUser: extUser,
  88. SignupAllowed: setting.AuthProxyAutoSignUp,
  89. }
  90. err := bus.Dispatch(cmd)
  91. if err != nil {
  92. ctx.Handle(500, "Failed to login as user specified in auth proxy header", err)
  93. return true
  94. }
  95. query.UserId = cmd.Result.Id
  96. }
  97. if err := bus.Dispatch(query); err != nil {
  98. ctx.Handle(500, "Failed to find user", err)
  99. return true
  100. }
  101. ctx.SignedInUser = query.Result
  102. ctx.IsSignedIn = true
  103. expiration := time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute
  104. value := query.UserId
  105. // This <if> is here to make sure we do not
  106. // rewrite the expiration all the time
  107. if inCache == false {
  108. if err = store.Set(cacheKey, value, expiration); err != nil {
  109. ctx.Handle(500, "Couldn't write a user in cache key", err)
  110. return true
  111. }
  112. }
  113. return true
  114. }
  115. var syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
  116. ldapCfg := login.LdapCfg
  117. if len(ldapCfg.Servers) < 1 {
  118. return fmt.Errorf("No LDAP servers available")
  119. }
  120. for _, server := range ldapCfg.Servers {
  121. author := login.NewLdapAuthenticator(server)
  122. if err := author.SyncUser(query); err != nil {
  123. return err
  124. }
  125. }
  126. return nil
  127. }
  128. func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error {
  129. if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 {
  130. return nil
  131. }
  132. proxies := strings.Split(setting.AuthProxyWhitelist, ",")
  133. var proxyObjs []*net.IPNet
  134. for _, proxy := range proxies {
  135. proxyObjs = append(proxyObjs, coerceProxyAddress(proxy))
  136. }
  137. sourceIP, _, _ := net.SplitHostPort(remoteAddr)
  138. sourceObj := net.ParseIP(sourceIP)
  139. for _, proxyObj := range proxyObjs {
  140. if proxyObj.Contains(sourceObj) {
  141. return nil
  142. }
  143. }
  144. return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, sourceIP)
  145. }
  146. func coerceProxyAddress(proxyAddr string) *net.IPNet {
  147. proxyAddr = strings.TrimSpace(proxyAddr)
  148. if !strings.Contains(proxyAddr, "/") {
  149. proxyAddr = strings.Join([]string{proxyAddr, "32"}, "/")
  150. }
  151. _, network, err := net.ParseCIDR(proxyAddr)
  152. if err != nil {
  153. fmt.Println(err)
  154. }
  155. return network
  156. }