From 6004efeec09813ac229b539409cfda8fbf412244 Mon Sep 17 00:00:00 2001 From: Juha Kosonen Date: Thu, 20 Feb 2020 16:21:12 +0200 Subject: [PATCH] Add Robot test for running kube-hunter Integrated to k8s layer as an optional test. JIRA: VAL-96 Change-Id: I0d11a3aa9438a7fe3dbe52a1358af5ddcaabff61 Signed-off-by: Juha Kosonen --- .coafile | 3 +- bluval/bluval-base.yaml | 3 + bluval/bluval-iec.yaml | 4 + bluval/bluval-kni.yaml | 4 + bluval/bluval-rec.yaml | 4 + bluval/bluval-unicycle.yaml | 4 + docker/k8s/pip-requirements.txt | 1 + docker/kube-hunter/Dockerfile | 40 ++++++++++ docker/kube-hunter/Makefile | 26 ++++++ docker/kube-hunter/pip-requirements.txt | 1 + tests/k8s/kube-hunter/job.yaml | 31 ++++++++ tests/k8s/kube-hunter/kube-hunter.resource | 123 +++++++++++++++++++++++++++++ tests/k8s/kube-hunter/kube-hunter.robot | 39 +++++++++ 13 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 docker/kube-hunter/Dockerfile create mode 100644 docker/kube-hunter/Makefile create mode 100644 docker/kube-hunter/pip-requirements.txt create mode 100644 tests/k8s/kube-hunter/job.yaml create mode 100644 tests/k8s/kube-hunter/kube-hunter.resource create mode 100644 tests/k8s/kube-hunter/kube-hunter.robot diff --git a/.coafile b/.coafile index fd4a0cc..c472b5d 100644 --- a/.coafile +++ b/.coafile @@ -36,7 +36,8 @@ bears = YAMLLintBear files = **.yaml, **.yml use_spaces = true max_line_length = 120 -ignore = tests/k8s/conformance/custom_repos.yaml +ignore = tests/k8s/conformance/custom_repos.yaml, + tests/k8s/kube-hunter/job.yaml [all.Python] bears = PyLintBear diff --git a/bluval/bluval-base.yaml b/bluval/bluval-base.yaml index ab860f9..845a700 100644 --- a/bluval/bluval-base.yaml +++ b/bluval/bluval-base.yaml @@ -54,6 +54,9 @@ blueprint: - name: ha what: ha + - + name: kube-hunter + what: kube-hunter k8s_networking: &k8s_networking - name: node_connectivity diff --git a/bluval/bluval-iec.yaml b/bluval/bluval-iec.yaml index 1762e5e..1da0158 100644 --- a/bluval/bluval-iec.yaml +++ b/bluval/bluval-iec.yaml @@ -25,3 +25,7 @@ blueprint: name: conformance what: conformance optional: "False" + - + name: kube-hunter + what: kube-hunter + optional: "True" diff --git a/bluval/bluval-kni.yaml b/bluval/bluval-kni.yaml index 75f941b..c615ffb 100644 --- a/bluval/bluval-kni.yaml +++ b/bluval/bluval-kni.yaml @@ -37,3 +37,7 @@ blueprint: - name: conformance what: conformance + - + name: kube-hunter + what: kube-hunter + optional: "True" diff --git a/bluval/bluval-rec.yaml b/bluval/bluval-rec.yaml index 8bc3203..07df9da 100644 --- a/bluval/bluval-rec.yaml +++ b/bluval/bluval-rec.yaml @@ -52,3 +52,7 @@ blueprint: name: etcd_ha what: etcd_ha optional: "True" + - + name: kube-hunter + what: kube-hunter + optional: "True" diff --git a/bluval/bluval-unicycle.yaml b/bluval/bluval-unicycle.yaml index 02e6b31..2f0768d 100644 --- a/bluval/bluval-unicycle.yaml +++ b/bluval/bluval-unicycle.yaml @@ -46,6 +46,10 @@ blueprint: name: etcd_ha what: etcd_ha optional: "True" + - + name: kube-hunter + what: kube-hunter + optional: "True" openstack: &openstack_unicycle - name: tempest diff --git a/docker/k8s/pip-requirements.txt b/docker/k8s/pip-requirements.txt index 18b264c..59a75a8 100644 --- a/docker/k8s/pip-requirements.txt +++ b/docker/k8s/pip-requirements.txt @@ -3,3 +3,4 @@ robotframework-httplibrary robotframework-jsonlibrary robotframework-requests robotframework-sshlibrary +kube-hunter diff --git a/docker/kube-hunter/Dockerfile b/docker/kube-hunter/Dockerfile new file mode 100644 index 0000000..7e65f93 --- /dev/null +++ b/docker/kube-hunter/Dockerfile @@ -0,0 +1,40 @@ +############################################################################## +# Copyright (c) 2020 AT&T, ENEA AB, Nokia and others # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you maynot 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. # +############################################################################## + +FROM python:3.6-alpine3.9 as build + +# Install dependencies +COPY pip-requirements.txt /wheels/requirements/pip-requirements.txt + +RUN apk --no-cache add --update \ + linux-headers \ + build-base + +# Build binaries +WORKDIR /wheels +RUN pip3 install wheel +RUN pip3 wheel -r /wheels/requirements/pip-requirements.txt + +# Copy binaries in the final container +FROM python:3.6-alpine3.9 +COPY --from=build /wheels /wheels + +RUN pip3 install -r /wheels/requirements/pip-requirements.txt \ + -f /wheels && \ + rm -rf /wheels && \ + rm -rf /root/.cache/pip/* + +ENTRYPOINT ["kube-hunter"] diff --git a/docker/kube-hunter/Makefile b/docker/kube-hunter/Makefile new file mode 100644 index 0000000..4a059ba --- /dev/null +++ b/docker/kube-hunter/Makefile @@ -0,0 +1,26 @@ +############################################################################## +# Copyright (c) 2020 AT&T, ENEA AB, Nokia and others # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you maynot 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. # +############################################################################## + +.PHONY: all +all: push-image .push_manifest + +.PHONY: build +build: .build + +.PHONY: push-image +push-image: .push_image + +include ../build.mk diff --git a/docker/kube-hunter/pip-requirements.txt b/docker/kube-hunter/pip-requirements.txt new file mode 100644 index 0000000..6052d92 --- /dev/null +++ b/docker/kube-hunter/pip-requirements.txt @@ -0,0 +1 @@ +kube-hunter diff --git a/tests/k8s/kube-hunter/job.yaml b/tests/k8s/kube-hunter/job.yaml new file mode 100644 index 0000000..62079c5 --- /dev/null +++ b/tests/k8s/kube-hunter/job.yaml @@ -0,0 +1,31 @@ +############################################################################## +# Copyright (c) 2020 AT&T Intellectual Property. # +# Copyright (c) 2020 Nokia. # +# # +# 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. # +############################################################################## +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: kube-hunter +spec: + template: + spec: + containers: + - name: kube-hunter + image: {{ image }} + command: ["kube-hunter"] + args: ["--pod"] + restartPolicy: Never + backoffLimit: 4 diff --git a/tests/k8s/kube-hunter/kube-hunter.resource b/tests/k8s/kube-hunter/kube-hunter.resource new file mode 100644 index 0000000..561dea2 --- /dev/null +++ b/tests/k8s/kube-hunter/kube-hunter.resource @@ -0,0 +1,123 @@ +############################################################################## +# Copyright (c) 2020 AT&T Intellectual Property. # +# Copyright (c) 2020 Nokia. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you maynot 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. # +############################################################################## + + +*** Settings *** +Library Collections +Library OperatingSystem +Library Process +Library SSHLibrary +Library String + + +*** Variables *** +${REPORTDIR} ${LOG_PATH}/${SUITE_NAME.replace(' ','_')} +&{KUBE_HUNTER} path=akraino +... name=validation:kube-hunter-latest + + +*** Keywords *** +Open Connection And Log In + Open Connection ${HOST} + Login With Public Key ${USERNAME} ${SSH_KEYFILE} + +Get Cluster Address + ${result}= Run Process kubectl config view --minify + ... -o jsonpath\={.clusters[0].cluster.server} + Should Be Equal As Integers ${result.rc} 0 + ${addr}= Fetch From Right ${result.stdout} :// + ${addr}= Fetch From Left ${addr} : + Should Not Be Empty ${addr} + [Return] ${addr} + +Get Remote Addresses + ${result}= Run Process kubectl get nodes + ... -o jsonpath\={.items[*].status.addresses[?(@.type\=\="ExternalIP")].address} + Should Be Equal As Integers ${result.rc} 0 + Pass Execution If '${result.stdout}' == '${EMPTY}' No external node IPs exposed + @{addrs}= Split String ${result.stdout} + [Return] ${addrs} + +Upload To Internal Registry + [Arguments] ${path} ${name} + ${rc}= Execute Command + ... docker pull ${path}/${name} + ... return_stdout=False return_rc=True + Should Be Equal As Integers ${rc} 0 + ${rc}= Execute Command + ... docker tag ${path}/${name} ${INT_REG}/bluval/${name} + ... return_stdout=False return_rc=True + Should Be Equal As Integers ${rc} 0 + ${rc}= Execute Command + ... docker push ${INT_REG}/bluval/${name} + ... return_stdout=False return_rc=True + Should Be Equal As Integers ${rc} 0 + +Onboard Image + ${INT_REG}= Get Variable Value ${INTERNAL_REGISTRY} ${EMPTY} + Set Test Variable ${INT_REG} + Return From Keyword If $INT_REG == '${EMPTY}' + Open Connection And Log In + Upload To Internal Registry ${KUBE_HUNTER['path']} ${KUBE_HUNTER['name']} + Set To Dictionary ${KUBE_HUNTER} path=${INT_REG}/bluval + +Prepare Job Manifest + Run Process sed -i s|{{ image }}|${KUBE_HUNTER['path']}/${KUBE_HUNTER['name']}|g + ... ${CURDIR}/job.yaml + +Set Scan Status + [Arguments] ${log} + ${STATUS}= Evaluate "No vulnerabilities were found" in """${log}""" + Set Test Variable ${STATUS} + +Delete Scan Job + ${result}= Run Process kubectl delete job kube-hunter + Should Be Equal As Integers ${result.rc} 0 + +Should Discover No Vulnerabilities + Should Be True ${STATUS} + +Run Scan Within Pod + ${result}= Run Process kubectl apply -f job.yaml + Should Be Equal As Integers ${result.rc} 0 + ${result}= Run Process kubectl wait --for\=condition\=complete + ... --timeout\=15m job/kube-hunter + Should Be Equal As Integers ${result.rc} 0 + ${result}= Run Process kubectl get pods --selector\=job-name\=kube-hunter + ... -o jsonpath\={.items[*].metadata.name} + Should Be Equal As Integers ${result.rc} 0 + ${result}= Run Process kubectl logs ${result.stdout} + ... stdout=pod.log + Copy File pod.log ${REPORTDIR}/ + Should Be Equal As Integers ${result.rc} 0 + Set Scan Status ${result.stdout} + +Run Node Scan + ${addrs}= Get Remote Addresses + ${result}= Run Process kube-hunter --remote @{addrs} + ... stdout=node.log + Copy File node.log ${REPORTDIR}/ + Should Be Equal As Integers ${result.rc} 0 + Set Scan Status ${result.stdout} + +Run Cluster Scan + ${addr}= Get Cluster Address + ${result}= Run Process kube-hunter --remote ${addr} + ... stdout=cluster.log + Copy File cluster.log ${REPORTDIR}/ + Should Be Equal As Integers ${result.rc} 0 + Set Scan Status ${result.stdout} diff --git a/tests/k8s/kube-hunter/kube-hunter.robot b/tests/k8s/kube-hunter/kube-hunter.robot new file mode 100644 index 0000000..4ef9bf6 --- /dev/null +++ b/tests/k8s/kube-hunter/kube-hunter.robot @@ -0,0 +1,39 @@ +############################################################################## +# Copyright (c) 2020 AT&T Intellectual Property. # +# Copyright (c) 2020 Nokia. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you maynot 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. # +############################################################################## + + +*** Settings *** +Documentation Hunt for security weaknesses in Kubernetes cluster +Resource kube-hunter.resource + + +*** Test Cases *** +Cluster Remote Scanning + Run Cluster Scan + Should Discover No Vulnerabilities + +Node Remote Scanning + Run Node Scan + Should Discover No Vulnerabilities + +Inside-a-Pod Scanning + [Setup] Run Keywords Onboard Image + ... Prepare Job Manifest + Run Scan Within Pod + Should Discover No Vulnerabilities + [Teardown] Run Keywords Delete Scan Job + ... Close All Connections -- 2.16.6