Skip to content

Commit cee859a

Browse files
committed
Ensured non-root container images.
1 parent fba8c9f commit cee859a

File tree

6 files changed

+174
-5
lines changed

6 files changed

+174
-5
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ jobs:
3434
- name: Run Helm unit tests
3535
run: ./eoapi-cli test unit
3636

37+
- name: Check container images for root user
38+
run: ./eoapi-cli test images
39+
3740
integration-tests:
3841
name: Integration tests
3942
needs: fast-checks

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Ensured non-root container images [#382](https://github.com/developmentseed/eoapi-k8s/pull/382)
13+
1014
### Fixed
1115

1216
- Fixed Helm template to check queryables `file` field with schema validation [#380](https://github.com/developmentseed/eoapi-k8s/pull/380)

charts/eoapi/templates/_helpers/services.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Helper function for common init containers to wait for pgstac jobs
3434
{{- if .Values.pgstacBootstrap.enabled }}
3535
initContainers:
3636
- name: wait-for-pgstac-jobs
37-
image: alpine/k8s:1.28.0
37+
image: bitnami/kubectl:latest
3838
env:
3939
{{- include "eoapi.commonEnvVars" (dict "service" "init" "root" .) | nindent 2 }}
4040
resources:

charts/eoapi/templates/services/doc-server.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ spec:
4040
spec:
4141
containers:
4242
- name: doc-server
43-
image: nginx:alpine
43+
image: nginxinc/nginx-unprivileged:alpine
4444
volumeMounts:
4545
- name: {{ .Release.Name }}-doc-html
4646
mountPath: /usr/share/nginx/html
4747
ports:
48-
- containerPort: 80
48+
- containerPort: 8080
4949
volumes:
5050
- name: {{ .Release.Name }}-doc-html
5151
configMap:
@@ -71,6 +71,6 @@ spec:
7171
ports:
7272
- protocol: TCP
7373
port: 80
74-
targetPort: 80
74+
targetPort: 8080
7575
---
7676
{{- end }}

scripts/test.sh

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ COMMANDS:
2525
schema Validate Helm chart schema
2626
lint Run Helm lint on chart
2727
unit Run Helm unit tests
28+
images Check container images for root user
2829
integration Run integration tests with pytest
2930
notification Run notification tests with database access
3031
autoscaling Run autoscaling tests with pytest
@@ -115,6 +116,14 @@ test_unit() {
115116
fi
116117
}
117118

119+
test_images() {
120+
log_info "Checking container images for root user..."
121+
122+
check_requirements docker helm || return 1
123+
124+
"${SCRIPT_DIR}/test/images.sh"
125+
}
126+
118127
test_integration() {
119128
local pytest_args="${1:-}"
120129
export NAMESPACE="$NAMESPACE"
@@ -147,6 +156,7 @@ test_all() {
147156
test_schema || ((failed++))
148157
test_lint || ((failed++))
149158
test_unit || ((failed++))
159+
test_images || ((failed++))
150160

151161
if validate_cluster 2>/dev/null; then
152162
test_integration || ((failed++))
@@ -192,7 +202,7 @@ main() {
192202
pytest_args="$2"
193203
shift 2
194204
;;
195-
schema|lint|unit|notification|integration|autoscaling|all)
205+
schema|lint|unit|images|notification|integration|autoscaling|all)
196206
command="$1"
197207
shift
198208
break
@@ -217,6 +227,9 @@ main() {
217227
unit)
218228
test_unit
219229
;;
230+
images)
231+
test_images
232+
;;
220233
integration)
221234
test_integration "$pytest_args"
222235
;;

scripts/test/images.sh

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/usr/bin/env bash
2+
3+
# eoAPI Container Image Root User Check
4+
5+
set -euo pipefail
6+
7+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
9+
10+
source "${SCRIPT_DIR}/../lib/common.sh"
11+
12+
CHART_PATH="${PROJECT_ROOT}/charts/eoapi"
13+
PROFILE_PATH="${PROJECT_ROOT}/charts/eoapi/profiles/experimental.yaml"
14+
15+
echo "======================================"
16+
echo "Container Image Root User Audit"
17+
echo "======================================"
18+
echo ""
19+
20+
# Extract images from Helm templates
21+
if ! command -v helm &>/dev/null; then
22+
log_error "helm is required but not installed"
23+
exit 1
24+
fi
25+
26+
# Extract images, excluding testing-only images
27+
# Filters out: Helm test hooks, mock/sample/test images
28+
if [[ ! -f "$PROFILE_PATH" ]]; then
29+
log_error "Experimental profile not found: $PROFILE_PATH"
30+
exit 1
31+
fi
32+
33+
# Update Helm dependencies if needed
34+
log_debug "Updating Helm chart dependencies..."
35+
if ! helm dependency update "$CHART_PATH" &>/dev/null; then
36+
log_warn "Helm dependency update failed, continuing anyway..."
37+
fi
38+
39+
rendered_yaml=$(helm template test-release "$CHART_PATH" \
40+
--set gitSha=test \
41+
-f "$PROFILE_PATH" \
42+
--set stac-auth-proxy.enabled=false \
43+
2>&1 || \
44+
helm template test-release "$CHART_PATH" \
45+
--set gitSha=test \
46+
-f "$PROFILE_PATH" \
47+
--set stac-auth-proxy.env.OIDC_DISCOVERY_URL=https://dummy.example.com/.well-known/openid-configuration \
48+
2>&1)
49+
50+
if [[ -z "$rendered_yaml" ]] || echo "$rendered_yaml" | grep -q "Error:"; then
51+
log_error "Failed to render Helm templates"
52+
echo "$rendered_yaml" | head -20
53+
exit 1
54+
fi
55+
56+
images=()
57+
while IFS= read -r line; do
58+
[[ -n "$line" ]] && images+=("$line")
59+
done < <(
60+
# Extract images with context to identify test hooks
61+
echo "$rendered_yaml" | awk '
62+
BEGIN { in_test_hook = 0 }
63+
/^---/ { in_test_hook = 0 }
64+
/helm\.sh\/hook.*test/ { in_test_hook = 1 }
65+
/^\s+(- )?image:/ {
66+
image = $0
67+
gsub(/.*image:\s*/, "", image)
68+
gsub(/["'\''"]/, "", image)
69+
gsub(/^[[:space:]]+/, "", image)
70+
if (image && image != "") {
71+
# Skip if in test hook
72+
if (!in_test_hook) {
73+
# Skip images with testing patterns (but allow "test-release" in image names)
74+
if (image !~ /\/mock/ &&
75+
image !~ /\/sample/ &&
76+
image !~ /\/bats\// &&
77+
image !~ /mock-/ &&
78+
image !~ /-mock/ &&
79+
image !~ /sample/ &&
80+
image !~ /bats:/) {
81+
print image
82+
}
83+
}
84+
}
85+
}
86+
' | sort -u
87+
)
88+
89+
if [[ ${#images[@]} -eq 0 ]]; then
90+
log_error "No images found in Helm templates"
91+
log_info "Rendered YAML length: ${#rendered_yaml} characters"
92+
exit 1
93+
fi
94+
95+
log_debug "Found ${#images[@]} images to check"
96+
97+
total=0
98+
root_count=0
99+
non_root_count=0
100+
error_count=0
101+
102+
check_image() {
103+
local image=$1
104+
local user
105+
106+
echo -n "Checking: $image ... "
107+
108+
if docker pull "$image" &>/dev/null; then
109+
if ! user=$(docker inspect "$image" --format='{{.Config.User}}' 2>/dev/null); then
110+
echo -e "${RED}ERROR${NC} (Failed to inspect)"
111+
((error_count++))
112+
return
113+
fi
114+
115+
if [ -z "$user" ] || [ "$user" == "0" ] || [ "$user" == "root" ] || [ "$user" == "0:0" ]; then
116+
echo -e "${RED}⚠️ RUNS AS ROOT${NC} (User: ${user:-not set})"
117+
((root_count++))
118+
else
119+
echo -e "${GREEN}✓ Non-root${NC} (User: $user)"
120+
((non_root_count++))
121+
fi
122+
else
123+
echo -e "${YELLOW}SKIP${NC} (Failed to pull image)"
124+
((error_count++))
125+
fi
126+
}
127+
128+
for image in "${images[@]}"; do
129+
check_image "$image" || true
130+
((total++)) || true
131+
done
132+
133+
echo ""
134+
echo "======================================"
135+
echo "Summary"
136+
echo "======================================"
137+
echo "Total images checked: $total"
138+
echo -e "${RED}Running as root: $root_count${NC}"
139+
echo -e "${GREEN}Running as non-root: $non_root_count${NC}"
140+
echo -e "${YELLOW}Errors/Skipped: $error_count${NC}"
141+
echo ""
142+
143+
if [ $root_count -gt 0 ]; then
144+
echo -e "${RED}⚠️ WARNING: $root_count image(s) run as root user${NC}"
145+
exit 1
146+
else
147+
echo -e "${GREEN}✓ All images run as non-root user${NC}"
148+
exit 0
149+
fi

0 commit comments

Comments
 (0)