Skip to content

Commit

Permalink
🐛 Fix docker/container registry scanning. Clean the container code a …
Browse files Browse the repository at this point in the history
…bit (#3701)

* 🐛 Fix docker/container registry scanning. Start cleaning code up.

Signed-off-by: Preslav <preslav@mondoo.com>

* Use type.String() for the container connection.

Signed-off-by: Preslav <preslav@mondoo.com>

---------

Signed-off-by: Preslav <preslav@mondoo.com>
  • Loading branch information
preslavgerchev authored Apr 8, 2024
1 parent 0dfbdd3 commit 467ed2b
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 115 deletions.
55 changes: 29 additions & 26 deletions providers/os/connection/container/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,39 @@
package auth

import (
"strings"

"github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/rs/zerolog/log"
"go.mondoo.com/cnquery/v10/logger"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/vault"
"go.mondoo.com/cnquery/v10/providers/os/connection/container/image"
"go.mondoo.com/cnquery/v10/providers/os/connection/container/acr"
)

const (
acrIndicator = ".azurecr.io"
ecrIndicator = ".ecr."
)

func AuthOption(credentials []*vault.Credential) []image.Option {
remoteOpts := []image.Option{}
for i := range credentials {
cred := credentials[i]
switch cred.Type {
case vault.CredentialType_password:
log.Debug().Msg("add password authentication")
cfg := authn.AuthConfig{
Username: cred.User,
Password: string(cred.Secret),
}
remoteOpts = append(remoteOpts, image.WithAuthenticator((authn.FromConfig(cfg))))
case vault.CredentialType_bearer:
log.Debug().Str("token", string(cred.Secret)).Msg("add bearer authentication")
cfg := authn.AuthConfig{
Username: cred.User,
RegistryToken: string(cred.Secret),
}
remoteOpts = append(remoteOpts, image.WithAuthenticator((authn.FromConfig(cfg))))
default:
log.Warn().Msg("unknown credentials for container image")
logger.DebugJSON(credentials)
func getKeychains(name string) []authn.Keychain {
kcs := []authn.Keychain{
authn.DefaultKeychain,
}
if strings.Contains(name, ecrIndicator) {
kcs = append(kcs, authn.NewKeychainFromHelper(ecr.NewECRHelper()))
}
if strings.Contains(name, acrIndicator) {
acr, err := acr.NewAcrAuthHelper()
if err == nil {
kcs = append(kcs, authn.NewKeychainFromHelper(acr))
} else {
log.Debug().Err(err).Msg("failed to create ACR auth helper")
}
}
return remoteOpts
return kcs
}

// ConstructKeychain creates a keychain for the given registry name
// It will add the default docker keychain and additional keychains for ECR and ACR, if those are determined to be used
func ConstructKeychain(name string) authn.Keychain {
return authn.NewMultiKeychain(getKeychains(name)...)
}
26 changes: 26 additions & 0 deletions providers/os/connection/container/auth/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package auth

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestConstructKeychain(t *testing.T) {
t.Run("default keychain only", func(t *testing.T) {
keychain := getKeychains("test")
require.Equal(t, 1, len(keychain))
})
t.Run("default keychain and ecr keychain", func(t *testing.T) {
keychain := getKeychains("0000000000.dkr.ecr.us-east-1.amazonaws.com/test")
require.Equal(t, 2, len(keychain))
})

t.Run("default keychain and acr keychain", func(t *testing.T) {
keychain := getKeychains("test.azurecr.io")
require.Equal(t, 2, len(keychain))
})
}
84 changes: 38 additions & 46 deletions providers/os/connection/container/image/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ package image

import (
"crypto/tls"
"fmt"
"net"
"net/http"
"strings"
"time"

ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"go.mondoo.com/cnquery/v10/providers/os/connection/container/acr"
"github.com/rs/zerolog/log"
"go.mondoo.com/cnquery/v10/logger"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/vault"
"go.mondoo.com/cnquery/v10/providers/os/connection/container/auth"
)

// Option is a functional option
Expand All @@ -42,6 +42,38 @@ func WithAuthenticator(auth authn.Authenticator) Option {
}
}

func AuthOption(credentials []*vault.Credential) []Option {
remoteOpts := []Option{}
for i := range credentials {
cred := credentials[i]
switch cred.Type {
case vault.CredentialType_password:
log.Debug().Msg("add password authentication")
cfg := authn.AuthConfig{
Username: cred.User,
Password: string(cred.Secret),
}
remoteOpts = append(remoteOpts, WithAuthenticator((authn.FromConfig(cfg))))
case vault.CredentialType_bearer:
log.Debug().Str("token", string(cred.Secret)).Msg("add bearer authentication")
cfg := authn.AuthConfig{
Username: cred.User,
RegistryToken: string(cred.Secret),
}
remoteOpts = append(remoteOpts, WithAuthenticator((authn.FromConfig(cfg))))
default:
log.Warn().Msg("unknown credentials for container image")
logger.DebugJSON(credentials)
}
}
return remoteOpts
}

func DefaultAuthOpts(ref name.Reference) (authn.Authenticator, error) {
kc := auth.ConstructKeychain(ref.Name())
return kc.Resolve(ref.Context())
}

func GetImageDescriptor(ref name.Reference, opts ...Option) (*remote.Descriptor, error) {
o := &options{
insecure: false,
Expand All @@ -54,28 +86,8 @@ func GetImageDescriptor(ref name.Reference, opts ...Option) (*remote.Descriptor,
}

if o.auth == nil {
kc := authn.NewMultiKeychain(
authn.DefaultKeychain,
)
if strings.Contains(ref.Name(), ".ecr.") {
kc = authn.NewMultiKeychain(
authn.DefaultKeychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
)
}
if strings.Contains(ref.Name(), "azurecr.io") {
acr, err := acr.NewAcrAuthHelper()
if err != nil {
return nil, err
}
kc = authn.NewMultiKeychain(
authn.DefaultKeychain,
authn.NewKeychainFromHelper(acr),
)
}
auth, err := kc.Resolve(ref.Context())
auth, err := DefaultAuthOpts(ref)
if err != nil {
fmt.Printf("getting creds for %q: %v", ref, err)
return nil, err
}
o.auth = auth
Expand All @@ -96,28 +108,8 @@ func LoadImageFromRegistry(ref name.Reference, opts ...Option) (v1.Image, error)
}

if o.auth == nil {
kc := authn.NewMultiKeychain(
authn.DefaultKeychain,
)
if strings.Contains(ref.Name(), ".ecr.") {
kc = authn.NewMultiKeychain(
authn.DefaultKeychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
)
}
if strings.Contains(ref.Name(), "azurecr.io") {
acr, err := acr.NewAcrAuthHelper()
if err != nil {
return nil, err
}
kc = authn.NewMultiKeychain(
authn.DefaultKeychain,
authn.NewKeychainFromHelper(acr),
)
}
auth, err := kc.Resolve(ref.Context())
auth, err := DefaultAuthOpts(ref)
if err != nil {
fmt.Printf("getting creds for %q: %v", ref, err)
return nil, err
}
o.auth = auth
Expand Down
3 changes: 1 addition & 2 deletions providers/os/connection/container/image_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/rs/zerolog/log"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v10/providers/os/connection/container/auth"
"go.mondoo.com/cnquery/v10/providers/os/connection/container/image"
"go.mondoo.com/cnquery/v10/providers/os/connection/tar"
"go.mondoo.com/cnquery/v10/providers/os/id/containerid"
Expand Down Expand Up @@ -62,7 +61,7 @@ func NewRegistryImage(id uint32, conf *inventory.Config, asset *inventory.Asset)
log.Debug().Str("ref", ref.Name()).Msg("found valid container registry reference")

registryOpts := []image.Option{image.WithInsecure(conf.Insecure)}
remoteOpts := auth.AuthOption(conf.Credentials)
remoteOpts := image.AuthOption(conf.Credentials)
registryOpts = append(registryOpts, remoteOpts...)

img, err := image.LoadImageFromRegistry(ref, registryOpts...)
Expand Down
84 changes: 84 additions & 0 deletions providers/os/connection/container/registry_connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package container

import (
"errors"

"github.com/spf13/afero"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v10/providers/os/connection/shared"
"go.mondoo.com/cnquery/v10/providers/os/resources/discovery/container_registry"
)

var _ shared.Connection = &RegistryConnection{}

type RegistryConnection struct {
plugin.Connection
asset *inventory.Asset
}

func (r *RegistryConnection) Capabilities() shared.Capabilities {
return shared.Capabilities(0)
}

func (r *RegistryConnection) FileInfo(path string) (shared.FileInfoDetails, error) {
return shared.FileInfoDetails{}, plugin.ErrFileInfoNotImplemented
}

func (r *RegistryConnection) FileSystem() afero.Fs {
panic("unimplemented")
}

func (r *RegistryConnection) RunCommand(command string) (*shared.Command, error) {
return nil, errors.New("unimplemented")
}

func (r *RegistryConnection) Type() shared.ConnectionType {
return shared.Type_ContainerRegistry
}

func (r *RegistryConnection) UpdateAsset(asset *inventory.Asset) {
r.asset = asset
}

func NewRegistryConnection(id uint32, asset *inventory.Asset) (*RegistryConnection, error) {
conn := &RegistryConnection{
Connection: plugin.NewConnection(id, asset),
asset: asset,
}

return conn, nil
}

func (r *RegistryConnection) Name() string {
return "container-registry"
}

func (r *RegistryConnection) ID() uint32 {
return r.Connection.ID()
}

func (r *RegistryConnection) ParentID() uint32 {
return r.Connection.ParentID()
}

func (r *RegistryConnection) Close() error {
return nil
}

func (r *RegistryConnection) Asset() *inventory.Asset {
return r.asset
}

func (r *RegistryConnection) DiscoverImages() (*inventory.Inventory, error) {
resolver := container_registry.NewContainerRegistryResolver()
host := r.asset.Connections[0].Host
assets, err := resolver.ListRegistry(host)
if err != nil {
return nil, err
}
return inventory.New(inventory.WithAssets(assets...)), nil
}
2 changes: 1 addition & 1 deletion providers/os/connection/docker/container_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func NewContainerImageConnection(id uint32, conf *inventory.Config, asset *inven
}

// The requested image isn't locally available, but we can pull it from a remote registry.
if len(resolvedAssets) > 0 && resolvedAssets[0].Connections[0].Type == "container-registry" {
if len(resolvedAssets) > 0 && resolvedAssets[0].Connections[0].Type == shared.Type_RegistryImage.String() {
asset.Name = resolvedAssets[0].Name
asset.PlatformIds = resolvedAssets[0].PlatformIds
asset.Labels = resolvedAssets[0].Labels
Expand Down
Loading

0 comments on commit 467ed2b

Please sign in to comment.