Skip to content

Commit 9addaf7

Browse files
authored
Kubescape integration (#674)
* first draft * Remove composition from example * Fix syntax * Add script * fix indentation * add script inline * Add missing shell * Moving script to file * Pull base image from quay * make executable * change image base and version * fix script and variable names * Adding missing parameters * Add example and fix README --------- Signed-off-by: Laurent Rochette <laurent.rochette@codefresh.io>
1 parent 9b54695 commit 9addaf7

File tree

6 files changed

+341
-0
lines changed

6 files changed

+341
-0
lines changed

incubating/kubescape/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
## [1.0.0] - 2024-01-22
4+
5+
Original version

incubating/kubescape/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM quay.io/kubescape/kubescape-cli:v3.0.1
2+
3+
# Kubescape uses root privileges for writing the results to a file
4+
USER root
5+
6+
COPY entrypoint.sh /entrypoint.sh
7+
8+
ENTRYPOINT ["/entrypoint.sh"]

incubating/kubescape/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# kubescape CLI
2+
3+
Docker image which invokes security script using kubescape CLI
4+
5+
### Prerequisites:
6+
7+
Codefresh Subscription (Dedicated Infrastructure/Hybrid) - https://codefresh.io/
8+
9+
### Documentation:
10+
11+
kubescape CLI: https://github.com/kubescape/kubescape/blob/master/docs/getting-started.md

incubating/kubescape/entrypoint.sh

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/busybox/sh
2+
3+
# Checks if `string` contains `substring`.
4+
#
5+
# Arguments:
6+
# String to check.
7+
#
8+
# Returns:
9+
# 0 if `string` contains `substring`, otherwise 1.
10+
contains() {
11+
case "$1" in
12+
*$2*) return 0 ;;
13+
*) return 1 ;;
14+
esac
15+
}
16+
17+
set -e
18+
19+
# Kubescape uses the client name to make a request for checking for updates
20+
export KS_CLIENT="codefresh"
21+
22+
if [ -n "${FRAMEWORKS}" ] && [ -n "${CONTROLS}" ]; then
23+
echo "Framework and Control are specified. Please specify either one of them"
24+
exit 1
25+
fi
26+
27+
if [ -z "${FRAMEWORKS}" ] && [ -z "${CONTROLS}" ] && [ -z "${IMAGE}" ]; then
28+
echo "Neither Framework, Control nor image are specified. Please specify one of them"
29+
exit 1
30+
fi
31+
32+
33+
if [ -n "${FRAMEWORKS}" ] && [ -n "${IMAGE}" ] || [ -n "${CONTROLS}" ] && [ -n "${IMAGE}" ] ; then
34+
errmsg="Image and Framework / Control are specified. Kubescape does not support scanning both at the moment."
35+
errmsg="${errmsg} Please specify either one of them or neither."
36+
echo "${errmsg}"
37+
exit 1
38+
fi
39+
40+
if [ -n "${IMAGE}" ] && [ "${FIXFILES}" = "true" ]; then
41+
errmsg="The run requests both an image scan and file fix suggestions. Kubescape does not support fixing image scan results at the moment."
42+
errmsg="${errmsg} Please specify either one of them or neither."
43+
echo "${errmsg}"
44+
exit 1
45+
fi
46+
47+
# Split the controls by comma and concatenate with quotes around each control
48+
if [ -n "${CONTROLS}" ]; then
49+
controls=""
50+
set -f
51+
IFS=','
52+
set -- "${CONTROLS}"
53+
set +f
54+
unset IFS
55+
for control in "$@"; do
56+
control=$(echo "${control}" | xargs) # Remove leading/trailing whitespaces
57+
controls="${controls}\"${control}\","
58+
done
59+
controls=$(echo "${controls%?}")
60+
fi
61+
62+
frameworks_cmd=$([ -n "${FRAMEWORKS}" ] && echo "framework ${FRAMEWORKS}" || echo "")
63+
controls_cmd=$([ -n "${CONTROLS}" ] && echo control "${controls}" || echo "")
64+
65+
scan_input=$([ -n "${FILES}" ] && echo "${FILES}" || echo .)
66+
67+
output_formats="${FORMAT}"
68+
have_json_format="false"
69+
if [ -n "${output_formats}" ] && contains "${output_formats}" "json"; then
70+
have_json_format="true"
71+
fi
72+
73+
verbose=""
74+
if [ -n "${VERBOSE}" ] && [ "${VERBOSE}" != "false" ]; then
75+
verbose="--verbose"
76+
fi
77+
78+
exceptions=""
79+
if [ -n "$EXCEPTIONS" ]; then
80+
exceptions="--exceptions ${EXCEPTIONS}"
81+
fi
82+
83+
controls_config=""
84+
if [ -n "$CONTROLSCONFIG" ]; then
85+
controls_config="--controls-config ${CONTROLSCONFIG}"
86+
fi
87+
88+
should_fix_files="false"
89+
if [ "${FIXFILES}" = "true" ]; then
90+
should_fix_files="true"
91+
fi
92+
93+
# If a user requested Kubescape to fix their files, but forgot to ask for JSON
94+
# output, do it for them
95+
if [ "${should_fix_files}" = "true" ] && [ "${have_json_format}" != "true" ]; then
96+
output_formats="${output_formats},json"
97+
fi
98+
99+
output_file=$([ -n "${OUTPUTFILE}" ] && echo "${OUTPUTFILE}" || echo "results")
100+
101+
account_opt=$([ -n "${ACCOUNT}" ] && echo --account "${ACCOUNT}" || echo "")
102+
access_key_opt=$([ -n "${ACCESSKEY}" ] && echo --access-key "${ACCESSKEY}" || echo "")
103+
server_opt=$([ -n "${SERVER}" ] && echo --server "${SERVER}" || echo "")
104+
105+
# If account ID is empty, we load artifacts from the local path, otherwise we
106+
# load from the cloud (this will enable custom framework support)
107+
artifacts_path="/home/ks/.kubescape"
108+
artifacts_opt=$([ -n "${ACCOUNT}" ] && echo "" || echo --use-artifacts-from "${artifacts_path}")
109+
110+
if [ -n "${FAILEDTHRESHOLD}" ] && [ -n "${COMPLIANCETHRESHOLD}" ]; then
111+
echo "Both failedThreshold and complianceThreshold are specified. Please specify either one of them or neither"
112+
exit 1
113+
fi
114+
115+
fail_threshold_opt=$([ -n "${FAILEDTHRESHOLD}" ] && echo --fail-threshold "${FAILEDTHRESHOLD}" || echo "")
116+
compliance_threshold_opt=$([ -n "${COMPLIANCETHRESHOLD}" ] && echo --compliance-threshold "${COMPLIANCETHRESHOLD}" || echo "")
117+
118+
# When a user requests to fix files, the action should not fail because the
119+
# results exceed severity. This is subject to change in the future.
120+
severity_threshold_opt=$(
121+
[ -n "${SEVERITYTHRESHOLD}" ] &&
122+
[ "${should_fix_files}" = "false" ] &&
123+
echo --severity-threshold "${SEVERITYTHRESHOLD}" ||
124+
echo ""
125+
)
126+
127+
# Handle image scanning request
128+
image_subcmd=""
129+
echo "image is <${IMAGE}>"
130+
if [ -n "${IMAGE}" ]; then
131+
132+
# By default, assume we are not authenticated. This means we can pull public
133+
# images from the container runtime daemon
134+
image_arg="${IMAGE}"
135+
136+
severity_threshold_opt=$(
137+
[ -n "${SEVERITYTHRESHOLD}" ] &&
138+
echo --severity-threshold "${SEVERITYTHRESHOLD}" ||
139+
echo ""
140+
)
141+
142+
auth_opts=""
143+
if [ -n "${REGISTRYUSERNAME}" ] && [ -n "${REGISTRYPASSWORD}" ]; then
144+
auth_opts="--username=${REGISTRYUSERNAME} --password=${REGISTRYPASSWORD}"
145+
146+
# When trying to authenticate, we cannot assume that the runner has access
147+
# to an *authenticated* container runtime daemon, so we should always try
148+
# to pull images from the registry
149+
image_arg="registry://${image_arg}"
150+
else
151+
echo "NOTICE: Received no registry credentials, pulling without authentication."
152+
printf "Hint: If you provide credentials, make sure you include both the username and password.\n\n"
153+
fi
154+
155+
# Build the image scanning subcommand with options
156+
image_subcmd="image ${auth_opts}"
157+
# Override the scan input
158+
scan_input="${image_arg}"
159+
echo "Scan subcommand: ${image_subcmd}"
160+
fi
161+
162+
# TODO: include artifacts_opt once https://github.com/kubescape/kubescape/issues/1040 is resolved
163+
scan_command="kubescape scan ${image_subcmd} ${frameworks_cmd} ${controls_cmd} ${scan_input} ${account_opt} ${access_key_opt} ${server_opt} ${fail_threshold_opt} ${compliance_threshold_opt} ${severity_threshold_opt} --format ${output_formats} --output ${output_file} ${verbose} ${exceptions} ${controls_config}"
164+
165+
echo "${scan_command}"
166+
eval "${scan_command}"
167+
168+
if [ "$should_fix_files" = "true" ]; then
169+
fix_command="kubescape fix --no-confirm ${output_file}.json"
170+
eval "${fix_command}"
171+
fi

incubating/kubescape/icon.png

11.7 KB
Loading

incubating/kubescape/step.yaml

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
kind: step-type
2+
version: '1.0'
3+
metadata:
4+
name: kubescape
5+
version: 3.0.1
6+
title: Run a kubescape security scan
7+
isPublic: true
8+
description: Scan a cluster with the kubescape security service.
9+
sources:
10+
- 'https://github.com/codefresh-io/steps/tree/master/incubating/kubescape'
11+
stage: incubating
12+
maintainers:
13+
- name: Laurent Rochette
14+
email: laurent.rochette@codefresh.io
15+
- name: Matthias Bertschy
16+
email: matthiasb@armosec.io
17+
categories:
18+
- security
19+
official: true
20+
tags: []
21+
icon:
22+
type: image
23+
size:
24+
large:
25+
url: >-
26+
https://cdn.jsdelivr.net/gh/codefresh-io/steps/incubating/kubescape/icon.png
27+
examples:
28+
- description: example-1
29+
workflow:
30+
kubescape:
31+
type: kubescape:3.0.1
32+
arguments:
33+
VERBOSE: on
34+
OUTPUTFILE: results
35+
FRAMEWORKS: MITRE
36+
ACCESSKEY: ${{KEY}}
37+
ACCOUNT: ${{ACCOUNT_ID}}
38+
FORMAT: sarif
39+
40+
spec:
41+
arguments: |-
42+
{
43+
"definitions": {},
44+
"$schema": "http://json-schema.org/draft-07/schema#",
45+
"type": "object",
46+
"additionalProperties": false,
47+
"patterns": [],
48+
"required": [],
49+
"properties": {
50+
"FILES": {
51+
"type": "string",
52+
"description": "YAML files or Helm charts to scan for misconfigurations. The files need to be provided with the complete path from the root of the repository. Default is '.' which scans the whole repository"
53+
},
54+
"OUTPUTFILE": {
55+
"type": "string",
56+
"description": "Name of the output file where the scan result will be stored without the extension. Default is 'result'"
57+
},
58+
"FRAMEWORKS": {
59+
"type": "string",
60+
"description": "Security framework(s) to scan the files against. Multiple frameworks can be specified separated by a comma with no spaces. Example - nsa,devopsbest. Run kubescape list frameworks in the Kubescape CLI to get a list of all frameworks. Either frameworks have to be specified or controls."
61+
},
62+
"CONTROLS": {
63+
"type": "string",
64+
"description": "Security control(s) to scan the files against. Multiple controls can be specified separated by a comma with no spaces. Example - Configured liveness probe,Pods in default namespace. Run kubescape list controls in the Kubescape CLI to get a list of all controls. You can use either the complete control name or the control ID such as C-0001 to specify the control you want use. You must specify either the control(s) or the framework(s) you want used in the scan."
65+
},
66+
"ACCOUNT": {
67+
"type": "string",
68+
"description": "account ID for integrating with a third-party server"
69+
},
70+
"ACCESSKEY": {
71+
"type": "string",
72+
"description": "access-key for integrating with a third-party server"
73+
},
74+
"SERVER": {
75+
"type": "string",
76+
"description": "URL for integrating with a third-party server"
77+
},
78+
"FAILEDTHRESHOLD": {
79+
"type": "string",
80+
"description": "Failure threshold is the percent above which the command fails and returns exit code 1. Default is 0 i.e, action fails if any control fails"
81+
},
82+
"SEVERITYTHRESHOLD": {
83+
"type": "string",
84+
"description": "Severity threshold is the severity of a failed control at or above which the command terminates with an exit code 1. Default is 'high', i.e. the action fails if any High severity control fails"
85+
},
86+
"COMPLIANCETHRESHOLD": {
87+
"type": "string",
88+
"description": "Compliance threshold is the percent bellow which the command fails and returns exit code 1 (example: if set to 100 the command will fail if any control fails)"
89+
},
90+
"VERBOSE": {
91+
"type": "string",
92+
"description": "on|off - Display all of the input resources and not only failed resources. Default is 'off'"
93+
},
94+
"EXCEPTIONS": {
95+
"type": "string",
96+
"description": "The JSON file containing at least one resource and one policy. Refer exceptions docs for more info. Objects with exceptions will be presented as exclude and not fail."
97+
},
98+
"FORMAT": {
99+
"type": "string",
100+
"description": "Output format. Can take one or more formats, comma separated",
101+
"default": "junit"
102+
},
103+
"CONTROLSCONFIG": {
104+
"type": "string",
105+
"description": "The file containing controls configuration. Use 'kubescape download controls-inputs' to download the configured controls-inputs."
106+
},
107+
"FIXFILES": {
108+
"type": "string",
109+
"default": "false",
110+
"description": "Whether Kubescape will automatically fix files or not. If enabled, Kubescape will make fixes to the input files. You can then use these fixes to open Pull Requests from your CI/CD pipeline."
111+
},
112+
"IMAGE": {
113+
"type": "string",
114+
"description": "The image you wish to scan. Launches an image scan, which cannot run together with configuration scans."
115+
},
116+
"REGISTRYUSERNAME": {
117+
"type": "string",
118+
"description": "Username to a private registry that hosts the scanned image."
119+
},
120+
"REGISTRYPASSWORD": {
121+
"type": "string",
122+
"description": "Password to a private registry that hosts the scanned image."
123+
},
124+
"KS_IMAGE": {
125+
"type": "string",
126+
"default": "quay.io/codefreshplugins/kubescape",
127+
"description": "Kubescape image to use"
128+
},
129+
"KS_IMAGE_VERSION": {
130+
"type": "string",
131+
"default": "3.0.1",
132+
"description": "Version of the kubescape image to use"
133+
}
134+
}
135+
}
136+
stepsTemplate: |-
137+
kubescan:
138+
image: '[[.Arguments.KS_IMAGE]]:[[.Arguments.KS_IMAGE_VERSION]]'
139+
title: kubescape scan
140+
environment:
141+
[[ range $key, $val := .Arguments ]]
142+
- '[[ $key ]]=[[ $val ]]'
143+
[[- end ]]
144+
delimiters:
145+
left: '[['
146+
right: ']]'

0 commit comments

Comments
 (0)