Skip to content

Commit

Permalink
feat: basic implementation for Dependency Track
Browse files Browse the repository at this point in the history
  • Loading branch information
derkoe authored and ckotzbauer committed Feb 1, 2022
1 parent d3a5768 commit 549443d
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ All parameters are cli-flags.
| `git-author-email` | `true` when `git` target is used. | `""` | Author email to use for Git-Commits. |
| `pod-label-selector` | `false` | `""` | Kubernetes Label-Selector for pods. |
| `namespace-label-selector` | `false` | `""` | Kubernetes Label-Selector for namespaces. |
| `dtrack-base-url` | `true` when `dtrack` target is used | `""` | Dependency-Track base URL, e.g. 'https://dtrack.example.com' |
| `dtrack-api-key` | `true` when `dtrack` target is used | `""` | Dependency-Track API key |

The flags can be configured as args or as environment-variables prefixed with `SBOM_` to inject sensitive configs as secret values.

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ go 1.17
require (
github.com/anchore/syft v0.36.0
github.com/docker/cli v20.10.12+incompatible
github.com/google/uuid v1.3.0
github.com/novln/docker-parser v1.0.0
github.com/nscuro/dtrack-client v0.3.0
github.com/onsi/ginkgo/v2 v2.1.1
github.com/robfig/cron v1.2.0
github.com/sirupsen/logrus v1.8.1
Expand Down Expand Up @@ -44,7 +46,6 @@ require (
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
7 changes: 6 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
Expand Down Expand Up @@ -605,6 +606,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
Expand Down Expand Up @@ -735,6 +738,8 @@ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/novln/docker-parser v1.0.0 h1:PjEBd9QnKixcWczNGyEdfUrP6GR0YUilAqG7Wksg3uc=
github.com/novln/docker-parser v1.0.0/go.mod h1:oCeM32fsoUwkwByB5wVjsrsVQySzPWkl3JdlTn1txpE=
github.com/nscuro/dtrack-client v0.3.0 h1:d0mlxHzgUA4HDtrGMlaseWZsu28zKf0Wgy30XYN5oxE=
github.com/nscuro/dtrack-client v0.3.0/go.mod h1:g/zi6OOaWuk0Uhhq6APF3fet7QGHeK+OTBBfalnBGBA=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
Expand Down
2 changes: 2 additions & 0 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ var (
ConfigKeyGitAuthorEmail = "git-author-email"
ConfigKeyPodLabelSelector = "pod-label-selector"
ConfigKeyNamespaceLabelSelector = "namespace-label-selector"
ConfigKeyDependencaTrackBaseUrl = "dtrack-base-url"
ConfigKeyDependencaTrackApiKey = "dtrack-api-key"
)
4 changes: 4 additions & 0 deletions internal/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func initTargets(targetKeys []string) []target.Target {
t := target.NewGitTarget()
err = t.ValidateConfig()
targets = append(targets, t)
} else if ta == "dtrack" {
t := target.NewDependencaTrackTarget()
err = t.ValidateConfig()
targets = append(targets, t)
} else {
logrus.Fatalf("Unknown target %s", ta)
}
Expand Down
112 changes: 112 additions & 0 deletions internal/target/dtrack_target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package target

import (
"context"
"encoding/base64"
"fmt"
"strings"

"github.com/google/uuid"
dtrack "github.com/nscuro/dtrack-client"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"

"github.com/ckotzbauer/sbom-operator/internal"
)

type DependencaTrackTarget struct {
baseUrl string
apiKey string
imageToProject map[string]uuid.UUID
}

func NewDependencaTrackTarget() *DependencaTrackTarget {
baseUrl := viper.GetString(internal.ConfigKeyDependencaTrackBaseUrl)
apiKey := viper.GetString(internal.ConfigKeyDependencaTrackApiKey)
return &DependencaTrackTarget{
baseUrl: baseUrl,
apiKey: apiKey,
}
}

func (g *DependencaTrackTarget) ValidateConfig() error {
if g.baseUrl == "" {
return fmt.Errorf("%s is empty", internal.ConfigKeyDependencaTrackBaseUrl)
}
if g.apiKey == "" {
return fmt.Errorf("%s is empty", internal.ConfigKeyDependencaTrackApiKey)
}
return nil
}

func (g *DependencaTrackTarget) Initialize() {
client, _ := dtrack.NewClient(g.baseUrl, dtrack.WithAPIKey(g.apiKey))
projectsPage, err := client.Project.GetAll(context.TODO(), dtrack.PageOptions{
PageNumber: 1,
PageSize: 10,
})
if err != nil {
logrus.Errorf("Could not fetch projects: %v", err)
}

g.imageToProject = make(map[string]uuid.UUID)
for _, project := range projectsPage.Projects {
for _, property := range project.Properties {
if property.Name == "image-name" {
g.imageToProject[property.Value] = project.UUID
}
}
}
}

func (g *DependencaTrackTarget) ProcessSbom(imageID, sbom string) {
if sbom == "" {
logrus.Infof("Empty SBOM - skip image (image=%s)", imageID)
return
}

client, _ := dtrack.NewClient(g.baseUrl, dtrack.WithAPIKey(g.apiKey))
logrus.Infof("Sending SBOM to Dependency Track (image=%s)", imageID)

if !strings.ContainsRune(imageID, '@') {
logrus.Warnf("Image id %s does not contain an @sha256", imageID)
return
}
imageSplit := strings.Split(imageID, "@")
imageName := imageSplit[0]

projectId := g.imageToProject[imageName]
if projectId == uuid.Nil {
project, err := client.Project.Create(context.TODO(),
dtrack.Project{
Active: true,
Classifier: "APPLICATION",
Name: imageName,
Properties: []dtrack.ProjectProperty{
{Name: "image-name", Group: "container", Value: imageName, Type: "STRING"},
},
// TODO check if to add PURL: "pkg:docker/" + imageID,
})
if err != nil {
logrus.Errorf("Could not create project (%s): %v", imageName, err)
}
projectId = project.UUID
g.imageToProject[imageName] = projectId
}

if projectId == uuid.Nil {
logrus.Warnf("No project id for image %s", imageName)
return
}

sbomBase64 := base64.StdEncoding.EncodeToString([]byte(sbom))
uploadToken, err := client.BOM.Upload(context.TODO(), dtrack.BOMUploadRequest{ProjectUUID: &projectId, BOM: sbomBase64, AutoCreate: false})
if err != nil {
logrus.Errorf("Could not upload BOM: %v", err)
}
logrus.Infof("Uploaded SBOM (upload-token=%s)", uploadToken)
}

func (g *DependencaTrackTarget) Cleanup(allImages []string) {
g.imageToProject = nil
}
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&verbosity, internal.ConfigKeyVerbosity, "v", logrus.InfoLevel.String(), "Log-level (debug, info, warn, error, fatal, panic)")
rootCmd.PersistentFlags().StringVarP(&daemonCron, internal.ConfigKeyCron, "c", "@hourly", "Backround-Service interval (CRON)")
rootCmd.PersistentFlags().String(internal.ConfigKeyFormat, "json", "SBOM-Format.")
rootCmd.PersistentFlags().StringSlice(internal.ConfigKeyTargets, []string{"git"}, "Targets for created SBOMs.")
rootCmd.PersistentFlags().StringSlice(internal.ConfigKeyTargets, []string{"git"}, "Targets for created SBOMs (git, dtrack).")
rootCmd.PersistentFlags().Bool(internal.ConfigKeyIgnoreAnnotations, false, "Force analyzing of all images, including those from annotated pods.")
rootCmd.PersistentFlags().String(internal.ConfigKeyGitWorkingTree, "/work", "Directory to place the git-repo.")
rootCmd.PersistentFlags().String(internal.ConfigKeyGitRepository, "", "Git-Repository-URL (HTTPS).")
Expand All @@ -57,6 +57,8 @@ func init() {
rootCmd.PersistentFlags().String(internal.ConfigKeyGitAuthorEmail, "", "Author email to use for Git-Commits.")
rootCmd.PersistentFlags().String(internal.ConfigKeyPodLabelSelector, "", "Kubernetes Label-Selector for pods.")
rootCmd.PersistentFlags().String(internal.ConfigKeyNamespaceLabelSelector, "", "Kubernetes Label-Selector for namespaces.")
rootCmd.PersistentFlags().String(internal.ConfigKeyDependencaTrackBaseUrl, "", "Dependency-Track base URL, e.g. 'https://dtrack.example.com'")
rootCmd.PersistentFlags().String(internal.ConfigKeyDependencaTrackApiKey, "", "Dependency-Track API key")
}

func initConfig() {
Expand Down

0 comments on commit 549443d

Please sign in to comment.