mirror of
https://github.com/grafana/grafana.git
synced 2025-02-03 12:11:09 -06:00
AuthProxy: Can now login with auth proxy and get a login token (#20175)
* AuthProxy: Can now login with auth proxy and get a login token * added unit tests * renamed setting and updated docs * AuthProxy: minor tweak * Fixed tests and namings * spellfix * fix * remove unused setting, probably from merge conflict * fix
This commit is contained in:
parent
818aa8eefa
commit
be2bf1a297
@ -438,6 +438,7 @@ ldap_sync_ttl = 60
|
||||
sync_ttl = 60
|
||||
whitelist =
|
||||
headers =
|
||||
enable_login_token = false
|
||||
|
||||
#################################### Auth LDAP ###########################
|
||||
[auth.ldap]
|
||||
|
@ -399,6 +399,8 @@
|
||||
;ldap_sync_ttl = 60
|
||||
;whitelist = 192.168.1.1, 192.168.2.1
|
||||
;headers = Email:X-User-Email, Name:X-User-Name
|
||||
# Read the auth proxy docs for details on what the setting below enables
|
||||
;enable_login_token = false
|
||||
|
||||
#################################### Basic Auth ##########################
|
||||
[auth.basic]
|
||||
|
42
devenv/docker/blocks/nginx_proxy/nginx_login_only.conf
Normal file
42
devenv/docker/blocks/nginx_proxy/nginx_login_only.conf
Normal file
@ -0,0 +1,42 @@
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
|
||||
server {
|
||||
listen 10080;
|
||||
|
||||
location /grafana/ {
|
||||
################################################################
|
||||
# Enable these settings to test with basic auth and an auth proxy header
|
||||
# the htpasswd file contains an admin user with password admin and
|
||||
# user1: grafana and user2: grafana
|
||||
################################################################
|
||||
|
||||
|
||||
################################################################
|
||||
# To use the auth proxy header, set the following in custom.ini:
|
||||
# [auth.proxy]
|
||||
# enabled = true
|
||||
# header_name = X-WEBAUTH-USER
|
||||
# header_property = username
|
||||
################################################################
|
||||
|
||||
location /grafana/login {
|
||||
auth_basic "Restricted Content";
|
||||
auth_basic_user_file /etc/nginx/htpasswd;
|
||||
proxy_set_header X-WEBAUTH-USER $remote_user;
|
||||
proxy_pass http://localhost:3000/login;
|
||||
}
|
||||
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://localhost:3000/;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,6 +36,8 @@ whitelist =
|
||||
# Optionally define more headers to sync other user attributes
|
||||
# Example `headers = Name:X-WEBAUTH-NAME Email:X-WEBAUTH-EMAIL Groups:X-WEBAUTH-GROUPS`
|
||||
headers =
|
||||
# Checkout docs on this for more details on the below setting
|
||||
enable_login_token = false
|
||||
```
|
||||
|
||||
## Interacting with Grafana’s AuthProxy via curl
|
||||
@ -294,3 +296,13 @@ curl -H "X-WEBAUTH-USER: leonard" -H "X-WEBAUTH-GROUPS: lokiteamOnExternalSystem
|
||||
With this, the user `leonard` will be automatically placed into the Loki team as part of Grafana authentication.
|
||||
|
||||
[Learn more about Team Sync]({{< relref "auth/team-sync.md" >}})
|
||||
|
||||
|
||||
## Login token and session cookie
|
||||
|
||||
With `enable_login_token` set to `true` Grafana will, after successful auth proxy header validation, assign the user
|
||||
a login token and cookie. You only have to configure your auth proxy to provide headers for the /login route.
|
||||
Requests via other routes will be authenticated using the cookie.
|
||||
|
||||
Use settings `login_maximum_inactive_lifetime_days` and `login_maximum_lifetime_days` under `[auth]` to control session
|
||||
lifetime. [Read more about login tokens]({{< relref "auth/overview/#login-and-short-lived-tokens" >}})
|
||||
|
2
go.mod
2
go.mod
@ -67,7 +67,7 @@ require (
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
|
||||
github.com/yudai/pp v2.0.1+incompatible // indirect
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
|
||||
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
|
@ -57,18 +57,31 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.IsSignedIn {
|
||||
c.HTML(200, ViewIndex, viewData)
|
||||
if c.IsSignedIn {
|
||||
// Assign login token to auth proxy users if enable_login_token = true
|
||||
if setting.AuthProxyEnabled && setting.AuthProxyEnableLoginToken {
|
||||
hs.loginAuthProxyUser(c)
|
||||
}
|
||||
|
||||
if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 {
|
||||
c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/")
|
||||
c.Redirect(redirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(setting.AppSubUrl + "/")
|
||||
return
|
||||
}
|
||||
|
||||
if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 {
|
||||
c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/")
|
||||
c.Redirect(redirectTo)
|
||||
return
|
||||
}
|
||||
c.HTML(200, ViewIndex, viewData)
|
||||
}
|
||||
|
||||
c.Redirect(setting.AppSubUrl + "/")
|
||||
func (hs *HTTPServer) loginAuthProxyUser(c *models.ReqContext) {
|
||||
hs.loginUserWithUser(&models.User{
|
||||
Id: c.SignedInUser.UserId,
|
||||
Email: c.SignedInUser.Email,
|
||||
Login: c.SignedInUser.Login,
|
||||
}, c)
|
||||
}
|
||||
|
||||
func tryOAuthAutoLogin(c *models.ReqContext) bool {
|
||||
|
@ -10,7 +10,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -68,8 +70,6 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
|
||||
hs.LoginView(c)
|
||||
})
|
||||
|
||||
setting.OAuthService = &setting.OAuther{}
|
||||
setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
|
||||
setting.LoginCookieName = "grafana_session"
|
||||
setting.SecretKey = "login_testing"
|
||||
|
||||
@ -136,3 +136,59 @@ func TestLoginOAuthRedirect(t *testing.T) {
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, location[0], "/login/github")
|
||||
}
|
||||
|
||||
func TestAuthProxyLoginEnableLoginTokenDisabled(t *testing.T) {
|
||||
sc := setupAuthProxyLoginTest(false)
|
||||
|
||||
assert.Equal(t, sc.resp.Code, 302)
|
||||
location, ok := sc.resp.Header()["Location"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, location[0], "/")
|
||||
|
||||
_, ok = sc.resp.Header()["Set-Cookie"]
|
||||
assert.False(t, ok, "Set-Cookie does not exist")
|
||||
}
|
||||
|
||||
func TestAuthProxyLoginWithEnableLoginToken(t *testing.T) {
|
||||
sc := setupAuthProxyLoginTest(true)
|
||||
|
||||
assert.Equal(t, sc.resp.Code, 302)
|
||||
location, ok := sc.resp.Header()["Location"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, location[0], "/")
|
||||
|
||||
setCookie, ok := sc.resp.Header()["Set-Cookie"]
|
||||
assert.True(t, ok, "Set-Cookie exists")
|
||||
assert.Equal(t, "grafana_session=; Path=/; Max-Age=0; HttpOnly", setCookie[0])
|
||||
}
|
||||
|
||||
func setupAuthProxyLoginTest(enableLoginToken bool) *scenarioContext {
|
||||
mockSetIndexViewData()
|
||||
defer resetSetIndexViewData()
|
||||
|
||||
sc := setupScenarioContext("/login")
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
License: models.OSSLicensingService{},
|
||||
AuthTokenService: auth.NewFakeUserAuthTokenService(),
|
||||
log: log.New("hello"),
|
||||
}
|
||||
|
||||
sc.defaultHandler = Wrap(func(c *models.ReqContext) {
|
||||
c.IsSignedIn = true
|
||||
c.SignedInUser = &models.SignedInUser{
|
||||
UserId: 10,
|
||||
}
|
||||
hs.LoginView(c)
|
||||
})
|
||||
|
||||
setting.OAuthService = &setting.OAuther{}
|
||||
setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
|
||||
setting.AuthProxyEnabled = true
|
||||
setting.AuthProxyEnableLoginToken = enableLoginToken
|
||||
|
||||
sc.m.Get(sc.url, sc.defaultHandler)
|
||||
sc.fakeReqNoAssertions("GET", sc.url).exec()
|
||||
|
||||
return sc
|
||||
}
|
||||
|
@ -8,12 +8,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// cachePrefix is a prefix for the cache key
|
||||
cachePrefix = authproxy.CachePrefix
|
||||
)
|
||||
|
||||
var header = setting.AuthProxyHeaderName
|
||||
|
||||
func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext, orgID int64) bool {
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
authproxy "github.com/grafana/grafana/pkg/middleware/auth_proxy"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
@ -346,7 +347,7 @@ func TestMiddlewareContext(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
key := fmt.Sprintf(cachePrefix, base32.StdEncoding.EncodeToString([]byte(name+"-"+group)))
|
||||
key := fmt.Sprintf(authproxy.CachePrefix, base32.StdEncoding.EncodeToString([]byte(name+"-"+group)))
|
||||
err := sc.remoteCacheService.Set(key, int64(33), 0)
|
||||
So(err, ShouldBeNil)
|
||||
sc.fakeReq("GET", "/")
|
||||
|
@ -143,13 +143,14 @@ var (
|
||||
AnonymousOrgRole string
|
||||
|
||||
// Auth proxy settings
|
||||
AuthProxyEnabled bool
|
||||
AuthProxyHeaderName string
|
||||
AuthProxyHeaderProperty string
|
||||
AuthProxyAutoSignUp bool
|
||||
AuthProxySyncTtl int
|
||||
AuthProxyWhitelist string
|
||||
AuthProxyHeaders map[string]string
|
||||
AuthProxyEnabled bool
|
||||
AuthProxyHeaderName string
|
||||
AuthProxyHeaderProperty string
|
||||
AuthProxyAutoSignUp bool
|
||||
AuthProxyEnableLoginToken bool
|
||||
AuthProxySyncTtl int
|
||||
AuthProxyWhitelist string
|
||||
AuthProxyHeaders map[string]string
|
||||
|
||||
// Basic Auth
|
||||
BasicAuthEnabled bool
|
||||
@ -854,6 +855,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
return err
|
||||
}
|
||||
AuthProxyAutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
|
||||
AuthProxyEnableLoginToken = authProxy.Key("enable_login_token").MustBool(false)
|
||||
|
||||
ldapSyncVal := authProxy.Key("ldap_sync_ttl").MustInt()
|
||||
syncVal := authProxy.Key("sync_ttl").MustInt()
|
||||
|
Loading…
Reference in New Issue
Block a user