Skip to content

Commit

Permalink
refactor(k8s): scan config files as a folder (#7690)
Browse files Browse the repository at this point in the history
  • Loading branch information
afdesk authored Oct 21, 2024
1 parent f6acdf7 commit 010b213
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 45 deletions.
50 changes: 39 additions & 11 deletions pkg/k8s/scanner/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scanner
import (
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"

Expand All @@ -13,36 +14,63 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
)

var r = regexp.MustCompile("\\\\|/|:|\\*|\\?|<|>")
var r = regexp.MustCompile("[\\\\/:*?<>]")

func createTempFile(artifact *artifacts.Artifact) (string, error) {
func generateTempFileByArtifact(artifact *artifacts.Artifact, tempDir string) (string, error) {
filename := fmt.Sprintf("%s-%s-%s-*.yaml", artifact.Namespace, artifact.Kind, artifact.Name)

if runtime.GOOS == "windows" {
// removes characters not permitted in file/directory names on Windows
filename = filenameWindowsFriendly(filename)
}
file, err := os.CreateTemp("", filename)
file, err := os.CreateTemp(tempDir, filename)
if err != nil {
return "", xerrors.Errorf("creating tmp file error: %w", err)
return "", xerrors.Errorf("failed to create temporary file: %w", err)
}
shouldRemove := false
defer func() {
if err := file.Close(); err != nil {
log.Error("Failed to close temp file", log.String("path", file.Name()), log.Err(err))
log.Error("Failed to close temp file", log.FilePath(file.Name()), log.Err(err))
}
if shouldRemove {
removeFile(file.Name())
}
}()

if err := yaml.NewEncoder(file).Encode(artifact.RawResource); err != nil {
removeFile(filename)
return "", xerrors.Errorf("marshaling resource error: %w", err)
shouldRemove = true
return "", xerrors.Errorf("failed to encode artifact: %w", err)
}
return filepath.Base(file.Name()), nil
}

// generateTempDir creates a directory with yaml files generated from kubernetes artifacts
// returns a directory name, a map for mapping a temp target file to k8s artifact and error
func generateTempDir(arts []*artifacts.Artifact) (string, map[string]*artifacts.Artifact, error) {
tempDir, err := os.MkdirTemp("", "trivyk8s*")
if err != nil {
return "", nil, xerrors.Errorf("failed to create temp directory: %w", err)
}

m := make(map[string]*artifacts.Artifact)
for _, artifact := range arts {
filename, err := generateTempFileByArtifact(artifact, tempDir)
if err != nil {
log.Error("Failed to create temp file", log.FilePath(filename), log.Err(err))
continue
}
m[filename] = artifact
}
return tempDir, m, nil
}

return file.Name(), nil
func removeDir(dirname string) {
if err := os.RemoveAll(dirname); err != nil {
log.Error("Failed to remove temp directory", log.FilePath(dirname), log.Err(err))
}
}

func removeFile(filename string) {
if err := os.Remove(filename); err != nil {
log.Error("Failed to remove temp file", log.String("path", filename), log.Err(err))
log.Error("Failed to remove temp file", log.FilePath(filename), log.Err(err))
}
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/k8s/scanner/io_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ func Test_FilenameWindowsFriendly(t *testing.T) {
fileName: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`,
want: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`,
},
{
name: "name with no invalid - slash",
fileName: "-ClusterRoleBinding-system\\basic-user-725844313.yaml",
want: `-ClusterRoleBinding-system_basic-user-725844313.yaml`,
},
}

for _, test := range tests {
Expand Down
82 changes: 48 additions & 34 deletions pkg/k8s/scanner/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,18 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)

var resources []report.Resource

type scanResult struct {
vulns []report.Resource
misconfig report.Resource
// scans kubernetes artifacts as a scope of yaml files
if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) {
misconfigs, err := s.scanMisconfigs(ctx, resourceArtifacts)
if err != nil {
return report.Report{}, xerrors.Errorf("scanning misconfigurations error: %w", err)
}
resources = append(resources, misconfigs...)
}

onItem := func(ctx context.Context, artifact *artifacts.Artifact) (scanResult, error) {
scanResults := scanResult{}
if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages {
// scan images from kubernetes cluster in parallel
if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages {
onItem := func(ctx context.Context, artifact *artifacts.Artifact) ([]report.Resource, error) {
opts := s.opts
opts.Credentials = make([]ftypes.Credential, len(s.opts.Credentials))
copy(opts.Credentials, s.opts.Credentials)
Expand All @@ -106,33 +110,22 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
}
vulns, err := s.scanVulns(ctx, artifact, opts)
if err != nil {
return scanResult{}, xerrors.Errorf("scanning vulnerabilities error: %w", err)
return nil, xerrors.Errorf("scanning vulnerabilities error: %w", err)
}
scanResults.vulns = vulns
return vulns, nil
}
if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) {
misconfig, err := s.scanMisconfigs(ctx, artifact)
if err != nil {
return scanResult{}, xerrors.Errorf("scanning misconfigurations error: %w", err)
}
scanResults.misconfig = misconfig

onResult := func(result []report.Resource) error {
resources = append(resources, result...)
return nil
}
return scanResults, nil
}

onResult := func(result scanResult) error {
resources = append(resources, result.vulns...)
// don't add empty misconfig results to resources slice to avoid an empty resource
if result.misconfig.Results != nil {
resources = append(resources, result.misconfig)
p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult)
if err := p.Do(ctx); err != nil {
return report.Report{}, err
}
return nil
}

p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult)
if err := p.Do(ctx); err != nil {
return report.Report{}, err
}
if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner) {
k8sResource, err := s.scanK8sVulns(ctx, k8sCoreArtifacts)
if err != nil {
Expand Down Expand Up @@ -173,22 +166,43 @@ func (s *Scanner) scanVulns(ctx context.Context, artifact *artifacts.Artifact, o
return resources, nil
}

func (s *Scanner) scanMisconfigs(ctx context.Context, artifact *artifacts.Artifact) (report.Resource, error) {
configFile, err := createTempFile(artifact)
func (s *Scanner) scanMisconfigs(ctx context.Context, k8sArtifacts []*artifacts.Artifact) ([]report.Resource, error) {
dir, artifactsByFilename, err := generateTempDir(k8sArtifacts)
if err != nil {
return report.Resource{}, xerrors.Errorf("scan error: %w", err)
return nil, xerrors.Errorf("failed to generate temp dir: %w", err)
}

s.opts.Target = configFile
s.opts.Target = dir

configReport, err := s.runner.ScanFilesystem(ctx, s.opts)
// remove config file after scanning
removeFile(configFile)
// remove config files after scanning
removeDir(dir)

if err != nil {
return report.CreateResource(artifact, configReport, err), err
return nil, xerrors.Errorf("failed to scan filesystem: %w", err)
}
resources := make([]report.Resource, 0, len(k8sArtifacts))

for _, res := range configReport.Results {
artifact := artifactsByFilename[res.Target]

singleReport := types.Report{
SchemaVersion: configReport.SchemaVersion,
CreatedAt: configReport.CreatedAt,
ArtifactName: res.Target,
ArtifactType: configReport.ArtifactType,
Metadata: configReport.Metadata,
Results: types.Results{res},
}

resource, err := s.filter(ctx, singleReport, artifact)
if err != nil {
resource = report.CreateResource(artifact, singleReport, err)
}
resources = append(resources, resource)
}

return s.filter(ctx, configReport, artifact)
return resources, nil
}
func (s *Scanner) filter(ctx context.Context, r types.Report, artifact *artifacts.Artifact) (report.Resource, error) {
var err error
Expand Down

0 comments on commit 010b213

Please sign in to comment.