From 2b4ac15c498815d9e1a08cf711aac20290dcb635 Mon Sep 17 00:00:00 2001 From: Anna Vitova Date: Wed, 19 Apr 2023 15:35:09 +0200 Subject: [PATCH] feat(HMS-1246): Use permission checks in statuser --- cmd/pbackend/statuser.go | 20 +++++++++++++------- internal/clients/http/ec2/iam_client.go | 9 ++++++++- internal/kafka/source_result.go | 4 +++- internal/services/aws_iam_service.go | 6 +++--- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/cmd/pbackend/statuser.go b/cmd/pbackend/statuser.go index e7e89d5a..94f8779b 100644 --- a/cmd/pbackend/statuser.go +++ b/cmd/pbackend/statuser.go @@ -130,8 +130,7 @@ func checkSourceAvailabilityAzure(ctx context.Context) { Identity: s.Identity, ResourceType: "Application", } - // TODO: check if source is avavliable - WIP - sr.Status = kafka.StatusAvaliable + sr.Status = kafka.StatusAvailable chSend <- sr metrics.IncTotalSentAvailabilityCheckReqs(models.ProviderTypeAzure.String(), sr.Status.String(), nil) @@ -148,21 +147,28 @@ func checkSourceAvailabilityAWS(ctx context.Context) { logger.Trace().Msgf("Checking AWS source availability status %s", s.SourceApplicationID) metrics.ObserveAvailabilityCheckReqsDuration(models.ProviderTypeAWS.String(), func() error { var err error + var permissions []string sr := kafka.SourceResult{ ResourceID: s.SourceApplicationID, Identity: s.Identity, ResourceType: "Application", } - _, err = clients.GetEC2Client(ctx, &s.Authentication, "") + ec2Client, err := clients.GetEC2Client(ctx, &s.Authentication, "") if err != nil { sr.Status = kafka.StatusUnavailable sr.Err = err logger.Warn().Err(err).Msg("Could not get aws assumed client") - chSend <- sr } else { - sr.Status = kafka.StatusAvaliable - chSend <- sr + sr.Status = kafka.StatusAvailable + permissions, err = ec2Client.CheckPermission(ctx, &s.Authentication) + if err != nil { + sr.Err = err + sr.Status = kafka.StatusUnavailable + sr.MissingPermissions = permissions + logger.Warn().Err(err).Bool("missing_permissions", true).Str("source_id", sr.ResourceID).Msg("AWS permission check failed") + } } + chSend <- sr metrics.IncTotalSentAvailabilityCheckReqs(models.ProviderTypeAWS.String(), sr.Status.String(), err) return fmt.Errorf("error during check: %w", err) }) @@ -196,7 +202,7 @@ func checkSourceAvailabilityGCP(ctx context.Context) { logger.Warn().Err(err).Msg("Could not list gcp regions") chSend <- sr } else { - sr.Status = kafka.StatusAvaliable + sr.Status = kafka.StatusAvailable chSend <- sr } metrics.IncTotalSentAvailabilityCheckReqs(models.ProviderTypeGCP.String(), sr.Status.String(), err) diff --git a/internal/clients/http/ec2/iam_client.go b/internal/clients/http/ec2/iam_client.go index 4ef58c2f..aa085781 100644 --- a/internal/clients/http/ec2/iam_client.go +++ b/internal/clients/http/ec2/iam_client.go @@ -3,6 +3,7 @@ package ec2 import ( "context" "encoding/json" + "errors" "fmt" "net/url" "strings" @@ -15,6 +16,8 @@ import ( "github.com/aws/aws-sdk-go-v2/service/iam" ) +var AWSPermissionsMissing = errors.New("AWS permissions missing") + // Statement is a main policy element. type Statement struct { // Sid is an optional identifier of the Statement @@ -271,7 +274,11 @@ func (c *ec2Client) checkInlinePolicies(ctx context.Context, missingPermissions Effect: "Allow", Action: missingPermissions, } - return listMissingPermissions(inlinePoliciesStatement, missingStatement), nil + missing := listMissingPermissions(inlinePoliciesStatement, missingStatement) + if len(missingPermissions) != 0 { + return missing, fmt.Errorf("%w: %s", AWSPermissionsMissing, strings.Join(missing, ", ")) + } + return nil, nil } func (c *ec2Client) CheckPermission(ctx context.Context, auth *clients.Authentication) ([]string, error) { diff --git a/internal/kafka/source_result.go b/internal/kafka/source_result.go index dd4453a7..5c404fed 100644 --- a/internal/kafka/source_result.go +++ b/internal/kafka/source_result.go @@ -10,7 +10,7 @@ type StatusType string const ( StatusUnavailable StatusType = "unavailable" - StatusAvaliable StatusType = "available" + StatusAvailable StatusType = "available" ) type SourceResult struct { @@ -24,6 +24,8 @@ type SourceResult struct { Err error `json:"error"` Identity identity.Principal `json:"-"` + + MissingPermissions []string `json:"-"` } func (sr SourceResult) GenericMessage(ctx context.Context) (GenericMessage, error) { diff --git a/internal/services/aws_iam_service.go b/internal/services/aws_iam_service.go index 1ba35a5f..313dab90 100644 --- a/internal/services/aws_iam_service.go +++ b/internal/services/aws_iam_service.go @@ -50,13 +50,13 @@ func ValidatePermissions(w http.ResponseWriter, r *http.Request) { } logger.Info().Msgf("Listing permissions.") - permissions, err := ec2Client.CheckPermission(r.Context(), authentication) - if err != nil { + missingPermissions, err := ec2Client.CheckPermission(r.Context(), authentication) + if err != nil && missingPermissions == nil { renderError(w, r, payloads.NewAWSError(r.Context(), "unable to check aws permissions", err)) return } - if err := render.Render(w, r, payloads.NewPermissionsResponse(permissions)); err != nil { + if err := render.Render(w, r, payloads.NewPermissionsResponse(missingPermissions)); err != nil { renderError(w, r, payloads.NewRenderError(r.Context(), "unable to render missing permissions", err)) return }