Skip to content

Commit 2a4a57f

Browse files
committed
ci: test e2e with harbor
1 parent 17097a8 commit 2a4a57f

File tree

5 files changed

+227
-5
lines changed

5 files changed

+227
-5
lines changed

.github/workflows/ci-e2e.yaml

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
name: e2e-harbor-integration
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
branches:
7+
- master
8+
9+
jobs:
10+
e2e-test:
11+
runs-on: ubuntu-latest
12+
defaults:
13+
run:
14+
shell: nix develop --command bash -v {0}
15+
timeout-minutes: 60
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Install nix
22+
uses: DeterminateSystems/nix-installer-action@main
23+
24+
- name: Start Minikube (Docker driver)
25+
id: minikube
26+
run: |
27+
minikube start --driver=docker --cpus=2 --memory=2G --force
28+
echo "ip=$(minikube ip)" >> "$GITHUB_OUTPUT"
29+
30+
- name: Helm repos
31+
run: |
32+
helm repo add bitnami https://charts.bitnami.com/bitnami
33+
helm repo add sysdig https://charts.sysdig.com
34+
helm repo update
35+
36+
- name: Install Harbor (NodePort)
37+
id: harbor
38+
env:
39+
MINIKUBE_IP: ${{ steps.minikube.outputs.ip }}
40+
run: |
41+
HARBOR_URL="https://${MINIKUBE_IP}:30003"
42+
43+
helm install harbor bitnami/harbor \
44+
--namespace harbor \
45+
--create-namespace \
46+
--set service.type=NodePort \
47+
--set service.nodePorts.http=30002 \
48+
--set service.nodePorts.https=30003 \
49+
--set externalURL="${HARBOR_URL}"
50+
51+
HARBOR_USERNAME=admin
52+
HARBOR_PASSWORD=$(kubectl get secret -n harbor harbor-core-envvars -o jsonpath='{.data.HARBOR_ADMIN_PASSWORD}' | base64 -d)
53+
echo "::add-mask::${HARBOR_PASSWORD}"
54+
55+
echo "url=${HARBOR_URL}" >> "$GITHUB_OUTPUT"
56+
echo "username=${HARBOR_USERNAME}" >> "$GITHUB_OUTPUT"
57+
echo "password=${HARBOR_PASSWORD}" >> "$GITHUB_OUTPUT"
58+
59+
- name: Build adapter image with Nix
60+
run: nix build .#harbor-adapter-docker
61+
62+
- name: Load image into Docker & Minikube
63+
id: image_in_docker
64+
run: |
65+
PULL_STRING=$(docker load -i ./result -q | tail -n1 | cut -d: -f2- | tr -d ' ')
66+
REPOSITORY=$(echo "${PULL_STRING}" | cut -d: -f1)
67+
TAG=$(echo "${PULL_STRING}" | cut -d: -f2)
68+
69+
echo "pull_string=${PULL_STRING}" >> "$GITHUB_OUTPUT"
70+
echo "repository=${REPOSITORY}" >> "$GITHUB_OUTPUT"
71+
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
72+
73+
- name: Load image into Minikube
74+
env:
75+
PULL_STRING: ${{ steps.image_in_docker.outputs.pull_string }}
76+
run: |
77+
minikube image load "${PULL_STRING}"
78+
79+
- name: Deploy Sysdig Harbor scanner (use local image)
80+
env:
81+
REPOSITORY: ${{ steps.image_in_docker.outputs.repository }}
82+
TAG: ${{ steps.image_in_docker.outputs.tag }}
83+
SECURE_API_TOKEN: ${{ secrets.SECURE_API_TOKEN }}
84+
SECURE_URL: ${{ secrets.SECURE_URL }}
85+
run: |
86+
helm install harbor-scanner-sysdig-secure sysdig/harbor-scanner-sysdig-secure \
87+
--wait \
88+
--timeout 300s \
89+
--namespace harbor \
90+
--create-namespace \
91+
--set image.repository=$REPOSITORY \
92+
--set image.tag=$TAG \
93+
--set image.pullPolicy=Never \
94+
--set sysdig.secure.apiToken="$SECURE_API_TOKEN" \
95+
--set sysdig.secure.url="$SECURE_URL" \
96+
--set cliScanning.image="quay.io/sysdig/sysdig-cli-scanner:1.22.6"
97+
98+
- name: Wait for Harbor to be ready
99+
run: |
100+
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=harbor -n harbor --timeout=600s
101+
kubectl get pods -n harbor -o wide
102+
103+
- name: Log in with harbor-cli
104+
env:
105+
HARBOR_URL: ${{ steps.harbor.outputs.url }}
106+
HARBOR_USERNAME: ${{ steps.harbor.outputs.username }}
107+
HARBOR_PASSWORD: ${{ steps.harbor.outputs.password }}
108+
run: |
109+
harbor login "$HARBOR_URL" --username "$HARBOR_USERNAME" --password "$HARBOR_PASSWORD"
110+
111+
- name: Register scanner via Harbor API and set as default
112+
run: |
113+
harbor scanner create \
114+
--name "Sysdig-Local" \
115+
--description "Sysdig Scanner" \
116+
--url "http://harbor-scanner-sysdig-secure.harbor.svc.cluster.local:5000" \
117+
--skip-cert-verification \
118+
--auth None
119+
120+
harbor scanner set-default "Sysdig-Local"
121+
122+
- name: Push sample image
123+
id: image_in_harbor
124+
env:
125+
MINIKUBE_IP: ${{ steps.minikube.outputs.ip }}
126+
HARBOR_URL: ${{ steps.harbor.outputs.url }}
127+
HARBOR_USERNAME: ${{ steps.harbor.outputs.username }}
128+
HARBOR_PASSWORD: ${{ steps.harbor.outputs.password }}
129+
run: |
130+
REPO="alpine"
131+
PROJECT="library"
132+
NEW_TAG="test"
133+
134+
skopeo \
135+
--policy <(echo '{"default":[{"type":"insecureAcceptAnything"}]}') \
136+
copy "docker://${REPO}:latest" \
137+
"docker://${MINIKUBE_IP}:30003/${PROJECT}/${REPO}:${NEW_TAG}" \
138+
--dest-tls-verify=false \
139+
--dest-creds="${HARBOR_USERNAME}:${HARBOR_PASSWORD}"
140+
141+
DIGEST=$(harbor artifact list "${PROJECT}"/"${REPO}" -o json | jq -r .Payload[].digest)
142+
143+
echo "repo=${REPO}" >> "$GITHUB_OUTPUT"
144+
echo "project=${PROJECT}" >> "$GITHUB_OUTPUT"
145+
echo "tag=${NEW_TAG}" >> "$GITHUB_OUTPUT"
146+
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
147+
148+
- name: Trigger scan
149+
env:
150+
PROJECT: ${{ steps.image_in_harbor.outputs.project }}
151+
REPO: ${{ steps.image_in_harbor.outputs.repo }}
152+
DIGEST: ${{ steps.image_in_harbor.outputs.digest }}
153+
run: |
154+
harbor artifact scan start "${PROJECT}/${REPO}@${DIGEST}" -v
155+
156+
- name: Fetch logs from CLI Scanner
157+
run: |
158+
for i in {1..6}; do kubectl get pods -n harbor -l created-by=harbor-scanner-sysdig-secure -o name | grep -q . && break; sleep 5; done
159+
kubectl wait -n harbor --for=condition=ContainersReady pod -l created-by=harbor-scanner-sysdig-secure --timeout=300s
160+
kubectl logs -n harbor -l created-by=harbor-scanner-sysdig-secure --follow
161+
162+
- name: Check if Vulnerability report is generated in Harbor
163+
env:
164+
PROJECT: ${{ steps.image_in_harbor.outputs.project }}
165+
REPO: ${{ steps.image_in_harbor.outputs.repo }}
166+
DIGEST: ${{ steps.image_in_harbor.outputs.digest }}
167+
HARBOR_USERNAME: ${{ steps.harbor.outputs.username }}
168+
HARBOR_PASSWORD: ${{ steps.harbor.outputs.password }}
169+
HARBOR_URL: ${{ steps.harbor.outputs.url }}
170+
run: |
171+
for i in $(seq 1 30); do
172+
REPORT=$(
173+
curl -sk -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" \
174+
-H 'Accept: application/json' \
175+
"${HARBOR_URL}/api/v2.0/projects/${PROJECT}/repositories/${REPO}/artifacts/${DIGEST}/additions/vulnerabilities"
176+
)
177+
178+
GENERATED_AT=$(echo "$REPORT" | jq -r '."application/vnd.security.vulnerability.report; version=1.1".generated_at')
179+
180+
if [ -n "$GENERATED_AT" ] && [ "$GENERATED_AT" != "null" ]; then
181+
echo "Scan completed successfully, vulnerability report is available ✅"
182+
echo "Report details: $REPORT"
183+
exit 0
184+
else
185+
echo "Polling attempt ${i}/30: Vulnerability report not yet available."
186+
fi
187+
188+
sleep 10
189+
done
190+
191+
echo "Scan did not complete in time ❌"
192+
exit 1
193+
- name: Delete cluster
194+
if: always()
195+
run: |
196+
minikube delete || true

docker.nix

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
{ dockerTools, harbor-adapter }:
1+
{
2+
dockerTools,
3+
harbor-adapter,
4+
cacert,
5+
bash,
6+
curl,
7+
coreutils,
8+
}:
29
dockerTools.buildLayeredImage {
310
name = "sysdiglabs/harbor-scanner-sysdig-secure";
411
tag = harbor-adapter.version;
5-
contents = [ harbor-adapter ];
12+
contents = [
13+
harbor-adapter
14+
cacert
15+
];
616

717
# https://github.com/moby/moby/blob/46f7ab808b9504d735d600e259ca0723f76fb164/image/spec/spec.md#image-json-field-descriptions
818
config = {
@@ -11,5 +21,9 @@ dockerTools.buildLayeredImage {
1121
ExposedPorts = {
1222
"5000" = { };
1323
};
24+
Env = [
25+
"SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt"
26+
"NIX_SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt"
27+
];
1428
};
1529
}

flake.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
pre-commit
4343
sd
4444
trivy
45+
minikube
46+
kubernetes-helm
47+
kubectl
48+
skopeo
49+
harbor-cli
4550
];
4651

4752
inputsFrom = [

pkg/scanner/inline_adapter.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.J
129129
ValueFrom: nil,
130130
})
131131
envVars = appendLocalEnvVar(envVars, "NO_PROXY")
132-
cmdString := fmt.Sprintf("/home/nonroot/sysdig-cli-scanner -a %s --skiptlsverify --output-json=output.json ", i.secureURL)
132+
cmdString := fmt.Sprintf("/home/nonroot/sysdig-cli-scanner -a %s --skiptlsverify --console-log --output-json=output.json ", i.secureURL)
133133
// Add skiptlsverify if insecure
134134
if !i.verifySSL {
135135
cmdString += "--skiptlsverify "
@@ -169,11 +169,19 @@ func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.J
169169
return &batchv1.Job{
170170
ObjectMeta: metav1.ObjectMeta{
171171
Name: name,
172+
Labels: map[string]string{
173+
"created-by": "harbor-scanner-sysdig-secure",
174+
},
172175
},
173176
Spec: batchv1.JobSpec{
174177
TTLSecondsAfterFinished: &i.jobTTL,
175178
BackoffLimit: &backoffLimit,
176179
Template: corev1.PodTemplateSpec{
180+
ObjectMeta: metav1.ObjectMeta{
181+
Labels: map[string]string{
182+
"created-by": "harbor-scanner-sysdig-secure",
183+
},
184+
},
177185
Spec: corev1.PodSpec{
178186
RestartPolicy: corev1.RestartPolicyNever,
179187
SecurityContext: podSecurityContext,

pkg/secure/client.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package secure
22

33
import (
4-
"crypto/tls"
54
"encoding/json"
65
"errors"
76
"fmt"
@@ -37,7 +36,7 @@ func NewClient(apiToken string, secureURL string, verifySSL bool) Client {
3736
transport := http.DefaultTransport.(*http.Transport).Clone()
3837

3938
if !verifySSL {
40-
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
39+
transport.TLSClientConfig.InsecureSkipVerify = true
4140
}
4241

4342
return &client{

0 commit comments

Comments
 (0)