Skip to content
Merged
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ tests/allure/reports/*

# vscode
.vscode*
*.code-workspace
*.code-workspace
/evaluator/tmpBundles/bundle.tar.gz
17 changes: 11 additions & 6 deletions resources/fetchers/ecr_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (f *ECRFactory) CreateFrom(log *logp.Logger, cfg ECRFetcherConfig, ch chan
return nil, fmt.Errorf("failed to initialize AWS credentials: %w", err)
}

ecrPrivateProvider := awslib.NewEcrProvider(awsConfig)
ecrPrivateProvider := awslib.NewEcrProvider()
ecrPublicProvider := awslib.NewEcrPublicProvider()
kubeClient, err := f.KubernetesProvider.GetClient(cfg.KubeConfig, kubernetes.KubeClientOptions{})
if err != nil {
Expand All @@ -79,15 +79,19 @@ func (f *ECRFactory) CreateFrom(log *logp.Logger, cfg ECRFetcherConfig, ch chan
return nil, fmt.Errorf("could not retrieve user identity for ECR fetcher: %w", err)
}

privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, *identity.Account, awsConfig.Region)
privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, *identity.Account)

privateECRExecutor := PodDescriber{
FilterRegex: regexp.MustCompile(privateRepoRegex),
Provider: ecrPrivateProvider,
FilterRegex: regexp.MustCompile(privateRepoRegex),
Provider: ecrPrivateProvider,
ExtractRegion: ExtractRegionFromEcrImage,
ImageRegexIndex: EcrImageRegexGroup,
}
publicECRExecutor := PodDescriber{
FilterRegex: regexp.MustCompile(PublicRepoRegex),
Provider: ecrPublicProvider,
FilterRegex: regexp.MustCompile(PublicRepoRegex),
Provider: ecrPublicProvider,
ExtractRegion: ExtractRegionFromPublicEcrImage,
ImageRegexIndex: PublicEcrImageRegexIndex,
}

fe := &ECRFetcher{
Expand All @@ -99,6 +103,7 @@ func (f *ECRFactory) CreateFrom(log *logp.Logger, cfg ECRFetcherConfig, ch chan
publicECRExecutor,
},
resourceCh: ch,
awsConfig: awsConfig,
}
return fe, nil
}
12 changes: 11 additions & 1 deletion resources/fetchers/ecr_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ default_region: us1-east
"us1-east",
"my-account",
[]string{
"^my-account\\.dkr\\.ecr\\.us1-east\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?",

// this regex should identify images with an ecr regex template
// <account-id>.dkr.ecr.<region>.amazonaws.com/<repository-name>
"^my-account\\.dkr\\.ecr\\.([-\\w]+)\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?",
// this regex should identify images with a public ecr regex template
// public.ecr.aws/<aws-alias>/<repository>
"public\\.ecr\\.aws\\/\\w+\\/([-\\w\\.\\/]+)\\:?",
},
},
Expand Down Expand Up @@ -113,10 +118,15 @@ default_region: us1-east
s.NotNil(fetcher)

ecrFetcher, ok := fetcher.(*ECRFetcher)
expectedEcrImageRegexIndex := 2
expectedEcrPublicImageRegexIndex := 1

s.True(ok)
s.Equal(kubeclient, ecrFetcher.kubeClient)
s.Equal(test.expectedRegex[0], ecrFetcher.PodDescribers[0].FilterRegex.String())
s.Equal(expectedEcrImageRegexIndex, ecrFetcher.PodDescribers[0].ImageRegexIndex)
s.Equal(test.expectedRegex[1], ecrFetcher.PodDescribers[1].FilterRegex.String())
s.Equal(expectedEcrPublicImageRegexIndex, ecrFetcher.PodDescribers[1].ImageRegexIndex)
}
}

Expand Down
61 changes: 52 additions & 9 deletions resources/fetchers/ecr_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/elastic/cloudbeat/resources/providers/awslib"
v1 "k8s.io/api/core/v1"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/elastic/cloudbeat/resources/fetching"
"github.com/elastic/elastic-agent-libs/logp"
Expand All @@ -34,8 +35,15 @@ import (
)

const (
PrivateRepoRegexTemplate = "^%s\\.dkr\\.ecr\\.%s\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?"
// PrivateRepoRegexTemplate should identify images with an ecr regex template
// <account-id>.dkr.ecr.<region>.amazonaws.com/<repository-name>
PrivateRepoRegexTemplate = "^%s\\.dkr\\.ecr\\.([-\\w]+)\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?"
// PublicRepoRegex should identify images with a public ecr regex template
// public.ecr.aws/<aws-alias>/<repository>
PublicRepoRegex = "public\\.ecr\\.aws\\/\\w+\\/([-\\w\\.\\/]+)\\:?"
Comment on lines +42 to 43

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

if this is the expected format, using regex to extract the repo seems like overkill, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am using the regex for two things:

  1. Validating the format of the image so I can be sure it's an ECR image.
  2. Extract the repository name out of the format.

Do you have another suggestion?

EcrRegionRegexGroup = 1
PublicEcrImageRegexIndex = 1
EcrImageRegexGroup = 2
)

type ECRFetcher struct {
Expand All @@ -44,11 +52,14 @@ type ECRFetcher struct {
kubeClient k8s.Interface
PodDescribers []PodDescriber
resourceCh chan fetching.ResourceInfo
awsConfig aws.Config
}

type PodDescriber struct {
FilterRegex *regexp.Regexp
Provider awslib.EcrRepositoryDescriber
FilterRegex *regexp.Regexp
Provider awslib.EcrRepositoryDescriber
ExtractRegion func(describer PodDescriber, repo string) (string, error)
ImageRegexIndex int
}

type ECRFetcherConfig struct {
Expand Down Expand Up @@ -86,13 +97,27 @@ func (f *ECRFetcher) Fetch(ctx context.Context, cMetadata fetching.CycleMetadata
}
}
}

return nil
}

func (f *ECRFetcher) describePodImagesRepositories(ctx context.Context, podsList *v1.PodList, describer PodDescriber) ([]ecr.Repository, error) {
regionToReposMap := getAwsRepositories(podsList, describer)
f.log.Debugf("sending pods to ecrProviders: %v", regionToReposMap)
awsRepositories := make([]ecr.Repository, 0)
for region, repositories := range regionToReposMap {
// Add configuration
describedRepo, err := describer.Provider.DescribeRepositories(ctx, f.awsConfig, repositories, region)
if err != nil {
f.log.Errorf("could not retrieve pod's aws repositories for region %s: %w", region, err)
} else {
awsRepositories = append(awsRepositories, describedRepo.Repositories...)
}
}
return awsRepositories, nil
}

repositories := make([]string, 0)
func getAwsRepositories(podsList *v1.PodList, describer PodDescriber) map[string][]string {
reposByRegion := make(map[string][]string, 0)

for _, pod := range podsList.Items {
for _, container := range pod.Spec.Containers {
Expand All @@ -101,15 +126,33 @@ func (f *ECRFetcher) describePodImagesRepositories(ctx context.Context, podsList
// Takes only aws images
regexMatcher := describer.FilterRegex.FindStringSubmatch(image)
if regexMatcher != nil {
repository := regexMatcher[1]
repositories = append(repositories, repository)
repository := regexMatcher[describer.ImageRegexIndex]
region, err := describer.ExtractRegion(describer, image)
if err != nil {
logp.Error(err)
continue
}
reposByRegion[region] = append(reposByRegion[region], repository)
}
}
}
return reposByRegion
}

func ExtractRegionFromEcrImage(describer PodDescriber, image string) (string, error) {
regexMatcher := describer.FilterRegex.FindStringSubmatch(image)
if regexMatcher != nil {
repository := regexMatcher[EcrRegionRegexGroup]
return repository, nil
}

f.log.Debugf("sending pods to ecrProviders: %v", repositories)
return "", fmt.Errorf("could not extract region, image does not match the aws ecr template")
}

return describer.Provider.DescribeRepositories(ctx, repositories)
// ExtractRegionFromPublicEcrImage - Currently the public ecr provider is not functional.
// TODO - This method should be change as part of implementing the public ecr - https://github.com/elastic/security-team/issues/4035
func ExtractRegionFromPublicEcrImage(_ PodDescriber, _ string) (string, error) {
return "", nil
}

func (res ECRResource) GetData() interface{} {
Expand Down
Loading