[Backport release-1.4] Feat: enhance controller auth by removing useless features & add authentication for componentrevision+healthcheck (#4023)

* Feat: use application identity in gc & componentrevision & collectHealthStatus

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 63fc4bcc69)

* Chore: remove useless features and roles

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit f4ef77b2b3)

* Fix: remove DELETE from mutating webhook

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 75f3d5dc35)

* Chore: enhance deploy error display

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit e69079bdae)

* Fix: e2e test vela cli output match & controllerrevision recycle for serviceaccount impersonation

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 05b85573a2)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
github-actions[bot]
2022-05-27 16:00:04 +08:00
committed by GitHub
parent 371affb389
commit e20ef02a6a
21 changed files with 54 additions and 96 deletions

View File

@@ -25,7 +25,6 @@ import (
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/transport"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils"
)
@@ -54,11 +53,8 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
if exists && userInfo != nil {
if name := userInfo.GetName(); name != "" {
req.Header.Set(transport.ImpersonateUserHeader, name)
req.Header.Set(transport.ImpersonateGroupHeader, types.ClusterGatewayAccessorGroup)
for _, group := range userInfo.GetGroups() {
if group != types.ClusterGatewayAccessorGroup {
req.Header.Add(transport.ImpersonateGroupHeader, group)
}
req.Header.Add(transport.ImpersonateGroupHeader, group)
}
q := req.URL.Query()
q.Add(impersonateKey, "true")

View File

@@ -31,7 +31,6 @@ import (
featuregatetesting "k8s.io/component-base/featuregate/testing"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/features"
"github.com/oam-dev/kubevela/pkg/oam"
)
@@ -66,21 +65,21 @@ func TestImpersonatingRoundTripper(t *testing.T) {
return ContextWithUserInfo(ctx, app)
},
expectedUser: "system:serviceaccount:vela-system:default",
expectedGroup: []string{types.ClusterGatewayAccessorGroup},
expectedGroup: nil,
},
"without service account and app": {
ctxFn: func(ctx context.Context) context.Context {
return ContextWithUserInfo(ctx, nil)
},
expectedUser: "",
expectedGroup: []string{types.ClusterGatewayAccessorGroup},
expectedGroup: nil,
},
"without service account": {
ctxFn: func(ctx context.Context) context.Context {
return ContextWithUserInfo(ctx, &v1beta1.Application{})
},
expectedUser: AuthenticationDefaultUser,
expectedGroup: []string{types.ClusterGatewayAccessorGroup},
expectedGroup: nil,
},
"with user and groups": {
ctxFn: func(ctx context.Context) context.Context {
@@ -92,7 +91,7 @@ func TestImpersonatingRoundTripper(t *testing.T) {
return ContextWithUserInfo(ctx, app)
},
expectedUser: "username",
expectedGroup: []string{types.ClusterGatewayAccessorGroup, "kubevela:group1", "kubevela:group2"},
expectedGroup: []string{"kubevela:group1", "kubevela:group2"},
},
}
for name, ts := range testSets {

View File

@@ -30,6 +30,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/auth"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application/assemble"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
"github.com/oam-dev/kubevela/pkg/cue/process"
@@ -219,7 +220,7 @@ func (h *AppHandler) checkComponentHealth(appParser *appfile.Parser, appRev *v1b
if err != nil {
return false, err
}
wl.Ctx.SetCtx(ctx)
wl.Ctx.SetCtx(auth.ContextWithUserInfo(ctx, h.app))
readyWorkload, readyTraits, err := renderComponentsAndTraits(h.r.Client, manifest, appRev, clusterName, overrideNamespace, env)
if err != nil {
@@ -258,7 +259,7 @@ func (h *AppHandler) applyComponentFunc(appParser *appfile.Parser, appRev *v1bet
return nil, nil, false, errors.WithMessage(err, "cannot dispatch packaged workload resources")
}
}
wl.Ctx.SetCtx(ctx)
wl.Ctx.SetCtx(auth.ContextWithUserInfo(ctx, h.app))
readyWorkload, readyTraits, err := renderComponentsAndTraits(h.r.Client, manifest, appRev, clusterName, overrideNamespace, env)
if err != nil {

View File

@@ -42,6 +42,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile"
helmapi "github.com/oam-dev/kubevela/pkg/appfile/helm/flux2apis"
"github.com/oam-dev/kubevela/pkg/auth"
"github.com/oam-dev/kubevela/pkg/component"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/cue/model"
@@ -542,7 +543,7 @@ func (h *AppHandler) handleComponentRevisionNameSpecified(ctx context.Context, c
revisionName := comp.ExternalRevision
cr := &appsv1.ControllerRevision{}
if err := h.r.Client.Get(ctx, client.ObjectKey{Namespace: h.getComponentRevisionNamespace(ctx), Name: revisionName}, cr); err != nil {
if err := h.r.Client.Get(auth.ContextWithUserInfo(ctx, h.app), client.ObjectKey{Namespace: h.getComponentRevisionNamespace(ctx), Name: revisionName}, cr); err != nil {
if !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "failed to get controllerRevision:%s", revisionName)
}
@@ -592,7 +593,7 @@ func (h *AppHandler) handleComponentRevisionNameUnspecified(ctx context.Context,
listOpts := []client.ListOption{client.MatchingLabels{
oam.LabelControllerRevisionComponent: comp.Name,
}, client.InNamespace(h.getComponentRevisionNamespace(ctx))}
if err := h.r.List(ctx, crList, listOpts...); err != nil {
if err := h.r.List(auth.ContextWithUserInfo(ctx, h.app), crList, listOpts...); err != nil {
return err
}

View File

@@ -36,8 +36,6 @@ const (
// Edge Features
// ControllerAutoImpersonation enable the auto impersonation for controller (to use explicit identity for requests)
ControllerAutoImpersonation featuregate.Feature = "ControllerAutoImpersonation"
// AuthenticateApplication enable the authentication for application
AuthenticateApplication featuregate.Feature = "AuthenticateApplication"
)
@@ -47,7 +45,6 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
LegacyObjectTypeIdentifier: {Default: false, PreRelease: featuregate.Alpha},
DeprecatedObjectLabelSelector: {Default: false, PreRelease: featuregate.Alpha},
LegacyResourceTrackerGC: {Default: true, PreRelease: featuregate.Alpha},
ControllerAutoImpersonation: {Default: true, PreRelease: featuregate.Alpha},
AuthenticateApplication: {Default: false, PreRelease: featuregate.Alpha},
}

View File

@@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/pkg/auth"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/resourcetracker"
@@ -45,7 +46,7 @@ func (h *resourceKeeper) DispatchComponentRevision(ctx context.Context, cr *v1.C
if err = resourcetracker.RecordManifestsInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, []*unstructured.Unstructured{obj}, true, common.WorkflowResourceCreator); err != nil {
return errors.Wrapf(err, "failed to record componentrevision %s/%s/%s", oam.GetCluster(cr), cr.Namespace, cr.Name)
}
if err = h.Client.Create(multicluster.ContextWithClusterName(ctx, oam.GetCluster(cr)), cr); err != nil {
if err = h.Client.Create(auth.ContextWithUserInfo(multicluster.ContextWithClusterName(ctx, oam.GetCluster(cr)), h.app), cr); err != nil {
return errors.Wrapf(err, "failed to create componentrevision %s/%s/%s", oam.GetCluster(cr), cr.Namespace, cr.Name)
}
return nil
@@ -63,7 +64,7 @@ func (h *resourceKeeper) DeleteComponentRevision(ctx context.Context, cr *v1.Con
obj.SetName(cr.Name)
obj.SetNamespace(cr.Namespace)
obj.SetLabels(cr.Labels)
if err = h.Client.Delete(multicluster.ContextWithClusterName(ctx, oam.GetCluster(cr)), cr); err != nil && !errors2.IsNotFound(err) {
if err = h.Client.Delete(auth.ContextWithUserInfo(multicluster.ContextWithClusterName(ctx, oam.GetCluster(cr)), h.app), cr); err != nil && !errors2.IsNotFound(err) {
return errors.Wrapf(err, "failed to delete componentrevision %s/%s/%s", oam.GetCluster(cr), cr.Namespace, cr.Name)
}
if err = resourcetracker.DeletedManifestInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, obj, true); err != nil {

View File

@@ -35,6 +35,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/auth"
"github.com/oam-dev/kubevela/pkg/features"
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
"github.com/oam-dev/kubevela/pkg/multicluster"
@@ -182,7 +183,7 @@ func (h *gcHandler) scan(ctx context.Context) (inactiveRTs []*v1beta1.ResourceTr
if rt != nil {
inactive := true
for _, mr := range rt.Spec.ManagedResources {
entry := h.cache.get(ctx, mr)
entry := h.cache.get(auth.ContextWithUserInfo(ctx, h.app), mr)
if entry.err == nil && (entry.gcExecutorRT != rt || !entry.exists) {
continue
}
@@ -225,7 +226,7 @@ func (h *gcHandler) Mark(ctx context.Context) error {
// checkAndRemoveResourceTrackerFinalizer return (all resource recycled, error)
func (h *gcHandler) checkAndRemoveResourceTrackerFinalizer(ctx context.Context, rt *v1beta1.ResourceTracker) (bool, v1beta1.ManagedResource, error) {
for _, mr := range rt.Spec.ManagedResources {
entry := h.cache.get(ctx, mr)
entry := h.cache.get(auth.ContextWithUserInfo(ctx, h.app), mr)
if entry.err != nil {
return false, entry.mr, entry.err
}
@@ -257,6 +258,7 @@ func (h *gcHandler) Sweep(ctx context.Context) (finished bool, waiting []v1beta1
}
func (h *gcHandler) recycleResourceTracker(ctx context.Context, rt *v1beta1.ResourceTracker) error {
ctx = auth.ContextWithUserInfo(ctx, h.app)
switch h.cfg.order {
case v1alpha1.OrderDependency:
for _, mr := range rt.Spec.ManagedResources {
@@ -380,14 +382,16 @@ func (h *gcHandler) GarbageCollectComponentRevisionResourceTracker(ctx context.C
}
var managedResources []v1beta1.ManagedResource
for _, cr := range h._crRT.Spec.ManagedResources { // legacy code for rollout-plan
_ctx := multicluster.ContextWithClusterName(ctx, cr.Cluster)
_ctx = auth.ContextWithUserInfo(_ctx, h.app)
if _, exists := inUseComponents[cr.ComponentKey()]; !exists {
_cr := &appsv1.ControllerRevision{}
err := h.Client.Get(multicluster.ContextWithClusterName(ctx, cr.Cluster), cr.NamespacedName(), _cr)
err := h.Client.Get(_ctx, cr.NamespacedName(), _cr)
if err != nil && !multicluster.IsNotFoundOrClusterNotExists(err) {
return errors.Wrapf(err, "failed to get component revision %s", cr.ResourceKey())
}
if err == nil {
if err = h.Client.Delete(multicluster.ContextWithClusterName(ctx, cr.Cluster), _cr); err != nil && !kerrors.IsNotFound(err) {
if err = h.Client.Delete(_ctx, _cr); err != nil && !kerrors.IsNotFound(err) {
return errors.Wrapf(err, "failed to delete component revision %s", cr.ResourceKey())
}
}

View File

@@ -30,7 +30,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/auth"
"github.com/oam-dev/kubevela/pkg/features"
@@ -52,7 +51,7 @@ func (h *MutatingHandler) Handle(ctx context.Context, req admission.Request) adm
return admission.Patched("")
}
if slices.Contains(req.UserInfo.Groups, common.Group) || slices.Contains(h.skipUsers, req.UserInfo.Username) {
if slices.Contains(h.skipUsers, req.UserInfo.Username) {
return admission.Patched("")
}
@@ -86,11 +85,9 @@ func (h *MutatingHandler) InjectDecoder(d *admission.Decoder) error {
func RegisterMutatingHandler(mgr manager.Manager) {
server := mgr.GetWebhookServer()
handler := &MutatingHandler{}
if !utilfeature.DefaultMutableFeatureGate.Enabled(features.ControllerAutoImpersonation) {
if userInfo := utils.GetUserInfoFromConfig(mgr.GetConfig()); userInfo != nil {
klog.Infof("[ApplicationMutatingHandler] add skip user %s", userInfo.Username)
handler.skipUsers = []string{userInfo.Username}
}
if userInfo := utils.GetUserInfoFromConfig(mgr.GetConfig()); userInfo != nil {
klog.Infof("[ApplicationMutatingHandler] add skip user %s", userInfo.Username)
handler.skipUsers = []string{userInfo.Username}
}
server.Register("/mutating-core-oam-dev-v1beta1-applications", &webhook.Admission{Handler: handler})
}

View File

@@ -29,8 +29,8 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/features"
"github.com/oam-dev/kubevela/pkg/oam"
)
@@ -40,7 +40,7 @@ var _ = Describe("Test Application Mutator", func() {
var mutatingHandler *MutatingHandler
BeforeEach(func() {
mutatingHandler = &MutatingHandler{}
mutatingHandler = &MutatingHandler{skipUsers: []string{types.VelaCoreName}}
Expect(mutatingHandler.InjectDecoder(decoder)).Should(BeNil())
})
@@ -55,7 +55,7 @@ var _ = Describe("Test Application Mutator", func() {
Expect(utilfeature.DefaultMutableFeatureGate.Set(fmt.Sprintf("%s=true", features.AuthenticateApplication))).Should(Succeed())
resp := mutatingHandler.Handle(ctx, admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
UserInfo: authv1.UserInfo{Groups: []string{common.Group}},
UserInfo: authv1.UserInfo{Username: types.VelaCoreName},
}})
Expect(resp.Allowed).Should(BeTrue())
Expect(resp.Patches).Should(BeNil())

View File

@@ -208,7 +208,7 @@ func applyComponents(apply oamProvider.ComponentApply, healthCheck oamProvider.C
var reasons []string
for i, res := range results {
if res.err != nil {
errs = append(errs, res.err)
errs = append(errs, fmt.Errorf("error encountered in cluster %s: %w", todoTasks[i].placement.Cluster, res.err))
}
if !res.healthy {
allHealthy = false