Skip to content

Commit

Permalink
sbom-resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
an1l4 committed Mar 26, 2024
1 parent 99ca571 commit fcc05bf
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 77 deletions.
129 changes: 89 additions & 40 deletions agent/kubviz/plugins/trivy/trivy_sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,42 @@ import (
"log"
"os"
"os/exec"
"strings"

"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
"github.com/google/uuid"
"github.com/intelops/kubviz/agent/kubviz/plugins/outdated"
"github.com/intelops/kubviz/constants"
"github.com/intelops/kubviz/model"
"github.com/intelops/kubviz/pkg/opentelemetry"
"github.com/nats-io/nats.go"
"github.com/pkg/errors"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

func PublishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error {

for _, packageinfo := range report.Packages {
for _, pkg := range packageinfo.Packages {

metrics := model.SbomData{
ID: uuid.New().String(),
ClusterName: ClusterName,
ComponentName: report.CycloneDX.Metadata.Component.Name,
PackageName: pkg.Name,
PackageUrl: report.CycloneDX.Metadata.Component.PackageURL,
BomRef: report.CycloneDX.Metadata.Component.BOMRef,
SerialNumber: report.CycloneDX.SerialNumber,
CycloneDxVersion: report.CycloneDX.Version,
BomFormat: report.CycloneDX.BOMFormat,
}
metricsJson, err := json.Marshal(metrics)
if err != nil {
log.Println("error occurred while marshalling sbom metrics in agent", err.Error())
return err
}
_, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson)
if err != nil {
return err
}
log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID)
}
func PublishTrivySbomReport(report map[string]interface{}, js nats.JetStreamContext) error {

metrics := model.Sbom{
ID: uuid.New().String(),
ClusterName: ClusterName,
Report: report,
}
metricsJson, err := json.Marshal(metrics)
if err != nil {
log.Println("error occurred while marshalling sbom metrics in agent", err.Error())
return err
}
_, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson)
if err != nil {
return err
}
log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID)
return nil
}

func executeCommandSbom(command string) ([]byte, error) {

ctx := context.Background()
tracer := otel.Tracer("trivy-sbom")
_, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandSbom")
span.SetAttributes(attribute.String("trivy-sbom-agent", "sbom-command-running"))
defer span.End()

cmd := exec.Command("/bin/sh", "-c", command)
var outc, errc bytes.Buffer
cmd.Stdout = &outc
Expand All @@ -84,10 +68,9 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error {
ctx := context.Background()
tracer := otel.Tracer("trivy-sbom")
_, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivySbomScan")
span.SetAttributes(attribute.String("sbom", "sbom-creation"))
defer span.End()

images, err := outdated.ListImages(config)
images, err := ListImagesforSbom(config)

if err != nil {
log.Printf("failed to list images: %v", err)
Expand All @@ -111,13 +94,79 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error {
continue // Move on to the next image
}

var report cyclonedx.BOM
var report map[string]interface{}
err = json.Unmarshal(out, &report)
if err != nil {
log.Printf("Error unmarshaling JSON data for image sbom %s: %v", image.PullableImage, err)
continue // Move on to the next image in case of an error
}
PublishTrivySbomReport(report, js)
err = PublishTrivySbomReport(report, js)
if err != nil {
log.Printf("Error publishing Trivy SBOM report for image %s: %v", image.PullableImage, err)
continue
}
}
return nil
}

func ListImagesforSbom(config *rest.Config) ([]model.RunningImage, error) {
var err error
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "failed to create clientset")
}
ctx := context.Background()
namespaces, err := clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to list namespaces")
}

runningImages := []model.RunningImage{}
for _, namespace := range namespaces.Items {
pods, err := clientset.CoreV1().Pods(namespace.Name).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to list pods")
}

for _, pod := range pods.Items {
for _, initContainerStatus := range pod.Status.InitContainerStatuses {
pullable := initContainerStatus.ImageID
pullable = strings.TrimPrefix(pullable, "docker-pullable://")
runningImage := model.RunningImage{
Pod: pod.Name,
Namespace: pod.Namespace,
InitContainer: &initContainerStatus.Name,
Image: initContainerStatus.Image,
PullableImage: pullable,
}
runningImages = append(runningImages, runningImage)
}

for _, containerStatus := range pod.Status.ContainerStatuses {
pullable := containerStatus.ImageID
pullable = strings.TrimPrefix(pullable, "docker-pullable://")

runningImage := model.RunningImage{
Pod: pod.Name,
Namespace: pod.Namespace,
Container: &containerStatus.Name,
Image: containerStatus.Image,
PullableImage: pullable,
}
runningImages = append(runningImages, runningImage)
}
}
}

// Remove exact duplicates
cleanedImages := []model.RunningImage{}
seenImages := make(map[string]bool)
for _, runningImage := range runningImages {
if !seenImages[runningImage.PullableImage] {
cleanedImages = append(cleanedImages, runningImage)
seenImages[runningImage.PullableImage] = true
}
}

return cleanedImages, nil
}
76 changes: 66 additions & 10 deletions client/pkg/clickhouse/db_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type DBInterface interface {
InsertGitEvent(string)
InsertKubeScoreMetrics(model.KubeScoreRecommendations)
InsertTrivyImageMetrics(metrics model.TrivyImage)
InsertTrivySbomMetrics(metrics model.SbomData)
InsertTrivySbomMetrics(metrics model.Sbom)
InsertTrivyMetrics(metrics model.Trivy)
RetriveKetallEvent() ([]model.Resource, error)
RetriveOutdatedEvent() ([]model.CheckResultfinal, error)
Expand Down Expand Up @@ -841,11 +841,10 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) {

}
}
func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) {
func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) {
ctx := context.Background()
tracer := otel.Tracer("insert-trivy-sbom")
_, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertTrivySbomMetrics")
span.SetAttributes(attribute.String("trivy-sbom-client", "insert"))
defer span.End()

tx, err := c.conn.Begin()
Expand All @@ -857,16 +856,73 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) {
log.Fatalf("error preparing statement: %v", err)
}

data := metrics.Report
bomFormat, _ := data["bomFormat"].(string) //CycloneDX
serialNumber, _ := data["serialNumber"].(string) // exmplvalue:urn:uuid:146625a5-531a-40fa-a205-174448c6c569

// fetching metadata
metadata, ok := data["metadata"].(map[string]interface{})
if !ok {
log.Println("error: metadata not found or not in expected format")
return
}

// inside metadata
// taking component
component, ok := metadata["component"].(map[string]interface{})
if !ok {
log.Println("error: component not found or not in expected format")
return
}
//timestamp, _ := metadata["timestamp"].(time.Time)
var eventTime time.Time
rawTimestamp, ok := metadata["timestamp"].(string)
if !ok {
log.Println("error: timestamp not found or not in expected format")
return
}
eventTime, err = time.Parse(time.RFC3339, rawTimestamp)
if err != nil {
log.Println("error parsing timestamp:", err)
return
}
// inside metadata
// taking component
// inside component taking bomRef, componentType, componentName, packageURL
bomRef, _ := component["bom-ref"].(string) //pkg:oci/redis@sha256:873c49204b64258778a1f34d23a962de526021e9a63b09236d6d7c86e2dd13e9?repository_url=public.ecr.aws%2Fdocker%2Flibrary%2Fredis\u0026arch=amd64
componentType, _ := component["type"].(string) //container
componentName, _ := component["name"].(string) //public.ecr.aws/docker/library/redis@sha256:873c49204b64258778a1f34d23a962de526021e9a63b09236d6d7c86e2dd13e9
packageURL, _ := component["purl"].(string) //pkg:oci/redis@sha256:873c49204b64258778a1f34d23a962de526021e9a63b09236d6d7c86e2dd13e9?repository_url=public.ecr.aws%2Fdocker%2Flibrary%2Fredis\u0026arch=amd64
// fetching other componets
Components, ok := data["components"].([]interface{})
if !ok {
log.Println("error: components not found or not in expected format")
}
var otherComponentName string
// Iterate over the components to find the desired name
for _, otherComponent := range Components {
componentsMap, ok := otherComponent.(map[string]interface{})
if !ok {
log.Println("error: component not in expected format")
continue
}
if name, ok := componentsMap["name"].(string); ok {
otherComponentName = name // alpine
break
}
}

if _, err := stmt.Exec(
metrics.ID,
metrics.ClusterName,
metrics.ComponentName,
metrics.PackageName,
metrics.PackageUrl,
metrics.BomRef,
metrics.SerialNumber,
int32(metrics.CycloneDxVersion),
metrics.BomFormat,
bomFormat,
serialNumber,
bomRef,
componentName,
componentType,
packageURL,
eventTime,
otherComponentName,
); err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion client/pkg/clickhouse/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac
const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)"
const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, bom_format, serial_number, bom_ref, image_name, component_type, package_url, event_time, other_component_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
const InsertKuberhealthy string = "INSERT INTO kuberhealthy (CurrentUUID, CheckName, OK, Errors, RunDuration, Namespace, Node, LastRun, AuthoritativePod) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
2 changes: 1 addition & 1 deletion client/pkg/clients/kubviz_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) {
Consumer: cfg.TrivySbomConsumer,
Handler: func(msg *nats.Msg) {
msg.Ack()
var metrics model.SbomData
var metrics model.Sbom
err := json.Unmarshal(msg.Data, &metrics)
if err != nil {
log.Println("failed to unmarshal from nats", err)
Expand Down
21 changes: 2 additions & 19 deletions model/trivy_sbom.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
package model

import (
"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
)

type Sbom struct {
ID string
Report cyclonedx.BOM
}

type SbomData struct {
ID string
ID string
ClusterName string
ComponentName string
PackageName string
PackageUrl string
BomRef string
SerialNumber string
CycloneDxVersion int
BomFormat string
Report map[string]interface{}
}


13 changes: 7 additions & 6 deletions sql/0000015_trivysbom.up.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
CREATE TABLE IF NOT EXISTS trivysbom (
id UUID,
cluster_name String,
cluster_name String,
bom_format String,
serial_number String,
bom_ref String,
image_name String,
package_name String,
componet_type String,
package_url String,
bom_ref String,
serial_number String,
version INTEGER,
bom_format String,
event_time DateTime('UTC'),
other_component_name String,
ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}},
ExportedAt DateTime DEFAULT NULL
) ENGINE = MergeTree()
Expand Down

0 comments on commit fcc05bf

Please sign in to comment.