Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require Admin to restart Oauth2Proxy #674

Merged
merged 6 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/applications/applications_handler_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package applications

import (
"context"

"github.com/equinor/radix-api/api/utils/access"
"github.com/equinor/radix-api/models"
authorizationapi "k8s.io/api/authorization/v1"
Expand Down
7 changes: 6 additions & 1 deletion api/environments/component_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ func (eh EnvironmentHandler) RestartComponent(ctx context.Context, appName, envN
// RestartComponentAuxiliaryResource Restarts a component's auxiliary resource
func (eh EnvironmentHandler) RestartComponentAuxiliaryResource(ctx context.Context, appName, envName, componentName, auxType string) error {
log.Ctx(ctx).Info().Msgf("Restarting auxiliary resource %s for component %s, %s", auxType, componentName, appName)
if isAdmin, err := kubequery.IsRadixApplicationAdmin(ctx, eh.accounts.UserAccount.Client, appName); err != nil {
return err
} else if !isAdmin {
return http.ForbiddenError("you must be administrator to restart the Oauth2 Proxy service")
}

radixDeployment, err := kubequery.GetLatestRadixDeployment(ctx, eh.accounts.UserAccount.RadixClient, appName, envName)
if err != nil {
Expand Down Expand Up @@ -163,7 +168,7 @@ func canDeploymentBeRestarted(deployment *appsv1.Deployment) bool {
}

func (eh EnvironmentHandler) patchDeploymentForRestart(ctx context.Context, deployment *appsv1.Deployment) error {
deployClient := eh.accounts.UserAccount.Client.AppsV1().Deployments(deployment.GetNamespace())
deployClient := eh.accounts.ServiceAccount.Client.AppsV1().Deployments(deployment.GetNamespace())

return retry.RetryOnConflict(retry.DefaultRetry, func() error {
deployToPatch, err := deployClient.Get(ctx, deployment.GetName(), metav1.GetOptions{})
Expand Down
48 changes: 44 additions & 4 deletions api/environments/environment_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (
operatorutils "github.com/equinor/radix-operator/pkg/apis/utils"
"github.com/equinor/radix-operator/pkg/apis/utils/labels"
radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned"
"github.com/equinor/radix-operator/pkg/client/clientset/versioned/fake"
radixfake "github.com/equinor/radix-operator/pkg/client/clientset/versioned/fake"
"github.com/golang/mock/gomock"
kedav2 "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned"
kedafake "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned/fake"
Expand All @@ -43,10 +43,13 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
authorizationapiv1 "k8s.io/api/authorization/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
kubefake "k8s.io/client-go/kubernetes/fake"
testing2 "k8s.io/client-go/testing"
secretsstorevclient "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned"
secretproviderfake "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/fake"
)
Expand All @@ -64,10 +67,10 @@ const (
subscriptionId = "12347718-c8f8-4995-bfbb-02655ff1f89c"
)

func setupTest(t *testing.T, envHandlerOpts []EnvironmentHandlerOptions) (*commontest.Utils, *controllertest.Utils, *controllertest.Utils, kubernetes.Interface, radixclient.Interface, kedav2.Interface, prometheusclient.Interface, secretsstorevclient.Interface, *certclientfake.Clientset) {
func setupTest(t *testing.T, envHandlerOpts []EnvironmentHandlerOptions) (*commontest.Utils, *controllertest.Utils, *controllertest.Utils, *kubefake.Clientset, radixclient.Interface, kedav2.Interface, prometheusclient.Interface, secretsstorevclient.Interface, *certclientfake.Clientset) {
// Setup
kubeclient := kubefake.NewSimpleClientset()
radixClient := fake.NewSimpleClientset()
kubeclient := kubefake.NewClientset()
radixClient := radixfake.NewSimpleClientset()
kedaClient := kedafake.NewSimpleClientset()
prometheusclient := prometheusfake.NewSimpleClientset()
secretproviderclient := secretproviderfake.NewSimpleClientset()
Expand Down Expand Up @@ -1005,13 +1008,42 @@ func Test_GetEnvironmentEvents_Handler(t *testing.T) {

func TestRestartAuxiliaryResource(t *testing.T) {
auxType := "oauth"
called := 0

// Setup
commonTestUtils, environmentControllerTestUtils, _, kubeClient, _, _, _, _, _ := setupTest(t, nil)
kubeClient.Fake.PrependReactor("create", "*", func(action testing2.Action) (handled bool, ret runtime.Object, err error) {
createAction, ok := action.DeepCopy().(testing2.CreateAction)
if !ok {
return false, nil, nil
}

review, ok := createAction.GetObject().(*authorizationapiv1.SelfSubjectAccessReview)
if !ok {
return false, nil, nil
}

called++

if review.Spec.ResourceAttributes.Name != anyAppName {
return true, review, nil
}

assert.Equal(t, review.Spec.ResourceAttributes.Name, anyAppName)
assert.Equal(t, review.Spec.ResourceAttributes.Resource, v1.ResourceRadixRegistrations)
assert.Equal(t, review.Spec.ResourceAttributes.Verb, "patch")

review.Status.Allowed = true
return true, review, nil
})
_, err := commonTestUtils.ApplyRegistration(operatorutils.
NewRegistrationBuilder().
WithName(anyAppName))
require.NoError(t, err)
_, err = commonTestUtils.ApplyRegistration(operatorutils.
NewRegistrationBuilder().
WithName("forbidden"))
require.NoError(t, err)
_, err = commonTestUtils.ApplyApplication(operatorutils.
NewRadixApplicationBuilder().
WithAppName(anyAppName).
Expand Down Expand Up @@ -1045,9 +1077,17 @@ func TestRestartAuxiliaryResource(t *testing.T) {
responseChannel := environmentControllerTestUtils.ExecuteRequest("POST", fmt.Sprintf("/api/v1/applications/%s/environments/%s/components/%s/aux/%s/restart", anyAppName, anyEnvironment, anyComponentName, auxType))
response := <-responseChannel
assert.Equal(t, http.StatusOK, response.Code)
assert.Equal(t, 1, called)

kubeDeploy, _ := kubeClient.AppsV1().Deployments(envNs).Get(context.Background(), "comp1-aux-resource", metav1.GetOptions{})
assert.NotEmpty(t, kubeDeploy.Spec.Template.Annotations[restartedAtAnnotation])

// Test Forbidden for other app names

responseChannel = environmentControllerTestUtils.ExecuteRequest("POST", fmt.Sprintf("/api/v1/applications/%s/environments/%s/components/%s/aux/%s/restart", "forbidden", anyEnvironment, anyComponentName, auxType))
response = <-responseChannel
assert.Equal(t, http.StatusForbidden, response.Code)
assert.Equal(t, 2, called)
}

func Test_GetJobs(t *testing.T) {
Expand Down
18 changes: 9 additions & 9 deletions api/environments/environment_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/equinor/radix-common/utils/slice"
deployUtils "github.com/equinor/radix-operator/pkg/apis/deployment"
"github.com/equinor/radix-operator/pkg/apis/kube"
v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
k8sObjectUtils "github.com/equinor/radix-operator/pkg/apis/utils"
"github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -126,7 +126,7 @@ func (eh EnvironmentHandler) GetEnvironmentSummary(ctx context.Context, appName
if err != nil {
return nil, err
}
envNames := slice.Map(reList, func(re v1.RadixEnvironment) string { return re.Spec.EnvName })
envNames := slice.Map(reList, func(re radixv1.RadixEnvironment) string { return re.Spec.EnvName })
rdList, err := kubequery.GetRadixDeploymentsForEnvironments(ctx, eh.accounts.UserAccount.RadixClient, appName, envNames, 10)
if err != nil {
return nil, err
Expand Down Expand Up @@ -208,7 +208,7 @@ func (eh EnvironmentHandler) GetEnvironment(ctx context.Context, appName, envNam
}

// CreateEnvironment Handler for CreateEnvironment. Creates an environment if it does not exist
func (eh EnvironmentHandler) CreateEnvironment(ctx context.Context, appName, envName string) (*v1.RadixEnvironment, error) {
func (eh EnvironmentHandler) CreateEnvironment(ctx context.Context, appName, envName string) (*radixv1.RadixEnvironment, error) {
// ensure application exists
rr, err := eh.accounts.UserAccount.RadixClient.RadixV1().RadixRegistrations().Get(ctx, appName, metav1.GetOptions{})
if err != nil {
Expand Down Expand Up @@ -286,7 +286,7 @@ func (eh EnvironmentHandler) getNotOrphanedEnvNames(ctx context.Context, appName
}
return slice.Map(
slice.FindAll(reList, predicate.IsNotOrphanEnvironment),
func(re v1.RadixEnvironment) string { return re.Spec.EnvName },
func(re radixv1.RadixEnvironment) string { return re.Spec.EnvName },
), nil
}

Expand Down Expand Up @@ -315,7 +315,7 @@ func (eh EnvironmentHandler) StopEnvironment(ctx context.Context, appName, envNa
return err
}
if radixDeployment == nil {
return http.ValidationError(v1.KindRadixDeployment, "no radix deployments found")
return http.ValidationError(radixv1.KindRadixDeployment, "no radix deployments found")
}

log.Ctx(ctx).Info().Msgf("Stopping components in environment %s, %s", envName, appName)
Expand All @@ -335,7 +335,7 @@ func (eh EnvironmentHandler) ResetManuallyStoppedComponentsInEnvironment(ctx con
return err
}
if radixDeployment == nil {
return http.ValidationError(v1.KindRadixDeployment, "no radix deployments found")
return http.ValidationError(radixv1.KindRadixDeployment, "no radix deployments found")
}

log.Ctx(ctx).Info().Msgf("Starting components in environment %s, %s", envName, appName)
Expand All @@ -356,7 +356,7 @@ func (eh EnvironmentHandler) RestartEnvironment(ctx context.Context, appName, en
return err
}
if radixDeployment == nil {
return http.ValidationError(v1.KindRadixDeployment, "no radix deployments found")
return http.ValidationError(radixv1.KindRadixDeployment, "no radix deployments found")
}

log.Ctx(ctx).Info().Msgf("Restarting components in environment %s, %s", envName, appName)
Expand Down Expand Up @@ -423,7 +423,7 @@ func (eh EnvironmentHandler) getRadixCommonComponentUpdater(ctx context.Context,
return nil, err
}
if rd == nil {
return nil, http.ValidationError(v1.KindRadixDeployment, "no radix deployments found")
return nil, http.ValidationError(radixv1.KindRadixDeployment, "no radix deployments found")
}
baseUpdater := &baseComponentUpdater{
appName: appName,
Expand All @@ -432,7 +432,7 @@ func (eh EnvironmentHandler) getRadixCommonComponentUpdater(ctx context.Context,
radixDeployment: rd,
}
var updater radixDeployCommonComponentUpdater
var componentToPatch v1.RadixCommonDeployComponent
var componentToPatch radixv1.RadixCommonDeployComponent
componentIndex, componentToPatch := deployUtils.GetDeploymentComponent(rd, componentName)
if !radixutils.IsNil(componentToPatch) {
updater = &radixDeployComponentUpdater{base: baseUpdater}
Expand Down
13 changes: 13 additions & 0 deletions api/kubequery/radixapplication.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@ package kubequery
import (
"context"

"github.com/equinor/radix-api/api/utils/access"
radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
operatorUtils "github.com/equinor/radix-operator/pkg/apis/utils"
radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned"
authorizationapi "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)

// GetRadixApplication returns the RadixApplication for the specified application name.
func GetRadixApplication(ctx context.Context, client radixclient.Interface, appName string) (*radixv1.RadixApplication, error) {
ns := operatorUtils.GetAppNamespace(appName)
return client.RadixV1().RadixApplications(ns).Get(ctx, appName, metav1.GetOptions{})
}

func IsRadixApplicationAdmin(ctx context.Context, kubeClient kubernetes.Interface, appName string) (bool, error) {
return access.HasAccess(ctx, kubeClient, &authorizationapi.ResourceAttributes{
Verb: "patch",
Group: radixv1.GroupName,
Resource: radixv1.ResourceRadixRegistrations,
Version: "*",
Name: appName,
})
}
35 changes: 35 additions & 0 deletions api/kubequery/radixapplication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import (
radixfake "github.com/equinor/radix-operator/pkg/client/clientset/versioned/fake"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
authorizationapiv1 "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
testing2 "k8s.io/client-go/testing"
)

func Test_GetRadixApplication(t *testing.T) {
Expand All @@ -26,3 +30,34 @@ func Test_GetRadixApplication(t *testing.T) {
_, err = GetRadixApplication(context.Background(), client, "app2")
assert.True(t, errors.IsNotFound(err))
}

func Test_IsRadixApplicationAdmin(t *testing.T) {
called := 0
client := fake.NewClientset()
client.Fake.PrependReactor("create", "*", func(action testing2.Action) (handled bool, ret runtime.Object, err error) {
createAction, ok := action.DeepCopy().(testing2.CreateAction)
if !ok {
return false, nil, nil
}

review, ok := createAction.GetObject().(*authorizationapiv1.SelfSubjectAccessReview)
if !ok {
return false, nil, nil
}

called++
assert.Equal(t, review.Spec.ResourceAttributes, &authorizationapiv1.ResourceAttributes{
Verb: "patch",
Group: radixv1.GroupName,
Resource: radixv1.ResourceRadixRegistrations,
Version: "*",
Name: "any-app-name",
})
return true, review, nil
})

actual, err := IsRadixApplicationAdmin(context.Background(), client, "any-app-name")
require.NoError(t, err)
assert.False(t, actual, "Should be false since we dont set it to true in our reactor")
assert.Equal(t, 1, called)
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ toolchain go1.22.5

require (
github.com/cert-manager/cert-manager v1.15.0
github.com/equinor/radix-common v1.9.4
github.com/equinor/radix-common v1.9.5
github.com/equinor/radix-job-scheduler v1.11.0
github.com/equinor/radix-operator v1.58.3
github.com/equinor/radix-operator v1.59.1
github.com/evanphx/json-patch/v5 v5.9.0
github.com/felixge/httpsnoop v1.0.4
github.com/golang-jwt/jwt/v5 v5.2.1
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/equinor/radix-common v1.9.4 h1:ErSnB2tqlRwaQuQdaA0qzsReDtHDgubcvqRO098ncEw=
github.com/equinor/radix-common v1.9.4/go.mod h1:+g0Wj0D40zz29DjNkYKVmCVeYy4OsFWKI7Qi9rA6kpY=
github.com/equinor/radix-common v1.9.5 h1:p1xldkYUoavwIMguoxxOyVkOXLPA6K8qMsgzeztQtQw=
github.com/equinor/radix-common v1.9.5/go.mod h1:+g0Wj0D40zz29DjNkYKVmCVeYy4OsFWKI7Qi9rA6kpY=
github.com/equinor/radix-job-scheduler v1.11.0 h1:8wCmXOVl/1cto8q2WJQEE06Cw68/QmfoifYVR49vzkY=
github.com/equinor/radix-job-scheduler v1.11.0/go.mod h1:yPXn3kDcMY0Z3kBkosjuefsdY1x2g0NlBeybMmHz5hc=
github.com/equinor/radix-operator v1.58.3 h1:F4YhNkQ4uRONP125OTfG8hdy9PiyKlOWVO8/p2NIi70=
github.com/equinor/radix-operator v1.58.3/go.mod h1:DTPXOxU3uHPvji7qBGSK1b03iXROpX3l94kYjcOHkPM=
github.com/equinor/radix-operator v1.59.1 h1:wzb2tF4MWtGDWmyYuIv3oh17G5Bx8ztW9O+WgnI3QFc=
github.com/equinor/radix-operator v1.59.1/go.mod h1:uRW9SgVZ94hkpq87npVv2YVviRuXNJ1zgCleya1uvr8=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
Expand Down
Loading