diff --git a/pkg/docker/config/config.go b/pkg/docker/config/config.go new file mode 100644 index 0000000000000..a29ad9f2218ec --- /dev/null +++ b/pkg/docker/config/config.go @@ -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(:). +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 +} diff --git a/pkg/images/integration/set.go b/pkg/images/integration/set.go index 1ab3f8737629b..b312fc2c8d779 100644 --- a/pkg/images/integration/set.go +++ b/pkg/images/integration/set.go @@ -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) diff --git a/pkg/images/integration/set_impl.go b/pkg/images/integration/set_impl.go index 48fac9d7a57f9..29fab83639ac6 100644 --- a/pkg/images/integration/set_impl.go +++ b/pkg/images/integration/set_impl.go @@ -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) } } diff --git a/pkg/registries/factory.go b/pkg/registries/factory.go index 7e01362fd9501..13f99902435dc 100644 --- a/pkg/registries/factory.go +++ b/pkg/registries/factory.go @@ -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 { diff --git a/pkg/registries/factory_options.go b/pkg/registries/factory_options.go new file mode 100644 index 0000000000000..0252487ce4ee9 --- /dev/null +++ b/pkg/registries/factory_options.go @@ -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 +} diff --git a/pkg/scanners/anchore/anchore_test.go b/pkg/scanners/anchore/anchore_test.go index 0eb560dc47458..6c8aabddfb244 100644 --- a/pkg/scanners/anchore/anchore_test.go +++ b/pkg/scanners/anchore/anchore_test.go @@ -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{ diff --git a/sensor/common/registry/registry_store.go b/sensor/common/registry/registry_store.go new file mode 100644 index 0000000000000..1fc9496b118a7 --- /dev/null +++ b/sensor/common/registry/registry_store.go @@ -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] +} diff --git a/sensor/common/registry/singleton.go b/sensor/common/registry/singleton.go new file mode 100644 index 0000000000000..5e807f7cb4ff4 --- /dev/null +++ b/sensor/common/registry/singleton.go @@ -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 +} diff --git a/sensor/kubernetes/listener/resources/dispatcher.go b/sensor/kubernetes/listener/resources/dispatcher.go index 6ca07a0672f2b..717c7d114a460 100644 --- a/sensor/kubernetes/listener/resources/dispatcher.go +++ b/sensor/kubernetes/listener/resources/dispatcher.go @@ -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" @@ -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 ®istryImpl{ deploymentHandler: newDeploymentHandler(clusterID, serviceStore, deploymentStore, podStore, endpointManager, nsStore, @@ -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(), diff --git a/sensor/kubernetes/listener/resources/secrets.go b/sensor/kubernetes/listener/resources/secrets.go index 2452b46081ead..f9d9cbae957a6 100644 --- a/sensor/kubernetes/listener/resources/secrets.go +++ b/sensor/kubernetes/listener/resources/secrets.go @@ -1,6 +1,7 @@ package resources import ( + "context" "encoding/base64" "encoding/json" "errors" @@ -11,89 +12,24 @@ import ( "github.com/cloudflare/cfssl/certinfo" "github.com/stackrox/rox/generated/internalapi/central" "github.com/stackrox/rox/generated/storage" + "github.com/stackrox/rox/pkg/docker/config" + "github.com/stackrox/rox/pkg/features" "github.com/stackrox/rox/pkg/protoconv" "github.com/stackrox/rox/pkg/registries/docker" "github.com/stackrox/rox/pkg/registries/rhel" "github.com/stackrox/rox/pkg/urlfmt" "github.com/stackrox/rox/pkg/utils" "github.com/stackrox/rox/pkg/uuid" + "github.com/stackrox/rox/sensor/common/registry" v1 "k8s.io/api/core/v1" ) -const redhatRegistryEndpoint = "registry.redhat.io" +const ( + redhatRegistryEndpoint = "registry.redhat.io" -// 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(:). -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 -} - -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 -} + saAnnotation = "kubernetes.io/service-account.name" + defaultSA = "default" +) var dataTypeMap = map[string]storage.SecretType{ "-----BEGIN CERTIFICATE-----": storage.SecretType_PUBLIC_CERTIFICATE, @@ -187,24 +123,23 @@ func populateTypeData(secret *storage.Secret, dataFiles map[string][]byte) { } // secretDispatcher handles secret resource events. -type secretDispatcher struct{} +type secretDispatcher struct { + regStore *registry.Store +} // newSecretDispatcher creates and returns a new secret handler. -func newSecretDispatcher() *secretDispatcher { - return &secretDispatcher{} +func newSecretDispatcher(regStore *registry.Store) *secretDispatcher { + return &secretDispatcher{ + regStore: regStore, + } } -func dockerConfigToImageIntegration(registry string, dce dockerConfigEntry) *storage.ImageIntegration { +func dockerConfigToImageIntegration(registry string, dce config.DockerConfigEntry) *storage.ImageIntegration { registryType := docker.GenericDockerRegistryType if urlfmt.TrimHTTPPrefixes(registry) == redhatRegistryEndpoint { registryType = rhel.RedHatRegistryType } - username, password := dce.Username, dce.Password - // TODO(ROX-8465): Determine which Service Account's token to use to replace the credentials. - // if features.LocalImageScanning.Enabled() { - // } - return &storage.ImageIntegration{ Id: uuid.NewV4().String(), Type: registryType, @@ -212,16 +147,16 @@ func dockerConfigToImageIntegration(registry string, dce dockerConfigEntry) *sto IntegrationConfig: &storage.ImageIntegration_Docker{ Docker: &storage.DockerConfig{ Endpoint: registry, - Username: username, - Password: password, + Username: dce.Username, + Password: dce.Password, }, }, Autogenerated: true, } } -func processDockerConfigEvent(secret *v1.Secret, action central.ResourceAction) []*central.SensorEvent { - var dockerConfig dockerConfig +func (s *secretDispatcher) processDockerConfigEvent(secret *v1.Secret, action central.ResourceAction) []*central.SensorEvent { + var dockerConfig config.DockerConfig switch secret.Type { case v1.SecretTypeDockercfg: data, ok := secret.Data[v1.DockerConfigKey] @@ -237,28 +172,45 @@ func processDockerConfigEvent(secret *v1.Secret, action central.ResourceAction) if !ok { return nil } - var dockerConfigJSON dockerConfigJSON + var dockerConfigJSON config.DockerConfigJSON if err := json.Unmarshal(data, &dockerConfigJSON); err != nil { log.Error(err) return nil } dockerConfig = dockerConfigJSON.Auths default: - utils.Should(errors.New("only Docker Config secrets are allowed")) + _ = utils.Should(errors.New("only Docker Config secrets are allowed")) return nil } sensorEvents := make([]*central.SensorEvent, 0, len(dockerConfig)+1) registries := make([]*storage.ImagePullSecret_Registry, 0, len(dockerConfig)) + // In Kubernetes, the `default` service account always exists in each namespace (it is recreated upon deletion). + // The default service account always contains an API token. + // In OpenShift, the default service account also contains credentials for the + // OpenShift Container Registry, which is an internal image registry. + fromDefaultSA := secret.GetAnnotations()[saAnnotation] == defaultSA for registry, dce := range dockerConfig { - ii := dockerConfigToImageIntegration(registry, dce) - sensorEvents = append(sensorEvents, ¢ral.SensorEvent{ - // Only update is supported at this time. - Action: central.ResourceAction_UPDATE_RESOURCE, - Resource: ¢ral.SensorEvent_ImageIntegration{ - ImageIntegration: ii, - }, - }) + if features.LocalImageScanning.Enabled() { + if fromDefaultSA { + // Store the registry credentials so Sensor can reach it. + err := s.regStore.UpsertRegistry(context.Background(), secret.GetNamespace(), registry, dce) + if err != nil { + log.Errorf("Unable to upsert registry %q into store: %v", registry, err) + } + } + } + + if !features.LocalImageScanning.Enabled() || !fromDefaultSA { + ii := dockerConfigToImageIntegration(registry, dce) + sensorEvents = append(sensorEvents, ¢ral.SensorEvent{ + // Only update is supported at this time. + Action: central.ResourceAction_UPDATE_RESOURCE, + Resource: ¢ral.SensorEvent_ImageIntegration{ + ImageIntegration: ii, + }, + }) + } registries = append(registries, &storage.ImagePullSecret_Registry{ Name: registry, @@ -302,14 +254,14 @@ func secretToSensorEvent(action central.ResourceAction, secret *storage.Secret) } // ProcessEvent processes a secret resource event, and returns the sensor events to emit in response. -func (*secretDispatcher) ProcessEvent(obj, _ interface{}, action central.ResourceAction) []*central.SensorEvent { +func (s *secretDispatcher) ProcessEvent(obj, _ interface{}, action central.ResourceAction) []*central.SensorEvent { secret := obj.(*v1.Secret) switch secret.Type { case v1.SecretTypeDockerConfigJson, v1.SecretTypeDockercfg: - return processDockerConfigEvent(secret, action) + return s.processDockerConfigEvent(secret, action) case v1.SecretTypeServiceAccountToken: - // Filter out service account tokens because we have a service account field. + // Filter out service account tokens because we have a service account processor. return nil } diff --git a/sensor/kubernetes/listener/resources/secrets_test.go b/sensor/kubernetes/listener/resources/secrets_test.go new file mode 100644 index 0000000000000..14a4f3b4d66b5 --- /dev/null +++ b/sensor/kubernetes/listener/resources/secrets_test.go @@ -0,0 +1,129 @@ +package resources + +import ( + "context" + "testing" + + "github.com/stackrox/rox/generated/internalapi/central" + "github.com/stackrox/rox/pkg/features" + "github.com/stackrox/rox/pkg/registries/types" + "github.com/stackrox/rox/pkg/testutils" + "github.com/stackrox/rox/sensor/common/registry" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + openshift311DockerConfigSecret = &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "default-dockercfg-6167c", + Namespace: "test-ns", + Annotations: map[string]string{ + "kubernetes.io/service-account.name": "default", + }, + }, + Data: map[string][]byte{ + ".dockercfg": []byte(` +{ + "docker-registry.default.svc.cluster.local:5000": { + "username": "serviceaccount", + "password": "password", + "email": "serviceaccount@example.org" + } +}`), + }, + Type: "kubernetes.io/dockercfg", + } + + openshift4xDockerConfigSecret = &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "default-dockercfg-9w5gn", + Namespace: "test-ns", + Annotations: map[string]string{ + "kubernetes.io/service-account.name": "default", + }, + }, + Data: map[string][]byte{ + ".dockercfg": []byte(` +{ + "image-registry.openshift-image-registry.svc:5000": { + "username": "serviceaccount", + "password": "password", + "email": "serviceaccount@example.org" + } +}`), + }, + Type: "kubernetes.io/dockercfg", + } +) + +// alwaysInsecureCheckTLS is an implementation of registry.CheckTLS +// which always says the given address is insecure. +func alwaysInsecureCheckTLS(_ context.Context, _ string) (bool, error) { + return false, nil +} + +func TestOpenShiftRegistrySecret_311(t *testing.T) { + testutils.RunWithFeatureFlagEnabled(t, features.LocalImageScanning, testOpenShiftRegistrySecret311) +} + +func testOpenShiftRegistrySecret311(t *testing.T) { + regStore := registry.NewRegistryStore(alwaysInsecureCheckTLS) + d := newSecretDispatcher(regStore) + + _ = d.ProcessEvent(openshift311DockerConfigSecret, nil, central.ResourceAction_CREATE_RESOURCE) + + assert.Nil(t, regStore.GetAllInNamespace("random-ns")) + + regs := regStore.GetAllInNamespace(openshift311DockerConfigSecret.GetNamespace()) + assert.NotNil(t, regs) + assert.Len(t, regs.GetAll(), 1) + + expectedRegConfig := &types.Config{ + Username: "serviceaccount", + Password: "password", + Insecure: true, + URL: "https://docker-registry.default.svc.cluster.local:5000", + RegistryHostname: "docker-registry.default.svc.cluster.local:5000", + Autogenerated: false, + } + + assert.Equal(t, expectedRegConfig, regs.GetAll()[0].Config()) +} + +func TestOpenShiftRegistrySecret_4x(t *testing.T) { + testutils.RunWithFeatureFlagEnabled(t, features.LocalImageScanning, testOpenShiftRegistrySecret4x) +} + +func testOpenShiftRegistrySecret4x(t *testing.T) { + regStore := registry.NewRegistryStore(alwaysInsecureCheckTLS) + d := newSecretDispatcher(regStore) + + _ = d.ProcessEvent(openshift4xDockerConfigSecret, nil, central.ResourceAction_CREATE_RESOURCE) + + assert.Nil(t, regStore.GetAllInNamespace("random-ns")) + + regs := regStore.GetAllInNamespace(openshift4xDockerConfigSecret.GetNamespace()) + assert.NotNil(t, regs) + assert.Len(t, regs.GetAll(), 1) + + expectedRegConfig := &types.Config{ + Username: "serviceaccount", + Password: "password", + Insecure: true, + URL: "https://image-registry.openshift-image-registry.svc:5000", + RegistryHostname: "image-registry.openshift-image-registry.svc:5000", + Autogenerated: false, + } + + assert.Equal(t, expectedRegConfig, regs.GetAll()[0].Config()) +} diff --git a/sensor/kubernetes/listener/resources/serviceaccount.go b/sensor/kubernetes/listener/resources/serviceaccount.go index a0643eeb71f7f..b85da097d45ba 100644 --- a/sensor/kubernetes/listener/resources/serviceaccount.go +++ b/sensor/kubernetes/listener/resources/serviceaccount.go @@ -15,9 +15,8 @@ func newServiceAccountDispatcher() *serviceAccountDispatcher { return &serviceAccountDispatcher{} } -// Process processes a service account resource event, and returns the sensor events to emit in response. +// ProcessEvent processes a service account resource event, and returns the sensor events to emit in response. func (*serviceAccountDispatcher) ProcessEvent(obj, _ interface{}, action central.ResourceAction) []*central.SensorEvent { - serviceAccount := obj.(*v1.ServiceAccount) var serviceAccountSecrets []string