Skip to content

Commit

Permalink
Merge pull request #378 from dev-gaur/add_kustomize_support
Browse files Browse the repository at this point in the history
added kustomize support
  • Loading branch information
kanchwala-yusuf authored Nov 13, 2020
2 parents 19f5527 + 25f258c commit 5c96b2f
Show file tree
Hide file tree
Showing 21 changed files with 1,170 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ docs/_build/
/bin

.DS_Store

vendor/
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/hcl/v2 v2.3.0
github.com/hashicorp/terraform v0.12.28
github.com/iancoleman/strcase v0.1.1
github.com/mattn/go-isatty v0.0.5
github.com/mattn/go-isatty v0.0.8
github.com/open-policy-agent/opa v0.22.0
github.com/pelletier/go-toml v1.8.0
github.com/pkg/errors v0.9.1
Expand All @@ -25,4 +25,5 @@ require (
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
helm.sh/helm/v3 v3.4.0
honnef.co/go/tools v0.0.1-2020.1.6 // indirect
sigs.k8s.io/kustomize/api v0.6.5
)
812 changes: 800 additions & 12 deletions go.sum

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions pkg/iac-providers/kustomize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright (C) 2020 Accurics, Inc.
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 iacprovider

import (
"reflect"

kustomizev3 "github.com/accurics/terrascan/pkg/iac-providers/kustomize/v3"
)

// kustomize specific constants
const (
kustomize supportedIacType = "kustomize"
kustomizeV3 supportedIacVersion = "v3"
kustomizeDefaultIacVersion = kustomizeV3
)

// register kustomize as an IaC provider with terrascan
func init() {
// register iac provider
RegisterIacProvider(kustomize, kustomizeV3, kustomizeDefaultIacVersion, reflect.TypeOf(kustomizev3.KustomizeV3{}))
}
111 changes: 111 additions & 0 deletions pkg/iac-providers/kustomize/v3/load-dir.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package kustomizev3

import (
"errors"
"fmt"
"path/filepath"

k8sv1 "github.com/accurics/terrascan/pkg/iac-providers/kubernetes/v1"
"github.com/accurics/terrascan/pkg/iac-providers/output"
"github.com/accurics/terrascan/pkg/utils"
"go.uber.org/zap"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/krusty"
)

const (
kustomizedirectory string = "kustomization"
)

// LoadIacDir loads the kustomize directory and returns the ResourceConfig mapping which is evaluated by the policy engine
func (k *KustomizeV3) LoadIacDir(absRootDir string) (output.AllResourceConfigs, error) {

allResourcesConfig := make(map[string][]output.ResourceConfig)

files, err := utils.FindFilesBySuffixInDir(absRootDir, KustomizeFileNames())
if err != nil {
zap.S().Error("error while searching for iac files", zap.String("root dir", absRootDir), zap.Error(err))
return allResourcesConfig, err
}

if len(files) == 0 {
err = errors.New("could not find a kustomization.yaml/yml file in the directory")
zap.S().Error("error while searching for iac files", zap.String("root dir", absRootDir), zap.Error(err))
return allResourcesConfig, err
}

if len(files) > 1 {
err = errors.New("a directory cannot have more than 1 kustomization.yaml/yml file")
zap.S().Error("error while searching for iac files", zap.String("root dir", absRootDir), zap.Error(err))
return allResourcesConfig, err
}

kustomizeFileName := *files[0]
yamlkustomizeobj, err := utils.ReadYamlFile(filepath.Join(absRootDir, kustomizeFileName))

if len(yamlkustomizeobj) == 0 {
err = fmt.Errorf("unable to read any kustomization file in the directory : %v", err)
zap.S().Error("error while searching for iac files", zap.String("root dir", absRootDir), zap.Error(err))
return allResourcesConfig, err
}

// ResourceConfig representing the kustomization.y(a)ml file
config := output.ResourceConfig{
Name: filepath.Dir(absRootDir),
Type: kustomizedirectory,
Line: 1,
ID: kustomizedirectory + "." + filepath.Dir(absRootDir),
Source: filepath.Join(absRootDir, kustomizeFileName),
Config: yamlkustomizeobj,
}

allResourcesConfig[kustomizedirectory] = append(allResourcesConfig[kustomizedirectory], config)

// obtaining list of IacDocuments from the target working directory
iacDocuments, err := LoadKustomize(absRootDir, kustomizeFileName)
if err != nil {
zap.S().Error("error occurred while loading kustomize directory", zap.String("kustomize directory", absRootDir), zap.Error(err))
return nil, err
}

for _, doc := range iacDocuments {
var k k8sv1.K8sV1
var config *output.ResourceConfig

config, err = k.Normalize(doc)
if err != nil {
zap.S().Warn("unable to normalize data", zap.Error(err), zap.String("file", doc.FilePath))
continue
}

// TODO finding a better solution to detect accurate line number for tracing back the files causing violations
config.Line = 1
config.Source = doc.FilePath
allResourcesConfig[config.Type] = append(allResourcesConfig[config.Type], *config)
}

return allResourcesConfig, nil
}

// LoadKustomize loads up a 'kustomized' directory and returns a returns a list of IacDocuments
func LoadKustomize(basepath, filename string) ([]*utils.IacDocument, error) {
fSys := filesys.MakeFsOnDisk()
k := krusty.MakeKustomizer(fSys, krusty.MakeDefaultOptions())

m, err := k.Run(basepath)
if err != nil {
return nil, err
}

yaml, err := m.AsYaml()
if err != nil {
return nil, err
}

res, err := utils.LoadYAMLString(string(yaml), filename)
if err != nil {
return nil, err
}

return res, nil
}
18 changes: 18 additions & 0 deletions pkg/iac-providers/kustomize/v3/load-file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package kustomizev3

import (
"fmt"

"github.com/accurics/terrascan/pkg/iac-providers/output"
"go.uber.org/zap"
)

var (
errLoadIacFileNotSupported = fmt.Errorf("load iac file is not supported for kustomize")
)

// LoadIacFile is not supported for kustomize. Only loading directories that have kustomization.y(a)ml file are supported
func (k *KustomizeV3) LoadIacFile(absRootPath string) (allResourcesConfig output.AllResourceConfigs, err error) {
zap.S().Error(errLoadIacFileNotSupported)
return make(map[string][]output.ResourceConfig), errLoadIacFileNotSupported
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- pod.yaml
10 changes: 10 additions & 0 deletions pkg/iac-providers/kustomize/v3/testdata/multibases/base/pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx:1.7.9
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resources:
- ../base
namePrefix: dev-
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resources:
- dev
- stage
- prod
namePrefix: cluster-a-
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resources:
- ../base
namePrefix: prod-
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resources:
- ../base
namePrefix: staging-
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Good Morning!"
enableRisky: "false"
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: the-deployment
spec:
replicas: 3
selector:
matchLabels:
deployment: hello
template:
metadata:
labels:
deployment: hello
spec:
containers:
- name: the-container
image: monopole/hello:1
command: ["/hello",
"--port=8080",
"--enableRiskyFeature=$(ENABLE_RISKY)"]
ports:
- containerPort: 8080
env:
- name: ALT_GREETING
valueFrom:
configMapKeyRef:
name: the-map
key: altGreeting
- name: ENABLE_RISKY
valueFrom:
configMapKeyRef:
name: the-map
key: enableRisky
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Example configuration for the webserver
# at https://github.com/monopole/hello
commonLabels:
app: hello

resources:
- deployment.yaml
- service.yaml
- configMap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
kind: Service
apiVersion: v1
metadata:
name: the-service
spec:
selector:
deployment: hello
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080
23 changes: 23 additions & 0 deletions pkg/iac-providers/kustomize/v3/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package kustomizev3

import "github.com/accurics/terrascan/pkg/utils"

// KustomizeV3 struct
type KustomizeV3 struct{}

const (
// YAMLExtension yaml
YAMLExtension = "yaml"
// YAMLExtension2 yml
YAMLExtension2 = "yml"
// KustomizeFileName kustomization
KustomizeFileName = "kustomization"
)

// KustomizeFileNames returns the valid extensions for k8s (yaml, yml, json)
func KustomizeFileNames() []string {
return []string{
utils.AddFileExtension(KustomizeFileName, YAMLExtension),
utils.AddFileExtension(KustomizeFileName, YAMLExtension2),
}
}
2 changes: 1 addition & 1 deletion pkg/iac-providers/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var defaultIacVersions = make(map[supportedIacType]supportedIacVersion)

// RegisterIacProvider registers an IaC provider for terrascan
// if the Iac provider does not have a version, it can be kept empty
func RegisterIacProvider(iacType supportedIacType, iacVersion supportedIacVersion, defaultIacVersion supportedIacVersion, iacProvider reflect.Type) {
func RegisterIacProvider(iacType supportedIacType, iacVersion, defaultIacVersion supportedIacVersion, iacProvider reflect.Type) {

if iacVersion == "" {
iacVersion = defaultIacVersion
Expand Down
11 changes: 11 additions & 0 deletions pkg/policy/kustomize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package policy

const (
defaultKustomizeIacType supportedIacType = "kustomize"
defaultKustomizeIacVersion supportedIacVersion = "v3"
)

func init() {
// Register helm as a provider with terrascan
RegisterCloudProvider(kubernetes, defaultKustomizeIacType, defaultKustomizeIacVersion)
}
16 changes: 16 additions & 0 deletions pkg/utils/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,19 @@ func FindFilesBySuffix(basePath string, suffixes []string) (map[string][]*string

return retMap, nil
}

// FindFilesBySuffixInDir finds all the immediate files within a given directory that have the specified suffixes
// IT DOES NOT LOOK INTO ANY SUBDIRECTORY. JUST A SINGLE LEVEL FILE SEARCH.
// Returns an array for string pointers as a list of files
func FindFilesBySuffixInDir(basePath string, suffixes []string) ([]*string, error) {
fileInfos, err := ioutil.ReadDir(basePath)
if err != nil {
return nil, err
}
return FilterFileInfoBySuffix(&fileInfos, suffixes), nil
}

// AddFileExtension returns full file name string after adding the extension to the filename
func AddFileExtension(file, ext string) string {
return fmt.Sprintf("%v.%v", file, ext)
}
Loading

0 comments on commit 5c96b2f

Please sign in to comment.