mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 23:26:39 -08:00
feat(db): added insecure skip tls verify to download trivy db (#2140)
Co-authored-by: Teppei Fukuda <knqyf263@gmail.com>
This commit is contained in:
@@ -15,6 +15,7 @@ OPTIONS:
|
||||
--exit-code value Exit code when vulnerabilities were found (default: 0) [$TRIVY_EXIT_CODE]
|
||||
--skip-db-update, --skip-update skip updating vulnerability database (default: false) [$TRIVY_SKIP_UPDATE, $TRIVY_SKIP_DB_UPDATE]
|
||||
--skip-policy-update skip updating built-in policies (default: false) [$TRIVY_SKIP_POLICY_UPDATE]
|
||||
--insecure allow insecure server connections when using SSL (default: false) [$TRIVY_INSECURE]
|
||||
--clear-cache, -c clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
|
||||
--ignore-unfixed display only fixed vulnerabilities (default: false) [$TRIVY_IGNORE_UNFIXED]
|
||||
--vuln-type value comma-separated list of vulnerability types (os,library) (default: "os,library") [$TRIVY_VULN_TYPE]
|
||||
|
||||
@@ -14,6 +14,7 @@ OPTIONS:
|
||||
--output value, -o value output file name [$TRIVY_OUTPUT]
|
||||
--exit-code value Exit code when vulnerabilities were found (default: 0) [$TRIVY_EXIT_CODE]
|
||||
--skip-db-update, --skip-update skip updating vulnerability database (default: false) [$TRIVY_SKIP_UPDATE, $TRIVY_SKIP_DB_UPDATE]
|
||||
--insecure allow insecure server connections when using SSL (default: false) [$TRIVY_INSECURE]
|
||||
--skip-policy-update skip updating built-in policies (default: false) [$TRIVY_SKIP_POLICY_UPDATE]
|
||||
--clear-cache, -c clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
|
||||
--ignore-unfixed display only fixed vulnerabilities (default: false) [$TRIVY_IGNORE_UNFIXED]
|
||||
|
||||
@@ -18,6 +18,7 @@ OPTIONS:
|
||||
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") [$TRIVY_SEVERITY]
|
||||
--offline-scan do not issue API requests to identify dependencies (default: false) [$TRIVY_OFFLINE_SCAN]
|
||||
--db-repository value OCI repository to retrieve trivy-db from (default: "ghcr.io/aquasecurity/trivy-db") [$TRIVY_DB_REPOSITORY]
|
||||
--insecure allow insecure server connections when using SSL (default: false) [$TRIVY_INSECURE]
|
||||
--skip-files value specify the file paths to skip traversal (accepts multiple inputs) [$TRIVY_SKIP_FILES]
|
||||
--skip-dirs value specify the directories where the traversal is skipped (accepts multiple inputs) [$TRIVY_SKIP_DIRS]
|
||||
--artifact-type value, --type value input artifact type (image, fs, repo, archive) (default: "image") [$TRIVY_ARTIFACT_TYPE]
|
||||
|
||||
@@ -10,6 +10,7 @@ USAGE:
|
||||
OPTIONS:
|
||||
--skip-db-update, --skip-update skip updating vulnerability database (default: false) [$TRIVY_SKIP_UPDATE, $TRIVY_SKIP_DB_UPDATE]
|
||||
--download-db-only download/update vulnerability database but don't run a scan (default: false) [$TRIVY_DOWNLOAD_DB_ONLY]
|
||||
--insecure allow insecure server connections when using SSL (default: false) [$TRIVY_INSECURE]
|
||||
--reset remove all caches and database (default: false) [$TRIVY_RESET]
|
||||
--cache-backend value cache backend (e.g. redis://localhost:6379) (default: "fs") [$TRIVY_CACHE_BACKEND]
|
||||
--cache-ttl value cache TTL when using redis as cache backend (default: 0s) [$TRIVY_CACHE_TTL]
|
||||
|
||||
@@ -516,6 +516,7 @@ func NewFilesystemCommand() *cli.Command {
|
||||
&exitCodeFlag,
|
||||
&skipDBUpdateFlag,
|
||||
&skipPolicyUpdateFlag,
|
||||
&insecureFlag,
|
||||
&clearCacheFlag,
|
||||
&ignoreUnfixedFlag,
|
||||
&vulnTypeFlag,
|
||||
@@ -564,6 +565,7 @@ func NewRootfsCommand() *cli.Command {
|
||||
&outputFlag,
|
||||
&exitCodeFlag,
|
||||
&skipDBUpdateFlag,
|
||||
&insecureFlag,
|
||||
&skipPolicyUpdateFlag,
|
||||
&clearCacheFlag,
|
||||
&ignoreUnfixedFlag,
|
||||
@@ -695,6 +697,7 @@ func NewServerCommand() *cli.Command {
|
||||
Flags: []cli.Flag{
|
||||
&skipDBUpdateFlag,
|
||||
&downloadDBOnlyFlag,
|
||||
&insecureFlag,
|
||||
&resetFlag,
|
||||
&cacheBackendFlag,
|
||||
&cacheTTL,
|
||||
@@ -828,6 +831,7 @@ func NewK8sCommand() *cli.Command {
|
||||
&severityFlag,
|
||||
&exitCodeFlag,
|
||||
&skipDBUpdateFlag,
|
||||
&insecureFlag,
|
||||
&skipPolicyUpdateFlag,
|
||||
&clearCacheFlag,
|
||||
&ignoreUnfixedFlag,
|
||||
@@ -887,6 +891,7 @@ func NewSbomCommand() *cli.Command {
|
||||
&severityFlag,
|
||||
&offlineScan,
|
||||
&dbRepositoryFlag,
|
||||
&insecureFlag,
|
||||
stringSliceFlag(skipFiles),
|
||||
stringSliceFlag(skipDirs),
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type Option struct {
|
||||
option.SbomOption
|
||||
option.SecretOption
|
||||
option.KubernetesOption
|
||||
option.OtherOption
|
||||
|
||||
// We don't want to allow disabled analyzers to be passed by users,
|
||||
// but it differs depending on scanning modes.
|
||||
@@ -46,6 +47,7 @@ func NewOption(c *cli.Context) (Option, error) {
|
||||
SbomOption: option.NewSbomOption(c),
|
||||
SecretOption: option.NewSecretOption(c),
|
||||
KubernetesOption: option.NewKubernetesOption(c),
|
||||
OtherOption: option.NewOtherOption(c),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ func (r *Runner) initDB(c Option) error {
|
||||
|
||||
// download the database file
|
||||
noProgress := c.Quiet || c.NoProgress
|
||||
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, c.DBRepository, noProgress, c.SkipDBUpdate); err != nil {
|
||||
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, c.DBRepository, noProgress, c.Insecure, c.SkipDBUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ func (c Cache) ClearArtifacts() error {
|
||||
}
|
||||
|
||||
// DownloadDB downloads the DB
|
||||
func DownloadDB(appVersion, cacheDir, dbRepository string, quiet, skipUpdate bool) error {
|
||||
client := db.NewClient(cacheDir, quiet, db.WithDBRepository(dbRepository))
|
||||
func DownloadDB(appVersion, cacheDir, dbRepository string, quiet, insecure, skipUpdate bool) error {
|
||||
client := db.NewClient(cacheDir, quiet, insecure, db.WithDBRepository(dbRepository))
|
||||
ctx := context.Background()
|
||||
needsUpdate, err := client.NeedsUpdate(appVersion, skipUpdate)
|
||||
if err != nil {
|
||||
|
||||
@@ -14,7 +14,6 @@ type ArtifactOption struct {
|
||||
Input string
|
||||
Timeout time.Duration
|
||||
ClearCache bool
|
||||
Insecure bool
|
||||
|
||||
SkipDirs []string
|
||||
SkipFiles []string
|
||||
@@ -33,13 +32,11 @@ func NewArtifactOption(c *cli.Context) ArtifactOption {
|
||||
SkipFiles: c.StringSlice("skip-files"),
|
||||
SkipDirs: c.StringSlice("skip-dirs"),
|
||||
OfflineScan: c.Bool("offline-scan"),
|
||||
Insecure: c.Bool("insecure"),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initialize the CLI context for artifact scanning
|
||||
func (c *ArtifactOption) Init(ctx *cli.Context, logger *zap.SugaredLogger) (err error) {
|
||||
|
||||
// kubernetes subcommand doesn't require any argument
|
||||
if ctx.Command.Name == "kubernetes" {
|
||||
return nil
|
||||
|
||||
14
pkg/commands/option/others.go
Normal file
14
pkg/commands/option/others.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package option
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
type OtherOption struct {
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
// NewOtherOption is the factory method to return other option
|
||||
func NewOtherOption(c *cli.Context) OtherOption {
|
||||
return OtherOption{
|
||||
Insecure: c.Bool("insecure"),
|
||||
}
|
||||
}
|
||||
@@ -6,25 +6,27 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
)
|
||||
|
||||
// Config holds the Trivy config
|
||||
type Config struct {
|
||||
// Option holds the Trivy config
|
||||
type Option struct {
|
||||
option.GlobalOption
|
||||
option.DBOption
|
||||
option.CacheOption
|
||||
option.OtherOption
|
||||
|
||||
Listen string
|
||||
Token string
|
||||
TokenHeader string
|
||||
}
|
||||
|
||||
// NewConfig is the factory method to return config
|
||||
func NewConfig(c *cli.Context) Config {
|
||||
// NewOption is the factory method to return config
|
||||
func NewOption(c *cli.Context) Option {
|
||||
// the error is ignored because logger is unnecessary
|
||||
gc, _ := option.NewGlobalOption(c) // nolint: errcheck
|
||||
return Config{
|
||||
return Option{
|
||||
GlobalOption: gc,
|
||||
DBOption: option.NewDBOption(c),
|
||||
CacheOption: option.NewCacheOption(c),
|
||||
OtherOption: option.NewOtherOption(c),
|
||||
|
||||
Listen: c.String("listen"),
|
||||
Token: c.String("token"),
|
||||
@@ -33,7 +35,7 @@ func NewConfig(c *cli.Context) Config {
|
||||
}
|
||||
|
||||
// Init initializes the config
|
||||
func (c *Config) Init() (err error) {
|
||||
func (c *Option) Init() (err error) {
|
||||
if err := c.DBOption.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -16,12 +16,12 @@ func TestNew(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want server.Config
|
||||
want server.Option
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: []string{"-quiet", "--no-progress", "--reset", "--skip-db-update", "--listen", "localhost:8080"},
|
||||
want: server.Config{
|
||||
want: server.Option{
|
||||
GlobalOption: option.GlobalOption{
|
||||
Quiet: true,
|
||||
},
|
||||
@@ -49,7 +49,7 @@ func TestNew(t *testing.T) {
|
||||
|
||||
tt.want.GlobalOption.Context = ctx
|
||||
|
||||
got := server.NewConfig(ctx)
|
||||
got := server.NewOption(ctx)
|
||||
assert.Equal(t, tt.want.GlobalOption.Quiet, got.Quiet, tt.name)
|
||||
assert.Equal(t, tt.want.DBOption, got.DBOption, tt.name)
|
||||
assert.Equal(t, tt.want.Listen, got.Listen, tt.name)
|
||||
@@ -88,7 +88,7 @@ func TestConfig_Init(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &server.Config{
|
||||
c := &server.Option{
|
||||
DBOption: tt.dbConfig,
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
// Run runs the scan
|
||||
func Run(ctx *cli.Context) error {
|
||||
return run(NewConfig(ctx))
|
||||
return run(NewOption(ctx))
|
||||
}
|
||||
|
||||
func run(c Config) (err error) {
|
||||
func run(c Option) (err error) {
|
||||
if err = log.InitLogger(c.Debug, c.Quiet); err != nil {
|
||||
return xerrors.Errorf("failed to initialize a logger: %w", err)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func run(c Config) (err error) {
|
||||
}
|
||||
|
||||
// download the database file
|
||||
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, c.DBRepository, true, c.SkipDBUpdate); err != nil {
|
||||
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, c.DBRepository, true, c.Insecure, c.SkipDBUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -53,5 +53,5 @@ func run(c Config) (err error) {
|
||||
}
|
||||
|
||||
server := rpcServer.NewServer(c.AppVersion, c.Listen, c.CacheDir, c.Token, c.TokenHeader)
|
||||
return server.ListenAndServe(cache)
|
||||
return server.ListenAndServe(cache, c.Insecure)
|
||||
}
|
||||
|
||||
20
pkg/db/db.go
20
pkg/db/db.go
@@ -60,13 +60,14 @@ func WithClock(clock clock.Clock) Option {
|
||||
type Client struct {
|
||||
*options
|
||||
|
||||
cacheDir string
|
||||
metadata metadata.Client
|
||||
quiet bool
|
||||
cacheDir string
|
||||
metadata metadata.Client
|
||||
quiet bool
|
||||
insecureSkipTLSVerify bool
|
||||
}
|
||||
|
||||
// NewClient is the factory method for DB client
|
||||
func NewClient(cacheDir string, quiet bool, opts ...Option) *Client {
|
||||
func NewClient(cacheDir string, quiet, insecure bool, opts ...Option) *Client {
|
||||
o := &options{
|
||||
clock: clock.RealClock{},
|
||||
dbRepository: defaultDBRepository,
|
||||
@@ -77,10 +78,11 @@ func NewClient(cacheDir string, quiet bool, opts ...Option) *Client {
|
||||
}
|
||||
|
||||
return &Client{
|
||||
options: o,
|
||||
cacheDir: cacheDir,
|
||||
metadata: metadata.NewClient(cacheDir),
|
||||
quiet: quiet,
|
||||
options: o,
|
||||
cacheDir: cacheDir,
|
||||
metadata: metadata.NewClient(cacheDir),
|
||||
quiet: quiet,
|
||||
insecureSkipTLSVerify: insecure, // insecure skip for download DB
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +185,7 @@ func (c *Client) updateDownloadedAt(dst string) error {
|
||||
func (c *Client) populateOCIArtifact() error {
|
||||
if c.artifact == nil {
|
||||
repo := fmt.Sprintf("%s:%d", c.dbRepository, db.SchemaVersion)
|
||||
art, err := oci.NewArtifact(repo, dbMediaType, c.quiet)
|
||||
art, err := oci.NewArtifact(repo, dbMediaType, c.quiet, c.insecureSkipTLSVerify)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("OCI artifact error: %w", err)
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
client := db.NewClient(cacheDir, true, db.WithClock(tt.clock))
|
||||
client := db.NewClient(cacheDir, true, false, db.WithClock(tt.clock))
|
||||
needsUpdate, err := client.NeedsUpdate("test", tt.skip)
|
||||
|
||||
switch {
|
||||
@@ -203,10 +203,10 @@ func TestClient_Download(t *testing.T) {
|
||||
img.LayersReturns([]v1.Layer{newFakeLayer(t, tt.input)}, nil)
|
||||
|
||||
// Mock OCI artifact
|
||||
art, err := oci.NewArtifact("db", mediaType, true, oci.WithImage(img))
|
||||
art, err := oci.NewArtifact("db", mediaType, true, false, oci.WithImage(img))
|
||||
require.NoError(t, err)
|
||||
|
||||
client := db.NewClient(cacheDir, true, db.WithOCIArtifact(art), db.WithClock(timeDownloadedAt))
|
||||
client := db.NewClient(cacheDir, true, false, db.WithOCIArtifact(art), db.WithClock(timeDownloadedAt))
|
||||
err = client.Download(context.Background(), cacheDir)
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
|
||||
@@ -2,7 +2,9 @@ package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/cheggaaa/pb/v3"
|
||||
@@ -37,7 +39,7 @@ type Artifact struct {
|
||||
}
|
||||
|
||||
// NewArtifact returns a new artifact
|
||||
func NewArtifact(repo, mediaType string, quiet bool, opts ...Option) (*Artifact, error) {
|
||||
func NewArtifact(repo, mediaType string, quiet, insecure bool, opts ...Option) (*Artifact, error) {
|
||||
o := &options{}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -50,7 +52,15 @@ func NewArtifact(repo, mediaType string, quiet bool, opts ...Option) (*Artifact,
|
||||
return nil, xerrors.Errorf("repository name error (%s): %w", repo, err)
|
||||
}
|
||||
|
||||
o.img, err = remote.Image(ref)
|
||||
var remoteOpts []remote.Option
|
||||
if insecure {
|
||||
t := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
remoteOpts = append(remoteOpts, remote.WithTransport(t))
|
||||
}
|
||||
|
||||
o.img, err = remote.Image(ref, remoteOpts...)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("OCI repository error: %w", err)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestNewArtifact(t *testing.T) {
|
||||
img := new(fakei.FakeImage)
|
||||
img.LayersReturns(tt.layersReturns.layers, tt.layersReturns.err)
|
||||
|
||||
_, err = oci.NewArtifact("repo", tt.mediaType, true, oci.WithImage(img))
|
||||
_, err = oci.NewArtifact("repo", tt.mediaType, true, false, oci.WithImage(img))
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.wantErr)
|
||||
@@ -128,7 +128,7 @@ func TestArtifact_Download(t *testing.T) {
|
||||
img.LayersReturns([]v1.Layer{flayer}, nil)
|
||||
|
||||
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
|
||||
artifact, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
|
||||
artifact, err := oci.NewArtifact("repo", mediaType, true, false, oci.WithImage(img))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = artifact.Download(context.Background(), tempDir)
|
||||
|
||||
@@ -45,12 +45,12 @@ func NewServer(appVersion, addr, cacheDir, token, tokenHeader string) Server {
|
||||
}
|
||||
|
||||
// ListenAndServe starts Trivy server
|
||||
func (s Server) ListenAndServe(serverCache cache.Cache) error {
|
||||
func (s Server) ListenAndServe(serverCache cache.Cache, insecure bool) error {
|
||||
requestWg := &sync.WaitGroup{}
|
||||
dbUpdateWg := &sync.WaitGroup{}
|
||||
|
||||
go func() {
|
||||
worker := newDBWorker(dbc.NewClient(s.cacheDir, true))
|
||||
worker := newDBWorker(dbc.NewClient(s.cacheDir, true, insecure))
|
||||
ctx := context.Background()
|
||||
for {
|
||||
time.Sleep(updateInterval)
|
||||
@@ -130,7 +130,7 @@ func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
|
||||
|
||||
log.Logger.Info("Updating DB...")
|
||||
if err = w.hotUpdate(ctx, cacheDir, dbUpdateWg, requestWg); err != nil {
|
||||
return xerrors.Errorf("failed DB hot update")
|
||||
return xerrors.Errorf("failed DB hot update: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user