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

Cloud data provider #1144

Merged
merged 25 commits into from
Jul 25, 2023
Merged

Cloud data provider #1144

merged 25 commits into from
Jul 25, 2023

Conversation

uri-weisman
Copy link
Contributor

@uri-weisman uri-weisman commented Jul 18, 2023

Summary of your changes

  1. Extend the existing aws data provider to serve all cloud benchmarks - AWS, GCP, and AZURE in the future.
  2. Create a new identity provider for GCP.
  3. Bump csp policies version.

Screenshot/Data

GCP

Screenshot 2023-07-19 at 10 52 50

AWS
Screenshot 2023-07-18 at 13 16 36

Related Issues

Checklist

  • I have added tests that prove my fix is effective or that my feature works
  • I have added the necessary README/documentation (if appropriate)

@mergify
Copy link

mergify bot commented Jul 18, 2023

This pull request does not have a backport label. Could you fix it @uri-weisman? 🙏
To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-v./d./d./d is the label to automatically backport to the 8./d branch. /d is the digit
    NOTE: backport-skip has been added to this pull request.

@github-actions
Copy link

github-actions bot commented Jul 18, 2023

📊 Allure Report - 💔 Some tests failed or were broken.

Result Count
🟥 Failed 4
🟩 Passed 29
⬜ Skipped 1

@uri-weisman uri-weisman marked this pull request as ready for review July 19, 2023 08:02
@uri-weisman uri-weisman requested a review from a team as a code owner July 19, 2023 08:02
@uri-weisman uri-weisman linked an issue Jul 24, 2023 that may be closed by this pull request
5 tasks
flavors/benchmark/eks.go Outdated Show resolved Hide resolved
flavors/benchmark/eks.go Outdated Show resolved Hide resolved
flavors/benchmark/gcp.go Outdated Show resolved Hide resolved
flavors/vulnerability.go Outdated Show resolved Hide resolved
@uri-weisman uri-weisman requested a review from olegsu July 25, 2023 06:57
@@ -60,25 +72,43 @@ func (a DataProvider) FetchData(_ string, id string) (types.Data, error) {
}

func (a DataProvider) EnrichEvent(event *beat.Event, resMetadata fetching.ResourceMetadata) error {
_, err := event.Fields.Put(cloudAccountIdField, strings.FirstNonEmpty(resMetadata.AwsAccountId, a.accountId))
err := insertIfNotEmpty(cloudAccountIdField, strings.FirstNonEmpty(resMetadata.AwsAccountId, a.accountId), event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker: make sure this doesn't break aws orgs account fields (LGTM but let's double check)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orestisfl how this can be validated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, manually since we don't have E2E tests yet.

However, the unit tests cover the case already so maybe it's overkill.

flavors/benchmark/aws.go Outdated Show resolved Hide resolved
flavors/benchmark/eks.go Outdated Show resolved Hide resolved
flavors/benchmark/gcp.go Outdated Show resolved Hide resolved
flavors/posture.go Outdated Show resolved Hide resolved
Comment on lines +35 to +92
type ProviderGetter interface {
GetIdentity(ctx context.Context, cfg config.GcpConfig) (*cloud.Identity, error)
}

type Provider struct {
service ResourceManager
logger *logp.Logger
}

// CloudResourceManagerService is a wrapper around the GCP resource manager service to make it easier to mock
type CloudResourceManagerService struct {
service *cloudresourcemanager.Service
}

type ResourceManager interface {
projectsGet(context.Context, string) (*cloudresourcemanager.Project, error)
}

func NewProvider(ctx context.Context, cfg *config.Config, logger *logp.Logger) *Provider {
gcpClientOpt, err := auth.GetGcpClientConfig(cfg, logger)
if err != nil {
logger.Errorf("failed to get GCP client config: %v", err)
return nil
}
gcpClientOpt = append(gcpClientOpt, option.WithScopes(cloudresourcemanager.CloudPlatformReadOnlyScope))
crmService, err := cloudresourcemanager.NewService(ctx, gcpClientOpt...)
if err != nil {
logger.Errorf("failed to create GCP resource manager service: %v", err)
return nil
}

return &Provider{
service: &CloudResourceManagerService{service: crmService},
logger: logger,
}
}

// GetIdentity returns GCP identity information
func (p *Provider) GetIdentity(ctx context.Context, cfg config.GcpConfig) (*cloud.Identity, error) {
proj, err := p.service.projectsGet(ctx, "projects/"+cfg.ProjectId)
if err != nil {
return nil, err
}

return &cloud.Identity{
Provider: provider,
ProjectId: proj.ProjectId,
ProjectName: proj.DisplayName,
}, nil
}

func (p *CloudResourceManagerService) projectsGet(ctx context.Context, id string) (*cloudresourcemanager.Project, error) {
project, err := p.service.Projects.Get(id).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("unable to get project with id '%v': %v", id, err)
}
return project, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning nil from NewProvider can introduce segfault risks. GetIdentity is only called once in the lifetime of the app so we can move all initialization code in the body of the function like we do for other Dependencies in the Benchmark

Suggested change
type ProviderGetter interface {
GetIdentity(ctx context.Context, cfg config.GcpConfig) (*cloud.Identity, error)
}
type Provider struct {
service ResourceManager
logger *logp.Logger
}
// CloudResourceManagerService is a wrapper around the GCP resource manager service to make it easier to mock
type CloudResourceManagerService struct {
service *cloudresourcemanager.Service
}
type ResourceManager interface {
projectsGet(context.Context, string) (*cloudresourcemanager.Project, error)
}
func NewProvider(ctx context.Context, cfg *config.Config, logger *logp.Logger) *Provider {
gcpClientOpt, err := auth.GetGcpClientConfig(cfg, logger)
if err != nil {
logger.Errorf("failed to get GCP client config: %v", err)
return nil
}
gcpClientOpt = append(gcpClientOpt, option.WithScopes(cloudresourcemanager.CloudPlatformReadOnlyScope))
crmService, err := cloudresourcemanager.NewService(ctx, gcpClientOpt...)
if err != nil {
logger.Errorf("failed to create GCP resource manager service: %v", err)
return nil
}
return &Provider{
service: &CloudResourceManagerService{service: crmService},
logger: logger,
}
}
// GetIdentity returns GCP identity information
func (p *Provider) GetIdentity(ctx context.Context, cfg config.GcpConfig) (*cloud.Identity, error) {
proj, err := p.service.projectsGet(ctx, "projects/"+cfg.ProjectId)
if err != nil {
return nil, err
}
return &cloud.Identity{
Provider: provider,
ProjectId: proj.ProjectId,
ProjectName: proj.DisplayName,
}, nil
}
func (p *CloudResourceManagerService) projectsGet(ctx context.Context, id string) (*cloudresourcemanager.Project, error) {
project, err := p.service.Projects.Get(id).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("unable to get project with id '%v': %v", id, err)
}
return project, nil
}
type ProviderGetter interface {
GetIdentity(ctx context.Context, cfg config.GcpConfig) (*cloud.Identity, error)
}
type Provider struct {
}
// CloudResourceManagerService is a wrapper around the GCP resource manager service to make it easier to mock
type CloudResourceManagerService struct {
service *cloudresourcemanager.Service
}
type ResourceManager interface {
projectsGet(context.Context, string) (*cloudresourcemanager.Project, error)
}
// GetIdentity returns GCP identity information
func (p *Provider) GetIdentity(ctx context.Context, logger *logp.Logger, cfg config.GcpConfig) (*cloud.Identity, error) {
gcpClientOpt, err := auth.GetGcpClientConfig(cfg, logger)
if err != nil {
return nil, err
}
gcpClientOpt = append(gcpClientOpt, option.WithScopes(cloudresourcemanager.CloudPlatformReadOnlyScope))
crmService, err := cloudresourcemanager.NewService(ctx, gcpClientOpt...)
if err != nil {
logger.Errorf("failed to create GCP resource manager service: %v", err)
return nil, err
}
proj, err := CloudResourceManagerService{service: crmService}.projectsGet(ctx, "projects/"+cfg.ProjectId)
if err != nil {
return nil, err
}
return &cloud.Identity{
Provider: provider,
ProjectId: proj.ProjectId,
ProjectName: proj.DisplayName,
}, nil
}
func (p CloudResourceManagerService) projectsGet(ctx context.Context, id string) (*cloudresourcemanager.Project, error) {
project, err := p.service.Projects.Get(id).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("unable to get project with id '%v': %v", id, err)
}
return project, nil
}

which also requires changing GetGcpClientConfig:

func GetGcpClientConfig(gcpCred config.GcpConfig, log *logp.Logger) ([]option.ClientOption, error) {

Copy link
Contributor Author

@uri-weisman uri-weisman Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orestisfl - I get your point but in your implementation, I won't be able to mock the cloudresourcemanager service.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we return also an error in that case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uri-weisman

How about:

// GetIdentity returns GCP identity information
func (p *Provider) GetIdentity(ctx context.Context, cfg config.GcpConfig, logger *logp.Logger) (*cloud.Identity, error) {
	if err := p.initialize(ctx, cfg, logger); err != nil {
		return nil, err
	}

	proj, err := p.service.projectsGet(ctx, "projects/"+cfg.ProjectId)
	if err != nil {
		return nil, err
	}

	return &cloud.Identity{
		Provider:    provider,
		ProjectId:   proj.ProjectId,
		ProjectName: proj.DisplayName,
	}, nil
}

func (p *Provider) initialize(ctx context.Context, cfg config.GcpConfig, logger *logp.Logger) error {
	if p.service != nil {
		return nil
	}

	gcpClientOpt, err := auth.GetGcpClientConfig(cfg, logger)
	if err != nil {
		return err
	}
	gcpClientOpt = append(gcpClientOpt, option.WithScopes(cloudresourcemanager.CloudPlatformReadOnlyScope))
	crmService, err := cloudresourcemanager.NewService(ctx, gcpClientOpt...)
	if err != nil {
		return err
	}

	p.service = &CloudResourceManagerService{service: crmService}
	return nil
}

@mergify
Copy link

mergify bot commented Jul 25, 2023

This pull request is now in conflicts. Could you fix it? 🙏
To fixup this pull request, you can check out it locally. See documentation: https://help.github.com/articles/checking-out-pull-requests-locally/

git fetch upstream
git checkout -b gcp_data_provider upstream/gcp_data_provider
git merge upstream/main
git push upstream gcp_data_provider

@orestisfl orestisfl self-requested a review July 25, 2023 14:01
@@ -71,11 +71,11 @@ func validateJSONFromFile(filePath string) error {

b, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("The file %q cannot be read", filePath)
return fmt.Errorf("the file %q cannot be read", filePath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("the file %q cannot be read", filePath)
return fmt.Errorf("file %q cannot be read", filePath)

}

if !json.Valid(b) {
return fmt.Errorf("The file %q does not contain valid JSON", filePath)
return fmt.Errorf("the file %q does not contain valid JSON", filePath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("the file %q does not contain valid JSON", filePath)
return fmt.Errorf("file %q does not contain valid JSON", filePath)

@uri-weisman uri-weisman merged commit e171e1f into elastic:main Jul 25, 2023
11 checks passed
@uri-weisman uri-weisman deleted the gcp_data_provider branch July 25, 2023 15:13
orestisfl added a commit to orestisfl/cloudbeat that referenced this pull request Jul 26, 2023
orestisfl added a commit to orestisfl/cloudbeat that referenced this pull request Jul 26, 2023
orestisfl added a commit that referenced this pull request Jul 27, 2023
orestisfl added a commit that referenced this pull request Jul 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[CIS GCP] Create a data provider
4 participants