-
Notifications
You must be signed in to change notification settings - Fork 7
/
badge.yaml
441 lines (356 loc) · 19.5 KB
/
badge.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# Copyright 2024 Defense Unicorns
# SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial
# yaml-language-server: $schema=https://raw.githubusercontent.com/defenseunicorns/uds-cli/main/tasks.schema.json
variables:
- name: CHART_PATH
description: Relative path to the directory with the uds-config chart
default: chart/
- name: GROUP_NAME
description: The name of the niche/group that the package fits into. (e.g. package,swf,lfai)
default: package
- name: COMMON_ZARF
description: Whether or not there is a common zarf.yaml file
default: "true"
- name: PACKAGE_DIR
description: Base directory where packages are located, relative to the root of the repository
default: .
tasks:
- name: verify-badge
description: Verifies that a UDS Package implements UDS Package Practices flagging things that are out of compliance
actions:
- description: Verify that the package meets UDS badging standards
shell:
linux: bash
darwin: bash
cmd: |
########
# COUNTS
########
WARNINGS_COUNT=0
ERRORS_COUNT=0
FAILURES_COUNT=0
###########
# FUNCTIONS
###########
gh_notice() {
if [[ "${CI}" == "true" ]]; then
printf "::notice::"
fi
echo "$1"
}
gh_warning() {
if [[ "${CI}" == "true" ]]; then
printf "::warning::"
fi
WARNINGS_COUNT=$((WARNINGS_COUNT + 1))
echo "$1"
}
gh_error() {
if [[ "${CI}" == "true" ]]; then
printf "::error::"
fi
ERRORS_COUNT=$((ERRORS_COUNT + 1))
echo "$1"
}
gh_failure() {
if [[ "${CI}" == "true" ]]; then
printf "::error::"
fi
FAILURES_COUNT=$((FAILURES_COUNT + 1))
echo "$1"
}
gh_group() {
if [[ "${CI}" == "true" ]]; then
echo "::group::$1"
else
echo "$1"
fi
}
gh_endgroup() {
if [[ "${CI}" == "true" ]]; then
echo "::endgroup::"
else
echo
fi
}
################
# INITIALIZATION
################
gh_group "📃 Setup Package Variables"
# Default values for vars, maru-runner issue workaround
CHART_PATH=${CHART_PATH:-chart/}
gh_notice " ℹ️ Chart Path: $CHART_PATH"
GROUP_NAME=${GROUP_NAME:-package}
gh_notice " ℹ️ Group Name: $GROUP_NAME"
COMMON_ZARF=${COMMON_ZARF:-true}
gh_notice " ℹ️ Common Zarf: $COMMON_ZARF"
PACKAGE_DIR=${PACKAGE_DIR:-"."}
gh_notice " ℹ️ Package Directory: $PACKAGE_DIR"
# Extract UDS_PACKAGE_NAME from zarf.yaml
UDS_PACKAGE_NAME=$(uds zarf tools yq '.metadata.name' "${PACKAGE_DIR}/zarf.yaml")
gh_notice " ℹ️ Package Name: $UDS_PACKAGE_NAME"
# Cover when GOBIN isn't set
export GOBIN=/tmp/gobin
gh_notice " ℹ️ Installing kubectl-validate..."
if go install sigs.k8s.io/kubectl-validate@latest; then
gh_notice " ✅ kubectl-validate installed"
else
gh_error " ❌ kubectl-validate failed to install"
fi
gh_endgroup
########################
# STATIC CODE VALIDATION
########################
gh_group "🔍 Package Structure Validation"
if [[ "${COMMON_ZARF}" == "true" ]]; then
NAMESPACE=$(uds zarf tools yq '.components[].charts[].namespace' "${PACKAGE_DIR}/common/zarf.yaml" | uniq)
else
gh_warning " ⚠️ There is no common zarf.yaml file"
NAMESPACE=$(uds zarf tools yq '.components[].charts[].namespace' "${PACKAGE_DIR}/zarf.yaml" | uniq)
fi
gh_notice " ℹ️ Namespace: $NAMESPACE"
# Should expose all configuration (uds.dev CRs, additional Secrets/ConfigMaps, etc) through a Helm chart (ideally in a chart or charts directory).
if [ "${COMMON_ZARF}" == "true" ]; then
COMMON_ZARF_MANIFEST_COUNT=$(uds zarf tools yq '.components[] | select(.manifests != null)' "${PACKAGE_DIR}/common/zarf.yaml" | wc -l)
if [ "$COMMON_ZARF_MANIFEST_COUNT" -gt 0 ]; then
gh_error " ❌ Manifests present in common/zarf.yaml"
else
gh_notice " ✅ No manifests present in common/zarf.yaml"
fi
fi
MAIN_ZARF_MANIFEST_COUNT=$(uds zarf tools yq '.components[] | select(.manifests != null)' "${PACKAGE_DIR}/zarf.yaml" | wc -l)
if [ "$MAIN_ZARF_MANIFEST_COUNT" -gt 0 ]; then
gh_error " ❌ Manifests present in zarf.yaml"
else
gh_notice " ✅ No manifests present in zarf.yaml"
fi
# Should implement or allow for multiple flavors (ideally with common definitions in a common directory)
ZARF_COMPONENTS_FLAVOR_COUNT=$(uds zarf tools yq '.components[] | select(.only.flavor != null)' "${PACKAGE_DIR}/zarf.yaml" | wc -l)
if [ "$ZARF_COMPONENTS_FLAVOR_COUNT" -eq 0 ]; then
gh_error " ❌ No flavors defined in zarf.yaml"
else
gh_notice " ✅ At least one flavor defined in zarf.yaml"
fi
gh_endgroup
gh_group "🔍 Package Testing Validation"
# Must implement Journey Testing to cover the basic user flows and features of the application, especially where an application interacts with an external service/interface.
if [ -d "${PACKAGE_DIR}/tests" ]; then
if [ "$(ls -A "${PACKAGE_DIR}/tests")" ]; then
gh_notice " ✅ Tests folder exists and has files"
else
gh_error " ❌ Tests folder exists but is empty"
fi
else
# Tests might be at the root of a mono-repository
if [ -d "tests" ]; then
if [ "$(ls -A "tests")" ]; then
gh_notice " ✅ Tests folder exists and has files"
else
gh_error " ❌ Tests folder exists but is empty"
fi
else
gh_error " ❌ Tests are not present for this package"
fi
fi
gh_endgroup
gh_group "🔍 Versioning Validation"
# Must be consistently versioned across flavors - this can take many forms but flavors should differ in image bases/builds not application versions.
ZARF_PKG_VERSION=$(uds zarf tools yq '.metadata.version' "${PACKAGE_DIR}/zarf.yaml" | cut -d '-' -f1)
IMAGE_WITH_ZARF_VERSION_COUNT=$(uds zarf tools yq '.components[].images[]' "${PACKAGE_DIR}/zarf.yaml" | grep -o "${ZARF_PKG_VERSION}" | wc -l)
if [ "$IMAGE_WITH_ZARF_VERSION_COUNT" -ge "$ZARF_COMPONENTS_FLAVOR_COUNT" ]; then
gh_notice " ✅ Version is consistent across flavors and package"
else
gh_warning " ⚠️ Version is not consistent across flavors and package"
fi
gh_endgroup
gh_group "🔍 UDS Config Chart Validation"
# Validate UDS Package/Config chart is valid
gh_notice " ℹ️ Validating uds-config chart..."
if ${GOBIN}/kubectl-validate <(helm template "${PACKAGE_DIR}/${CHART_PATH}" | grep -v '^ *#' | perl -p0e 's/---[\s---]*---/---/sg') &>/tmp/validate-output; then
gh_notice " ✅ uds-config chart is valid"
else
gh_error " ❌ uds-config chart is invalid"
cat /tmp/validate-output | sed 's/^/ /'
fi
gh_endgroup
#############################
# DYNAMIC PACKAGE VALIDATIONS
#############################
gh_group "🔍 Exemptions Validation"
# Must minimize the scope and number of the exemptions to only what is absolutely required by the application
EXEMPTION_COUNT=$(uds zarf tools kubectl get Exemptions -n "${NAMESPACE}" -o json | jq -r '.items | length')
if [ "$EXEMPTION_COUNT" -gt 0 ]; then
gh_warning " ⚠️ Exemptions are present, review needed"
else
gh_notice " ✅ No exemptions present"
fi
gh_endgroup
UDS_PACKAGE_CR_EXISTS="false"
gh_group "📃 Get the UDS Package CR"
# The following may fail if the UDS Package CR does not exist
set +e
# Try to retrieve the specific Package resource
UDS_PACKAGE_JSON=$(uds zarf tools kubectl get Package "$UDS_PACKAGE_NAME" -n "${NAMESPACE}" -o json 2>/dev/null)
# Check if the kubectl command succeeded
if [ $? -eq 0 ]; then
UDS_PACKAGE_CR_EXISTS="true"
gh_notice " ℹ️ Retrieved UDS Package JSON for $UDS_PACKAGE_NAME"
else
gh_failure " ⛔ UDS Package CR does not exist for $UDS_PACKAGE_NAME"
fi
set -e
gh_endgroup
if [ "$UDS_PACKAGE_CR_EXISTS" == "true" ]; then
gh_group "🔍 Istio Validation"
# Must define any external interfaces under the expose key.
ENDPOINTS="$(echo "$UDS_PACKAGE_JSON" | jq -r '.status.endpoints[]')"
gh_notice " ℹ️ Endpoints: $ENDPOINTS"
for ENDPOINT in $ENDPOINTS; do
# Skip endpoints starting with a '*'
if [[ "$ENDPOINT" == \** ]]; then
gh_notice " ⚠️ Skipping wildcard endpoint $ENDPOINT"
continue
fi
# Curl endpoint and check that status isn't 404 or 5XX
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://$ENDPOINT")
if [ "$STATUS" -eq 404 ] || [[ $STATUS == 5* ]]; then
gh_error " ❌ Endpoint $ENDPOINT is returning $STATUS"
else
gh_notice " ✅ Endpoint $ENDPOINT was successfully curl'd"
fi
done
echo
# Must deploy and operate successfully with Istio injection enabled in the namespace.
POD_COUNT=$(uds zarf tools kubectl get pods -n "${NAMESPACE}" --no-headers | wc -l)
POD_SIDECAR_COUNT=$(uds zarf tools kubectl get pods -n "${NAMESPACE}" -o yaml | uds zarf tools yq eval '[.items[] | select(.spec.containers[].name == "istio-proxy")] | length' -)
if [ "$POD_COUNT" -ne "$POD_SIDECAR_COUNT" ]; then
gh_error " ❌ Not all pods have the istio sidecar"
else
gh_notice " ✅ All pods have the istio sidecar"
fi
echo
# Should avoid workarounds such as disabling strict mTLS peer authentication.
PEERAUTH=$(uds zarf tools kubectl get Peerauthentication -n "${NAMESPACE}" -o=json | jq -r '.items[].spec.mtls.mode')
if [ "$PEERAUTH" != "STRICT" ] && [ "$PEERAUTH" != "" ]; then
gh_warning " ⚠️ Peerauth is not strict or inherited, review needed"
else
gh_notice " ✅ Peerauthentication is set to strict"
fi
echo
# Must define network policies under the allow key as required.
NETPOL_AMOUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.network.allow | length')
if [ "$NETPOL_AMOUNT" -eq 0 ]; then
gh_warning " ⚠️ No network policies defined, review needed"
else
gh_notice " ✅ Network policies are defined"
fi
# Should minimize network policies to specific selectors needed for Ingress/Egress traffic.
NETPOL_EXT_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.network.allow[] | select(.remoteGenerated != "IntraNamespace")' | jq -s 'length')
gh_notice " ℹ️ Non-IntraNamespace network policies: $NETPOL_EXT_COUNT"
NETPOL_EXT_SELECTOR_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.network.allow[] | select(.remoteGenerated != "IntraNamespace") | select(.selector != null)' | jq -s 'length')
gh_notice " ℹ️ Non-IntraNamespace network policies with selectors: $NETPOL_EXT_SELECTOR_COUNT"
if [ "$NETPOL_EXT_COUNT" -ne "$NETPOL_EXT_SELECTOR_COUNT" ]; then
gh_error " ❌ Not all applicable network policies are using selectors"
else
gh_notice " ✅ All applicable network policies are using selectors"
fi
NETPOL_EXT_NOTKUBE_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.network.allow[] | select(.remoteGenerated != "IntraNamespace" and .remoteGenerated != "KubeAPI")' | jq -s 'length')
gh_notice " ℹ️ Non-IntraNamespace, non-KubeAPI network policies: $NETPOL_EXT_NOTKUBE_COUNT"
NETPOL_EXT_NOTKUBE_PORT_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.network.allow[] | select(.remoteGenerated != "IntraNamespace" and .remoteGenerated != "KubeAPI") | select(.ports != null or .port != null)' | jq -s 'length')
gh_notice " ℹ️ Non-IntraNamespace, non-KubeAPI network policies with ports: $NETPOL_EXT_NOTKUBE_PORT_COUNT"
if [ "$NETPOL_EXT_NOTKUBE_COUNT" -ne "$NETPOL_EXT_NOTKUBE_PORT_COUNT" ]; then
gh_error " ❌ Not all applicable network policies are using ports"
else
gh_notice " ✅ All applicable network policies are using ports"
fi
NETPOL_ANYWHERE_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.network.allow[] | select(.remoteGenerated == "Anywhere")' | jq -s 'length')
if [ "$NETPOL_ANYWHERE_COUNT" -gt 0 ]; then
gh_warning " ⚠️ Network policies with 'remoteGenerated: Anywhere' are present, review needed"
else
gh_notice " ✅ No network policies with 'remoteGenerated: Anywhere' are present"
fi
gh_endgroup
gh_group "🔍 Keycloak/SSO Validation"
SSO_CLIENT_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.sso | length')
# Must use and create a Keycloak client through the sso key if the application provides a user login.
if [ "$SSO_CLIENT_COUNT" -gt 0 ]; then
gh_notice " ℹ️ There are $SSO_CLIENT_COUNT SSO clients defined"
# Should consider security options during implementation to provide the most secure default possible (i.e. SAML w/SCIM vs OIDC).
SSO_CLIENT_PROTOCOL=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.sso[].protocol')
if [ "$SSO_CLIENT_PROTOCOL" != "saml" ]; then
gh_warning " ⚠️ SAML is not the default protocol, review needed"
else
gh_notice " ✅ Default protocol is SAML"
fi
# Should name the client <App> Login (i.e. Mattermost Login) to provide login UX consistency.
SSO_CLIENT_NAME=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.sso[].name')
EXPECTED_CLIENT_NAME="${UDS_PACKAGE_NAME^} Login"
if [ "$SSO_CLIENT_NAME" != "$EXPECTED_CLIENT_NAME" ]; then
gh_warning " ⚠️ SSO client name not in preferred format, review needed"
else
gh_notice " ✅ SSO client name is correct"
fi
# Should clearly mark the client id with the group and app name uds-<group>-<application>-<protocol> (i.e. uds-swf-mattermost-saml) to provide consistency in the Keycloak UI.
SSO_CLIENT_ID=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.sso[].clientId')
EXPECTED_CLIENT_ID="uds-${GROUP_NAME}-${UDS_PACKAGE_NAME}-${SSO_CLIENT_PROTOCOL}"
if [ "$SSO_CLIENT_ID" != "$EXPECTED_CLIENT_ID" ]; then
gh_warning " ⚠️ SSO client id not in the format uds-<group>-<application>-<protocol>, review needed"
else
gh_notice " ✅ SSO client id is correct"
fi
## May end any generated secrets with -sso to easily locate them when querying the cluster.
SSO_SECRET_NAME=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.sso[].secretName')
if [ "$SSO_SECRET_NAME" != "" ]; then
gh_notice " ℹ️ SSO secret name is defined"
if [[ "$SSO_SECRET_NAME" != *-sso ]]; then
gh_warning " ⚠️ SSO secret name not in the format <application>-sso, review needed"
else
gh_notice " ✅ SSO secret name is in the correct format"
fi
fi
else
gh_warning " ⚠️ No SSO configuration found, review needed"
fi
gh_endgroup
gh_group "🔍 Monitoring Validation"
# Must implement monitors for each application metrics endpoint using its built-in chart monitors, the Package CR monitor key, or manual monitors in the config chart.
PKG_MONITOR_COUNT=$(echo "$UDS_PACKAGE_JSON" | jq -r '.spec.monitor | length')
if [ "$PKG_MONITOR_COUNT" -eq 0 ]; then
gh_notice " ℹ️ No monitors defined in the package, checking for ServiceMonitors"
# Check for built-in ServiceMonitors
SERVICE_MONITOR_COUNT=$(uds zarf tools kubectl get ServiceMonitor -n "${NAMESPACE}" -o json | jq -r '.items | length')
if [ "$SERVICE_MONITOR_COUNT" -eq 0 ]; then
gh_notice " ℹ️ No ServiceMonitors defined, checking for PodMonitors"
# Check for built-in PodMonitors
POD_MONITOR_COUNT=$(uds zarf tools kubectl get PodMonitor -n "${NAMESPACE}" -o json | jq -r '.items | length')
if [ "$POD_MONITOR_COUNT" -eq 0 ]; then
gh_error " ❌ No monitors defined"
else
gh_notice " ✅ PodMonitor found"
gh_warning " ⚠️ There may be more monitors to implement, review needed"
fi
else
gh_notice " ✅ ServiceMonitor found"
gh_warning " ⚠️ There may be more monitors to implement, review needed"
fi
else
gh_notice " ✅ Monitor defined in UDS Package"
gh_warning " ⚠️ There may be more monitors to implement, review needed"
fi
gh_endgroup
fi
#########
# SUMMARY
#########
if [ "$FAILURES_COUNT" -gt 0 ]; then
gh_failure "⛔ $FAILURES_COUNT failures found"
fi
if [ "$ERRORS_COUNT" -gt 0 ]; then
gh_error "❌ $ERRORS_COUNT errors found"
fi
if [ "$WARNINGS_COUNT" -gt 0 ]; then
gh_warning "⚠️ $WARNINGS_COUNT warnings found"
fi
if [ "$FAILURES_COUNT" -eq 0 ] && [ "$ERRORS_COUNT" -eq 0 ] && [ "$WARNINGS_COUNT" -eq 0 ]; then
gh_notice "✅ No failures, errors nor warnings found"
fi