diff --git a/.github/workflows/gobuild.yml b/.github/workflows/gobuild.yml index 86e8197fe..898440dde 100644 --- a/.github/workflows/gobuild.yml +++ b/.github/workflows/gobuild.yml @@ -29,6 +29,9 @@ jobs: - name: Run unit tests run: make unit-tests + + - name: install kind + run: make install-kind - name: Run e2e tests run: make e2e-tests diff --git a/Makefile b/Makefile index ca7c7ac3c..e8deff520 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ help: @echo "test\n\texecute unit and integration tests" @echo "unit-tests\n\texecute unit tests" @echo "e2e-tests\n\texecute e2e tests" + @echo "e2e-admission-control-tests\n\texecute e2e admission control tests" @echo "validate\n\trun all validations" # build terrascan binary @@ -91,6 +92,14 @@ unit-tests: e2e-tests: build ./scripts/run-e2e.sh +# run e2e validating webhook +e2e-admission-control-tests: build + ./scripts/e2e-admission-control.sh + +# install kind +install-kind: + ./scripts/install-kind.sh + # build terrascan docker image docker-build: ./scripts/docker-build.sh diff --git a/go.mod b/go.mod index 4874f3313..21e6e98b4 100644 --- a/go.mod +++ b/go.mod @@ -34,13 +34,14 @@ require ( github.com/spf13/cobra v1.1.1 github.com/zclconf/go-cty v1.7.1 go.uber.org/zap v1.16.0 - golang.org/x/mod v0.4.2 // indirect - golang.org/x/sys v0.0.0-20210415045647-66c3f260301c + golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 + golang.org/x/tools v0.1.1 // 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 - honnef.co/go/tools v0.1.3 // indirect + honnef.co/go/tools v0.1.4 // indirect k8s.io/api v0.19.2 k8s.io/apimachinery v0.19.2 + k8s.io/client-go v10.0.0+incompatible sigs.k8s.io/kustomize/api v0.8.5 ) diff --git a/go.sum b/go.sum index 1875706aa..d1a44be32 100644 --- a/go.sum +++ b/go.sum @@ -919,6 +919,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= @@ -1065,8 +1066,9 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1083,6 +1085,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1141,10 +1144,14 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4= -golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q= +golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1216,8 +1223,9 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= +golang.org/x/tools v0.1.1/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= @@ -1374,8 +1382,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +honnef.co/go/tools v0.1.4 h1:SadWOkti5uVN1FAMgxn165+Mw00fuQKyk4Gyn/inxNQ= +honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA= diff --git a/pkg/http-server/file-scan.go b/pkg/http-server/file-scan.go index 3ab6e36cc..3e041e04a 100644 --- a/pkg/http-server/file-scan.go +++ b/pkg/http-server/file-scan.go @@ -35,6 +35,7 @@ import ( // scanFile accepts uploaded file and runs scan on it func (g *APIHandler) scanFile(w http.ResponseWriter, r *http.Request) { + zap.S().Debug("handle: file scan request") // get url params params := mux.Vars(r) diff --git a/pkg/http-server/health.go b/pkg/http-server/health.go index ff5e54712..6040fb05a 100644 --- a/pkg/http-server/health.go +++ b/pkg/http-server/health.go @@ -18,9 +18,12 @@ package httpserver import ( "net/http" + + "go.uber.org/zap" ) // Health returns the health of the http server func (g *APIHandler) Health(w http.ResponseWriter, r *http.Request) { + zap.S().Debug("handle: health check request") w.WriteHeader(http.StatusOK) } diff --git a/pkg/http-server/remote-repo.go b/pkg/http-server/remote-repo.go index 06ca9c2ef..a07946c3c 100644 --- a/pkg/http-server/remote-repo.go +++ b/pkg/http-server/remote-repo.go @@ -50,6 +50,7 @@ type scanRemoteRepoReq struct { // scanRemoteRepo downloads the remote Iac repository and scans it for // violations func (g *APIHandler) scanRemoteRepo(w http.ResponseWriter, r *http.Request) { + zap.S().Debug("handle: remote repository scan request") // get url params params := mux.Vars(r) diff --git a/pkg/http-server/webhook-scan-logs.go b/pkg/http-server/webhook-scan-logs.go index d4733aefd..082a01421 100644 --- a/pkg/http-server/webhook-scan-logs.go +++ b/pkg/http-server/webhook-scan-logs.go @@ -69,6 +69,7 @@ type webhookDisplayedShowLog struct { } func (g *APIHandler) getLogs(w http.ResponseWriter, r *http.Request) { + zap.S().Debug("handle: validating webhook's get logs request") if !config.GetK8sAdmissionControl().Dashboard { apiErrorResponse(w, ErrDashboardDisabled.Error(), http.StatusBadRequest) @@ -134,6 +135,7 @@ func (g *APIHandler) getLogs(w http.ResponseWriter, r *http.Request) { } func (g *APIHandler) getLogByUID(w http.ResponseWriter, r *http.Request) { + zap.S().Info("handle: validating webhook's get log by uid request") if !config.GetK8sAdmissionControl().Dashboard { apiErrorResponse(w, ErrDashboardDisabled.Error(), http.StatusBadRequest) diff --git a/pkg/http-server/webhook-scan.go b/pkg/http-server/webhook-scan.go index ff88b7fc1..49a26dbab 100644 --- a/pkg/http-server/webhook-scan.go +++ b/pkg/http-server/webhook-scan.go @@ -31,6 +31,7 @@ import ( // validateK8SWebhook handles the incoming validating admission webhook from kubernetes API server func (g *APIHandler) validateK8SWebhook(w http.ResponseWriter, r *http.Request) { + zap.S().Debug("handle: validating webhook request") var ( params = mux.Vars(r) diff --git a/scripts/e2e-admission-control.sh b/scripts/e2e-admission-control.sh new file mode 100755 index 000000000..09c734064 --- /dev/null +++ b/scripts/e2e-admission-control.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +export TERRASCAN_BIN_PATH=${PWD}/bin/terrascan + +go test -p 1 -v ./test/e2e/validatingwebhook/... \ No newline at end of file diff --git a/scripts/install-kind.sh b/scripts/install-kind.sh new file mode 100755 index 000000000..83647ea15 --- /dev/null +++ b/scripts/install-kind.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# install kind +sudo curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64 +sudo chmod 755 ./kind +sudo mv ./kind /usr/local/bin +sudo chown -R $USER /usr/local/bin/kind \ No newline at end of file diff --git a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human.txt b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human.txt index f7df239ea..ff76e63ba 100644 --- a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human.txt +++ b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human.txt @@ -13,7 +13,7 @@ Scan Summary - File/Folder : /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/k8s/kubernetes_ingress_violation IaC Type : k8s Scanned At : 2021-03-02 15:51:59.060974 +0000 UTC - Policies Validated : 7 + Policies Validated : 8 Violated Policies : 1 Low : 0 Medium : 0 diff --git a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human_verbose.txt b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human_verbose.txt index 5054789d6..894905481 100644 --- a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human_verbose.txt +++ b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_human_verbose.txt @@ -18,7 +18,7 @@ Scan Summary - File/Folder : /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/k8s/kubernetes_ingress_violation IaC Type : k8s Scanned At : 2021-03-02 15:52:18.820415 +0000 UTC - Policies Validated : 7 + Policies Validated : 8 Violated Policies : 1 Low : 0 Medium : 0 diff --git a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_json.txt b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_json.txt index 3ed713e39..913068367 100644 --- a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_json.txt +++ b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_json.txt @@ -18,7 +18,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/k8s/kubernetes_ingress_violation", "iac_type": "k8s", "scanned_at": "2021-03-02 15:52:42.748543 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 1, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_junit_xml.txt b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_junit_xml.txt index 98f68ac20..6b49d4a4b 100644 --- a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_junit_xml.txt +++ b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_junit_xml.txt @@ -1,5 +1,5 @@ - - + + diff --git a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_xml.txt b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_xml.txt index 78d5100f6..f7faaf259 100644 --- a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_xml.txt +++ b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_xml.txt @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_yaml.txt b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_yaml.txt index b459d61c9..99612a4d6 100644 --- a/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_yaml.txt +++ b/test/e2e/scan/golden/k8s_scans/k8s/kubernetes_ingress_violations/kubernetes_ingress_yaml.txt @@ -14,7 +14,7 @@ results: file/folder: /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/k8s/kubernetes_ingress_violation iac_type: k8s scanned_at: 2021-03-02 15:53:30.996294 +0000 UTC - policies_validated: 7 + policies_validated: 8 violated_policies: 1 low: 0 medium: 0 diff --git a/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt b/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt index 900816327..543a4c947 100644 --- a/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt +++ b/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt @@ -6,7 +6,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/resource_skipping/kubernetes", "iac_type": "k8s", "scanned_at": "2021-03-02 16:01:16.973652 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 0, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/resource_skipping/terraform_file_resource_skipping.txt b/test/e2e/scan/golden/resource_skipping/terraform_file_resource_skipping.txt index 8da85deb7..91de041ad 100644 --- a/test/e2e/scan/golden/resource_skipping/terraform_file_resource_skipping.txt +++ b/test/e2e/scan/golden/resource_skipping/terraform_file_resource_skipping.txt @@ -179,7 +179,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/resource_skipping/terraform", "iac_type": "terraform", "scanned_at": "2021-03-02 16:00:46.95002 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 11, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/rules_filtering/skip_multiple_rules.txt b/test/e2e/scan/golden/rules_filtering/skip_multiple_rules.txt index 742441bb1..dac8de89c 100644 --- a/test/e2e/scan/golden/rules_filtering/skip_multiple_rules.txt +++ b/test/e2e/scan/golden/rules_filtering/skip_multiple_rules.txt @@ -19,7 +19,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_db_instance_violation", "iac_type": "terraform", "scanned_at": "2021-03-02 15:59:24.626711 +0000 UTC", - "policies_validated": 4, + "policies_validated": 5, "violated_policies": 1, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/rules_filtering/skip_single_rule.txt b/test/e2e/scan/golden/rules_filtering/skip_single_rule.txt index b053f71e4..e36bd8519 100644 --- a/test/e2e/scan/golden/rules_filtering/skip_single_rule.txt +++ b/test/e2e/scan/golden/rules_filtering/skip_single_rule.txt @@ -103,7 +103,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_db_instance_violation", "iac_type": "terraform", "scanned_at": "2021-03-02 13:53:14.255721 +0000 UTC", - "policies_validated": 6, + "policies_validated": 7, "violated_policies": 8, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human.txt index bfe0192da..c674012f9 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human.txt @@ -14,7 +14,7 @@ Scan Summary - File/Folder : /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_ami_violation IaC Type : terraform Scanned At : 2021-03-02 15:45:17.636568 +0000 UTC - Policies Validated : 7 + Policies Validated : 8 Violated Policies : 1 Low : 0 Medium : 1 diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human_verbose.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human_verbose.txt index c1313c44a..6487faf04 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human_verbose.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_human_verbose.txt @@ -19,7 +19,7 @@ Scan Summary - File/Folder : /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_ami_violation IaC Type : terraform Scanned At : 2021-03-02 15:45:36.980596 +0000 UTC - Policies Validated : 7 + Policies Validated : 8 Violated Policies : 1 Low : 0 Medium : 1 diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json.txt index 529907af8..cc9e93c94 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json.txt @@ -19,7 +19,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_ami_violation", "iac_type": "terraform", "scanned_at": "2021-03-02 15:45:55.603722 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 1, "low": 0, "medium": 1, diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_all.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_all.txt index 9713e8c4b..fb84ea819 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_all.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_all.txt @@ -31,7 +31,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_ami_violation", "iac_type": "all", "scanned_at": "2021-03-02 15:45:55.603722 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 1, "low": 0, "medium": 1, diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_recursive.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_recursive.txt index 54b01bd40..7c5fdf434 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_recursive.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_json_recursive.txt @@ -31,7 +31,7 @@ "file/folder": "/Users/pankajpatil/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/terraform_recursive", "iac_type": "terraform", "scanned_at": "2021-04-18 12:45:51.597994 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 1, "low": 0, "medium": 1, diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_junit_xml.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_junit_xml.txt index 28153188b..3f61daea1 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_junit_xml.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_junit_xml.txt @@ -1,5 +1,5 @@ - - + + diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_xml.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_xml.txt index ee7626527..1328be501 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_xml.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_xml.txt @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_yaml.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_yaml.txt index afaa372d3..f4ee1b044 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_yaml.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_ami_violations/aws_ami_violation_yaml.txt @@ -15,7 +15,7 @@ results: file/folder: /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_ami_violation iac_type: terraform scanned_at: 2021-03-02 15:46:38.259501 +0000 UTC - policies_validated: 7 + policies_validated: 8 violated_policies: 1 low: 0 medium: 1 diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json.txt index 2c6534c0b..77a862c8b 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json.txt @@ -175,7 +175,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_db_instance_violation", "iac_type": "terraform", "scanned_at": "2021-03-02 15:48:06.923537 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 14, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json_show_passed.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json_show_passed.txt index 92ade3e97..cfb10a173 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json_show_passed.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_json_show_passed.txt @@ -2,11 +2,11 @@ "results": { "passed_rules": [ { - "rule_name": "rdsCAExpired", - "description": "Ensure Certificate used in RDS instance is updated", - "rule_id": "AWS.RDS.DS.High.1042", - "severity": "HIGH", - "category": "Data Security" + "rule_name": "ensurePrivateIP", + "description": "Vulnerable to CVE-2020-8554", + "rule_id": "AC-K8-NS-SE-M-0188", + "severity": "MEDIUM", + "category": "Network Security" }, { "rule_name": "noHttps", @@ -21,6 +21,13 @@ "rule_id": "AWS.EC2.Encryption\u0026KeyManagement.Medium.0688", "severity": "MEDIUM", "category": "Encryption \u0026 KeyManagement" + }, + { + "rule_name": "rdsCAExpired", + "description": "Ensure Certificate used in RDS instance is updated", + "rule_id": "AWS.RDS.DS.High.1042", + "severity": "HIGH", + "category": "Data Security" } ], "violations": [ @@ -198,7 +205,7 @@ "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_db_instance_violation", "iac_type": "terraform", "scanned_at": "2021-03-03 07:01:04.624061 +0000 UTC", - "policies_validated": 7, + "policies_validated": 8, "violated_policies": 14, "low": 0, "medium": 0, diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_xml.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_xml.txt index 244324cde..b5aa442b8 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_xml.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_xml.txt @@ -18,5 +18,5 @@ - + \ No newline at end of file diff --git a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_yaml.txt b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_yaml.txt index 3ec76b582..70726979e 100644 --- a/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_yaml.txt +++ b/test/e2e/scan/golden/terraform_scans/aws/aws_db_instance_violations/aws_db_instance_yaml.txt @@ -145,7 +145,7 @@ results: file/folder: /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_db_instance_violation iac_type: terraform scanned_at: 2021-03-02 15:48:25.531792 +0000 UTC - policies_validated: 7 + policies_validated: 8 violated_policies: 14 low: 0 medium: 0 diff --git a/test/e2e/scan/golden/terraform_scans/scanned_with_no_aws_policies.txt b/test/e2e/scan/golden/terraform_scans/scanned_with_no_aws_policies.txt index 0372f74b8..2337d152b 100644 --- a/test/e2e/scan/golden/terraform_scans/scanned_with_no_aws_policies.txt +++ b/test/e2e/scan/golden/terraform_scans/scanned_with_no_aws_policies.txt @@ -3,7 +3,7 @@ Scan Summary - File/Folder : /Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/aws/aws_ami_violation IaC Type : terraform Scanned At : 2021-03-02 15:50:51.746328 +0000 UTC - Policies Validated : 1 + Policies Validated : 2 Violated Policies : 0 Low : 0 Medium : 0 diff --git a/test/e2e/server/server_file_scan_test.go b/test/e2e/server/server_file_scan_test.go index 17089ec77..d60905aeb 100644 --- a/test/e2e/server/server_file_scan_test.go +++ b/test/e2e/server/server_file_scan_test.go @@ -225,8 +225,8 @@ var _ = Describe("Server File Scan", func() { err := json.Unmarshal(responseBytes, &responseEngineOutput) Expect(err).NotTo(HaveOccurred()) - // There are total 7 rules in the test policies directory, out of which 1 is skipped - Expect(responseEngineOutput.ViolationStore.Summary.TotalPolicies).To(BeIdenticalTo(6)) + // There are total 8 rules in the test policies directory, out of which 1 is skipped + Expect(responseEngineOutput.ViolationStore.Summary.TotalPolicies).To(BeIdenticalTo(7)) }) }) diff --git a/test/e2e/server/server_remote_scan_test.go b/test/e2e/server/server_remote_scan_test.go index 6c4eac844..6ef13b5d0 100644 --- a/test/e2e/server/server_remote_scan_test.go +++ b/test/e2e/server/server_remote_scan_test.go @@ -416,8 +416,8 @@ var _ = Describe("Server Remote Scan", func() { err := json.Unmarshal(responseBytes, &responseEngineOutput) Expect(err).NotTo(HaveOccurred()) - // There are total 7 rules in the test policies directory, out of which 1 is skipped - Expect(responseEngineOutput.ViolationStore.Summary.TotalPolicies).To(BeIdenticalTo(6)) + // There are total 8 rules in the test policies directory, out of which 1 is skipped + Expect(responseEngineOutput.ViolationStore.Summary.TotalPolicies).To(BeIdenticalTo(7)) }) }) diff --git a/test/e2e/test_data/policies/k8s/cve_2020_8554/AC-K8-NS-SE-M-0188.json b/test/e2e/test_data/policies/k8s/cve_2020_8554/AC-K8-NS-SE-M-0188.json new file mode 100644 index 000000000..70157bc7e --- /dev/null +++ b/test/e2e/test_data/policies/k8s/cve_2020_8554/AC-K8-NS-SE-M-0188.json @@ -0,0 +1,15 @@ +{ + "name": "ensurePrivateIP", + "file": "ensurePrivateIP.rego", + "template_args": { + "name": "ensurePrivateIP", + "prefix": "", + "resource_type": "kubernetes_service", + "suffix": "" + }, + "severity": "MEDIUM", + "description": "Vulnerable to CVE-2020-8554", + "reference_id": "AC-K8-NS-SE-M-0188", + "category": "Network Security", + "version": 1 +} \ No newline at end of file diff --git a/test/e2e/test_data/policies/k8s/cve_2020_8554/ensurePrivateIP.rego b/test/e2e/test_data/policies/k8s/cve_2020_8554/ensurePrivateIP.rego new file mode 100644 index 000000000..e195b2885 --- /dev/null +++ b/test/e2e/test_data/policies/k8s/cve_2020_8554/ensurePrivateIP.rego @@ -0,0 +1,15 @@ +package accurics + +{{.prefix}}{{.name}}{{.suffix}}[service.id] { + service := input.{{.resource_type}}[_] + type_check(service.config.spec) + object.get(service.config.spec, "externalIPs", "undefined") != "undefined" +} + +type_check(spec) { + spec.type == "ClusterIP" +} + +type_check(spec) { + object.get(spec, "type", "undefined") == "undefined" +} \ No newline at end of file diff --git a/test/e2e/validatingwebhook/certgen.go b/test/e2e/validatingwebhook/certgen.go new file mode 100644 index 000000000..f1f6343ec --- /dev/null +++ b/test/e2e/validatingwebhook/certgen.go @@ -0,0 +1,98 @@ +/* + 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 validatingwebhook + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "os" + "time" +) + +// code in this file is borrowed from https://gist.github.com/samuel/8b500ddd3f6118d052b5e6bc16bc4c09, +// modified as per our need + +// GenerateCertificates generates tls certificate files with the path specified +func GenerateCertificates(certFilePath, privateKeyPath string) error { + // ip address of the machine would be required to be added as + // subject alternate name + ipAddr, err := GetIP() + if err != nil { + return err + } + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return err + } + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"acme.org"}, + Country: []string{"IN"}, + }, + IPAddresses: []net.IP{ipAddr}, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour * 1), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return fmt.Errorf("failed to create certificate, err: %s", err.Error()) + } + + out := &bytes.Buffer{} + pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + certFile, err := os.Create(certFilePath) + if err != nil { + return err + } + defer certFile.Close() + certFile.Write(out.Bytes()) + + out.Reset() + + pem.Encode(out, pemBlockForKey(priv)) + privKeyFile, err := os.Create(privateKeyPath) + if err != nil { + return err + } + defer privKeyFile.Close() + privKeyFile.Write(out.Bytes()) + + return nil +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + default: + return nil + } +} diff --git a/test/e2e/validatingwebhook/kubeclient.go b/test/e2e/validatingwebhook/kubeclient.go new file mode 100644 index 000000000..938fdd808 --- /dev/null +++ b/test/e2e/validatingwebhook/kubeclient.go @@ -0,0 +1,172 @@ +/* + 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 validatingwebhook + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/mitchellh/go-homedir" + admissionv1 "k8s.io/api/admissionregistration/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +const ( + namespace = "default" +) + +// KubernetesClient will connect to local k8s cluster, +// and help perform resource operation +type KubernetesClient struct { + client *kubernetes.Clientset +} + +// NewKubernetesClient creates a new Kubernetes client +func NewKubernetesClient() (*KubernetesClient, error) { + kubernetesClient := new(KubernetesClient) + var err error + kubernetesClient.client, err = kubernetesClient.getK8sClient() + if err != nil { + return nil, err + } + return kubernetesClient, nil +} + +// getK8sClient creates a kubernetes clientset with default config path +func (k *KubernetesClient) getK8sClient() (*kubernetes.Clientset, error) { + home, err := homedir.Dir() + if err != nil { + return nil, fmt.Errorf("home directory not found, error: %s", err.Error()) + } + + configPath := filepath.Join(home, ".kube", "config") + + config, err := clientcmd.BuildConfigFromFlags("", configPath) + if err != nil { + return nil, fmt.Errorf("failed to create k8s config, error: %s", err.Error()) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create K8s clientset, error: %s", err.Error()) + } + + return clientset, nil +} + +// CreateValidatingWebhookConfiguration creates a ValidatingWebhookConfiguration +func (k *KubernetesClient) CreateValidatingWebhookConfiguration(webhookFile, certFile, apiKey, port string) (*admissionv1.ValidatingWebhookConfiguration, error) { + webhooks := admissionv1.ValidatingWebhookConfiguration{} + data, err := ioutil.ReadFile(webhookFile) + if err != nil { + return nil, err + } + + reader := bytes.NewReader(data) + decoder := yaml.NewYAMLOrJSONDecoder(reader, 1024) + err = decoder.Decode(&webhooks) + if err != nil { + return nil, err + } + + certData, err := ioutil.ReadFile(certFile) + if err != nil { + return nil, err + } + + webhook := &webhooks.Webhooks[0] + webhook.ClientConfig.CABundle = certData + ip, err := GetIP() + if err != nil { + return nil, err + } + + url := fmt.Sprintf("https://%s:%s/v1/k8s/webhooks/%s/scan/validate", ip.String(), port, apiKey) + webhook.ClientConfig.URL = &url + + admr := k.client.AdmissionregistrationV1() + + createdWebhookConfig, err := admr.ValidatingWebhookConfigurations().Create(context.TODO(), &webhooks, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + return createdWebhookConfig, nil +} + +// DeleteValidatingWebhookConfiguration will delete the specified webhook name +func (k *KubernetesClient) DeleteValidatingWebhookConfiguration(webhookConfigName string) error { + return k.client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.TODO(), webhookConfigName, metav1.DeleteOptions{}) +} + +// CreatePod will create a pod by parsing a resource file +func (k *KubernetesClient) CreatePod(resourceFile string) (*v1.Pod, error) { + pod := v1.Pod{} + data, err := ioutil.ReadFile(resourceFile) + if err != nil { + return nil, err + } + reader := bytes.NewReader(data) + decoder := yaml.NewYAMLOrJSONDecoder(reader, 1024) + err = decoder.Decode(&pod) + if err != nil { + return nil, err + } + + createdPod, err := k.client.CoreV1().Pods(namespace).Create(context.TODO(), &pod, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + return createdPod, nil +} + +// DeletePod will delete the specified pod name +func (k *KubernetesClient) DeletePod(podName string) error { + return k.client.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{}) +} + +// CreateService will a service by parsing a resource file +func (k *KubernetesClient) CreateService(resourceFile string) (*v1.Service, error) { + service := v1.Service{} + data, err := ioutil.ReadFile(resourceFile) + if err != nil { + return nil, err + } + reader := bytes.NewReader(data) + decoder := yaml.NewYAMLOrJSONDecoder(reader, 1024) + err = decoder.Decode(&service) + if err != nil { + return nil, err + } + + createdService, err := k.client.CoreV1().Services(namespace).Create(context.TODO(), &service, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + return createdService, nil +} + +// DeleteService will delete the specified service name +func (k *KubernetesClient) DeleteService(serviceName string) error { + return k.client.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metav1.DeleteOptions{}) +} diff --git a/test/e2e/validatingwebhook/test-data/yamls/pod.yaml b/test/e2e/validatingwebhook/test-data/yamls/pod.yaml new file mode 100644 index 000000000..96acd2574 --- /dev/null +++ b/test/e2e/validatingwebhook/test-data/yamls/pod.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: myapp + labels: + name: myapp +spec: + containers: + - name: nginx-pod + image: nginx + resources: + limits: + memory: "128Mi" + cpu: "100m" \ No newline at end of file diff --git a/test/e2e/validatingwebhook/test-data/yamls/service.yaml b/test/e2e/validatingwebhook/test-data/yamls/service.yaml new file mode 100644 index 000000000..a19b41479 --- /dev/null +++ b/test/e2e/validatingwebhook/test-data/yamls/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: allowed-external-ip +spec: + type: ClusterIP + selector: + app: MyApp + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + externalIPs: + - 192.168.10.10 + - 8.8.8.8 + - 203.0.113.0 \ No newline at end of file diff --git a/test/e2e/validatingwebhook/test-data/yamls/webhook.yaml b/test/e2e/validatingwebhook/test-data/yamls/webhook.yaml new file mode 100644 index 000000000..a91f59b30 --- /dev/null +++ b/test/e2e/validatingwebhook/test-data/yamls/webhook.yaml @@ -0,0 +1,23 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: localhost-validating-webhook +webhooks: + - name: localhost.terrascan.server + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + - services + failurePolicy: Fail + clientConfig: + url: "" + caBundle: "" + sideEffects: None + admissionReviewVersions: ["v1"] diff --git a/test/e2e/validatingwebhook/validating_webhook_suite_test.go b/test/e2e/validatingwebhook/validating_webhook_suite_test.go new file mode 100644 index 000000000..344005784 --- /dev/null +++ b/test/e2e/validatingwebhook/validating_webhook_suite_test.go @@ -0,0 +1,29 @@ +/* + 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 validatingwebhook_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestValidatingWebhook(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "ValidatingWebhook Suite") +} diff --git a/test/e2e/validatingwebhook/validating_webhook_test.go b/test/e2e/validatingwebhook/validating_webhook_test.go new file mode 100644 index 000000000..b202c1d8c --- /dev/null +++ b/test/e2e/validatingwebhook/validating_webhook_test.go @@ -0,0 +1,490 @@ +/* + 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 validatingwebhook_test + +import ( + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/accurics/terrascan/pkg/config" + "github.com/accurics/terrascan/pkg/utils" + "github.com/accurics/terrascan/test/e2e/server" + "github.com/accurics/terrascan/test/e2e/validatingwebhook" + "github.com/accurics/terrascan/test/helper" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" + admissionv1 "k8s.io/api/admissionregistration/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" +) + +const ( + certsFolder = "certs" + k8sWebhookAPIKey = "K8S_WEBHOOK_API_KEY" + apiKeyValue = "accurics" + defaultTimeout = 10 +) + +var ( + kubeClient *validatingwebhook.KubernetesClient + terrascanBinaryPath string + certFileAbsPath string + privKeyFileAbsPath string + policyRootRelPath = filepath.Join("..", "test_data", "policies") + webhookYamlRelPath = filepath.Join("test-data", "yamls", "webhook.yaml") + podYamlRelPath = filepath.Join("test-data", "yamls", "pod.yaml") + serviceYamlPath = filepath.Join("test-data", "yamls", "service.yaml") +) + +var _ = Describe("ValidatingWebhook", func() { + + BeforeSuite(func() { + + // delete the default cluster if it is already running + err := validatingwebhook.DeleteDefaultKindCluster() + if err != nil { + message := fmt.Sprintf("error while deleting cluster. err: %v", err) + Fail(message) + } + + // create a new cluster + err = validatingwebhook.CreateDefaultKindCluster() + if err != nil { + message := fmt.Sprintf("error while creating cluster. err: %v", err) + Fail(message) + } + + // get k8s client + kubeClient, err = validatingwebhook.NewKubernetesClient() + if err != nil { + errMessage := fmt.Sprintf("failed to connected to default k8s cluster, error: %s", err.Error()) + Fail(errMessage) + } + + // get terrascan binary path + terrascanBinaryPath = helper.GetTerrascanBinaryPath() + + // create tls certificates for server + certFileAbsPath, privKeyFileAbsPath, err = validatingwebhook.CreateCertificate(certsFolder, "server.crt", "priv.key") + if err != nil { + errMessage := fmt.Sprintf("failed to create certificates, error: %s", err.Error()) + Fail(errMessage) + } + + // sleep added so that the default serviceaccount is get created + // this logic needs to be improved + time.Sleep(20 * time.Second) + }) + + AfterSuite(func() { + if utils.IsWindowsPlatform() { + gexec.Kill() + } else { + gexec.Terminate() + } + + // delete the cluster + err := validatingwebhook.DeleteDefaultKindCluster() + if err != nil { + message := fmt.Sprintf("error while deleting cluster. err: %v", err) + Fail(message) + } + + os.RemoveAll(certsFolder) + }) + + Describe("terrascan server as validating webhook with various available config options", func() { + + When("validating webhook with default 'k8s-admission-control' config", func() { + + Context("by default validating webhook runs in blind mode", func() { + var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() + var session *gexec.Session + var webhookConfig *admissionv1.ValidatingWebhookConfiguration + var configFileName string + + It("server should start running on port 9010", func() { + configFileName = "config1.toml" + // create a config file with default config values + err := validatingwebhook.CreateTerrascanConfigFile(configFileName, policyRootRelPath, nil) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv(k8sWebhookAPIKey, apiKeyValue) + args := []string{"server", "-c", configFileName, "--cert-path", certFileAbsPath, "--key-path", privKeyFileAbsPath, "-l", "debug"} + session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, args...) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("http server listening at port 9010")) + }) + + Context("in blind mode, log end points return error response", func() { + port := "9010" + myIP, err := validatingwebhook.GetIP() + When("request is made to the get a single log endpoint", func() { + It("should return a 400 bad request response", func() { + Expect(err).NotTo(HaveOccurred()) + + requestURL := fmt.Sprintf("http://%s:%s/k8s/webhooks/logs/%s", myIP.To4(), port, apiKeyValue) + resp, err := server.MakeHTTPRequest("GET", requestURL) + Expect(err).NotTo(HaveOccurred()) + Expect(resp).NotTo(BeNil()) + Expect(resp.StatusCode).To(BeIdenticalTo(http.StatusBadRequest)) + }) + }) + + When("request is made to the get a single log endpoint", func() { + It("should return a 400 bad request response", func() { + Expect(err).NotTo(HaveOccurred()) + + requestURL := fmt.Sprintf("http://%s:%s/k8s/webhooks/%s/logs", myIP.To4(), port, apiKeyValue) + resp, err := server.MakeHTTPRequest("GET", requestURL) + Expect(err).NotTo(HaveOccurred()) + Expect(resp).NotTo(BeNil()) + Expect(resp.StatusCode).To(BeIdenticalTo(http.StatusBadRequest)) + }) + }) + }) + + When("request is made to add server as a validating webhook", func() { + It("should get registered with k8s cluster as validating webhook successfully", func() { + + webhookFilePath, err := filepath.Abs(webhookYamlRelPath) + Expect(err).NotTo(HaveOccurred()) + + webhookConfig, err = kubeClient.CreateValidatingWebhookConfiguration(webhookFilePath, certFileAbsPath, apiKeyValue, "9010") + Expect(err).NotTo(HaveOccurred()) + }) + + When("pod creation addmission requested is sent to server", func() { + It("server should get the addmission request to review", func() { + // remove the config file + defer os.Remove(configFileName) + + createPod(session, webhookConfig.GetName()) + }) + }) + }) + }) + }) + + When("validating webhook config has 'dashboard' and 'save-requests' is enabled", func() { + var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() + var session *gexec.Session + var webhookConfig *admissionv1.ValidatingWebhookConfiguration + var configFileName string + var port string + + It("server should start running on port 9011", func() { + port = "9011" + configFileName = "config2.toml" + + // create a config file with 'dashboard' set to true + terrascanConfig := config.TerrascanConfig{ + K8sAdmissionControl: config.K8sAdmissionControl{ + Dashboard: true, + SaveRequests: true, + }, + } + err := validatingwebhook.CreateTerrascanConfigFile(configFileName, policyRootRelPath, &terrascanConfig) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv(k8sWebhookAPIKey, apiKeyValue) + args := []string{"server", "-c", configFileName, "--cert-path", certFileAbsPath, "--key-path", privKeyFileAbsPath, "-p", port, "-l", "debug"} + session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, args...) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("http server listening at port 9011")) + }) + + When("request is made to add server as a validating webhook", func() { + It("should get registered with k8s cluster as validating webhook successfully", func() { + + webhookFileAbsPath, err := filepath.Abs(filepath.Join(webhookYamlRelPath)) + Expect(err).NotTo(HaveOccurred()) + + webhookConfig, err = kubeClient.CreateValidatingWebhookConfiguration(webhookFileAbsPath, certFileAbsPath, apiKeyValue, port) + Expect(err).NotTo(HaveOccurred()) + }) + + When("pod creation addmission requested is sent to server", func() { + It("server should get the addmission request to review", func() { + // remove the config file + defer os.Remove(configFileName) + + createPod(session, webhookConfig.GetName()) + }) + }) + }) + }) + + When("validating webhook config has 'denied-severity' specified", func() { + + Context("service to be created violates a policy with specified denied severity", func() { + var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() + var session *gexec.Session + var webhookConfig *admissionv1.ValidatingWebhookConfiguration + var configFileName string + var port string + + It("server should start running on port 9012", func() { + port = "9012" + configFileName = "config3.toml" + + // create a config file with desired severity specified + terrascanConfig := config.TerrascanConfig{ + K8sAdmissionControl: config.K8sAdmissionControl{ + DeniedSeverity: "MEDIUM", + }, + } + err := validatingwebhook.CreateTerrascanConfigFile(configFileName, policyRootRelPath, &terrascanConfig) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv(k8sWebhookAPIKey, apiKeyValue) + args := []string{"server", "-c", configFileName, "--cert-path", certFileAbsPath, "--key-path", privKeyFileAbsPath, "-p", port, "-l", "debug"} + session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, args...) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("http server listening at port 9012")) + }) + + When("request is made to add server as a validating webhook", func() { + It("should get registered with k8s cluster as validating webhook successfully", func() { + + webhookFilePath, err := filepath.Abs(filepath.Join(webhookYamlRelPath)) + Expect(err).NotTo(HaveOccurred()) + + webhookConfig, err = kubeClient.CreateValidatingWebhookConfiguration(webhookFilePath, certFileAbsPath, apiKeyValue, port) + Expect(err).NotTo(HaveOccurred()) + }) + + When("service creation addmission requested is sent to server", func() { + It("server should get the addmission request to review and reject the request", func() { + // remove the config file + defer os.Remove(configFileName) + + createService(session, webhookConfig.GetName(), true) + }) + }) + }) + }) + + Context("service to be created violates a policy which doesn't have the desired severity", func() { + var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() + var session *gexec.Session + var webhookConfig *admissionv1.ValidatingWebhookConfiguration + var configFileName string + var port string + + It("server should start running on port 9013", func() { + port = "9013" + configFileName = "config4.toml" + + // create a config file with desired severity specified + terrascanConfig := config.TerrascanConfig{ + K8sAdmissionControl: config.K8sAdmissionControl{ + DeniedSeverity: "HIGH", + }, + } + err := validatingwebhook.CreateTerrascanConfigFile(configFileName, policyRootRelPath, &terrascanConfig) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv(k8sWebhookAPIKey, apiKeyValue) + args := []string{"server", "-c", configFileName, "--cert-path", certFileAbsPath, "--key-path", privKeyFileAbsPath, "-p", port, "-l", "debug"} + session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, args...) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("http server listening at port 9013")) + }) + + When("request is made to add server as a validating webhook", func() { + It("should get registered with k8s cluster as validating webhook successfully", func() { + + webhookFilePath, err := filepath.Abs(filepath.Join(webhookYamlRelPath)) + Expect(err).NotTo(HaveOccurred()) + + webhookConfig, err = kubeClient.CreateValidatingWebhookConfiguration(webhookFilePath, certFileAbsPath, apiKeyValue, port) + Expect(err).NotTo(HaveOccurred()) + }) + + When("service creation addmission requested is sent to server", func() { + It("server should get the addmission request to review and reject the request", func() { + // remove the config file + defer os.Remove(configFileName) + + createService(session, webhookConfig.GetName(), false) + }) + }) + }) + }) + }) + + When("validating webhook config has 'denied-categories' specified", func() { + Context("service to be created violates the denied category", func() { + var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() + var session *gexec.Session + var webhookConfig *admissionv1.ValidatingWebhookConfiguration + var configFileName string + var port string + + It("server should start running on port 9014", func() { + port = "9014" + configFileName = "config5.toml" + + // create a config file with desired severity specified + terrascanConfig := config.TerrascanConfig{ + K8sAdmissionControl: config.K8sAdmissionControl{ + Categories: []string{"Network Security"}, + }, + } + err := validatingwebhook.CreateTerrascanConfigFile(configFileName, policyRootRelPath, &terrascanConfig) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv(k8sWebhookAPIKey, apiKeyValue) + args := []string{"server", "-c", configFileName, "--cert-path", certFileAbsPath, "--key-path", privKeyFileAbsPath, "-p", port, "-l", "debug"} + session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, args...) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("http server listening at port 9014")) + }) + + When("request is made to add server as a validating webhook", func() { + It("should get registered with k8s cluster as validating webhook successfully", func() { + + webhookFilePath, err := filepath.Abs(filepath.Join(webhookYamlRelPath)) + Expect(err).NotTo(HaveOccurred()) + + webhookConfig, err = kubeClient.CreateValidatingWebhookConfiguration(webhookFilePath, certFileAbsPath, apiKeyValue, port) + Expect(err).NotTo(HaveOccurred()) + }) + + When("service creation addmission requested is sent to server", func() { + It("server should get the addmission request to review and reject the request", func() { + // remove the config file + defer os.Remove(configFileName) + + createService(session, webhookConfig.GetName(), true) + }) + }) + }) + }) + + Context("service to be created does not violate the denied category", func() { + var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() + var session *gexec.Session + var webhookConfig *admissionv1.ValidatingWebhookConfiguration + var configFileName string + var port string + + It("server should start running on port 9015", func() { + port = "9015" + configFileName = "config6.toml" + + // create a config file with desired severity specified + terrascanConfig := config.TerrascanConfig{ + K8sAdmissionControl: config.K8sAdmissionControl{ + Categories: []string{"Doesn't Exist"}, + }, + } + err := validatingwebhook.CreateTerrascanConfigFile(configFileName, policyRootRelPath, &terrascanConfig) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv(k8sWebhookAPIKey, apiKeyValue) + args := []string{"server", "-c", configFileName, "--cert-path", certFileAbsPath, "--key-path", privKeyFileAbsPath, "-p", port, "-l", "debug"} + session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, args...) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("http server listening at port 9015")) + }) + + When("request is made to add server as a validating webhook", func() { + It("should get registered with k8s cluster as validating webhook successfully", func() { + + webhookFilePath, err := filepath.Abs(filepath.Join(webhookYamlRelPath)) + Expect(err).NotTo(HaveOccurred()) + + webhookConfig, err = kubeClient.CreateValidatingWebhookConfiguration(webhookFilePath, certFileAbsPath, apiKeyValue, port) + Expect(err).NotTo(HaveOccurred()) + }) + + When("service creation addmission requested is sent to server", func() { + It("server should get the addmission request to review and reject the request", func() { + // remove the config file + defer os.Remove(configFileName) + + createService(session, webhookConfig.GetName(), false) + }) + }) + }) + }) + }) + }) +}) + +// createService creates a service and asserts for reject status, +// and deletes the resources +func createService(session *gexec.Session, webhookName string, shouldBeDenied bool) { + serviceYamlAbsPath, err := filepath.Abs(filepath.Join(serviceYamlPath)) + Expect(err).NotTo(HaveOccurred()) + + service, err := kubeClient.CreateService(serviceYamlAbsPath) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("handle: validating webhook request")) + + if shouldBeDenied { + Expect(err).To(HaveOccurred()) + if e, ok := err.(*k8serr.StatusError); ok { + Expect(e.Status().Code).To(BeNumerically("==", 403)) + } else { + errMessage := fmt.Sprintf("expected error to be of type 'k8s.io/apimachinery/pkg/api/errors.StatusError', got of type %T", err) + Fail(errMessage) + } + Expect(service).To(BeNil()) + } else { + Expect(err).NotTo(HaveOccurred()) + + err = kubeClient.DeleteService(service.GetName()) + Expect(err).NotTo(HaveOccurred()) + } + + // delete validating webhook configuration + err = kubeClient.DeleteValidatingWebhookConfiguration(webhookName) + Expect(err).NotTo(HaveOccurred()) + + if utils.IsWindowsPlatform() { + session.Kill() + } else { + session.Terminate() + } +} + +// createPod creates a pod and asserts for reject status, +// and deletes the resources +func createPod(session *gexec.Session, webhookName string) { + podYamlAbsPath, err := filepath.Abs(filepath.Join(podYamlRelPath)) + Expect(err).NotTo(HaveOccurred()) + + pod, err := kubeClient.CreatePod(podYamlAbsPath) + Eventually(session.Err, defaultTimeout).Should(gbytes.Say("handle: validating webhook request")) + Expect(err).NotTo(HaveOccurred()) + Expect(pod).NotTo(BeNil()) + + // delete pod + err = kubeClient.DeletePod(pod.GetName()) + Expect(err).NotTo(HaveOccurred()) + + // delete validating webhook configuration + err = kubeClient.DeleteValidatingWebhookConfiguration(webhookName) + Expect(err).NotTo(HaveOccurred()) + + if utils.IsWindowsPlatform() { + session.Kill() + } else { + session.Terminate() + } +} diff --git a/test/e2e/validatingwebhook/validatingwebhook_utils.go b/test/e2e/validatingwebhook/validatingwebhook_utils.go new file mode 100644 index 000000000..3c250507e --- /dev/null +++ b/test/e2e/validatingwebhook/validatingwebhook_utils.go @@ -0,0 +1,125 @@ +/* + 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 validatingwebhook + +import ( + "fmt" + "net" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/accurics/terrascan/pkg/config" + "github.com/accurics/terrascan/pkg/utils" + "github.com/pelletier/go-toml" +) + +// CreateTerrascanConfigFile creates a config file with test policy path +func CreateTerrascanConfigFile(configFileName, policyRootRelPath string, terrascanConfig *config.TerrascanConfig) error { + policyAbsPath, err := filepath.Abs(policyRootRelPath) + if err != nil { + return err + } + + if utils.IsWindowsPlatform() { + policyAbsPath = strings.ReplaceAll(policyAbsPath, "\\", "\\\\") + } + + if terrascanConfig == nil { + terrascanConfig = &config.TerrascanConfig{} + } + + terrascanConfig.BasePath = policyAbsPath + terrascanConfig.RepoPath = policyAbsPath + + // create config file in work directory + file, err := os.Create(configFileName) + if err != nil { + return fmt.Errorf("config file creation failed, err: %v", err) + } + + contentBytes, err := toml.Marshal(terrascanConfig) + if err != nil { + return err + } + + _, err = file.WriteString(string(contentBytes)) + if err != nil { + return fmt.Errorf("error while writing to config file, err: %v", err) + } + return nil +} + +// CreateCertificate creates certificates required to run server in the folder specified +func CreateCertificate(certsFolder, certFileName, privKeyFileName string) (string, string, error) { + // create certs folder to keep certificates + os.Mkdir(certsFolder, 0755) + certFileAbsPath, err := filepath.Abs(filepath.Join(certsFolder, "server.crt")) + if err != nil { + return "", "", err + } + privKeyFileAbsPath, err := filepath.Abs(filepath.Join(certsFolder, "priv.key")) + if err != nil { + return "", "", err + } + err = GenerateCertificates(certFileAbsPath, privKeyFileAbsPath) + if err != nil { + return "", "", err + } + + return certFileAbsPath, privKeyFileAbsPath, nil +} + +// DeleteDefaultKindCluster deletes the default kind cluster +func DeleteDefaultKindCluster() error { + cmd := exec.Command("kind", "delete", "cluster") + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + return err + } + return nil +} + +// CreateDefaultKindCluster creates the default kind cluster +func CreateDefaultKindCluster() error { + cmd := exec.Command("kind", "create", "cluster") + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + return err + } + return nil +} + +// GetIP finds preferred outbound ip of the machine +func GetIP() (net.IP, error) { + addrs, err := net.InterfaceAddrs() + if err != nil { + return nil, err + } + + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.To4(), nil + } + } + } + return nil, fmt.Errorf("could not find ip address of the machine") +}