Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
727c183
for now
RTann Jan 14, 2022
2684b49
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 17, 2022
aea8089
updates
RTann Jan 18, 2022
6e0a192
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 18, 2022
d0df329
simple test
RTann Jan 18, 2022
8397df4
style
RTann Jan 18, 2022
3563322
updates
RTann Jan 18, 2022
c82d99e
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 18, 2022
196eb1c
for now
RTann Jan 19, 2022
cf04865
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 19, 2022
2d53f13
updates
RTann Jan 19, 2022
53b9aef
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 24, 2022
aa30729
update factory opts
RTann Jan 24, 2022
2a9538c
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 24, 2022
2b53677
debug
RTann Jan 24, 2022
93550ab
style
RTann Jan 24, 2022
1539c6c
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 25, 2022
325beeb
update
RTann Jan 25, 2022
ee746c3
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 25, 2022
b12da60
add more logs
RTann Jan 25, 2022
1d6baf9
remove log
RTann Jan 25, 2022
e6b48cd
remove logs
RTann Jan 25, 2022
c26e0f6
Merge branch 'master' into ROX-8401-registry-store
RTann Jan 26, 2022
9733eeb
updates
RTann Jan 26, 2022
d49f63e
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 1, 2022
7cb1dcf
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 1, 2022
dbef706
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 2, 2022
bf2c6bf
update error
RTann Feb 2, 2022
d902fa6
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 2, 2022
63588ab
unit test
RTann Feb 2, 2022
a3c78c0
update unit tests
RTann Feb 3, 2022
3c1e2a7
comments
RTann Feb 3, 2022
a21592d
add debug log
RTann Feb 3, 2022
d80ed18
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 3, 2022
7f8290a
remove annoying log
RTann Feb 3, 2022
6ba4d8a
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 3, 2022
59049ee
update log
RTann Feb 3, 2022
575870c
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 4, 2022
383cc5e
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 4, 2022
a17663f
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 4, 2022
d0d0540
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 7, 2022
6ee9f65
PR udpates
RTann Feb 8, 2022
15e362f
PR udpates
RTann Feb 8, 2022
d2687bd
style
RTann Feb 8, 2022
9900b2d
add ctx and comment fixes
RTann Feb 8, 2022
6e937c3
Merge branch 'master' into ROX-8401-registry-store
RTann Feb 8, 2022
05fa6c6
style
RTann Feb 8, 2022
b06b402
rename checkTLS for clarity
RTann Feb 8, 2022
ec8592c
update secret
RTann Feb 8, 2022
2501cef
move closer to usage
RTann Feb 8, 2022
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
84 changes: 84 additions & 0 deletions pkg/docker/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package config

import (
"encoding/base64"
"encoding/json"
"strings"

"github.com/pkg/errors"
)

// The following types are copied from the Kubernetes codebase,
// since it is not placed in any of the officially supported client
// libraries.

// DockerConfigJSON represents ~/.docker/config.json file info
// see https://github.com/docker/docker/pull/12009.
type DockerConfigJSON struct {
Auths DockerConfig `json:"auths"`
}

// DockerConfig represents the config file used by the docker CLI.
// This config that represents the credentials that should be used
// when pulling images from specific image repositories.
type DockerConfig map[string]DockerConfigEntry

// DockerConfigEntry is an entry in the DockerConfig.
type DockerConfigEntry struct {
Username string
Password string
Email string
}

// DockerConfigEntryWithAuth is used solely for deserializing the Auth field
// into a DockerConfigEntry during JSON deserialization.
type DockerConfigEntryWithAuth struct {
// +optional
Username string `json:"username,omitempty"`
// +optional
Password string `json:"password,omitempty"`
// +optional
Email string `json:"email,omitempty"`
// +optional
Auth string `json:"auth,omitempty"`
}

// decodeDockerConfigFieldAuth deserializes the "auth" field from dockercfg into a
// username and a password. The format of the auth field is base64(<username>:<password>).
func decodeDockerConfigFieldAuth(field string) (username, password string, err error) {
decoded, err := base64.StdEncoding.DecodeString(field)
if err != nil {
return
}

parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
err = errors.New("unable to parse auth field")
return
}

username = parts[0]
password = parts[1]

return
}

// UnmarshalJSON unmarshals the given JSON data into a *DockerConfigEntry.
func (d *DockerConfigEntry) UnmarshalJSON(data []byte) error {
var tmp DockerConfigEntryWithAuth
err := json.Unmarshal(data, &tmp)
if err != nil {
return err
}

d.Username = tmp.Username
d.Password = tmp.Password
d.Email = tmp.Email

if len(tmp.Auth) == 0 {
return nil
}

d.Username, d.Password, err = decodeDockerConfigFieldAuth(tmp.Auth)
return err
}
2 changes: 1 addition & 1 deletion pkg/images/integration/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Set interface {

// NewSet returns a new Set instance.
func NewSet(reporter integrationhealth.Reporter) Set {
registryFactory := registries.NewFactory()
registryFactory := registries.NewFactory(registries.FactoryOptions{})
registrySet := registries.NewSet(registryFactory)

scannerFactory := scanners.NewFactory(registrySet)
Expand Down
2 changes: 1 addition & 1 deletion pkg/images/integration/set_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (e *setImpl) UpdateImageIntegration(integration *storage.ImageIntegration)
err = e.scannerSet.UpdateImageIntegration(integration)
case storage.ImageIntegrationCategory_NODE_SCANNER: // This is because node scanners are implemented into image integrations
default:
err = fmt.Errorf("Source category '%s' has not been implemented", category)
err = fmt.Errorf("source category %q has not been implemented", category)
}
}

Expand Down
39 changes: 22 additions & 17 deletions pkg/registries/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,34 @@ type Factory interface {
CreateRegistry(source *storage.ImageIntegration) (types.ImageRegistry, error)
}

type creatorWrapper func() (string, func(integration *storage.ImageIntegration) (types.Registry, error))
// CreatorWrapper is a wrapper around a Creator which also returns the registry's name.
type CreatorWrapper func() (string, func(integration *storage.ImageIntegration) (types.Registry, error))

// AllCreatorFuncs defines all known registry creators.
var AllCreatorFuncs = []CreatorWrapper{
artifactRegistryFactory.Creator,
artifactoryFactory.Creator,
dockerFactory.Creator,
dtrFactory.Creator,
ecrFactory.Creator,
googleFactory.Creator,
quayFactory.Creator,
tenableFactory.Creator,
nexusFactory.Creator,
azureFactory.Creator,
rhelFactory.Creator,
ibmFactory.Creator,
}

// NewFactory creates a new scanner factory.
func NewFactory() Factory {
func NewFactory(opts FactoryOptions) Factory {
reg := &factoryImpl{
creators: make(map[string]Creator),
}

// Add registries to factory.
//////////////////////////////
creatorFuncs := []creatorWrapper{
artifactRegistryFactory.Creator,
artifactoryFactory.Creator,
dockerFactory.Creator,
dtrFactory.Creator,
ecrFactory.Creator,
googleFactory.Creator,
quayFactory.Creator,
tenableFactory.Creator,
nexusFactory.Creator,
azureFactory.Creator,
rhelFactory.Creator,
ibmFactory.Creator,
creatorFuncs := AllCreatorFuncs
if len(opts.CreatorFuncs) > 0 {
creatorFuncs = opts.CreatorFuncs
}

for _, creatorFunc := range creatorFuncs {
Expand Down
8 changes: 8 additions & 0 deletions pkg/registries/factory_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package registries

// FactoryOptions specifies optional configuration parameters for a registry factory.
type FactoryOptions struct {
// CreatorFuncs specifies which registries to add to the factory.
// By default, AllCreatorFuncs is used.
CreatorFuncs []CreatorWrapper
}
2 changes: 1 addition & 1 deletion pkg/scanners/anchore/anchore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestAnchore(t *testing.T) {
t.Skipf("Skipping Anchore integration test")
}

registryFactory := registries.NewFactory()
registryFactory := registries.NewFactory(registries.FactoryOptions{})
registrySet := registries.NewSet(registryFactory)

err := registrySet.UpdateImageIntegration(&storage.ImageIntegration{
Expand Down
105 changes: 105 additions & 0 deletions sensor/common/registry/registry_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package registry

import (
"context"

"github.com/pkg/errors"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/pkg/docker/config"
"github.com/stackrox/rox/pkg/logging"
"github.com/stackrox/rox/pkg/registries"
dockerFactory "github.com/stackrox/rox/pkg/registries/docker"
"github.com/stackrox/rox/pkg/sync"
"github.com/stackrox/rox/pkg/tlscheck"
)

var (
log = logging.LoggerForModule()
)

// Store stores cluster-internal registries by namespace.
// It is assumed all the registries are Docker registries.
type Store struct {
factory registries.Factory
// store maps a namespace to the names of registries accessible from within the namespace.
store map[string]registries.Set

mutex sync.RWMutex

checkTLS CheckTLS
}

// CheckTLS defines a function which checks if the given address is using TLS.
// An example implementation of this is tlscheck.CheckTLS.
type CheckTLS func(ctx context.Context, origAddr string) (bool, error)

// NewRegistryStore creates a new registry store.
// The passed-in CheckTLS is used to check if a registry uses TLS.
// If checkTLS is nil, tlscheck.CheckTLS is used by default.
func NewRegistryStore(checkTLS CheckTLS) *Store {
store := &Store{
factory: registries.NewFactory(registries.FactoryOptions{
CreatorFuncs: []registries.CreatorWrapper{dockerFactory.Creator},
}),
store: make(map[string]registries.Set),

checkTLS: tlscheck.CheckTLS,
}

if checkTLS != nil {
store.checkTLS = checkTLS
}

return store
}

func (rs *Store) getRegistries(namespace string) registries.Set {
rs.mutex.Lock()
defer rs.mutex.Unlock()

regs := rs.store[namespace]
if regs == nil {
regs = registries.NewSet(rs.factory)
rs.store[namespace] = regs
}

return regs
}

// UpsertRegistry upserts the given registry with the given credentials in the given namespace into the store.
func (rs *Store) UpsertRegistry(ctx context.Context, namespace, registry string, dce config.DockerConfigEntry) error {
secure, err := rs.checkTLS(ctx, registry)
if err != nil {
return errors.Wrapf(err, "unable to check TLS for registry %q", registry)
}

regs := rs.getRegistries(namespace)
err = regs.UpdateImageIntegration(&storage.ImageIntegration{
Name: registry,
Type: "docker",
Categories: []storage.ImageIntegrationCategory{storage.ImageIntegrationCategory_REGISTRY},
IntegrationConfig: &storage.ImageIntegration_Docker{
Docker: &storage.DockerConfig{
Endpoint: registry,
Username: dce.Username,
Password: dce.Password,
Insecure: !secure,
},
},
})
if err != nil {
return errors.Wrapf(err, "updating registry store with registry %q", registry)
}

log.Debugf("Upserted registry %q for namespace %q into store", registry, namespace)

return nil
}

// GetAllInNamespace returns all the registries within a given namespace.
func (rs *Store) GetAllInNamespace(namespace string) registries.Set {
rs.mutex.RLock()
defer rs.mutex.RUnlock()

return rs.store[namespace]
}
16 changes: 16 additions & 0 deletions sensor/common/registry/singleton.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package registry

import "github.com/stackrox/rox/pkg/sync"

var (
once sync.Once
rStore *Store
)

// Singleton returns a singleton of the registry storage.
func Singleton() *Store {
once.Do(func() {
rStore = NewRegistryStore(nil)
})
return rStore
}
4 changes: 3 additions & 1 deletion sensor/kubernetes/listener/resources/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/stackrox/rox/sensor/common/config"
"github.com/stackrox/rox/sensor/common/detector"
"github.com/stackrox/rox/sensor/common/metrics"
"github.com/stackrox/rox/sensor/common/registry"
complianceOperatorDispatchers "github.com/stackrox/rox/sensor/kubernetes/listener/resources/complianceoperator/dispatchers"
"github.com/stackrox/rox/sensor/kubernetes/listener/resources/rbac"
"github.com/stackrox/rox/sensor/kubernetes/orchestratornamespaces"
Expand Down Expand Up @@ -61,6 +62,7 @@ func NewDispatcherRegistry(clusterID string, podLister v1Listers.PodLister, prof
endpointManager := newEndpointManager(serviceStore, deploymentStore, podStore, nodeStore, entityStore)
rbacUpdater := rbac.NewStore()
portExposureReconciler := newPortExposureReconciler(deploymentStore, serviceStore)
registryStore := registry.Singleton()

return &registryImpl{
deploymentHandler: newDeploymentHandler(clusterID, serviceStore, deploymentStore, podStore, endpointManager, nsStore,
Expand All @@ -70,7 +72,7 @@ func NewDispatcherRegistry(clusterID string, podLister v1Listers.PodLister, prof
namespaceDispatcher: newNamespaceDispatcher(nsStore, serviceStore, deploymentStore, podStore),
serviceDispatcher: newServiceDispatcher(serviceStore, deploymentStore, endpointManager, portExposureReconciler),
osRouteDispatcher: newRouteDispatcher(serviceStore, portExposureReconciler),
secretDispatcher: newSecretDispatcher(),
secretDispatcher: newSecretDispatcher(registryStore),
networkPolicyDispatcher: newNetworkPolicyDispatcher(),
nodeDispatcher: newNodeDispatcher(serviceStore, deploymentStore, nodeStore, endpointManager),
serviceAccountDispatcher: newServiceAccountDispatcher(),
Expand Down
Loading