Skip to content

Commit

Permalink
Authenticate and log users (#682)
Browse files Browse the repository at this point in the history
* naming

* fic test build failures

* Add mock, refactor validator to make testing possible

* remove env

* Initialize Metrics Port

* Fix Tests

* Replace username with azure object id

* Add tests

* Fix merge conflicts

* cleanup linting

* migrate start job handler from jobs controller to application controller

* remove unused image tag names

* cleanup job handler, remove connection to applications

* fix tests

* fix linting/cip

* Remove unused token/impersonation from accounts

* Move anonPrincipals and Azure Princiapls to internal structures to enforce proper creation

* rename create authorize middleware

* create custom middleware for logging authenticated user

* correct logging kube client user/server

* rewrite authentication middleware test

* rewrite authentication middleware test

* rename appNamespace

* Add config

* Add config

* Use k8s host from kube config file

* Use k8s host from kube config file

* disable CORS middlewawre

* remove unused constant
  • Loading branch information
Richard87 authored Oct 8, 2024
1 parent ae97fd8 commit 494432d
Show file tree
Hide file tree
Showing 45 changed files with 1,199 additions and 1,002 deletions.
10 changes: 4 additions & 6 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"RADIX_CONTAINER_REGISTRY":"radixdev.azurecr.io",
"PIPELINE_IMG_TAG": "master-latest",
"TEKTON_IMG_TAG": "main-latest",
"K8S_API_HOST": "https://weekly-24-clusters-dev-16ede4-uk527vqt.hcp.northeurope.azmk8s.io:443",
"RADIX_CLUSTER_TYPE": "development",
"RADIX_DNS_ZONE": "dev.radix.equinor.com",
"RADIX_CLUSTERNAME": "weekly-24",
Expand All @@ -25,11 +24,10 @@
"PROMETHEUS_URL":"http://localhost:9091",
"RADIX_APP":"radix-api",
"LOG_LEVEL":"info",
"LOG_PRETTY":"true"
},
"args": [
"--useOutClusterClient=false"
]
"LOG_PRETTY":"true",
"OIDC_AUDIENCE": "6dae42f8-4368-4678-94ff-3960e28e3630",
"OIDC_ISSUER": "https://sts.windows.net/3aa4a235-b6e2-48d5-9195-7fcf05b459b0/"
}
}
]
}
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mocks: bootstrap
mockgen -source ./api/events/event_handler.go -destination ./api/events/mock/event_handler_mock.go -package mock
mockgen -source ./api/environmentvariables/env_vars_handler.go -destination ./api/environmentvariables/env_vars_handler_mock.go -package environmentvariables
mockgen -source ./api/environmentvariables/env_vars_handler_factory.go -destination ./api/environmentvariables/env_vars_handler_factory_mock.go -package environmentvariables
mockgen -source ./api/utils/token/validator.go -destination ./api/utils/token/mock/validator_mock.go -package mock

.PHONY: test
test:
Expand Down
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,6 @@ The following env vars are needed. Useful default values in brackets.
kubectl -n monitor port-forward svc/prometheus-operator-prometheus 9091:9090
```

You also probably want to start with the argument `--useOutClusterClient=false`. When `useOutClusterClient` is `false`, several debugging settings are enabled:
* a service principal with superpowers is used to authorize the requests, and the client's `Authorization` bearer token is ignored.
* the Radix API will connect to the currently-configured `kubectl` context and ignore `K8S_API_HOST`.
* the server CORS settings are modified to accept the `X-Requested-With` header in incoming requests. This is necessary to allow direct requests from web browser while e.g. debugging [radix-web-console](https://github.com/equinor/radix-web-console).
* verbose debugging output from CORS rule evaluation is logged to console.


If you are using VSCode, there is a convenient launch configuration in `.vscode`.

#### Validate code
Expand Down
3 changes: 1 addition & 2 deletions api/alerting/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
certclientfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake"
alertModels "github.com/equinor/radix-api/api/alerting/models"
"github.com/equinor/radix-api/models"
radixmodels "github.com/equinor/radix-common/models"
operatoralert "github.com/equinor/radix-operator/pkg/apis/alert"
"github.com/equinor/radix-operator/pkg/apis/kube"
radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
Expand All @@ -34,7 +33,7 @@ func (s *HandlerTestSuite) SetupTest() {
kedaClient := kedafake.NewSimpleClientset()
secretProviderClient := secretproviderfake.NewSimpleClientset()
certClient := certclientfake.NewSimpleClientset()
s.accounts = models.NewAccounts(kubeClient, radixClient, kedaClient, secretProviderClient, nil, certClient, kubeClient, radixClient, kedaClient, secretProviderClient, nil, certClient, "", radixmodels.Impersonation{})
s.accounts = models.NewAccounts(kubeClient, radixClient, kedaClient, secretProviderClient, nil, certClient, kubeClient, radixClient, kedaClient, secretProviderClient, nil, certClient)
}

func TestHandlerTestSuite(t *testing.T) {
Expand Down
144 changes: 91 additions & 53 deletions api/applications/applications_controller_test.go

Large diffs are not rendered by default.

60 changes: 25 additions & 35 deletions api/applications/applications_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"time"

applicationModels "github.com/equinor/radix-api/api/applications/models"
"github.com/equinor/radix-api/api/deployments"
"github.com/equinor/radix-api/api/environments"
job "github.com/equinor/radix-api/api/jobs"
jobModels "github.com/equinor/radix-api/api/jobs/models"
"github.com/equinor/radix-api/api/kubequery"
"github.com/equinor/radix-api/api/middleware/auth"
apimodels "github.com/equinor/radix-api/api/models"
"github.com/equinor/radix-api/internal/config"
"github.com/equinor/radix-api/models"
radixhttp "github.com/equinor/radix-common/net/http"
radixutils "github.com/equinor/radix-common/utils"
Expand Down Expand Up @@ -51,33 +51,26 @@ type hasAccessToGetConfigMapFunc func(ctx context.Context, kubeClient kubernetes

// ApplicationHandler Instance variables
type ApplicationHandler struct {
jobHandler job.JobHandler
environmentHandler environments.EnvironmentHandler
accounts models.Accounts
config ApplicationHandlerConfig
namespace string
hasAccessToGetConfigMap func(ctx context.Context, kubeClient kubernetes.Interface, namespace string, configMapName string) (bool, error)
config config.Config
hasAccessToGetConfigMap hasAccessToGetConfigMapFunc
tektonImageTag string
pipelineImageTag string
}

// NewApplicationHandler Constructor
func NewApplicationHandler(accounts models.Accounts, config ApplicationHandlerConfig, hasAccessToGetConfigMap hasAccessToGetConfigMapFunc) ApplicationHandler {
func NewApplicationHandler(accounts models.Accounts, config config.Config, hasAccessToGetConfigMap hasAccessToGetConfigMapFunc) ApplicationHandler {
return ApplicationHandler{
accounts: accounts,
jobHandler: job.Init(accounts, deployments.Init(accounts)),
environmentHandler: environments.Init(environments.WithAccounts(accounts)),
accounts: accounts,
config: config,
namespace: getApiNamespace(config),
hasAccessToGetConfigMap: hasAccessToGetConfigMap,
tektonImageTag: config.TektonImageTag,
pipelineImageTag: config.PipelineImageTag,
}
}

func getApiNamespace(config ApplicationHandlerConfig) string {
if namespace := operatorUtils.GetEnvironmentNamespace(config.AppName, config.EnvironmentName); len(namespace) > 0 {
return namespace
}
panic("missing RADIX_APP or RADIX_ENVIRONMENT environment variables")
}

func (ah *ApplicationHandler) getUserAccount() models.Account {
return ah.accounts.UserAccount
}
Expand Down Expand Up @@ -129,11 +122,7 @@ func (ah *ApplicationHandler) RegisterApplication(ctx context.Context, applicati
var err error

application := applicationRegistrationRequest.ApplicationRegistration

creator, err := ah.accounts.GetOriginator()
if err != nil {
return nil, err
}
creator := auth.GetOriginator(ctx)

application.RadixConfigFullName = cleanFileFullName(application.RadixConfigFullName)
if len(application.RadixConfigFullName) > 0 {
Expand Down Expand Up @@ -442,7 +431,7 @@ func (ah *ApplicationHandler) TriggerPipelinePromote(ctx context.Context, appNam
return nil, radixhttp.ValidationError("Radix Application Pipeline", "Deployment name, from environment and to environment are required for \"promote\" pipeline")
}

log.Ctx(ctx).Info().Msgf("Creating promote pipeline job for %s using deployment %s from environment %s into environment %s", appName, deploymentName, fromEnvironment, toEnvironment)
log.Ctx(ctx).Info().Msgf("Creating promote pipeline jobController for %s using deployment %s from environment %s into environment %s", appName, deploymentName, fromEnvironment, toEnvironment)

pipeline, err := jobPipeline.GetPipelineFromName("promote")
if err != nil {
Expand All @@ -457,7 +446,7 @@ func (ah *ApplicationHandler) TriggerPipelinePromote(ctx context.Context, appNam

jobParameters := pipelineParameters.MapPipelineParametersPromoteToJobParameter()
jobParameters.CommitID = radixDeployment.GetLabels()[kube.RadixCommitLabel]
jobSummary, err := ah.jobHandler.HandleStartPipelineJob(ctx, appName, pipeline, jobParameters)
jobSummary, err := HandleStartPipelineJob(ctx, ah.accounts.UserAccount.RadixClient, appName, ah.pipelineImageTag, ah.tektonImageTag, pipeline, jobParameters)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -497,7 +486,7 @@ func (ah *ApplicationHandler) TriggerPipelineDeploy(ctx context.Context, appName
return nil, radixhttp.ValidationError("Radix Application Pipeline", "To environment is required for \"deploy\" pipeline")
}

log.Ctx(ctx).Info().Msgf("Creating deploy pipeline job for %s into environment %s", appName, toEnvironment)
log.Ctx(ctx).Info().Msgf("Creating deploy pipeline jobController for %s into environment %s", appName, toEnvironment)

pipeline, err := jobPipeline.GetPipelineFromName("deploy")
if err != nil {
Expand All @@ -506,7 +495,7 @@ func (ah *ApplicationHandler) TriggerPipelineDeploy(ctx context.Context, appName

jobParameters := pipelineParameters.MapPipelineParametersDeployToJobParameter()

jobSummary, err := ah.jobHandler.HandleStartPipelineJob(ctx, appName, pipeline, jobParameters)
jobSummary, err := HandleStartPipelineJob(ctx, ah.accounts.UserAccount.RadixClient, appName, ah.pipelineImageTag, ah.tektonImageTag, pipeline, jobParameters)
if err != nil {
return nil, err
}
Expand All @@ -521,7 +510,7 @@ func (ah *ApplicationHandler) TriggerPipelineApplyConfig(ctx context.Context, ap
return nil, err
}

log.Ctx(ctx).Info().Msgf("Creating apply config pipeline job for %s", appName)
log.Ctx(ctx).Info().Msgf("Creating apply config pipeline jobController for %s", appName)

pipeline, err := jobPipeline.GetPipelineFromName("apply-config")
if err != nil {
Expand All @@ -530,7 +519,7 @@ func (ah *ApplicationHandler) TriggerPipelineApplyConfig(ctx context.Context, ap

jobParameters := pipelineParameters.MapPipelineParametersApplyConfigToJobParameter()

jobSummary, err := ah.jobHandler.HandleStartPipelineJob(ctx, appName, pipeline, jobParameters)
jobSummary, err := HandleStartPipelineJob(ctx, ah.accounts.UserAccount.RadixClient, appName, ah.pipelineImageTag, ah.tektonImageTag, pipeline, jobParameters)
if err != nil {
return nil, err
}
Expand All @@ -553,7 +542,7 @@ func (ah *ApplicationHandler) triggerPipelineBuildOrBuildDeploy(ctx context.Cont
return nil, applicationModels.AppNameAndBranchAreRequiredForStartingPipeline()
}

log.Ctx(ctx).Info().Msgf("Creating build pipeline job for %s on branch %s for commit %s", appName, branch, commitID)
log.Ctx(ctx).Info().Msgf("Creating build pipeline jobController for %s on branch %s for commit %s", appName, branch, commitID)

radixRegistration, err := ah.getUserAccount().RadixClient.RadixV1().RadixRegistrations().Get(ctx, appName, metav1.GetOptions{})
if err != nil {
Expand Down Expand Up @@ -581,7 +570,7 @@ func (ah *ApplicationHandler) triggerPipelineBuildOrBuildDeploy(ctx context.Cont

log.Ctx(ctx).Info().Msgf("Creating build pipeline job for %s on branch %s for commit %s", appName, branch, commitID)

jobSummary, err := ah.jobHandler.HandleStartPipelineJob(ctx, appName, pipeline, jobParameters)
jobSummary, err := HandleStartPipelineJob(ctx, ah.accounts.UserAccount.RadixClient, appName, ah.pipelineImageTag, ah.tektonImageTag, pipeline, jobParameters)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -749,31 +738,32 @@ func (ah *ApplicationHandler) validateUserIsMemberOfAdGroups(ctx context.Context
}
return nil
}
radixApiAppNamespace := operatorUtils.GetEnvironmentNamespace(ah.config.AppName, ah.config.EnvironmentName)
name := fmt.Sprintf("access-validation-%s", appName)
labels := map[string]string{"radix-access-validation": "true"}
configMapName := fmt.Sprintf("%s-%s", name, strings.ToLower(operatorUtils.RandString(6)))
role, err := createRoleToGetConfigMap(ctx, ah.accounts.ServiceAccount.Client, ah.namespace, name, labels, configMapName)
role, err := createRoleToGetConfigMap(ctx, ah.accounts.ServiceAccount.Client, radixApiAppNamespace, name, labels, configMapName)
if err != nil {
return err
}
defer func() {
err = deleteRole(context.Background(), ah.accounts.ServiceAccount.Client, ah.namespace, role.GetName())
err = deleteRole(context.Background(), ah.accounts.ServiceAccount.Client, radixApiAppNamespace, role.GetName())
if err != nil {
log.Ctx(ctx).Warn().Msgf("Failed to delete role %s: %v", role.GetName(), err)
}
}()
roleBinding, err := createRoleBindingForRole(ctx, ah.accounts.ServiceAccount.Client, ah.namespace, role, name, adGroups, labels)
roleBinding, err := createRoleBindingForRole(ctx, ah.accounts.ServiceAccount.Client, radixApiAppNamespace, role, name, adGroups, labels)
if err != nil {
return err
}
defer func() {
err = deleteRoleBinding(context.Background(), ah.accounts.ServiceAccount.Client, ah.namespace, roleBinding.GetName())
err = deleteRoleBinding(context.Background(), ah.accounts.ServiceAccount.Client, radixApiAppNamespace, roleBinding.GetName())
if err != nil {
log.Ctx(ctx).Warn().Msgf("Failed to delete role binding %s: %v", roleBinding.GetName(), err)
}
}()

valid, err := ah.hasAccessToGetConfigMap(ctx, ah.accounts.UserAccount.Client, ah.namespace, configMapName)
valid, err := ah.hasAccessToGetConfigMap(ctx, ah.accounts.UserAccount.Client, radixApiAppNamespace, configMapName)
if err != nil {
return err
}
Expand Down
48 changes: 0 additions & 48 deletions api/applications/applications_handler_config.go

This file was deleted.

5 changes: 3 additions & 2 deletions api/applications/applications_handler_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/equinor/radix-api/api/utils/access"
"github.com/equinor/radix-api/internal/config"
"github.com/equinor/radix-api/models"
authorizationapi "k8s.io/api/authorization/v1"
"k8s.io/client-go/kubernetes"
Expand All @@ -15,11 +16,11 @@ type ApplicationHandlerFactory interface {
}

type applicationHandlerFactory struct {
config ApplicationHandlerConfig
config config.Config
}

// NewApplicationHandlerFactory creates a new ApplicationHandlerFactory
func NewApplicationHandlerFactory(config ApplicationHandlerConfig) ApplicationHandlerFactory {
func NewApplicationHandlerFactory(config config.Config) ApplicationHandlerFactory {
return &applicationHandlerFactory{
config: config,
}
Expand Down
Loading

0 comments on commit 494432d

Please sign in to comment.