Use GitLab's advisory DB with cluster image scanning
Why are we doing this work
Similar to Use GitLab's advisory DB with container scanning (#349160 - closed), we want Cluster Image Scanning to use the latest advisories from gemnasium-db.
Relevant links
Non-functional requirements
-
Documentation: -
Feature flag: -
Performance: -
Testing:
Implementation plan
We need to make changes to three components, top-down:
- The
starboard-operator
Helm chart needs to accept adbRepository
value and write it to thestarboard-trivy-config
ConfigMap.
Toggle diff
diff --git a/deploy/helm/templates/config.yaml b/deploy/helm/templates/config.yaml
@@ -57,6 +57,7 @@ data:
trivy.nonSslRegistry.{{ $key }}: {{ $registry | quote }}
{{- end }}
trivy.severity: {{ .severity | quote }}
+ trivy.dbRepository: {{ .dbRepository | quote }}
{{- if .ignoreUnfixed }}
trivy.ignoreUnfixed: {{ .ignoreUnfixed | quote }}
{{- end }}
diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml
@@ -166,6 +166,9 @@ trivy:
#
# serverCustomHeaders: "foo=bar"
+ # OCI registry to pull advisory databases from.
+ dbRepository: "ghcr.io/aquasecurity/trivy-db"
+
kubeBench:
imageRef: docker.io/aquasec/kube-bench:v0.6.5
- The
starboard-operator
program's Trivy plugin needs to read thedbRepository
value from the ConfigMap and pass it along as a parameter to thetrivy-download-db
initContainers it spawns. This initContainer precedes every container scan and retrieves the advisory database.
Toggle diff
diff --git a/pkg/plugin/trivy/plugin.go b/pkg/plugin/trivy/plugin.go
@@ -43,6 +43,7 @@ const (
keyTrivyGitHubToken = "trivy.githubToken"
keyTrivySkipFiles = "trivy.skipFiles"
keyTrivySkipDirs = "trivy.skipDirs"
+ keyTrivyDBRepository = "trivy.dbRepository"
keyTrivyServerURL = "trivy.serverURL"
keyTrivyServerTokenHeader = "trivy.serverTokenHeader"
@@ -205,6 +206,11 @@ func (c Config) setResourceLimit(configKey string, k8sResourceList *corev1.Resou
return nil
}
+func (c Config) GetDBRepository() string {
+ dbRepository, _ := c.GetRequiredData(keyTrivyDBRepository)
+ return dbRepository
+}
+
type plugin struct {
clock ext.Clock
idGenerator ext.IDGenerator
@@ -238,6 +244,7 @@ func (p *plugin) Init(ctx starboard.PluginContext) error {
keyTrivySeverity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL",
keyTrivyMode: string(Standalone),
keyTrivyTimeout: "5m0s",
+ keyTrivyDBRepository: "ghcr.io/aquasecurity/trivy-db",
keyResourcesRequestsCPU: "100m",
keyResourcesRequestsMemory: "100M",
@@ -405,6 +412,8 @@ func (p *plugin) getPodSpecForStandaloneMode(ctx starboard.PluginContext, config
"/tmp/trivy/.cache",
"image",
"--download-db-only",
+ "--db-repository",
+ config.GetDBRepository(),
},
Resources: requirements,
VolumeMounts: []corev1.VolumeMount{
@@ -1033,6 +1042,8 @@ func (p *plugin) getPodSpecForStandaloneFSMode(ctx starboard.PluginContext, conf
"--download-db-only",
"--cache-dir",
"/var/starboard/trivy-db",
+ "--db-repository",
+ config.GetDBRepository(),
},
Resources: requirements,
VolumeMounts: volumeMounts,
- The trivy program needs to accept a new command-line flag
--db-repository
which determines the advisory database OCI registry.
Toggle diff
diff --git a/pkg/commands/app.go b/pkg/commands/app.go
@@ -313,6 +313,13 @@ var (
EnvVars: []string{"TRIVY_INSECURE"},
}
+ dbRepositoryFlag = cli.StringFlag{
+ Name: "db-repository",
+ Usage: "OCI repository to retrieve trivy-db from",
+ Value: "ghcr.io/aquasecurity/trivy-db",
+ EnvVars: []string{"TRIVY_DB_REPOSITORY"},
+ }
+
// Global flags
globalFlags = []cli.Flag{
&quietFlag,
@@ -434,6 +441,7 @@ func NewImageCommand() *cli.Command {
&redisBackendKey,
&offlineScan,
&insecureFlag,
+ &dbRepositoryFlag,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),
},
@@ -470,6 +478,7 @@ func NewFilesystemCommand() *cli.Command {
&ignorePolicy,
&listAllPackages,
&offlineScan,
+ &dbRepositoryFlag,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),
stringSliceFlag(configPolicy),
diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go
@@ -120,7 +120,7 @@ func initFSCache(c Option) (cache.Cache, error) {
func initDB(c Option) error {
// download the database file
noProgress := c.Quiet || c.NoProgress
- if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.SkipDBUpdate); err != nil {
+ if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.SkipDBUpdate, c.DBRepository); err != nil {
return err
}
diff --git a/pkg/commands/operation/operation.go b/pkg/commands/operation/operation.go
@@ -93,7 +93,7 @@ func (c Cache) ClearArtifacts() error {
}
// DownloadDB downloads the DB
-func DownloadDB(appVersion, cacheDir string, quiet, skipUpdate bool) error {
+func DownloadDB(appVersion, cacheDir string, quiet, skipUpdate bool, dbRepository string) error {
client := db.NewClient(cacheDir, quiet)
ctx := context.Background()
needsUpdate, err := client.NeedsUpdate(appVersion, skipUpdate)
@@ -104,7 +104,8 @@ func DownloadDB(appVersion, cacheDir string, quiet, skipUpdate bool) error {
if needsUpdate {
log.Logger.Info("Need to update DB")
log.Logger.Info("Downloading DB...")
- if err = client.Download(ctx, cacheDir); err != nil {
+ log.Logger.Infof("Repository: %s", dbRepository)
+ if err = client.Download(ctx, cacheDir, dbRepository); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
}
diff --git a/pkg/commands/option/db.go b/pkg/commands/option/db.go
@@ -14,6 +14,7 @@ type DBOption struct {
SkipDBUpdate bool
Light bool
NoProgress bool
+ DBRepository string
}
// NewDBOption is the factory method to return the DBOption
@@ -24,6 +25,7 @@ func NewDBOption(c *cli.Context) DBOption {
SkipDBUpdate: c.Bool("skip-db-update"),
Light: c.Bool("light"),
NoProgress: c.Bool("no-progress"),
+ DBRepository: c.String("db-repository"),
}
}
diff --git a/pkg/commands/server/run.go b/pkg/commands/server/run.go
@@ -40,7 +40,7 @@ func run(c Config) (err error) {
}
// download the database file
- if err = operation.DownloadDB(c.AppVersion, c.CacheDir, true, c.SkipDBUpdate); err != nil {
+ if err = operation.DownloadDB(c.AppVersion, c.CacheDir, true, c.SkipDBUpdate, c.DBRepository); err != nil {
return err
}
diff --git a/pkg/db/db.go b/pkg/db/db.go
@@ -15,15 +15,12 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
)
-const (
- dbRepository = "ghcr.io/aquasecurity/trivy-db"
- dbMediaType = "application/vnd.aquasec.trivy.db.layer.v1.tar+gzip"
-)
+const dbMediaType = "application/vnd.aquasec.trivy.db.layer.v1.tar+gzip"
// Operation defines the DB operations
type Operation interface {
NeedsUpdate(cliVersion string, skip bool) (need bool, err error)
- Download(ctx context.Context, dst string) (err error)
+ Download(ctx context.Context, dst string, dbRepository string) (err error)
}
type options struct {
@@ -129,13 +126,13 @@ func (c *Client) isNewDB(meta metadata.Metadata) bool {
}
// Download downloads the DB file
-func (c *Client) Download(ctx context.Context, dst string) error {
+func (c *Client) Download(ctx context.Context, dst string, dbRepository string) error {
// Remove the metadata file under the cache directory before downloading DB
if err := c.metadata.Delete(); err != nil {
log.Logger.Debug("no metadata file")
}
- if err := c.populateOCIArtifact(); err != nil {
+ if err := c.populateOCIArtifact(dbRepository); err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}
@@ -168,7 +165,7 @@ func (c *Client) updateDownloadedAt(dst string) error {
return nil
}
-func (c *Client) populateOCIArtifact() error {
+func (c *Client) populateOCIArtifact(dbRepository string) error {
if c.artifact == nil {
repo := fmt.Sprintf("%s:%d", dbRepository, db.SchemaVersion)
art, err := oci.NewArtifact(repo, dbMediaType, c.quiet)
diff --git a/pkg/rpc/server/listen.go b/pkg/rpc/server/listen.go
@@ -142,7 +142,7 @@ func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, re
}
defer os.RemoveAll(tmpDir)
- if err = w.dbClient.Download(ctx, tmpDir); err != nil {
+ if err = w.dbClient.Download(ctx, tmpDir, "ghcr.io/aquasecurity/trivy-db"); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}