feat(client): configure TLS InsecureSkipVerify for server connection (#1287)

Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
Christian Zunker
2022-02-13 10:34:34 +01:00
committed by GitHub
parent de6c3cbb6c
commit aa6e1eb6f9
9 changed files with 139 additions and 25 deletions

View File

@@ -19,6 +19,10 @@ linters-settings:
locale: US locale: US
goimports: goimports:
local-prefixes: github.com/aquasecurity local-prefixes: github.com/aquasecurity
gosec:
excludes:
- G204
- G402
linters: linters:
disable-all: true disable-all: true
@@ -53,9 +57,6 @@ issues:
- linters: - linters:
- gosec - gosec
text: "Deferring unsafe method" text: "Deferring unsafe method"
- linters:
- gosec
text: "G204: Subprocess launched with variable"
- linters: - linters:
- errcheck - errcheck
text: "Close` is not checked" text: "Close` is not checked"

17
pkg/cache/remote.go vendored
View File

@@ -2,6 +2,7 @@ package cache
import ( import (
"context" "context"
"crypto/tls"
"net/http" "net/http"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@@ -19,13 +20,19 @@ type RemoteCache struct {
client rpcCache.Cache client rpcCache.Cache
} }
// RemoteURL to hold remote host
type RemoteURL string
// NewRemoteCache is the factory method for RemoteCache // NewRemoteCache is the factory method for RemoteCache
func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ArtifactCache { func NewRemoteCache(url string, customHeaders http.Header, insecure bool) cache.ArtifactCache {
ctx := client.WithCustomHeaders(context.Background(), customHeaders) ctx := client.WithCustomHeaders(context.Background(), customHeaders)
c := rpcCache.NewCacheProtobufClient(string(url), &http.Client{})
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecure,
},
},
}
c := rpcCache.NewCacheProtobufClient(url, httpClient)
return &RemoteCache{ctx: ctx, client: c} return &RemoteCache{ctx: ctx, client: c}
} }

View File

@@ -135,7 +135,7 @@ func TestRemoteCache_PutArtifact(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders) c := cache.NewRemoteCache(ts.URL, tt.args.customHeaders, false)
err := c.PutArtifact(tt.args.imageID, tt.args.imageInfo) err := c.PutArtifact(tt.args.imageID, tt.args.imageInfo)
if tt.wantErr != "" { if tt.wantErr != "" {
require.NotNil(t, err, tt.name) require.NotNil(t, err, tt.name)
@@ -196,7 +196,7 @@ func TestRemoteCache_PutBlob(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders) c := cache.NewRemoteCache(ts.URL, tt.args.customHeaders, false)
err := c.PutBlob(tt.args.diffID, tt.args.layerInfo) err := c.PutBlob(tt.args.diffID, tt.args.layerInfo)
if tt.wantErr != "" { if tt.wantErr != "" {
require.NotNil(t, err, tt.name) require.NotNil(t, err, tt.name)
@@ -274,7 +274,7 @@ func TestRemoteCache_MissingBlobs(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders) c := cache.NewRemoteCache(ts.URL, tt.args.customHeaders, false)
gotMissingImage, gotMissingLayerIDs, err := c.MissingBlobs(tt.args.imageID, tt.args.layerIDs) gotMissingImage, gotMissingLayerIDs, err := c.MissingBlobs(tt.args.imageID, tt.args.layerIDs)
if tt.wantErr != "" { if tt.wantErr != "" {
require.NotNil(t, err, tt.name) require.NotNil(t, err, tt.name)
@@ -289,3 +289,49 @@ func TestRemoteCache_MissingBlobs(t *testing.T) {
}) })
} }
} }
func TestRemoteCache_PutArtifactInsecure(t *testing.T) {
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
defer ts.Close()
type args struct {
imageID string
imageInfo types.ArtifactInfo
insecure bool
}
tests := []struct {
name string
args args
wantErr string
}{
{
name: "happy path",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
imageInfo: types.ArtifactInfo{},
insecure: true,
},
},
{
name: "sad path",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
imageInfo: types.ArtifactInfo{},
insecure: false,
},
wantErr: "certificate signed by unknown authority",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(ts.URL, nil, tt.args.insecure)
err := c.PutArtifact(tt.args.imageID, tt.args.imageInfo)
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
assert.NoError(t, err, tt.name)
})
}
}

View File

@@ -309,6 +309,7 @@ var (
insecureFlag = cli.BoolFlag{ insecureFlag = cli.BoolFlag{
Name: "insecure", Name: "insecure",
Usage: "allow insecure server connections when using SSL", Usage: "allow insecure server connections when using SSL",
Value: false,
EnvVars: []string{"TRIVY_INSECURE"}, EnvVars: []string{"TRIVY_INSECURE"},
} }
@@ -585,6 +586,7 @@ func NewClientCommand() *cli.Command {
stringSliceFlag(configPolicy), stringSliceFlag(configPolicy),
&listAllPackages, &listAllPackages,
&offlineScan, &offlineScan,
&insecureFlag,
// original flags // original flags
&token, &token,

View File

@@ -18,14 +18,14 @@ import (
) )
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
url client.RemoteURL, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) ( url client.RemoteURL, insecure client.Insecure, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (
scanner.Scanner, func(), error) { scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteDockerSet) wire.Build(scanner.RemoteDockerSet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
customHeaders client.CustomHeaders, url client.RemoteURL, artifactOption artifact.Option, customHeaders client.CustomHeaders, url client.RemoteURL, insecure client.Insecure, artifactOption artifact.Option,
configScannerOption config.ScannerOption) (scanner.Scanner, error) { configScannerOption config.ScannerOption) (scanner.Scanner, error) {
wire.Build(scanner.RemoteArchiveSet) wire.Build(scanner.RemoteArchiveSet)
return scanner.Scanner{}, nil return scanner.Scanner{}, nil

View File

@@ -138,7 +138,7 @@ func disabledAnalyzers(opt Option) []analyzer.Type {
} }
func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func(), error) { func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func(), error) {
remoteCache := cache.NewRemoteCache(cache.RemoteURL(opt.RemoteAddr), opt.CustomHeaders) remoteCache := cache.NewRemoteCache(opt.RemoteAddr, opt.CustomHeaders, opt.Insecure)
// ScannerOptions is filled only when config scanning is enabled. // ScannerOptions is filled only when config scanning is enabled.
var configScannerOptions config.ScannerOption var configScannerOptions config.ScannerOption
@@ -168,7 +168,7 @@ func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func()
if opt.Input != "" { if opt.Input != "" {
// Scan tar file // Scan tar file
s, err := initializeArchiveScanner(ctx, opt.Input, remoteCache, client.CustomHeaders(opt.CustomHeaders), s, err := initializeArchiveScanner(ctx, opt.Input, remoteCache, client.CustomHeaders(opt.CustomHeaders),
client.RemoteURL(opt.RemoteAddr), artifactOpt, configScannerOptions) client.RemoteURL(opt.RemoteAddr), client.Insecure(opt.Insecure), artifactOpt, configScannerOptions)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err) return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
} }
@@ -182,7 +182,7 @@ func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func()
} }
s, cleanup, err := initializeDockerScanner(ctx, opt.Target, remoteCache, client.CustomHeaders(opt.CustomHeaders), s, cleanup, err := initializeDockerScanner(ctx, opt.Target, remoteCache, client.CustomHeaders(opt.CustomHeaders),
client.RemoteURL(opt.RemoteAddr), dockerOpt, artifactOpt, configScannerOptions) client.RemoteURL(opt.RemoteAddr), client.Insecure(opt.Insecure), dockerOpt, artifactOpt, configScannerOptions)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err) return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err)
} }

View File

@@ -1,6 +1,6 @@
// Code generated by Wire. DO NOT EDIT. // Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire //go:generate wire
//go:build !wireinject //go:build !wireinject
// +build !wireinject // +build !wireinject
@@ -22,8 +22,8 @@ import (
// Injectors from inject.go: // Injectors from inject.go:
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) { func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, insecure client.Insecure, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
scannerScanner := client.NewProtobufClient(url) scannerScanner := client.NewProtobufClient(url, insecure)
clientScanner := client.NewScanner(customHeaders, scannerScanner) clientScanner := client.NewScanner(customHeaders, scannerScanner)
typesImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOpt) typesImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOpt)
if err != nil { if err != nil {
@@ -40,8 +40,8 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
}, nil }, nil
} }
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) { func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, insecure client.Insecure, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
scannerScanner := client.NewProtobufClient(url) scannerScanner := client.NewProtobufClient(url, insecure)
clientScanner := client.NewScanner(customHeaders, scannerScanner) clientScanner := client.NewScanner(customHeaders, scannerScanner)
typesImage, err := image.NewArchiveImage(filePath) typesImage, err := image.NewArchiveImage(filePath)
if err != nil { if err != nil {

View File

@@ -2,15 +2,15 @@ package client
import ( import (
"context" "context"
"crypto/tls"
"net/http" "net/http"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/types"
"github.com/google/wire" "github.com/google/wire"
"golang.org/x/xerrors" "golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
r "github.com/aquasecurity/trivy/pkg/rpc" r "github.com/aquasecurity/trivy/pkg/rpc"
rpc "github.com/aquasecurity/trivy/rpc/scanner" rpc "github.com/aquasecurity/trivy/rpc/scanner"
) )
@@ -24,9 +24,20 @@ var SuperSet = wire.NewSet(
// RemoteURL for RPC remote host // RemoteURL for RPC remote host
type RemoteURL string type RemoteURL string
// Insecure for RPC remote host
type Insecure bool
// NewProtobufClient is the factory method to return RPC scanner // NewProtobufClient is the factory method to return RPC scanner
func NewProtobufClient(remoteURL RemoteURL) rpc.Scanner { func NewProtobufClient(remoteURL RemoteURL, insecure Insecure) rpc.Scanner {
return rpc.NewScannerProtobufClient(string(remoteURL), &http.Client{}) httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: bool(insecure),
},
},
}
return rpc.NewScannerProtobufClient(string(remoteURL), httpClient)
} }
// CustomHeaders for holding HTTP headers // CustomHeaders for holding HTTP headers

View File

@@ -3,6 +3,8 @@ package client
import ( import (
"context" "context"
"errors" "errors"
"net/http"
"net/http/httptest"
"testing" "testing"
"github.com/golang/protobuf/ptypes/timestamp" "github.com/golang/protobuf/ptypes/timestamp"
@@ -283,3 +285,48 @@ func TestScanner_Scan(t *testing.T) {
}) })
} }
} }
func TestScanner_ScanServerInsecure(t *testing.T) {
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
defer ts.Close()
type args struct {
request *scanner.ScanRequest
insecure bool
}
tests := []struct {
name string
args args
wantErr string
}{
{
name: "happy path",
args: args{
request: &scanner.ScanRequest{},
insecure: true,
},
},
{
name: "sad path",
args: args{
request: &scanner.ScanRequest{},
insecure: false,
},
wantErr: "certificate signed by unknown authority",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewProtobufClient(RemoteURL(ts.URL), Insecure(tt.args.insecure))
_, err := s.Scan(context.Background(), tt.args.request)
if tt.wantErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantErr)
return
}
require.NoError(t, err)
})
}
}