Add build-scan-push workflow
Build Docker Image & Security / build-scan-push (push) Successful in 2m21s
Build Docker Image & Security / build-scan-push (push) Successful in 2m21s
This commit is contained in:
@@ -0,0 +1,169 @@
|
|||||||
|
name: Build Docker Image & Security
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
build-scan-push:
|
||||||
|
runs-on: running-man
|
||||||
|
env:
|
||||||
|
# The image is under "homelab" namespace so it can stay private
|
||||||
|
IMAGE: git.sysmd.uk/homelab/sysmd-games:latest
|
||||||
|
COSIGN_VERSION: v3.0.5
|
||||||
|
GRYPE_VERSION: v0.110.0
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Login to registry
|
||||||
|
run: |
|
||||||
|
echo "$REGISTRY_TOKEN" | docker login git.sysmd.uk -u "$REGISTRY_USER" --password-stdin
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
docker build --no-cache -t ${IMAGE} .
|
||||||
|
- name: Install Cosign
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
curl -fLO https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64
|
||||||
|
chmod +x cosign-linux-amd64
|
||||||
|
mv cosign-linux-amd64 /usr/local/bin/cosign
|
||||||
|
- name: Install Grype
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VERSION_NO_V="${GRYPE_VERSION#v}"
|
||||||
|
FILE="grype_${VERSION_NO_V}_linux_amd64.tar.gz"
|
||||||
|
BASE="https://github.com/anchore/grype/releases/download/${GRYPE_VERSION}"
|
||||||
|
|
||||||
|
curl -fLO ${BASE}/${FILE}
|
||||||
|
curl -fLO ${BASE}/grype_${VERSION_NO_V}_checksums.txt
|
||||||
|
curl -fLO ${BASE}/grype_${VERSION_NO_V}_checksums.txt.sig
|
||||||
|
curl -fLO ${BASE}/grype_${VERSION_NO_V}_checksums.txt.pem
|
||||||
|
|
||||||
|
cosign verify-blob \
|
||||||
|
--signature grype_${VERSION_NO_V}_checksums.txt.sig \
|
||||||
|
--certificate grype_${VERSION_NO_V}_checksums.txt.pem \
|
||||||
|
--certificate-identity-regexp "https://github.com/anchore/grype" \
|
||||||
|
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||||
|
grype_${VERSION_NO_V}_checksums.txt
|
||||||
|
|
||||||
|
CHECK=$(grep " ${FILE}$" grype_${VERSION_NO_V}_checksums.txt)
|
||||||
|
[ -n "$CHECK" ] || { echo "Missing checksum"; exit 1; }
|
||||||
|
|
||||||
|
echo "$CHECK" | sha256sum -c -
|
||||||
|
|
||||||
|
tar -xzf ${FILE}
|
||||||
|
mv grype /usr/local/bin/
|
||||||
|
- name: Scan image with Grype
|
||||||
|
id: grype
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
grype ${IMAGE} -o json > grype.json
|
||||||
|
|
||||||
|
echo "Fixable HIGH/CRITICAL vulnerabilities:"
|
||||||
|
jq -r '
|
||||||
|
.matches[]
|
||||||
|
| select(
|
||||||
|
(
|
||||||
|
.vulnerability.severity == "High" or
|
||||||
|
.vulnerability.severity == "Critical"
|
||||||
|
)
|
||||||
|
and
|
||||||
|
(
|
||||||
|
(.vulnerability.fix.versions | length) > 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| "\(.artifact.name)@\(.artifact.version) -> \(.vulnerability.id) [\(.vulnerability.severity)] | fixed: \(.vulnerability.fix.versions[0])"
|
||||||
|
' grype.json
|
||||||
|
|
||||||
|
# Fail only on fixable HIGH/CRITICAL
|
||||||
|
jq -e '
|
||||||
|
[
|
||||||
|
.matches[]
|
||||||
|
| select(
|
||||||
|
(
|
||||||
|
.vulnerability.severity == "High" or
|
||||||
|
.vulnerability.severity == "Critical"
|
||||||
|
)
|
||||||
|
and
|
||||||
|
(
|
||||||
|
(.vulnerability.fix.versions | length) > 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
| length == 0
|
||||||
|
' grype.json
|
||||||
|
- name: Notify Node-RED on vulnerabilities
|
||||||
|
if: steps.grype.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
jq '
|
||||||
|
{
|
||||||
|
repo: "mdaleo404/games",
|
||||||
|
image: "'${IMAGE}'",
|
||||||
|
summary: (
|
||||||
|
"Total: " +
|
||||||
|
(
|
||||||
|
[
|
||||||
|
.matches[]
|
||||||
|
| select(
|
||||||
|
(
|
||||||
|
.vulnerability.severity == "High" or
|
||||||
|
.vulnerability.severity == "Critical"
|
||||||
|
)
|
||||||
|
and
|
||||||
|
(
|
||||||
|
(.vulnerability.fix.versions | length) > 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
] | length | tostring
|
||||||
|
)
|
||||||
|
),
|
||||||
|
vulnerabilities: [
|
||||||
|
.matches[]
|
||||||
|
| select(
|
||||||
|
(
|
||||||
|
.vulnerability.severity == "High" or
|
||||||
|
.vulnerability.severity == "Critical"
|
||||||
|
)
|
||||||
|
and
|
||||||
|
(
|
||||||
|
(.vulnerability.fix.versions | length) > 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| {
|
||||||
|
library: .artifact.name,
|
||||||
|
cve: .vulnerability.id,
|
||||||
|
severity: .vulnerability.severity,
|
||||||
|
installed: .artifact.version,
|
||||||
|
fixed: (.vulnerability.fix.versions[0]),
|
||||||
|
title: .vulnerability.description,
|
||||||
|
url: .vulnerability.dataSource
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
' grype.json \
|
||||||
|
| curl -s -X POST https://nodered.sysmd.uk/vulns-alert \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @-
|
||||||
|
- name: Fail workflow if vulnerabilities found
|
||||||
|
if: steps.grype.outcome == 'failure'
|
||||||
|
run: exit 1
|
||||||
|
- name: Push image
|
||||||
|
if: steps.grype.outcome != 'failure'
|
||||||
|
run: |
|
||||||
|
docker push ${IMAGE}
|
||||||
|
- name: Deploy to server
|
||||||
|
if: steps.grype.outcome != 'failure'
|
||||||
|
uses: appleboy/ssh-action@v1.2.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.DEPLOYMENT_HOST }}
|
||||||
|
username: ${{ secrets.DEPLOYMENT_USER }}
|
||||||
|
key: ${{ secrets.DEPLOYMENT_SSH_KEY }}
|
||||||
|
port: ${{ secrets.DEPLOYMENT_SSH_PORT }}
|
||||||
|
script: |
|
||||||
|
sudo docker compose -f /opt/sysmd-games/docker-compose.yml pull && \
|
||||||
|
sudo docker compose -f /opt/sysmd-games/docker-compose.yml up -d
|
||||||
|
host_key: 'false'
|
||||||
Reference in New Issue
Block a user