mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8s: Add org ID and role authorizers (#75701)
This commit is contained in:
parent
0af7247612
commit
c31ddce0b6
50
pkg/services/grafana-apiserver/auth/authorizer/org/org_id.go
Normal file
50
pkg/services/grafana-apiserver/auth/authorizer/org/org_id.go
Normal file
@ -0,0 +1,50 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
grafanarequest "github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
)
|
||||
|
||||
var _ authorizer.Authorizer = &OrgIDAuthorizer{}
|
||||
|
||||
type OrgIDAuthorizer struct {
|
||||
log log.Logger
|
||||
org org.Service
|
||||
}
|
||||
|
||||
func ProvideOrgIDAuthorizer(orgService org.Service) *OrgIDAuthorizer {
|
||||
return &OrgIDAuthorizer{log: log.New("grafana-apiserver.authorizer.orgid"), org: orgService}
|
||||
}
|
||||
|
||||
func (auth OrgIDAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
|
||||
signedInUser, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("error getting signed in user: %v", err), nil
|
||||
}
|
||||
|
||||
orgID, ok := grafanarequest.ParseOrgID(a.GetNamespace())
|
||||
if !ok {
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
}
|
||||
|
||||
query := org.GetUserOrgListQuery{UserID: signedInUser.UserID}
|
||||
result, err := auth.org.GetUserOrgList(ctx, &query)
|
||||
if err != nil {
|
||||
return authorizer.DecisionDeny, "error getting user org list", err
|
||||
}
|
||||
|
||||
for _, org := range result {
|
||||
if org.OrgID == orgID {
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
}
|
||||
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("user %d is not a member of org %d", signedInUser.UserID, orgID), nil
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
)
|
||||
|
||||
var _ authorizer.Authorizer = &OrgIDAuthorizer{}
|
||||
|
||||
type OrgRoleAuthorizer struct {
|
||||
log log.Logger
|
||||
org org.Service
|
||||
}
|
||||
|
||||
func ProvideOrgRoleAuthorizer(orgService org.Service) *OrgRoleAuthorizer {
|
||||
return &OrgRoleAuthorizer{log: log.New("grafana-apiserver.authorizer.orgrole"), org: orgService}
|
||||
}
|
||||
|
||||
func (auth OrgRoleAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
|
||||
signedInUser, err := appcontext.User(ctx)
|
||||
if err != nil {
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("error getting signed in user: %v", err), nil
|
||||
}
|
||||
|
||||
switch signedInUser.OrgRole {
|
||||
case org.RoleAdmin:
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
case org.RoleEditor:
|
||||
switch a.GetVerb() {
|
||||
case "get", "list", "watch", "create", "update", "patch", "delete", "put", "post":
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
default:
|
||||
return authorizer.DecisionDeny, errorMessageForGrafanaOrgRole(string(signedInUser.OrgRole), a), nil
|
||||
}
|
||||
case org.RoleViewer:
|
||||
switch a.GetVerb() {
|
||||
case "get", "list", "watch":
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
default:
|
||||
return authorizer.DecisionDeny, errorMessageForGrafanaOrgRole(string(signedInUser.OrgRole), a), nil
|
||||
}
|
||||
case org.RoleNone:
|
||||
return authorizer.DecisionDeny, errorMessageForGrafanaOrgRole(string(signedInUser.OrgRole), a), nil
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
}
|
||||
|
||||
func errorMessageForGrafanaOrgRole(grafanaOrgRole string, a authorizer.Attributes) string {
|
||||
return fmt.Sprintf("Grafana org role (%s) didn't allow %s access on requested resource=%s, path=%s", grafanaOrgRole, a.GetVerb(), a.GetResource(), a.GetPath())
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package org
|
||||
|
||||
import "github.com/google/wire"
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideOrgIDAuthorizer,
|
||||
ProvideOrgRoleAuthorizer,
|
||||
)
|
22
pkg/services/grafana-apiserver/auth/authorizer/provider.go
Normal file
22
pkg/services/grafana-apiserver/auth/authorizer/provider.go
Normal file
@ -0,0 +1,22 @@
|
||||
package authorizer
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
"k8s.io/apiserver/pkg/authorization/union"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/auth/authorizer/org"
|
||||
)
|
||||
|
||||
func ProvideAuthorizer(
|
||||
orgIDAuthorizer *org.OrgIDAuthorizer,
|
||||
orgRoleAuthorizer *org.OrgRoleAuthorizer,
|
||||
) authorizer.Authorizer {
|
||||
authorizers := []authorizer.Authorizer{
|
||||
authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup),
|
||||
orgIDAuthorizer,
|
||||
orgRoleAuthorizer,
|
||||
}
|
||||
return union.New(authorizers...)
|
||||
}
|
12
pkg/services/grafana-apiserver/auth/authorizer/wireset.go
Normal file
12
pkg/services/grafana-apiserver/auth/authorizer/wireset.go
Normal file
@ -0,0 +1,12 @@
|
||||
package authorizer
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/auth/authorizer/org"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
org.WireSet,
|
||||
ProvideAuthorizer,
|
||||
)
|
@ -9,6 +9,10 @@ import (
|
||||
|
||||
func OrgIDFrom(ctx context.Context) (int64, bool) {
|
||||
ns := request.NamespaceValue(ctx)
|
||||
return ParseOrgID(ns)
|
||||
}
|
||||
|
||||
func ParseOrgID(ns string) (int64, bool) {
|
||||
if len(ns) < 5 || ns[:4] != "org-" {
|
||||
return 0, false
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
|
||||
"k8s.io/apiserver/pkg/endpoints/responsewriter"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
@ -101,11 +101,14 @@ type service struct {
|
||||
rr routing.RouteRegister
|
||||
handler web.Handler
|
||||
builders []APIGroupBuilder
|
||||
|
||||
authorizer authorizer.Authorizer
|
||||
}
|
||||
|
||||
func ProvideService(
|
||||
cfg *setting.Cfg,
|
||||
rr routing.RouteRegister,
|
||||
authz authorizer.Authorizer,
|
||||
) (*service, error) {
|
||||
s := &service{
|
||||
etcd_servers: cfg.SectionWithEnvOverrides("grafana-apiserver").Key("etcd_servers").Strings(","),
|
||||
@ -114,6 +117,7 @@ func ProvideService(
|
||||
dataPath: path.Join(cfg.DataPath, "k8s"),
|
||||
stopCh: make(chan struct{}),
|
||||
builders: []APIGroupBuilder{},
|
||||
authorizer: authz,
|
||||
}
|
||||
|
||||
// This will be used when running as a dskit service
|
||||
@ -171,8 +175,6 @@ func (s *service) start(ctx context.Context) error {
|
||||
o.SecureServing.BindPort = 6443
|
||||
o.Authentication.RemoteKubeConfigFileOptional = true
|
||||
o.Authorization.RemoteKubeConfigFileOptional = true
|
||||
o.Authorization.AlwaysAllowPaths = []string{"*"}
|
||||
o.Authorization.AlwaysAllowGroups = []string{user.SystemPrivilegedGroup, "grafana"}
|
||||
o.Etcd.StorageConfig.Transport.ServerList = s.etcd_servers
|
||||
|
||||
o.Admission = nil
|
||||
@ -220,6 +222,7 @@ func (s *service) start(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
serverConfig.Authorization.Authorizer = s.authorizer
|
||||
serverConfig.Authentication.Authenticator = authenticator
|
||||
|
||||
// Get the list of groups the server will support
|
||||
@ -283,10 +286,6 @@ func (s *service) start(ctx context.Context) error {
|
||||
|
||||
req.Header.Set("X-Remote-User", strconv.FormatInt(signedInUser.UserID, 10))
|
||||
req.Header.Set("X-Remote-Group", "grafana")
|
||||
req.Header.Set("X-Remote-Extra-token-name", signedInUser.Name)
|
||||
req.Header.Set("X-Remote-Extra-org-role", string(signedInUser.OrgRole))
|
||||
req.Header.Set("X-Remote-Extra-org-id", strconv.FormatInt(signedInUser.OrgID, 10))
|
||||
req.Header.Set("X-Remote-Extra-user-id", strconv.FormatInt(signedInUser.UserID, 10))
|
||||
|
||||
resp := responsewriter.WrapForHTTP1Or2(c.Resp)
|
||||
prepared.GenericAPIServer.Handler.ServeHTTP(resp, req)
|
||||
|
@ -2,6 +2,8 @@ package grafanaapiserver
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/auth/authorizer"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
@ -9,4 +11,5 @@ var WireSet = wire.NewSet(
|
||||
wire.Bind(new(RestConfigProvider), new(*service)),
|
||||
wire.Bind(new(Service), new(*service)),
|
||||
wire.Bind(new(APIRegistrar), new(*service)),
|
||||
authorizer.WireSet,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user