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

(v2)(feat.) ecr registry and cross registry #1071

Merged
merged 3 commits into from
May 5, 2023
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
2 changes: 1 addition & 1 deletion deepfence_server/model/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func (ra *RegistryAddReq) CreateRegistry(ctx context.Context, rContext context.C
m.container_registry_ids = REDUCE(distinctElements = [], element IN COALESCE(m.container_registry_ids, []) + $pgId | CASE WHEN NOT element in distinctElements THEN distinctElements + element ELSE distinctElements END)`
_, err = tx.Run(query, map[string]interface{}{"node_id": registryID, "registry_type": ra.RegistryType, "pgId": cr.ID})

return err
return tx.Commit()
}

func (ru *RegistryUpdateReq) UpdateRegistry(ctx context.Context, pgClient *postgresqlDb.Queries, r int32) error {
Expand Down
128 changes: 128 additions & 0 deletions deepfence_server/pkg/registry/ecr/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package ecr

import (
"fmt"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/deepfence/ThreatMapper/deepfence_server/model"
)

func listImages(awsAccessKey, awsSecretKey, awsRegion string) ([]model.ContainerImage, error) {
// Set up AWS session with access key ID and secret access key
sess, err := session.NewSession(&aws.Config{
Region: aws.String(awsRegion),
Credentials: credentials.NewStaticCredentials(awsAccessKey, awsSecretKey, ""),
})
if err != nil {
return nil, fmt.Errorf("error creating session: %v", err)
}

// Create ECR client
svc := ecr.New(sess)

// Call DescribeRepositories API
result, err := svc.DescribeRepositories(nil)
if err != nil {
return nil, fmt.Errorf("error describing repositories: %v", err)
}

// Create slice of ContainerImage structs
var containerImages []model.ContainerImage

var imageResult []*ecr.ListImagesOutput
// List images for each repository
for _, repo := range result.Repositories {
// Set up input parameters
input := &ecr.ListImagesInput{
RepositoryName: aws.String(*repo.RepositoryName),
}

// Call ListImages API
result, err := svc.ListImages(input)
if err != nil {
return nil, fmt.Errorf("error listing images for repository %s: %v", *repo.RepositoryName, err)
}
imageResult = append(imageResult, result)
// Add containers to ContainerImage struct
for _, image := range result.ImageIds {
if image.ImageTag != nil {
var containerImage model.ContainerImage
containerImage.Name = *repo.RepositoryUri
containerImage.ID = model.DigestToID(*image.ImageDigest)
containerImage.Tag = *image.ImageTag
containerImage.DockerImageID = *image.ImageDigest
containerImage.Metadata = model.Metadata{
"digest": *image.ImageDigest,
"last_updated": time.Now().Unix(),
}
containerImages = append(containerImages, containerImage)
}
}
}

return containerImages, nil
}
func listImagesCrossAccount(awsRegion, awsAccountID, targetAccountRoleARN string) ([]model.ContainerImage, error) {
// Create session with default credentials provider chain
sess, err := session.NewSession(&aws.Config{
Region: aws.String(awsRegion),
})
if err != nil {
return nil, fmt.Errorf("error creating session: %v", err)
}

// Assume role in target account
creds := stscreds.NewCredentials(sess, targetAccountRoleARN)

// Create ECR client
svc := ecr.New(sess, &aws.Config{
Region: aws.String(awsRegion),
Credentials: creds,
})

// Call DescribeRepositories API
result, err := svc.DescribeRepositories(&ecr.DescribeRepositoriesInput{
RegistryId: aws.String(awsAccountID),
})
if err != nil {
return nil, fmt.Errorf("error describing repositories: %v", err)
}

// Create slice of ContainerImage structs
var containerImages []model.ContainerImage

// List images for each repository
for _, repo := range result.Repositories {
// Set up input parameters
input := &ecr.ListImagesInput{
RepositoryName: aws.String(*repo.RepositoryName),
}

// Call ListImages API
result, err := svc.ListImages(input)
if err != nil {
return nil, fmt.Errorf("error listing images for repository %s: %v", *repo.RepositoryName, err)
}

// Add containers to ContainerImage struct
for _, image := range result.ImageIds {
var containerImage model.ContainerImage
containerImage.Name = *repo.RepositoryUri
containerImage.Tag = *image.ImageTag
containerImage.ID = model.DigestToID(*image.ImageDigest)
containerImage.DockerImageID = *image.ImageDigest
containerImage.Metadata = model.Metadata{
"digest": *image.ImageDigest,
"last_updated": time.Now().Unix(),
}
containerImages = append(containerImages, containerImage)
}
}

return containerImages, nil
}
83 changes: 83 additions & 0 deletions deepfence_server/pkg/registry/ecr/ecr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ecr

import (
"encoding/json"

"github.com/deepfence/ThreatMapper/deepfence_server/model"
"github.com/deepfence/golang_deepfence_sdk/utils/encryption"
)

func New(requestByte []byte) (*RegistryECR, error) {
r := RegistryECR{}
err := json.Unmarshal(requestByte, &r)
if err != nil {
return &r, err
}
return &r, nil
}

func (e *RegistryECR) IsValidCredential() bool {
return true
}

func (e *RegistryECR) EncryptSecret(aes encryption.AES) error {
var err error
e.Secret.AWSSecretAccessKey, err = aes.Encrypt(e.Secret.AWSSecretAccessKey)
return err
}

func (e *RegistryECR) DecryptSecret(aes encryption.AES) error {
var err error
e.Secret.AWSSecretAccessKey, err = aes.Decrypt(e.Secret.AWSSecretAccessKey)
return err
}

func (e *RegistryECR) EncryptExtras(aes encryption.AES) error {
return nil
}

func (e *RegistryECR) DecryptExtras(aes encryption.AES) error {
return nil
}

func (e *RegistryECR) FetchImagesFromRegistry() ([]model.ContainerImage, error) {
// based on iamrole we need to fetch images
if e.NonSecret.UseIAMRole == "true" {
return listImagesCrossAccount(e.NonSecret.AWSRegionName, e.NonSecret.AWSAccountID, e.NonSecret.TargetAccountRoleARN)
}
return listImages(e.NonSecret.AWSAccessKeyID, e.Secret.AWSSecretAccessKey, e.NonSecret.AWSRegionName)
}

// getters
func (e *RegistryECR) GetSecret() map[string]interface{} {
var secret map[string]interface{}
b, _ := json.Marshal(e.Secret)
json.Unmarshal(b, &secret)
return secret
}

func (e *RegistryECR) GetExtras() map[string]interface{} {
return map[string]interface{}{}
}

func (e *RegistryECR) GetNamespace() string {
if e.NonSecret.IsPublic == "true" {
if e.NonSecret.UseIAMRole == "true" {
return e.NonSecret.AWSAccountID
}
return e.NonSecret.AWSAccessKeyID
} else {
if e.NonSecret.UseIAMRole == "true" {
return e.NonSecret.AWSRegionName + "_" + e.NonSecret.AWSAccountID
}
}
return e.NonSecret.AWSRegionName + "_" + e.NonSecret.AWSAccessKeyID
}

func (e *RegistryECR) GetRegistryType() string {
return e.RegistryType
}

func (e *RegistryECR) GetUsername() string {
return ""
}
66 changes: 66 additions & 0 deletions deepfence_server/pkg/registry/ecr/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package ecr

import "time"

type RegistryECR struct {
Name string `json:"name"`
NonSecret NonSecret `json:"non_secret"`
Secret Secret `json:"secret"`
RegistryType string `json:"registry_type"`
}

type NonSecret struct {
UseIAMRole string `json:"use_iam_role"`
IsPublic string `json:"is_public"`
AWSAccessKeyID string `json:"aws_access_key_id"`
AWSRegionName string `json:"aws_region_name"`
AWSAccountID string `json:"aws_account_id"` // legacy: registry_id
TargetAccountRoleARN string `json:"target_account_role_arn"`
}

type Secret struct {
AWSSecretAccessKey string `json:"aws_secret_access_key"`
}

type ImageWithTag struct {
Name string
Images Images
}

type ImageTag struct {
Count int `json:"count,omitempty"`
Next string `json:"next,omitempty"`
Previous interface{} `json:"previous,omitempty"`
Results []Results `json:"results,omitempty"`
}
type Images struct {
Architecture string `json:"architecture,omitempty"`
Features string `json:"features,omitempty"`
Variant interface{} `json:"variant,omitempty"`
Digest string `json:"digest,omitempty"`
Os string `json:"os,omitempty"`
OsFeatures string `json:"os_features,omitempty"`
OsVersion interface{} `json:"os_version,omitempty"`
Size int `json:"size,omitempty"`
Status string `json:"status,omitempty"`
LastPulled time.Time `json:"last_pulled,omitempty"`
LastPushed time.Time `json:"last_pushed,omitempty"`
}
type Results struct {
Creator int `json:"creator,omitempty"`
ID int `json:"id,omitempty"`
Images []Images `json:"images,omitempty"`
LastUpdated time.Time `json:"last_updated,omitempty"`
LastUpdater int `json:"last_updater,omitempty"`
LastUpdaterUsername string `json:"last_updater_username,omitempty"`
Name string `json:"name,omitempty"`
Repository int `json:"repository,omitempty"`
FullSize int `json:"full_size,omitempty"`
V2 bool `json:"v2,omitempty"`
TagStatus string `json:"tag_status,omitempty"`
TagLastPulled time.Time `json:"tag_last_pulled,omitempty"`
TagLastPushed time.Time `json:"tag_last_pushed,omitempty"`
MediaType string `json:"media_type,omitempty"`
ContentType string `json:"content_type,omitempty"`
Digest string `json:"digest,omitempty"`
}
51 changes: 51 additions & 0 deletions deepfence_server/pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/acr"
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/dockerhub"
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/dockerprivate"
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/ecr"
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/gcr"
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/harbor"
"github.com/deepfence/ThreatMapper/deepfence_server/pkg/registry/jfrog"
Expand Down Expand Up @@ -37,7 +38,10 @@ func GetRegistry(rType string, requestByte []byte) (Registry, error) {
r, err = harbor.New(requestByte)
case constants.JFROG:
r, err = jfrog.New(requestByte)
case constants.ECR:
r, err = ecr.New(requestByte)
}

return r, err
}

Expand Down Expand Up @@ -218,6 +222,34 @@ func GetRegistryWithRegistryRow(row postgresql_db.GetContainerRegistriesRow) (Re
},
}
return r, nil

case constants.ECR:
var nonSecret map[string]string
var secret map[string]string
err := json.Unmarshal(row.NonSecret, &nonSecret)
if err != nil {
return nil, err
}
err = json.Unmarshal(row.EncryptedSecret, &secret)
if err != nil {
return nil, err
}
r = &ecr.RegistryECR{
RegistryType: row.RegistryType,
Name: row.Name,
NonSecret: ecr.NonSecret{
UseIAMRole: nonSecret["use_iam_role"],
IsPublic: nonSecret["is_public"],
AWSAccessKeyID: nonSecret["aws_access_key_id"],
AWSRegionName: nonSecret["aws_region_name"],
AWSAccountID: nonSecret["aws_account_id"],
TargetAccountRoleARN: nonSecret["target_account_role_arn"],
},
Secret: ecr.Secret{
AWSSecretAccessKey: secret["aws_secret_access_key"],
},
}
return r, err
}
return r, err
}
Expand Down Expand Up @@ -334,6 +366,25 @@ func GetRegistryWithRegistrySafeRow(row postgresql_db.GetContainerRegistriesSafe
},
}
return r, nil
case constants.ECR:
var nonSecret map[string]string
err := json.Unmarshal(row.NonSecret, &nonSecret)
if err != nil {
return nil, err
}
r = &ecr.RegistryECR{
RegistryType: row.RegistryType,
Name: row.Name,
NonSecret: ecr.NonSecret{
UseIAMRole: nonSecret["use_iam_role"],
IsPublic: nonSecret["is_public"],
AWSAccessKeyID: nonSecret["aws_access_key_id"],
AWSRegionName: nonSecret["aws_region_name"],
AWSAccountID: nonSecret["aws_account_id"],
TargetAccountRoleARN: nonSecret["target_account_role_arn"],
},
}
return r, nil
}
return r, err
}
Expand Down
Loading