Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

Commit

Permalink
ssh topology scanner initial implementation (#557)
Browse files Browse the repository at this point in the history
* ssh topology scanner initial implementation

* add missing licenses

* fix some lint issues

* remove bad unit test

* change infofinders to infofinder

* singular InfoType

* singular InfoType

* avoid using buffered channels

* fix some typos

* update vmclarity-tools-base

* set root home if exists when getting home user dirs
  • Loading branch information
FrimIdan authored Aug 30, 2023
1 parent efac30a commit 0d95d37
Show file tree
Hide file tree
Showing 30 changed files with 1,012 additions and 5 deletions.
18 changes: 14 additions & 4 deletions .families.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
sbom:
enabled: true
enabled: false
analyzers_list:
- "syft"
- "gomod"
Expand All @@ -26,7 +26,7 @@ sbom:
token: "token"

vulnerabilities:
enabled: true
enabled: false
scanners_list:
- "grype"
inputs:
Expand Down Expand Up @@ -68,7 +68,7 @@ secrets:
binary_path: "/usr/local/bin/gitleaks"

exploits:
enabled: true
enabled: false
scanners_list:
- "exploitdb"
inputs: []
Expand All @@ -78,10 +78,20 @@ exploits:
base_url: "http://localhost:1326"

misconfiguration:
enabled: true
enabled: false
scanners_list:
- "fake"
inputs:
- input: "./"
input_type: "rootfs"
scanners_configs: {}


infofinder:
enabled: true
scanners_list:
- "sshTopology"
inputs:
- input: "/"
input_type: "rootfs"
scanners_configs: {}
2 changes: 1 addition & 1 deletion Dockerfile.cli
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1.2
ARG VMCLARITY_TOOLS_BASE=ghcr.io/openclarity/vmclarity-tools-base:v0.2.0@sha256:0e0ed706dc297366af44d736c71aefa350b54a0214290aa81b3603462e39872b
ARG VMCLARITY_TOOLS_BASE=ghcr.io/openclarity/vmclarity-tools-base:v0.3.0@sha256:c592419f5f3f184909363e080b01aea5deef63374b856aed74334afa3bdef793
FROM --platform=$BUILDPLATFORM golang:1.20.7-alpine AS builder

RUN apk add --update --no-cache ca-certificates git
Expand Down
21 changes: 21 additions & 0 deletions pkg/cli/presenter/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/openclarity/vmclarity/pkg/shared/families"
"github.com/openclarity/vmclarity/pkg/shared/families/exploits"
"github.com/openclarity/vmclarity/pkg/shared/families/infofinder"
"github.com/openclarity/vmclarity/pkg/shared/families/malware"
"github.com/openclarity/vmclarity/pkg/shared/families/rootkits"
"github.com/openclarity/vmclarity/pkg/shared/families/sbom"
Expand Down Expand Up @@ -54,6 +55,8 @@ func (p *DefaultPresenter) ExportFamilyResult(ctx context.Context, res families.
err = p.ExportRootkitResult(ctx, res)
case types.Malware:
err = p.ExportMalwareResult(ctx, res)
case types.InfoFinder:
err = p.ExportInfoFinderResult(ctx, res)
}

return err
Expand Down Expand Up @@ -167,3 +170,21 @@ func (p *DefaultPresenter) ExportRootkitResult(_ context.Context, res families.F
}
return nil
}

func (p *DefaultPresenter) ExportInfoFinderResult(_ context.Context, res families.FamilyResult) error {
infoFinderResults, ok := res.Result.(*infofinder.Results)
if !ok {
return fmt.Errorf("failed to convert to infofinder results")
}

bytes, err := json.Marshal(infoFinderResults)
if err != nil {
return fmt.Errorf("failed to marshal infofinder results: %w", err)
}

if err = p.Write(bytes, "infofinder.json"); err != nil {
return fmt.Errorf("failed to output infofinder results: %w", err)
}

return nil
}
2 changes: 2 additions & 0 deletions pkg/cli/presenter/vmclarity.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func (v *VMClarityPresenter) ExportFamilyResult(ctx context.Context, res familie
err = v.ExportRootkitResult(ctx, res)
case types.Malware:
err = v.ExportMalwareResult(ctx, res)
case types.InfoFinder:
err = fmt.Errorf("InfoFinder family is unsupported")
}

return err
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/state/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func (l *LocalState) MarkFamilyScanInProgress(ctx context.Context, familyType ty
logger.Info("Rootkit scan is in progress")
case types.Malware:
logger.Info("Malware scan is in progress")
case types.InfoFinder:
logger.Info("InfoFinder scan is in progress")
}
return nil
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/state/vmclarity.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ func (v *VMClarityState) MarkFamilyScanInProgress(ctx context.Context, familyTyp
err = v.markRootkitsScanInProgress(ctx)
case types.Malware:
err = v.markMalwareScanInProgress(ctx)
case types.InfoFinder:
err = fmt.Errorf("InfoFinder family is unsupported")
}
return err
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/shared/families/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
kubeclarityutils "github.com/openclarity/kubeclarity/shared/pkg/utils"

"github.com/openclarity/vmclarity/pkg/shared/families/exploits"
infofinderTypes "github.com/openclarity/vmclarity/pkg/shared/families/infofinder/types"
"github.com/openclarity/vmclarity/pkg/shared/families/malware"
misconfigurationTypes "github.com/openclarity/vmclarity/pkg/shared/families/misconfiguration/types"
"github.com/openclarity/vmclarity/pkg/shared/families/rootkits"
Expand All @@ -39,6 +40,7 @@ type Config struct {
Rootkits rootkits.Config `json:"rootkits" yaml:"rootkits" mapstructure:"rootkits"`
Malware malware.Config `json:"malware" yaml:"malware" mapstructure:"malware"`
Misconfiguration misconfigurationTypes.Config `json:"misconfiguration" yaml:"misconfiguration" mapstructure:"misconfiguration"`
InfoFinder infofinderTypes.Config `json:"infofinder" yaml:"infofinder" mapstructure:"infofinder"`

// Enrichers
Exploits exploits.Config `json:"exploits" yaml:"exploits" mapstructure:"exploits"`
Expand Down Expand Up @@ -111,6 +113,17 @@ func SetMountPointsForFamiliesInput(mountPoints []string, familiesConfig *Config
},
)
}

if familiesConfig.InfoFinder.Enabled {
familiesConfig.InfoFinder.Inputs = append(
familiesConfig.InfoFinder.Inputs,
types.Input{
StripPathFromResult: utils.PointerTo(true),
Input: mountDir,
InputType: string(kubeclarityutils.ROOTFS),
},
)
}
}
return familiesConfig
}
97 changes: 97 additions & 0 deletions pkg/shared/families/infofinder/family.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright © 2023 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package infofinder

import (
"context"
"fmt"
"time"

"github.com/openclarity/kubeclarity/shared/pkg/job_manager"
"github.com/openclarity/kubeclarity/shared/pkg/utils"

"github.com/openclarity/vmclarity/pkg/shared/families/infofinder/job"
infofinderTypes "github.com/openclarity/vmclarity/pkg/shared/families/infofinder/types"
"github.com/openclarity/vmclarity/pkg/shared/families/interfaces"
"github.com/openclarity/vmclarity/pkg/shared/families/results"
"github.com/openclarity/vmclarity/pkg/shared/families/types"
familiesutils "github.com/openclarity/vmclarity/pkg/shared/families/utils"
"github.com/openclarity/vmclarity/pkg/shared/log"
)

type InfoFinder struct {
conf infofinderTypes.Config
}

func (i InfoFinder) Run(ctx context.Context, _ *results.Results) (interfaces.IsResults, error) {
logger := log.GetLoggerFromContextOrDiscard(ctx).WithField("family", "info finder")
logger.Info("InfoFinder Run...")

infoFinderResults := NewResults()

manager := job_manager.New(i.conf.ScannersList, i.conf.ScannersConfig, logger, job.Factory)
for _, input := range i.conf.Inputs {
startTime := time.Now()
managerResults, err := manager.Run(utils.SourceType(input.InputType), input.Input)
if err != nil {
return nil, fmt.Errorf("failed to scan input %q for info: %v", input.Input, err)
}
endTime := time.Now()
inputSize, err := familiesutils.GetInputSize(input)
if err != nil {
logger.Warnf("Failed to calculate input %v size: %v", input, err)
}

// Merge results.
for name, result := range managerResults {
logger.Infof("Merging result from %q", name)
if assetScan, ok := result.(*infofinderTypes.ScannerResult); ok {
if familiesutils.ShouldStripInputPath(input.StripPathFromResult, i.conf.StripInputPaths) {
assetScan = stripPathFromResult(assetScan, input.Input)
}
infoFinderResults.AddScannerResult(assetScan)
} else {
return nil, fmt.Errorf("received bad scanner result type %T, expected infofinderTypes.ScannerResult", result)
}
}
infoFinderResults.Metadata.InputScans = append(infoFinderResults.Metadata.InputScans, types.CreateInputScanMetadata(startTime, endTime, inputSize, input))
}

logger.Info("InfoFinder Done...")

return infoFinderResults, nil
}

// stripPathFromResult strip input path from results wherever it is found.
func stripPathFromResult(result *infofinderTypes.ScannerResult, path string) *infofinderTypes.ScannerResult {
for i := range result.Infos {
result.Infos[i].Path = familiesutils.TrimMountPath(result.Infos[i].Path, path)
}
return result
}

func (i InfoFinder) GetType() types.FamilyType {
return types.InfoFinder
}

// ensure types implement the requisite interfaces.
var _ interfaces.Family = &InfoFinder{}

func New(conf infofinderTypes.Config) *InfoFinder {
return &InfoFinder{
conf: conf,
}
}
28 changes: 28 additions & 0 deletions pkg/shared/families/infofinder/job/job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright © 2023 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package job

import (
"github.com/openclarity/kubeclarity/shared/pkg/job_manager"

"github.com/openclarity/vmclarity/pkg/shared/families/infofinder/sshtopology"
)

var Factory = job_manager.NewJobFactory()

func init() {
Factory.Register(sshtopology.ScannerName, sshtopology.New)
}
68 changes: 68 additions & 0 deletions pkg/shared/families/infofinder/result.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright © 2023 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package infofinder

import (
"time"

"github.com/openclarity/vmclarity/pkg/shared/families/infofinder/types"
familiestypes "github.com/openclarity/vmclarity/pkg/shared/families/types"
)

type FlattenedInfos struct {
ScannerName string `json:"ScannerName"`
types.Info
}

type Results struct {
Metadata familiestypes.Metadata `json:"Metadata"`
Infos []FlattenedInfos `json:"Infos"`
}

func NewResults() *Results {
return &Results{
Metadata: familiestypes.Metadata{
Timestamp: time.Now(),
Scanners: []string{},
},
Infos: []FlattenedInfos{},
}
}

func (*Results) IsResults() {}

func (r *Results) addScannerNameToMetadata(name string) {
for _, scannerName := range r.Metadata.Scanners {
if scannerName == name {
return
}
}
r.Metadata.Scanners = append(r.Metadata.Scanners, name)
}

func (r *Results) AddScannerResult(scannerResult *types.ScannerResult) {
r.addScannerNameToMetadata(scannerResult.ScannerName)

for _, info := range scannerResult.Infos {
r.Infos = append(r.Infos, FlattenedInfos{
ScannerName: scannerResult.ScannerName,
Info: info,
})
}

// bump the timestamp as there are new results
r.Metadata.Timestamp = time.Now()
}
Loading

0 comments on commit 0d95d37

Please sign in to comment.