Skip to content

Commit

Permalink
resourceconfig output should include container image list while scann…
Browse files Browse the repository at this point in the history
…ing for k8s family
  • Loading branch information
devang-gaur committed Jul 5, 2021
1 parent d3c529e commit 571242c
Show file tree
Hide file tree
Showing 15 changed files with 633 additions and 258 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ replace (
require (
github.com/awslabs/goformation/v4 v4.19.1
github.com/ghodss/yaml v1.0.0
github.com/go-errors/errors v1.0.1 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
Expand Down Expand Up @@ -38,8 +39,8 @@ require (
github.com/spf13/cobra v1.1.1
github.com/zclconf/go-cty v1.8.2
go.uber.org/zap v1.16.0
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273
golang.org/x/tools v0.1.3 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
golang.org/x/tools v0.1.4 // indirect
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
helm.sh/helm/v3 v3.4.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,8 @@ golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3M
golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ=
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down Expand Up @@ -1260,6 +1262,8 @@ golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
152 changes: 152 additions & 0 deletions pkg/iac-providers/kubernetes/v1/extract-images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package k8sv1

import (
"encoding/json"
"github.com/accurics/terrascan/pkg/iac-providers/output"
"github.com/accurics/terrascan/pkg/utils"
yamltojson "github.com/ghodss/yaml"
"github.com/pkg/errors"
"go.uber.org/zap"
k8sappsv1 "k8s.io/api/apps/v1"
k8sbatchv1 "k8s.io/api/batch/v1"
k8sbatchv1beta1 "k8s.io/api/batch/v1beta1"
k8scorev1 "k8s.io/api/core/v1"
)

func (k *K8sV1) extractContainerImages(kind string, doc *utils.IacDocument) ([]output.ContainerNameAndImage, []output.ContainerNameAndImage, error) {
var containerImages = make([]output.ContainerNameAndImage, 0)
var initContainerImages = make([]output.ContainerNameAndImage, 0)
var data []byte
var err error

if doc.Data == nil {
return containerImages, initContainerImages, errors.Errorf("document does not have any resource data for unmarshalling")
}

if doc.Type == utils.YAMLDoc {
data, err = yamltojson.YAMLToJSON(doc.Data)
if err != nil {
return nil, nil, err
}
} else {
data = doc.Data
}

switch kind {
case "Pod":
pod := k8scorev1.Pod{}
err = json.Unmarshal(data, &pod)
if err != nil {
err := errors.Errorf("error unmarshalling pod: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range pod.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range pod.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "Deployment":
deployment := k8sappsv1.Deployment{}
err = json.Unmarshal(data, &deployment)
if err != nil {
err := errors.Errorf("error unmarshalling deployment: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range deployment.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range deployment.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "ReplicationController":
rc := k8scorev1.ReplicationController{}
err = json.Unmarshal(data, &rc)
if err != nil {
err := errors.Errorf("error unmarshalling replicationcontroller: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range rc.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range rc.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "Job":
job := k8sbatchv1.Job{}
err = json.Unmarshal(data, &job)
if err != nil {
err := errors.Errorf("error unmarshalling job: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range job.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range job.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "CronJob":
cronjob := k8sbatchv1beta1.CronJob{}
err = json.Unmarshal(data, &cronjob)
if err != nil {
err := errors.Errorf("error unmarshalling cronjob: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range cronjob.Spec.JobTemplate.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "StatefulSet":
ss := k8sappsv1.StatefulSet{}
err = json.Unmarshal(data, &ss)
if err != nil {
err := errors.Errorf("error unmarshalling statefulset: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range ss.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range ss.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "ReplicaSet":
rs := k8sappsv1.ReplicaSet{}
err = json.Unmarshal(data, &rs)
if err != nil {
err := errors.Errorf("error unmarshalling replicaset: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range rs.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range rs.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
case "DaemonSet":
ds := k8sappsv1.DaemonSet{}
err = json.Unmarshal(data, &ds)
if err != nil {
err := errors.Errorf("error unmarshalling daemonset: %v", err)
zap.S().Errorf(err.Error())
return nil, nil, err
}
for _, container := range ds.Spec.Template.Spec.Containers {
containerImages = append(containerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
for _, container := range ds.Spec.Template.Spec.InitContainers {
initContainerImages = append(initContainerImages, output.ContainerNameAndImage{Name: container.Name, Image: container.Image})
}
default:
zap.S().Debugf("the container image extraction for kubernetes workload of kind %s is not supported.", kind)
}
return containerImages, initContainerImages, nil
}
153 changes: 153 additions & 0 deletions pkg/iac-providers/kubernetes/v1/extract-images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package k8sv1

import (
"github.com/accurics/terrascan/pkg/iac-providers/kubernetes/v1/testdata"
"github.com/accurics/terrascan/pkg/iac-providers/output"
"github.com/accurics/terrascan/pkg/utils"
"reflect"
"testing"
)

func TestK8sV1ExtractContainerImages(t *testing.T) {
type args struct {
doc *utils.IacDocument
kind string
}
tests := []struct {
name string
k *K8sV1
args args
wantContainerImageList []output.ContainerNameAndImage
wantInitContainerImageList []output.ContainerNameAndImage
wantErr bool
}{
{
name: "empty document object",
args: args{
doc: &utils.IacDocument{},
kind: "CRD",
},
wantErr: true,
wantContainerImageList: []output.ContainerNameAndImage{},
wantInitContainerImageList: []output.ContainerNameAndImage{},
},
{
name: "pod json document object",
args: args{
doc: &utils.IacDocument{
Type: "json",
Data: testdata.PodJSONTemplate,
},
kind: "Pod",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "healthz", Image: "k8s.gcr.io/exechealthz-amd64:1.2"}},
wantInitContainerImageList: []output.ContainerNameAndImage{},
},
{
name: "pod yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.PodYAMLTemplate,
},
kind: "Pod",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "myapp-container", Image: "nginx"}},
wantInitContainerImageList: []output.ContainerNameAndImage{{Name: "myapp-container", Image: "busybox"}},
},
{
name: "cronjob yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.CronJobYAMLTemplate,
},
kind: "CronJob",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "hello", Image: "busybox"}},
wantInitContainerImageList: []output.ContainerNameAndImage{},
},
{
name: "job yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.JobYAMLTemplate,
},
kind: "Job",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "c", Image: "gcr.io/terrascan/job-wq-1"}},
wantInitContainerImageList: []output.ContainerNameAndImage{},
},
{
name: "deployment yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.DeploymentYAMLTemplate,
},
kind: "Deployment",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "nginx", Image: "nginx:1.14.2"}},
wantInitContainerImageList: []output.ContainerNameAndImage{{Name: "init", Image: "busybox"}},
},
{
name: "daemonset yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.DaemonSetYAMLTemplate,
},
kind: "DaemonSet",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "fluentd-elasticsearch", Image: "quay.io/fluentd_elasticsearch/fluentd:v2.5.2"}},
wantInitContainerImageList: []output.ContainerNameAndImage{},
},
{
name: "replicaset yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.ReplicaSetYAMLTemplate,
},
kind: "ReplicaSet",
},
wantContainerImageList: []output.ContainerNameAndImage{{Name: "php-redis", Image: "gcr.io/google_samples/gb-frontend:v3"}},
wantInitContainerImageList: []output.ContainerNameAndImage{},
},
{
name: "replicationcontroller yaml document object",
args: args{
doc: &utils.IacDocument{
Type: "yaml",
Data: testdata.ReplicationControllerTemplate,
},
kind: "ReplicationController",
},
wantContainerImageList: []output.ContainerNameAndImage{
{Name: "nginx", Image: "nginx:latest"},
{Name: "sidecar1", Image: "sidecar-image-1"},
{Name: "sidecar2", Image: "sidecar-image-2"}},
wantInitContainerImageList: []output.ContainerNameAndImage{
{Name: "init1", Image: "init-image-1"},
{Name: "init2", Image: "init-image-2"},
{Name: "init3", Image: "init-image-3"}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
k := &K8sV1{}
gotContainerImageList, gotInitContainerImageList, err := k.extractContainerImages(tt.args.kind, tt.args.doc)
if (err != nil) != tt.wantErr {
t.Errorf("K8sV1.extractContainerImages() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotContainerImageList, tt.wantContainerImageList) {
t.Errorf("K8sV1.extractResource() got = %v, want %v", gotContainerImageList, tt.wantContainerImageList)
}
if !reflect.DeepEqual(gotInitContainerImageList, tt.wantInitContainerImageList) {
t.Errorf("K8sV1.extractResource() got = %v, want %v", gotInitContainerImageList, tt.wantInitContainerImageList)
}
})
}
}
Loading

0 comments on commit 571242c

Please sign in to comment.