The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/middleware/auth_proxy.go

174 lines
4.7 KiB

package middleware
import (
"fmt"
"net"
"net/mail"
"strings"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
)
var AUTH_PROXY_SESSION_VAR = "authProxyHeaderValue"
func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
if !setting.AuthProxyEnabled {
return false
}
proxyHeaderValue := ctx.Req.Header.Get(setting.AuthProxyHeaderName)
if len(proxyHeaderValue) == 0 {
return false
}
// if auth proxy ip(s) defined, check if request comes from one of those
if err := checkAuthenticationProxy(ctx.Req.RemoteAddr, proxyHeaderValue); err != nil {
ctx.Handle(407, "Proxy authentication required", err)
return true
}
// initialize session
if err := ctx.Session.Start(ctx.Context); err != nil {
log.Error(3, "Failed to start session", err)
return false
}
query := &m.GetSignedInUserQuery{OrgId: orgID}
// if this session has already been authenticated by authProxy just load the user
sessProxyValue := ctx.Session.Get(AUTH_PROXY_SESSION_VAR)
if sessProxyValue != nil && sessProxyValue.(string) == proxyHeaderValue && getRequestUserId(ctx) > 0 {
query.UserId = getRequestUserId(ctx)
if err := bus.Dispatch(query); err != nil {
ctx.Handle(500, "Failed to find user", err)
return true
}
} else {
extUser := &m.ExternalUserInfo{
AuthModule: "authproxy",
AuthId: proxyHeaderValue,
}
if setting.AuthProxyHeaderProperty == "username" {
extUser.Login = proxyHeaderValue
// only set Email if it can be parsed as an email address
emailAddr, emailErr := mail.ParseAddress(proxyHeaderValue)
if emailErr == nil {
extUser.Email = emailAddr.Address
}
} else if setting.AuthProxyHeaderProperty == "email" {
extUser.Email = proxyHeaderValue
extUser.Login = proxyHeaderValue
} else {
ctx.Handle(500, "Auth proxy header property invalid", nil)
return true
}
// add/update user in grafana
cmd := &m.UpsertUserCommand{
ReqContext: ctx,
ExternalUser: extUser,
SignupAllowed: setting.AuthProxyAutoSignUp,
}
err := bus.Dispatch(cmd)
if err != nil {
ctx.Handle(500, "Failed to login as user specified in auth proxy header", err)
return true
}
query.UserId = cmd.Result.Id
if err := bus.Dispatch(query); err != nil {
ctx.Handle(500, "Failed to find user", err)
return true
}
// Make sure that we cannot share a session between different users!
if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId {
// remove session
if err := ctx.Session.Destory(ctx.Context); err != nil {
log.Error(3, "Failed to destroy session, err")
}
// initialize a new session
if err := ctx.Session.Start(ctx.Context); err != nil {
log.Error(3, "Failed to start session", err)
}
}
ctx.Session.Set(AUTH_PROXY_SESSION_VAR, proxyHeaderValue)
}
// When ldap is enabled, sync userinfo and org roles
if err := syncGrafanaUserWithLdapUser(ctx, query); err != nil {
if err == login.ErrInvalidCredentials {
ctx.Handle(500, "Unable to authenticate user", err)
return false
}
ctx.Handle(500, "Failed to sync user", err)
return false
}
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
ctx.Session.Set(session.SESS_KEY_USERID, ctx.UserId)
return true
}
var syncGrafanaUserWithLdapUser = func(ctx *m.ReqContext, query *m.GetSignedInUserQuery) error {
if !setting.LdapEnabled {
return nil
}
expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix()
var lastLdapSync int64
if lastLdapSyncInSession := ctx.Session.Get(session.SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
lastLdapSync = lastLdapSyncInSession.(int64)
}
if lastLdapSync < expireEpoch {
ldapCfg := login.LdapCfg
for _, server := range ldapCfg.Servers {
author := login.NewLdapAuthenticator(server)
if err := author.SyncSignedInUser(ctx, query.Result); err != nil {
return err
}
}
ctx.Session.Set(session.SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
}
return nil
}
func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error {
if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 {
return nil
}
proxies := strings.Split(setting.AuthProxyWhitelist, ",")
sourceIP, _, err := net.SplitHostPort(remoteAddr)
if err != nil {
return err
}
// Compare allowed IP addresses to actual address
for _, proxyIP := range proxies {
if sourceIP == strings.TrimSpace(proxyIP) {
return nil
}
}
return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, sourceIP)
}