mirror of
https://github.com/grafana/grafana.git
synced 2025-01-24 15:27:01 -06:00
JWT: Add JWT proxy setup devenv (#51731)
* JWT: Add JWT Auth devenv * Auth: JWT allow retrieving login token Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * JWT: Add JWT Auth Proxy devenv * JWT: Add instructions to readme * JWT: Add JWT users * JWT: Remove oauth users * revert session changes, unnecessary Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
This commit is contained in:
parent
5a65a12278
commit
b79b53cbdb
@ -576,7 +576,7 @@ tls_client_cert =
|
||||
tls_client_key =
|
||||
tls_client_ca =
|
||||
use_pkce = false
|
||||
auth_style =
|
||||
auth_style =
|
||||
|
||||
#################################### Basic Auth ##########################
|
||||
[auth.basic]
|
||||
@ -597,6 +597,7 @@ enable_login_token = false
|
||||
#################################### Auth JWT ##########################
|
||||
[auth.jwt]
|
||||
enabled = false
|
||||
enable_login_token = false
|
||||
header_name =
|
||||
email_claim =
|
||||
username_claim =
|
||||
@ -762,7 +763,7 @@ instrumentations_console_enabled = false
|
||||
instrumentations_webvitals_enabled = false
|
||||
|
||||
# Api Key, only applies to Grafana Javascript Agent provider
|
||||
api_key =
|
||||
api_key =
|
||||
|
||||
#################################### Usage Quotas ########################
|
||||
[quota]
|
||||
|
5486
devenv/docker/blocks/jwt_proxy/cloak.sql
Normal file
5486
devenv/docker/blocks/jwt_proxy/cloak.sql
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
#/bin/sh
|
||||
|
||||
VERSION=12.0.1 # set version here
|
||||
|
||||
cd /tmp
|
||||
git clone git@github.com:keycloak/keycloak-containers.git
|
||||
cd keycloak-containers/server
|
||||
git checkout $VERSION
|
||||
docker build -t "quay.io/keycloak/keycloak:${VERSION}" .
|
54
devenv/docker/blocks/jwt_proxy/docker-compose.yaml
Normal file
54
devenv/docker/blocks/jwt_proxy/docker-compose.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
oauthkeycloakdb:
|
||||
image: postgres:12.2
|
||||
container_name: oauthkeycloakdb
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: password
|
||||
volumes:
|
||||
- ./docker/blocks/jwt_proxy/cloak.sql:/docker-entrypoint-initdb.d/cloak.sql
|
||||
restart: unless-stopped
|
||||
|
||||
oauthkeycloak:
|
||||
image: quay.io/keycloak/keycloak:12.0.1
|
||||
container_name: oauthkeycloak
|
||||
environment:
|
||||
DB_VENDOR: POSTGRES
|
||||
DB_ADDR: oauthkeycloakdb
|
||||
DB_DATABASE: keycloak
|
||||
DB_USER: keycloak
|
||||
DB_PASSWORD: password
|
||||
KEYCLOAK_USER: admin
|
||||
KEYCLOAK_PASSWORD: admin
|
||||
PROXY_ADDRESS_FORWARDING: "true"
|
||||
ports:
|
||||
- 8087:8080
|
||||
depends_on:
|
||||
- oauthkeycloakdb
|
||||
links:
|
||||
- "oauthkeycloakdb:oauthkeycloakdb"
|
||||
restart: unless-stopped
|
||||
|
||||
oauthproxy:
|
||||
image: docker.io/bitnami/oauth2-proxy:7.3.0
|
||||
container_name: oauthproxy
|
||||
command: [
|
||||
"--cookie-secret=yI-CWT5s4sBR2Zd0DDJJlTYc0aQ3jwGH15jYA18ZAQA=",
|
||||
"--upstream=http://localhost:3000",
|
||||
"--provider=keycloak",
|
||||
"--client-id=grafana-oauth",
|
||||
"--client-secret=d17b9ea9-bcb1-43d2-b132-d339e55872a8",
|
||||
"--login-url=http://127.0.0.1:8087/auth/realms/grafana/protocol/openid-connect/auth",
|
||||
"--redeem-url=http://127.0.0.1:8087/auth/realms/grafana/protocol/openid-connect/token",
|
||||
"--profile-url=http://127.0.0.1:8087/auth/realms/grafana/protocol/openid-connect/userinfo",
|
||||
"--validate-url=http://127.0.0.1:8087/auth/realms/grafana/protocol/openid-connect/userinfo",
|
||||
"--cookie-secure=false",
|
||||
"--http-address=0.0.0.0:8088",
|
||||
"--redirect-url=http://127.0.0.1:8088/oauth2/callback",
|
||||
"--pass-access-token=true",
|
||||
"--email-domain=*",
|
||||
]
|
||||
network_mode: "host"
|
||||
depends_on:
|
||||
- oauthkeycloak
|
||||
restart: unless-stopped
|
1
devenv/docker/blocks/jwt_proxy/jwks.json
Normal file
1
devenv/docker/blocks/jwt_proxy/jwks.json
Normal file
@ -0,0 +1 @@
|
||||
{"keys":[{"kid":"On2FQuJ8Y-909uJGWQEDkbzG-GRNmMc43HslEgVv_VQ","kty":"RSA","alg":"RS256","use":"sig","n":"qDmQHfTcOQOzmNJbVvtvuS8p_EgmiscP7vA_PZNyKx9O7utyGuoAmJH8e2w8gLIDDWHl5_x8aAIl_-TTPTSiyX8I68ryIdR28ZSe5u4pRdpXCVvJpOefKNIxQCTH7rs4KuRj0HZ2u1mu1Vz5_CeCCoKwKSmheD3u1xTJ8-VxQmdqfGxhuKtnkof7977HWOWy4GLDFqxyYHgihP_MmSeTmXUhVeZI6IOCqHMpF8eFWVGKM6V8rIKf8QO2K_vDJBM_3C933vMY8mqSQXbI3G54x-0myAaQXr4JkxjvUGKg5YC3ZXw7AjfZv_W_fQOG0GYp2hQ0akR4KNKT3XPNmpMVlQ","e":"AQAB","x5c":["MIICnTCCAYUCBgF+u1ir8jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdncmFmYW5hMB4XDTIyMDIwMjE2NDkxN1oXDTMyMDIwMjE2NTA1N1owEjEQMA4GA1UEAwwHZ3JhZmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKg5kB303DkDs5jSW1b7b7kvKfxIJorHD+7wPz2TcisfTu7rchrqAJiR/HtsPICyAw1h5ef8fGgCJf/k0z00osl/COvK8iHUdvGUnubuKUXaVwlbyaTnnyjSMUAkx+67OCrkY9B2drtZrtVc+fwnggqCsCkpoXg97tcUyfPlcUJnanxsYbirZ5KH+/e+x1jlsuBiwxascmB4IoT/zJknk5l1IVXmSOiDgqhzKRfHhVlRijOlfKyCn/EDtiv7wyQTP9wvd97zGPJqkkF2yNxueMftJsgGkF6+CZMY71BioOWAt2V8OwI32b/1v30DhtBmKdoUNGpEeCjSk91zzZqTFZUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABlW64QxuREB81VMGsyhj4Q5RykFaVuD5O8YlwUpmVfAVLzb0Drf54Kn4bnpnckKyYV+T+HsN4QXt81UE41xH0Aai2H3vrGH+PJf6aLPCDE+jpMqtN3n6IgImJXJPL8upMfhhWDv4nkM4uynEwWupzmrKi4oJuTETSMktJby4o6//XWnCzCVMoAGFJU4gtjBUzOMLW26zD+yc+BuUtfR3HzItVHSZKQSNSFO0kVS68RgrER8qJw07z3BOJ2bPpPM0PYyEngGMaowz/T6lI32ymGMWYMAnslthS1KAW9xcTBwnrW1nMhe5a0LPxIktys/wJtxIHZLc5sOddGT4xYklLg=="],"x5t":"prs-h1NBqOSJMH-tQWLTqguWets","x5t#S256":"YjK3HobZW8xbNL1IPDgFhCM41UC5c0hG2cxaF6v961Q"}]}
|
66
devenv/docker/blocks/jwt_proxy/readme.md
Normal file
66
devenv/docker/blocks/jwt_proxy/readme.md
Normal file
@ -0,0 +1,66 @@
|
||||
# OAUTH BLOCK
|
||||
## Devenv setup jwt auth
|
||||
|
||||
To launch the block, use the oauth source. Ex:
|
||||
|
||||
```bash
|
||||
make devenv sources="jwt_proxy"
|
||||
```
|
||||
|
||||
Here is the conf you need to add to your configuration file (conf/custom.ini):
|
||||
|
||||
```ini
|
||||
[auth]
|
||||
signout_redirect_url = http://127.0.0.1:8088/oauth2/sign_out
|
||||
|
||||
[auth.jwt]
|
||||
enabled = true
|
||||
enable_login_token = true
|
||||
header_name = X-Forwarded-Access-Token
|
||||
username_claim = login
|
||||
email_claim = email
|
||||
jwk_set_file = devenv/docker/blocks/oauth/jwks.json
|
||||
cache_ttl = 60m
|
||||
expected_claims = {"iss": "http://localhost:8087/auth/realms/grafana", "azp": "grafana-oauth"}
|
||||
auto_sign_up = true
|
||||
```
|
||||
|
||||
Access Grafana through:
|
||||
|
||||
```sh
|
||||
http://127.0.0.1:8088
|
||||
```
|
||||
|
||||
## Backing up keycloak DB
|
||||
|
||||
In case you want to make changes to the devenv setup, you can dump keycloack's DB:
|
||||
|
||||
```bash
|
||||
cd devenv;
|
||||
docker-compose exec -T oauthkeycloakdb bash -c "pg_dump -U keycloak keycloak" > docker/blocks/oauth/cloak.sql
|
||||
```
|
||||
|
||||
## Connecting to keycloack:
|
||||
|
||||
- keycloak admin: http://localhost:8087
|
||||
- keycloak admin login: admin:admin
|
||||
- grafana jwt viewer login: jwt-viewer:grafana
|
||||
- grafana jwt editor login: jwt-editor:grafana
|
||||
- grafana jwt admin login: jwt-admin:grafana
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Mac M1 Users
|
||||
|
||||
The new arm64 architecture does not build for the latest docker image of keycloack. Refer to https://github.com/docker/for-mac/issues/5310 for the issue to see if it resolved.
|
||||
Until then you need to build the docker image locally and then run `devenv`.
|
||||
|
||||
1. Remove any lingering keycloack image
|
||||
```sh
|
||||
$ docker rmi $(docker images | grep 'keycloack')
|
||||
```
|
||||
1. Build keycloack image locally
|
||||
```sh
|
||||
$ ./docker-build-keycloack-m1-image.sh
|
||||
```
|
||||
1. Start from beginning of this readme
|
1
devenv/docker/blocks/oauth/jwks.json
Normal file
1
devenv/docker/blocks/oauth/jwks.json
Normal file
@ -0,0 +1 @@
|
||||
{"keys":[{"kid":"On2FQuJ8Y-909uJGWQEDkbzG-GRNmMc43HslEgVv_VQ","kty":"RSA","alg":"RS256","use":"sig","n":"qDmQHfTcOQOzmNJbVvtvuS8p_EgmiscP7vA_PZNyKx9O7utyGuoAmJH8e2w8gLIDDWHl5_x8aAIl_-TTPTSiyX8I68ryIdR28ZSe5u4pRdpXCVvJpOefKNIxQCTH7rs4KuRj0HZ2u1mu1Vz5_CeCCoKwKSmheD3u1xTJ8-VxQmdqfGxhuKtnkof7977HWOWy4GLDFqxyYHgihP_MmSeTmXUhVeZI6IOCqHMpF8eFWVGKM6V8rIKf8QO2K_vDJBM_3C933vMY8mqSQXbI3G54x-0myAaQXr4JkxjvUGKg5YC3ZXw7AjfZv_W_fQOG0GYp2hQ0akR4KNKT3XPNmpMVlQ","e":"AQAB","x5c":["MIICnTCCAYUCBgF+u1ir8jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdncmFmYW5hMB4XDTIyMDIwMjE2NDkxN1oXDTMyMDIwMjE2NTA1N1owEjEQMA4GA1UEAwwHZ3JhZmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKg5kB303DkDs5jSW1b7b7kvKfxIJorHD+7wPz2TcisfTu7rchrqAJiR/HtsPICyAw1h5ef8fGgCJf/k0z00osl/COvK8iHUdvGUnubuKUXaVwlbyaTnnyjSMUAkx+67OCrkY9B2drtZrtVc+fwnggqCsCkpoXg97tcUyfPlcUJnanxsYbirZ5KH+/e+x1jlsuBiwxascmB4IoT/zJknk5l1IVXmSOiDgqhzKRfHhVlRijOlfKyCn/EDtiv7wyQTP9wvd97zGPJqkkF2yNxueMftJsgGkF6+CZMY71BioOWAt2V8OwI32b/1v30DhtBmKdoUNGpEeCjSk91zzZqTFZUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABlW64QxuREB81VMGsyhj4Q5RykFaVuD5O8YlwUpmVfAVLzb0Drf54Kn4bnpnckKyYV+T+HsN4QXt81UE41xH0Aai2H3vrGH+PJf6aLPCDE+jpMqtN3n6IgImJXJPL8upMfhhWDv4nkM4uynEwWupzmrKi4oJuTETSMktJby4o6//XWnCzCVMoAGFJU4gtjBUzOMLW26zD+yc+BuUtfR3HzItVHSZKQSNSFO0kVS68RgrER8qJw07z3BOJ2bPpPM0PYyEngGMaowz/T6lI32ymGMWYMAnslthS1KAW9xcTBwnrW1nMhe5a0LPxIktys/wJtxIHZLc5sOddGT4xYklLg=="],"x5t":"prs-h1NBqOSJMH-tQWLTqguWets","x5t#S256":"YjK3HobZW8xbNL1IPDgFhCM41UC5c0hG2cxaF6v961Q"}]}
|
@ -1,6 +1,6 @@
|
||||
# OAUTH BLOCK
|
||||
|
||||
## Devenv setup
|
||||
## Devenv setup oauth
|
||||
|
||||
To launch the block, use the oauth source. Ex:
|
||||
```bash
|
||||
@ -29,6 +29,76 @@ api_url = http://localhost:8087/auth/realms/grafana/protocol/openid-connect/user
|
||||
role_attribute_path = contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer'
|
||||
```
|
||||
|
||||
## Devenv setup jwt auth
|
||||
|
||||
To launch the block, use the oauth source. Ex:
|
||||
|
||||
```bash
|
||||
make devenv sources="oauth"
|
||||
```
|
||||
|
||||
Here is the conf you need to add to your configuration file (conf/custom.ini):
|
||||
|
||||
```ini
|
||||
[auth.jwt]
|
||||
enabled = true
|
||||
header_name = X-JWT-Assertion
|
||||
username_claim = login
|
||||
email_claim = email
|
||||
jwk_set_file = devenv/docker/blocks/oauth/jwks.json
|
||||
cache_ttl = 60m
|
||||
expected_claims = {"iss": "http://localhost:8087/auth/realms/grafana", "azp": "grafana-oauth"}
|
||||
auto_sign_up = true
|
||||
```
|
||||
|
||||
You can obtain a jwt token by using the following command for oauth-admin:
|
||||
|
||||
```sh
|
||||
curl --request POST \
|
||||
--url http://localhost:8087/auth/realms/grafana/protocol/openid-connect/token \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data client_id=grafana-oauth \
|
||||
--data grant_type=password \
|
||||
--data client_secret=d17b9ea9-bcb1-43d2-b132-d339e55872a8 \
|
||||
--data scope=openid \
|
||||
--data username=oauth-admin \
|
||||
--data password=grafana
|
||||
```
|
||||
|
||||
|
||||
Grafana call example:
|
||||
|
||||
```sh
|
||||
curl --request GET \
|
||||
--url http://127.0.0.1:3000/api/folders \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'X-JWT-Assertion: eyJ......'
|
||||
```
|
||||
|
||||
### Alternative devenv setup jwk_set_url
|
||||
|
||||
Run a reverse proxy pointing to the jwk_set_url (only an https-uri can be used as jwk_set_url).
|
||||
|
||||
Ex (using localtunnel):
|
||||
|
||||
```sh
|
||||
npx localtunnel --port 8087
|
||||
```
|
||||
|
||||
And using the following conf:
|
||||
|
||||
```ini
|
||||
[auth.jwt]
|
||||
enabled = true
|
||||
header_name = X-JWT-Assertion
|
||||
username_claim = login
|
||||
email_claim = email
|
||||
jwk_set_url = <YOUR REVERSE PROXY URL>/auth/realms/grafana/protocol/openid-connect/certs
|
||||
cache_ttl = 60m
|
||||
expected_claims = {"iss": "http://localhost:8087/auth/realms/grafana", "azp": "grafana-oauth"}
|
||||
auto_sign_up = true
|
||||
```
|
||||
|
||||
## Backing up keycloak DB
|
||||
|
||||
In case you want to make changes to the devenv setup, you can dump keycloack's DB:
|
||||
@ -62,4 +132,3 @@ $ docker rmi $(docker images | grep 'keycloack')
|
||||
$ ./docker-build-keycloack-m1-image.sh
|
||||
```
|
||||
1. Start from beginning of this readme
|
||||
|
||||
|
@ -124,7 +124,7 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
|
||||
user := &user.User{ID: c.SignedInUser.UserId, Email: c.SignedInUser.Email, Login: c.SignedInUser.Login}
|
||||
err := hs.loginUserWithUser(user, c)
|
||||
if err != nil {
|
||||
c.Handle(hs.Cfg, 500, "Failed to sign in user", err)
|
||||
c.Handle(hs.Cfg, http.StatusInternalServerError, "Failed to sign in user", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package contexthandler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@ -23,7 +24,7 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
|
||||
claims, err := h.JWTAuthService.Verify(ctx.Req.Context(), jwtToken)
|
||||
if err != nil {
|
||||
ctx.Logger.Debug("Failed to verify JWT", "error", err)
|
||||
ctx.JsonApiErr(401, InvalidJWT, err)
|
||||
ctx.JsonApiErr(http.StatusUnauthorized, InvalidJWT, err)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -33,7 +34,7 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
|
||||
|
||||
if sub == "" {
|
||||
ctx.Logger.Warn("Got a JWT without the mandatory 'sub' claim", "error", err)
|
||||
ctx.JsonApiErr(401, InvalidJWT, err)
|
||||
ctx.JsonApiErr(http.StatusUnauthorized, InvalidJWT, err)
|
||||
return true
|
||||
}
|
||||
extUser := &models.ExternalUserInfo{
|
||||
@ -56,7 +57,7 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
|
||||
|
||||
if query.Login == "" && query.Email == "" {
|
||||
ctx.Logger.Debug("Failed to get an authentication claim from JWT")
|
||||
ctx.JsonApiErr(401, InvalidJWT, err)
|
||||
ctx.JsonApiErr(http.StatusUnauthorized, InvalidJWT, err)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -80,10 +81,10 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
|
||||
"username_claim", query.Login,
|
||||
)
|
||||
err = login.ErrInvalidCredentials
|
||||
ctx.JsonApiErr(401, UserNotFound, err)
|
||||
ctx.JsonApiErr(http.StatusUnauthorized, UserNotFound, err)
|
||||
} else {
|
||||
ctx.Logger.Error("Failed to get signed in user", "error", err)
|
||||
ctx.JsonApiErr(401, InvalidJWT, err)
|
||||
ctx.JsonApiErr(http.StatusUnauthorized, InvalidJWT, err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user