mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 03:56:36 +00:00
Fix: make the impersonation feature work (#5261)
* Fix: make the impersonation feature work Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Fix: update the document of the chart Signed-off-by: barnettZQG <barnett.zqg@gmail.com> Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
This commit is contained in:
@@ -149,7 +149,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `kubeClient.qps` | The qps for reconcile clients, default is 100 | `100` |
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 200 | `200` |
|
||||
| `authentication.enabled` | Enable authentication for application | `false` |
|
||||
| `authentication.withUser` | Application authentication will impersonate as the request User | `false` |
|
||||
| `authentication.withUser` | Application authentication will impersonate as the request User | `true` |
|
||||
| `authentication.defaultUser` | Application authentication will impersonate as the User if no user provided in Application | `kubevela:vela-core` |
|
||||
| `authentication.groupPattern` | Application authentication will impersonate as the request Group that matches the pattern | `kubevela:*` |
|
||||
|
||||
|
||||
@@ -11,21 +11,6 @@ metadata:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ if .Values.authentication.enabled }} {{ include "kubevela.fullname" . }}:manager {{ else }} "cluster-admin" {{ end }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
{{ if .Values.authentication.enabled }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
@@ -66,9 +51,41 @@ rules:
|
||||
- apiGroups: ["authorization.k8s.io"]
|
||||
resources: ["subjectaccessreviews"]
|
||||
verbs: ["*"]
|
||||
{{ end }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:manager-authentication-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "kubevela.fullname" . }}:manager
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
{{ else }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: "cluster-admin"
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
{{ end }}
|
||||
---
|
||||
# permissions to do leader election.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
|
||||
@@ -263,6 +263,6 @@ kubeClient:
|
||||
## @param authentication.groupPattern Application authentication will impersonate as the request Group that matches the pattern
|
||||
authentication:
|
||||
enabled: false
|
||||
withUser: false
|
||||
withUser: true
|
||||
defaultUser: kubevela:vela-core
|
||||
groupPattern: kubevela:*
|
||||
|
||||
@@ -17,10 +17,13 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
"flag"
|
||||
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/config"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
)
|
||||
|
||||
// ServerRunOptions contains everything necessary to create and run api server
|
||||
@@ -40,6 +43,9 @@ func NewServerRunOptions() *ServerRunOptions {
|
||||
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||
fs := fss.FlagSet("generic")
|
||||
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
||||
feature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("featuregate"))
|
||||
features.APIServerMutableFeatureGate.AddFlag(fss.FlagSet("featuregate"))
|
||||
local := flag.NewFlagSet("klog", flag.ExitOnError)
|
||||
klog.InitFlags(local)
|
||||
fs.AddGoFlagSet(local)
|
||||
return fss
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
if addonVersion == nil {
|
||||
return nil, errors.Errorf("specified version %s not exist", utils.Sanitize(version))
|
||||
}
|
||||
klog.Infof("Addon '%s' with version '%s' found from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
klog.V(5).Infof("Addon '%s' with version '%s' found from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
for _, chartURL := range addonVersion.URLs {
|
||||
if !utils.IsValidURL(chartURL) {
|
||||
chartURL, err = utils.JoinURL(i.url, chartURL)
|
||||
@@ -174,7 +174,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
addonPkg.AvailableVersions = availableVersions
|
||||
addonPkg.RegistryName = i.name
|
||||
addonPkg.Meta.SystemRequirements = LoadSystemRequirements(addonVersion.Annotations)
|
||||
klog.Infof("Addon '%s' with version '%s' loaded successfully from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
klog.V(5).Infof("Addon '%s' with version '%s' loaded successfully from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
return addonPkg, nil
|
||||
}
|
||||
return nil, ErrFetch
|
||||
|
||||
@@ -48,7 +48,7 @@ func CreateEnv(ctx context.Context, kubeClient client.Client, ds datastore.DataS
|
||||
env.Namespace = env.Name
|
||||
}
|
||||
|
||||
// create namespace at first
|
||||
// Creating the namespace at first.
|
||||
err = util.CreateOrUpdateNamespace(ctx, kubeClient, env.Namespace,
|
||||
util.MergeOverrideLabels(map[string]string{
|
||||
oam.LabelControlPlaneNamespaceUsage: oam.VelaNamespaceUsageEnv,
|
||||
|
||||
@@ -239,7 +239,9 @@ func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := managePrivilegesForEnvironment(ctx, p.KubeClient, env, false); err != nil {
|
||||
// Updating the role and role binding can't use the login user permissions.
|
||||
updateRoleCtx := utils.WithProject(ctx, "")
|
||||
if err := managePrivilegesForEnvironment(updateRoleCtx, p.KubeClient, env, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -289,12 +291,14 @@ func (p *envServiceImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequ
|
||||
}
|
||||
}
|
||||
|
||||
err = repository.CreateEnv(ctx, p.KubeClient, p.Store, newEnv)
|
||||
// Creating the namespace can't use the login user permissions.
|
||||
createNamespaceCtx := utils.WithProject(ctx, "")
|
||||
err = repository.CreateEnv(createNamespaceCtx, p.KubeClient, p.Store, newEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := managePrivilegesForEnvironment(ctx, p.KubeClient, newEnv, false); err != nil {
|
||||
if err := managePrivilegesForEnvironment(createNamespaceCtx, p.KubeClient, newEnv, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
apiutils "github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
@@ -322,7 +323,8 @@ func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.Creat
|
||||
if namespace == "" {
|
||||
namespace = req.Name
|
||||
}
|
||||
if err := utils.CreateNamespace(ctx, p.K8sClient, namespace); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
createCtx := apiutils.WithProject(ctx, "")
|
||||
if err := utils.CreateNamespace(createCtx, p.K8sClient, namespace); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, bcode.ErrProjectNamespaceFail
|
||||
}
|
||||
|
||||
|
||||
@@ -148,10 +148,11 @@ func (dt *targetServiceImpl) CreateTarget(ctx context.Context, req apisv1.Create
|
||||
if req.Cluster == nil {
|
||||
req.Cluster = &apisv1.ClusterTarget{ClusterName: multicluster.ClusterLocalName, Namespace: req.Name}
|
||||
}
|
||||
if err := repository.CreateTargetNamespace(ctx, dt.K8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil {
|
||||
createTargetCtx := utils.WithProject(ctx, "")
|
||||
if err := repository.CreateTargetNamespace(createTargetCtx, dt.K8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := managePrivilegesForTarget(ctx, dt.K8sClient, &target, false); err != nil {
|
||||
if err := managePrivilegesForTarget(createTargetCtx, dt.K8sClient, &target, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := repository.CreateTarget(ctx, dt.Store, &target)
|
||||
@@ -167,7 +168,8 @@ func (dt *targetServiceImpl) UpdateTarget(ctx context.Context, target *model.Tar
|
||||
return nil, err
|
||||
}
|
||||
// Compatible with historical data, if the existing Target has not been authorized, perform an update action.
|
||||
if err := managePrivilegesForTarget(ctx, dt.K8sClient, targetModel, false); err != nil {
|
||||
updateCtx := utils.WithProject(ctx, "")
|
||||
if err := managePrivilegesForTarget(updateCtx, dt.K8sClient, targetModel, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailTarget(ctx, targetModel)
|
||||
|
||||
@@ -148,7 +148,7 @@ func (n *env) create(req *restful.Request, res *restful.Response) {
|
||||
// Call the Domain layer code
|
||||
env, err := n.EnvService.CreateEnv(req.Request.Context(), createReq)
|
||||
if err != nil {
|
||||
klog.Errorf("create application failure %s", err.Error())
|
||||
klog.Errorf("create environment failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
+15
-22
@@ -24,8 +24,8 @@ import (
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
)
|
||||
|
||||
@@ -41,6 +41,9 @@ const KubeVelaAdminGroupPrefix = "kubevela:admin:"
|
||||
// TemplateReaderGroup This group includes the permission that read the ConfigMap in the vela-system namespace.
|
||||
const TemplateReaderGroup = "template-reader"
|
||||
|
||||
// UXDefaultGroup This group means directly using the original identity registered by the cluster.
|
||||
const UXDefaultGroup = "kubevela:ux"
|
||||
|
||||
// ContextWithUserInfo extract user from context (parse username and project) for impersonation
|
||||
func ContextWithUserInfo(ctx context.Context) context.Context {
|
||||
if !features.APIServerFeatureGate.Enabled(features.APIServerEnableImpersonation) {
|
||||
@@ -50,10 +53,12 @@ func ContextWithUserInfo(ctx context.Context) context.Context {
|
||||
if username, ok := UsernameFrom(ctx); ok {
|
||||
userInfo.Name = username
|
||||
}
|
||||
if project, ok := ProjectFrom(ctx); ok {
|
||||
userInfo.Groups = []string{KubeVelaProjectGroupPrefix + project}
|
||||
if project, ok := ProjectFrom(ctx); ok && project != "" {
|
||||
userInfo.Groups = []string{KubeVelaProjectGroupPrefix + project, auth.KubeVelaClientGroup}
|
||||
} else {
|
||||
userInfo.Groups = []string{UXDefaultGroup}
|
||||
}
|
||||
if userInfo.Name == model.DefaultAdminUserName && !features.APIServerFeatureGate.Enabled(features.APIServerEnableAdminImpersonation) {
|
||||
if userInfo.Name == model.DefaultAdminUserName && features.APIServerFeatureGate.Enabled(features.APIServerEnableAdminImpersonation) {
|
||||
return ctx
|
||||
}
|
||||
return request.WithUser(ctx, userInfo)
|
||||
@@ -83,33 +88,25 @@ func (c *authAppClient) Status() client.StatusWriter {
|
||||
|
||||
// Create .
|
||||
func (c *authAppClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
|
||||
if _, ok := obj.(*v1beta1.Application); ok {
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
}
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
return c.Client.Create(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Delete .
|
||||
func (c *authAppClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
|
||||
if _, ok := obj.(*v1beta1.Application); ok {
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
}
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
return c.Client.Delete(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Update .
|
||||
func (c *authAppClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
|
||||
if _, ok := obj.(*v1beta1.Application); ok {
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
}
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
return c.Client.Update(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Patch .
|
||||
func (c *authAppClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
if _, ok := obj.(*v1beta1.Application); ok {
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
}
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
return c.Client.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
|
||||
@@ -119,16 +116,12 @@ type authAppStatusClient struct {
|
||||
|
||||
// Update .
|
||||
func (c *authAppStatusClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
|
||||
if _, ok := obj.(*v1beta1.Application); ok {
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
}
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
return c.StatusWriter.Update(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Patch .
|
||||
func (c *authAppStatusClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
if _, ok := obj.(*v1beta1.Application); ok {
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
}
|
||||
ctx = ContextWithUserInfo(ctx)
|
||||
return c.StatusWriter.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,6 @@ const (
|
||||
func init() {
|
||||
runtime.Must(APIServerMutableFeatureGate.Add(map[featuregate.Feature]featuregate.FeatureSpec{
|
||||
APIServerEnableImpersonation: {Default: false, PreRelease: featuregate.Alpha},
|
||||
APIServerEnableAdminImpersonation: {Default: false, PreRelease: featuregate.Alpha},
|
||||
APIServerEnableAdminImpersonation: {Default: true, PreRelease: featuregate.Alpha},
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user