Skip to content

Commit

Permalink
Merge pull request #15 from armosec/acr-aws
Browse files Browse the repository at this point in the history
* add acr client * add aws client
  • Loading branch information
refaelm92 authored Nov 26, 2024
2 parents 2fa7cfc + 7b0209d commit b01f37f
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 0 deletions.
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ require (
github.com/Masterminds/semver/v3 v3.3.0
github.com/armosec/armoapi-go v0.0.474
github.com/armosec/gojay v1.2.17
github.com/aws/aws-sdk-go-v2 v1.32.5
github.com/aws/aws-sdk-go-v2/config v1.28.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.46
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.6
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1
github.com/docker/docker v27.3.1+incompatible
github.com/google/go-containerregistry v0.20.2
github.com/hashicorp/go-version v1.7.0
Expand All @@ -21,6 +26,15 @@ require (
require (
cloud.google.com/go/compute/metadata v0.5.2 // indirect
github.com/armosec/utils-k8s-go v0.0.30 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
Expand All @@ -33,6 +47,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
Expand Down
32 changes: 32 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@ github.com/armosec/gojay v1.2.17 h1:VSkLBQzD1c2V+FMtlGFKqWXNsdNvIKygTKJI9ysY8eM=
github.com/armosec/gojay v1.2.17/go.mod h1:vuvX3DlY0nbVrJ0qCklSS733AWMoQboq3cFyuQW9ybc=
github.com/armosec/utils-k8s-go v0.0.30 h1:Gj8MJck0jZPSLSq8ZMiRPT3F/laOYQdaLxXKKcjijt4=
github.com/armosec/utils-k8s-go v0.0.30/go.mod h1:t0vvPJhYE+X+bOsaMsD2SzWU7WkJmV2Ltn9hg66AIe8=
github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo=
github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0=
github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o=
github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.6 h1:zg+3FGHA0PBs0KM25qE/rOf2o5zsjNa1g/Qq83+SDI0=
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.6/go.mod h1:ZSq54Z9SIsOTf1Efwgw1msilSs4XVEfVQiP9nYVnKpM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
Expand Down Expand Up @@ -88,6 +116,10 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
Expand Down
163 changes: 163 additions & 0 deletions registryclients/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package registryclients

import (
"context"
"encoding/base64"
"fmt"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/registryx/common"
"github.com/armosec/registryx/registries/defaultregistry"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/sts"
dockerregistry "github.com/docker/docker/api/types/registry"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"strings"
)

const (
registryURIFormat = "%s.dkr.ecr.%s.amazonaws.com"
registrySessionName = "AWSRegistryClientSession"
)

type AWSRegistryClient struct {
Registry *armotypes.AWSImageRegistry
registryURI string
username string
password string
}

func NewAWSRegistryClient(registry *armotypes.AWSImageRegistry) (*AWSRegistryClient, error) {
if registry.AccessKeyID != "" && registry.SecretAccessKey != "" {
return newAWSRegistryClientUsingCredentials(registry, registry.AccessKeyID, registry.SecretAccessKey, registry.RegistryRegion)
}

return newAWSRegistryClientUsingIAMRole(registry, registry.RoleARN, registry.RegistryRegion)
}

func newAWSRegistryClientUsingCredentials(registry *armotypes.AWSImageRegistry, accessKeyID, secretAccessKey, region string) (*AWSRegistryClient, error) {
cfg, err := config.LoadDefaultConfig(context.Background(),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, "")),
config.WithRegion(region),
)
if err != nil {
return nil, fmt.Errorf("failed to load AWS config: %w", err)
}

return newAWSRegistryClientUsingConfig(registry, cfg)
}

func newAWSRegistryClientUsingIAMRole(registry *armotypes.AWSImageRegistry, roleARN, region string) (*AWSRegistryClient, error) {
cfg, err := config.LoadDefaultConfig(context.Background(),
config.WithRegion(region),
)
if err != nil {
return nil, fmt.Errorf("failed to load AWS config: %w", err)
}
stsClient := sts.NewFromConfig(cfg)
assumeRoleOutput, err := stsClient.AssumeRole(context.Background(), &sts.AssumeRoleInput{
RoleArn: aws.String(roleARN),
RoleSessionName: aws.String(registrySessionName),
})
if err != nil {
return nil, fmt.Errorf("failed to assume role: %w", err)
}

assumeRoleCfg, err := config.LoadDefaultConfig(context.Background(),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
aws.ToString(assumeRoleOutput.Credentials.AccessKeyId),
aws.ToString(assumeRoleOutput.Credentials.SecretAccessKey),
aws.ToString(assumeRoleOutput.Credentials.SessionToken),
)),
config.WithRegion(region),
)
if err != nil {
return nil, fmt.Errorf("failed to load AWS config with assumed role credentials: %w", err)
}

return newAWSRegistryClientUsingConfig(registry, assumeRoleCfg)
}

func newAWSRegistryClientUsingConfig(registry *armotypes.AWSImageRegistry, cfg aws.Config) (*AWSRegistryClient, error) {
stsClient := sts.NewFromConfig(cfg)
identity, err := stsClient.GetCallerIdentity(context.Background(), &sts.GetCallerIdentityInput{})
if err != nil {
return nil, fmt.Errorf("failed to get caller identity: %w", err)
}

ecrClient := ecr.NewFromConfig(cfg)
output, err := ecrClient.GetAuthorizationToken(context.Background(), &ecr.GetAuthorizationTokenInput{})
if err != nil {
return nil, fmt.Errorf("failed to get authorization token: %w", err)
}

if len(output.AuthorizationData) == 0 {
return nil, fmt.Errorf("no authorization data received")
}

authData := output.AuthorizationData[0]
decodedToken, err := base64.StdEncoding.DecodeString(aws.ToString(authData.AuthorizationToken))
if err != nil {
return nil, fmt.Errorf("failed to decode authorization token: %w", err)
}

tokenParts := strings.SplitN(string(decodedToken), ":", 2)
if len(tokenParts) != 2 {
return nil, fmt.Errorf("invalid authorization token format")
}

username := tokenParts[0]
password := tokenParts[1]

return &AWSRegistryClient{
Registry: registry,
registryURI: fmt.Sprintf(registryURIFormat, *identity.Account, cfg.Region),
username: username,
password: password,
}, nil
}

func (a *AWSRegistryClient) GetAllRepositories(ctx context.Context) ([]string, error) {
registry, err := name.NewRegistry(a.registryURI)
if err != nil {
return nil, err
}
iRegistry, err := defaultregistry.NewRegistry(&authn.AuthConfig{Username: a.username, Password: a.password}, &registry, &common.RegistryOptions{})
if err != nil {
return nil, err
}

return getAllRepositories(ctx, iRegistry)
}

func (a *AWSRegistryClient) GetImagesToScan(_ context.Context) (map[string]string, error) {
registry, err := name.NewRegistry(a.registryURI)
if err != nil {
return nil, err
}
iRegistry, err := defaultregistry.NewRegistry(&authn.AuthConfig{Username: a.username, Password: a.password}, &registry, &common.RegistryOptions{})
if err != nil {
return nil, err
}
images := make(map[string]string, len(a.Registry.Repositories))
for _, repository := range a.Registry.Repositories {
tag, err := getImageLatestTag(repository, iRegistry)
if err != nil {
return nil, err
} else if tag == "" {
return nil, fmt.Errorf("failed to find latest tag for repository %s", repository)
}
images[fmt.Sprintf("%s/%s", a.registryURI, repository)] = tag
}
return images, nil
}

func (a *AWSRegistryClient) GetDockerAuth() (*dockerregistry.AuthConfig, error) {
return &dockerregistry.AuthConfig{
Username: a.username,
Password: a.password,
}, nil
}
58 changes: 58 additions & 0 deletions registryclients/azure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package registryclients

import (
"context"
"fmt"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/registryx/common"
"github.com/armosec/registryx/registries/defaultregistry"
dockerregistry "github.com/docker/docker/api/types/registry"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
)

type AzureRegistryClient struct {
Registry *armotypes.AzureImageRegistry
}

func (a *AzureRegistryClient) GetAllRepositories(ctx context.Context) ([]string, error) {
registry, err := name.NewRegistry(a.Registry.LoginServer)
if err != nil {
return nil, err
}
iRegistry, err := defaultregistry.NewRegistry(&authn.AuthConfig{Username: a.Registry.Username, Password: a.Registry.AccessToken}, &registry, &common.RegistryOptions{})
if err != nil {
return nil, err
}

return getAllRepositories(ctx, iRegistry)
}

func (a *AzureRegistryClient) GetImagesToScan(_ context.Context) (map[string]string, error) {
registry, err := name.NewRegistry(a.Registry.LoginServer)
if err != nil {
return nil, err
}
iRegistry, err := defaultregistry.NewRegistry(&authn.AuthConfig{Username: a.Registry.Username, Password: a.Registry.AccessToken}, &registry, &common.RegistryOptions{})
if err != nil {
return nil, err
}
images := make(map[string]string, len(a.Registry.Repositories))
for _, repository := range a.Registry.Repositories {
tag, err := getImageLatestTag(repository, iRegistry)
if err != nil {
return nil, err
} else if tag == "" {
return nil, fmt.Errorf("failed to find latest tag for repository %s", repository)
}
images[fmt.Sprintf("%s/%s", a.Registry.LoginServer, repository)] = tag
}
return images, nil
}

func (a *AzureRegistryClient) GetDockerAuth() (*dockerregistry.AuthConfig, error) {
return &dockerregistry.AuthConfig{
Username: a.Registry.Username,
Password: a.Registry.AccessToken,
}, nil
}
12 changes: 12 additions & 0 deletions registryclients/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ func GetRegistryClient(registry armotypes.ContainerImageRegistry) (interfaces.Re
} else {
return nil, fmt.Errorf("failed to convert registry to GoogleImageRegistry type")
}
case armotypes.Azure:
if azureRegistry, ok := registry.(*armotypes.AzureImageRegistry); ok {
return &AzureRegistryClient{Registry: azureRegistry}, nil
} else {
return nil, fmt.Errorf("failed to convert registry to AzureImageRegistry type")
}
case armotypes.AWS:
if awsRegistry, ok := registry.(*armotypes.AWSImageRegistry); ok {
return NewAWSRegistryClient(awsRegistry)
} else {
return nil, fmt.Errorf("failed to convert registry to AWSImageRegistry type")
}
}
return nil, fmt.Errorf("unsupported provider %s", provider)
}

0 comments on commit b01f37f

Please sign in to comment.