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:
DmitriyLewen
2022-05-26 17:54:39 +06:00
committed by GitHub
parent 1e1ccbec52
commit b7ec642572
18 changed files with 75 additions and 39 deletions

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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),

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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

View 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"),
}
}

View File

@@ -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
}

View File

@@ -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,
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
}