Java CI with Maven #129
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time | |
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven | |
name: Java CI with Maven | |
on: | |
#push: | |
#branches: [ main ] | |
#pull_request: | |
#branches: [ main ] | |
workflow_dispatch: | |
jobs: | |
build: | |
permissions: | |
security-events: write | |
id-token: write | |
contents: read | |
attestations: write | |
packages: write | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
java: [ '17' ] | |
# outputs: | |
# my_output: ${{ steps.generate_sbom.outputs.result }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up JDK ${{ matrix.java }} | |
uses: actions/setup-java@v4 | |
with: | |
java-version: ${{ matrix.java }} | |
distribution: 'adopt' | |
cache: maven | |
- name: Build with Maven Wrapper | |
run: | | |
pwd | |
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) | |
# echo "$(date +"%d.%m.%Y").$BRANCH_NAME.$GITHUB_RUN_NUMBER" | |
RESULT=$(date +"%d.%m.%Y").$BRANCH_NAME.$GITHUB_RUN_NUMBER | |
echo $RESULT | |
./mvnw -B package > app.jar | |
# - name: Rego Policy | |
# run: | | |
# cat <<EOF > policy.rego | |
# package signature | |
# allow[msg] { | |
# input.Data.author_name != "saurav631" | |
# msg := sprintf("Invalid Git Author: %v", [input.Data]) | |
# } | |
# allow[msg] { | |
# input.Data.branch != "main" | |
# msg := sprintf("Invalid branch name: %v", [input.Data]) | |
# } | |
# EOF | |
# # - uses: actions/checkout@v4 | |
# # with: | |
# # fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis | |
# - name: Read Rego Policy | |
# run: | | |
# cat policy.rego | |
- name: Generate artifact attestation | |
uses: actions/attest-build-provenance@v1 | |
# outputs: | |
# Attestation: | |
with: | |
subject-path: ./app.jar | |
- name: verify jar attesttion | |
env: | |
GH_TOKEN: ${{secrets.GH_TOKEN}} | |
run: | | |
# gh attestation download app.jar -o github | |
# wget https://github.com/saurav631/spring-petclinic/attestations/1598170/download -o sigstore.json | |
# gh attestation verify github --owner saurav631 --bundle sigstore.json | |
# gh attestation verify app.jar --owner saurav631 --bundle ./saurav631-spring-petclinic-attestation-1598170.sigstore.json --format=json | |
gh attestation verify app.jar --owner saurav631 --format=json | jq . | |
# gh attestation verify app.jar --owner saurv631 --bundle application/vnd.dev.sigstore.bundle.v0.3+json --format=json | |
# gh attestation verify sigstore.json --owner saurav631 --format=json | |
# - name: Set up JDK 17 | |
# uses: actions/setup-java@v1 | |
# with: | |
# java-version: 17 | |
# - name: Cache SonarQube packages | |
# uses: actions/cache@v1 | |
# with: | |
# path: ~/.sonar/cache | |
# key: ${{ runner.os }}-sonar | |
# restore-keys: ${{ runner.os }}-sonar | |
# - name: Cache Maven packages | |
# uses: actions/cache@v1 | |
# with: | |
# path: ~/.m2 | |
# key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} | |
# restore-keys: ${{ runner.os }}-m2 | |
# - name: Build and analyze | |
# env: | |
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
# SONAR_HOST_URL: http://20.235.200.36:9000/ | |
# run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=PetClinic -Dsonar.projectName='PetClinic' | |
# - name: Check SonarQube Quality Gate | |
# run: | | |
# sleep 10 | |
# STATUS=$(curl -s -u ${{ secrets.SONAR_TOKEN }} "http://20.235.200.36:9000/api/qualitygates/project_status?projectKey=PetClinic" | jq -r '.projectStatus.status') | |
# echo "Quality Gate Status: $STATUS" | |
# if [ "$STATUS" != "OK" ]; then | |
# echo "SonarQube Quality Gate failed" | |
# exit 1 | |
# fi | |
# - name: Install & Running TruffleHog | |
# run: | | |
# pip install truffleHog | |
# trufflehog git_url https://github.com/saurav631/spring-petclinic/ --repo_path . | |
- name: Install Syft | |
run: | | |
curl -sSL https://github.com/anchore/syft/releases/download/v0.66.0/syft_0.66.0_linux_amd64.tar.gz | tar xz -C /usr/local/bin syft | |
- name: Generate SBOM with Syft | |
# id: generate_sbom | |
run: | | |
# echo "result=`syft dir:. -o cyclonedx-json > sbom.json`" >> $GITHUB_ENV | |
syft dir:. -o cyclonedx-json > sbom.json | |
- name: Upload SBOM | |
uses: actions/upload-artifact@v4 | |
with: | |
name: sbom | |
path: sbom.json | |
- name: Generate SBOM attestation | |
uses: actions/attest-sbom@v1 | |
with: | |
subject-path: ./sbom.json | |
sbom-path: ./sbom.json | |
- name: verify SBOM attesttion | |
env: | |
GH_TOKEN: ${{secrets.GH_TOKEN}} | |
run: | | |
# gh attestation verify sbom.json --owner saurav631 --bundle ./sbom.json | |
gh attestation verify ./sbom.json --owner saurav631 --format=json | jq . | |
# gh attestation verify app.jar --owner saurav631 --format=json | |
# gh attestation verify sbom.json --owner saurav631 --bundle ./saurav631-spring-petclinic-attestation-1599265.sigstore.json | |
# - name: Dependency Scanning with OWASP Dependency-Check | |
# run: | | |
# /opt/dependency-check/bin/dependency-check.sh --project my_project --out dependency-check-report --scan . | |
# - name: Upload Dependency Check Report | |
# uses: actions/upload-artifact@v3 | |
# with: | |
# name: dependency-check-report | |
# path: dependency-check-report | |
# - name: Scan Docker Images for Vulnerabilities | |
# run: | | |
# sudo apt-get update | |
# sudo apt-get install -y docker.io | |
# sudo systemctl start docker | |
# sudo systemctl enable docker | |
# curl -sSL https://github.com/aquasecurity/trivy/releases/download/v0.36.0/trivy_0.36.0_Linux-64bit.deb -o trivy.deb | |
# sudo dpkg -i trivy.deb | |
# trivy image my_microservice:latest | |
# - name: Deploy Microservices | |
# run: | | |
# kubectl apply -f k8s/deployment.yaml | |
- name: Build Docker Image | |
run: ./mvnw spring-boot:build-image | |
- name: Log in to GitHub Container Registry | |
uses: docker/login-action@v2 | |
with: | |
# username: ${{ secrets.CR_Username }} | |
username: ${{ github.actor }} | |
# password: ${{ secrets.CR_Password }} | |
password: ${{secrets.GH_TOKEN}} | |
registry: ghcr.io | |
- name: Tag Docker Image | |
run: | | |
docker tag docker.io/library/spring-petclinic:3.3.0-SNAPSHOT ghcr.io/saurav631/spring-petclinic:latest | |
- name: Push Docker Image | |
run: | | |
docker push ghcr.io/saurav631/spring-petclinic:latest | |
- name: Run Trivy Vulnerability Scanner | |
uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe | |
with: | |
image-ref: 'ghcr.io/saurav631/spring-petclinic:latest' | |
format: 'template' | |
template: '@/contrib/sarif.tpl' | |
output: 'trivy-results.sarif' | |
severity: 'CRITICAL,HIGH' | |
- name: Upload Trivy Scan Results to GitHub Security Tab | |
uses: github/codeql-action/upload-sarif@v3 | |
with: | |
sarif_file: 'trivy-results.sarif' | |
sign: | |
name: Sign Container Image | |
runs-on: ubuntu-latest | |
needs: build | |
steps: | |
- name: Check Out Source Code | |
uses: actions/checkout@v3 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v2 | |
- name: Download file artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: sbom | |
- name: Log in to GitHub Container Registry | |
uses: docker/login-action@v2 | |
with: | |
# username: ${{ secrets.CR_Username }} | |
username: ${{github.actor}} | |
# password: ${{ secrets.CR_Password }} | |
password: ${{secrets.GH_TOKEN}} | |
registry: ghcr.io | |
# - name: Install Cosign CLI | |
# run: | | |
# curl -sSL -o /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.1.1/cosign-linux-amd64 | |
# chmod +x /usr/local/bin/cosign | |
# cosign version | |
- name: Install Cosign CLI | |
run: | | |
curl -sSL -o /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.2.3/cosign-linux-amd64 | |
chmod +x /usr/local/bin/cosign | |
cosign version | |
- name: Cosign Help | |
run: | | |
cosign verify-attestation --help | |
# - name: Create Decrypted Signing Key File | |
# run: | | |
# echo "${{ secrets.COSIGN_KEY }}" > cosign.key | |
# - name: Sign Docker Image with Cosign | |
# run: | | |
# cosign sign --key cosign.key ghcr.io/saurav631/spring-petclinic:latest | |
- name: sign container image | |
run: | | |
cosign sign --key env://COSIGN_KEY ghcr.io/saurav631/spring-petclinic:latest --yes | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
# - name: Create Public Key File_ | |
# run: echo "${{ secrets.Cosign_Pub }}" > cosign.pub | |
- name: Public Key Creation | |
run: | | |
cosign public-key --key env://COSIGN_KEY > cosign.pub | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Verify Docker Image Signature | |
run: | | |
cosign verify --key cosign.pub ghcr.io/saurav631/spring-petclinic:latest | jq . #--insecure-ignore-tlog #--check-claims=false | |
- name: Attach SBOM | |
id: attach-sbom | |
run: | | |
set -e # Exit on error | |
# Run the cosign attach sbom command and capture its output | |
# echo "sbom: ${{ needs.build.outputs.my_output }}" | |
# output=$(cosign attach sbom --sbom ${{ needs.build.outputs.my_output }} ghcr.io/saurav631/spring-petclinic:latest) || { echo "Failed to run cosign attach sbom"; exit 1; } | |
# output=$(cosign attach sbom --sbom sbom.json ghcr.io/saurav631/spring-petclinic:latest) || { echo "Failed to run cosign attach sbom"; exit 1; } | |
output=$(cosign attach sbom --sbom sbom.json ghcr.io/saurav631/spring-petclinic:latest 2>&1) || { echo "Failed to run cosign attach sbom"; exit 1; } | |
# output=$(cosign attach sbom --sbom sbom.json ghcr.io/saurav631/spring-petclinic:latest) || { echo "Failed to run cosign attach sbom"; exit 1; } | |
# cosign attest --predicate <FILE> --type <TYPE> --key cosign.key <IMAGE> | |
echo "cat output --> showing output below" | |
echo "output: $output" | |
# Extract the SHA from the output | |
sbom_filename=$(echo "$output" | grep -oP 'sha256-[\w\d]+\.sbom') || { echo "Failed to extract SBOM filename"; exit 1; } | |
echo "sbom_filename: $sbom_filename" | |
# Check if the extraction was successful | |
if [ -z "$sbom_filename" ]; then | |
echo "Error: No SBOM filename found in the output" | |
exit 1 | |
fi | |
# Set the extracted SHA as an environment variable | |
echo "SBOM_FILENAME=$sbom_filename" >> $GITHUB_ENV | |
- name: Sign the SBOM attached with the Image | |
run: | | |
set -e # Exit on error | |
# Use the environment variable to construct the command | |
if [ -z "$SBOM_FILENAME" ]; then | |
echo "Error: SBOM_FILENAME environment variable is not set" | |
exit 1 | |
fi | |
echo "Signing SBOM with filename: $SBOM_FILENAME" | |
cosign sign --key env://COSIGN_KEY ghcr.io/saurav631/spring-petclinic:$SBOM_FILENAME --yes || { echo "Failed to sign with SBOM"; exit 1; } | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Verify Sign of SBOM attached with the Image | |
run: | | |
set -e # Exit on error | |
cosign verify --key cosign.pub ghcr.io/saurav631/spring-petclinic:$SBOM_FILENAME | jq . || { echo "Failed to verify SBOM Sign"; exit 1; } | |
- name: Directly attest SBOM with the Image | |
run: | | |
set -e # Exit on error | |
# output=$(cosign attest --key env://COSIGN_KEY --type cyclonedx --predicate sbom.json ghcr.io/saurav631/spring-petclinic:latest --yes 2>&1) || { echo "Failed to attest sbom with the image"; exit 1; } | |
output=$(cosign attest --key env://COSIGN_KEY --predicate sbom.json ghcr.io/saurav631/spring-petclinic:latest --yes 2>&1) || { echo "Failed to attest sbom with the image"; exit 1; } | |
echo "output: $output" | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Verfiy the attested Image | |
run: | | |
set -e # Exit on error | |
# output1=$(cosign verify-attestation --key cosign.pub ghcr.io/saurav631/spring-petclinic:latest | jq -r .payload | base64 -D | jq . 2>&1) || { echo "Failed to verify the attested image"; exit 1; } | |
output1=$(cosign verify-attestation --key cosign.pub ghcr.io/saurav631/spring-petclinic:latest | jq . 2>&1) || { echo "Failed to verify the attested image"; exit 1; } | |
echo "output: $output1" | |
# - name: Sign SBOM | |
# run: | | |
# # set -e # Exit on error | |
# cosign sign-blob --yes --key env://COSIGN_KEY sbom.json --bundle cosign.bundle 2>&1 #| jq -r .payload | base64 --decode | jq . 2>&1 #|| { echo "Failed to sign the blob"; exit 1; } | |
# shell: bash | |
# env: | |
# COSIGN_KEY: ${{secrets.Cosign_Key}} | |
# COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
# - name: Push Signed SBOM to Registry | |
# run: | | |
# # set -e # Exit on error | |
# docker push ghcr.io/saurav631/sbom.json | |
- name: upload Signed SBOM to Github Registry | |
run: | | |
# set -e # Exit on error | |
# output2=$(cosign upload blob -f cosign.bundle ghcr.io/saurav631/spring-petclinic:latest 2>&1) #| jq .) #|| { echo "Failed to upload the blob to the Registry"; exit 1; } | |
# output2=$(cosign upload blob -f sbom.json ghcr.io/saurav631/spring-petclinic:latest 2>&1) #| jq .) #|| { echo "Failed to upload the blob to the Registry"; exit 1; } | |
output2=$(cosign upload blob -f sbom.json ghcr.io/saurav631/sbom 2>&1) #| jq .) | |
echo "output: $output2" | |
sbom_file=$(echo "$output2" | grep -oP 'sha256:[\w\d]{64}' | head -n 1) || { echo "Failed to extract SBOM filename"; exit 1; } | |
echo "sbom_file: $sbom_file" | |
# Set the extracted SHA as an environment variable | |
# echo "SBOM_FILE=$sbom_file" >> $GITHUB_ENV | |
- name: Sign the SBOM file using github url | |
run: | | |
cosign sign --key env://COSIGN_KEY ghcr.io/saurav631/sbom --yes | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Verify the signature of the SBOM file | |
run: | | |
cosign verify --key cosign.pub ghcr.io/saurav631/sbom | |
# - name: Download SBOM Digest Locally | |
# run: | | |
# # set -e # Exit on error | |
# curl -L ghcr.io/saurav631/demo/sbom.json/blobs/$SBOM_FILE | |
# - name: Verify SBOM Uploaded to Image Repo | |
# run: | | |
# cosign verify-blob sbom.json --bundle cosign.bundle --certificate-github-workflow-name=.github/workflows/maven-build.yml@refs/heads/main --certificate-github-workflow-ref=refs/heads/main --certificate-github-workflow-repository=https://github.com/saurav631/spring-petclinic --certificate-github-workflow-trigger=workflow_dispatch --key cosign.pub 2>&1 #| jq -r .payload | base64 --decode | jq . | |
# # shell: bash | |
# # env: | |
# # COSIGN_KEY: ${{secrets.Cosign_Key}} | |
# # COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Fetch commit details | |
run: | | |
# Get the commit ID | |
COMMIT_ID=$(git rev-parse HEAD) | |
# Get the branch name | |
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) | |
# Get the author name and email | |
AUTHOR_NAME=$(git log -1 --pretty=format:'%an') | |
AUTHOR_EMAIL=$(git log -1 --pretty=format:'%ae') | |
# Get the pull request number from GitHub context if it's a PR | |
if [ -n "${{ github.event.pull_request.number }}" ]; then | |
PR_NUMBER="${{ github.event.pull_request.number }}" | |
else | |
PR_NUMBER="Not applicable" | |
fi | |
# Get the GitHub repository URL | |
REPO_URL="https://github.com/${{ github.repository }}" | |
# echo "Commit ID: $COMMIT_ID" | |
# echo "Branch Name: $BRANCH_NAME" | |
# echo "Author Name: $AUTHOR_NAME" | |
# echo "Author Email: $AUTHOR_EMAIL" | |
# echo "Pull Request Number: $PR_NUMBER" | |
# echo "GitHub URL: $REPO_URL" | |
# Create JSON output | |
OUTPUT=$(jq -n \ | |
--arg commit_id "$COMMIT_ID" \ | |
--arg author_name "$AUTHOR_NAME" \ | |
--arg author_email "$AUTHOR_EMAIL" \ | |
--arg branch "$BRANCH_NAME" \ | |
--arg uri "$REPO_URL" \ | |
'{ | |
commit_id: $commit_id, | |
author_name: $author_name, | |
author_email: $author_email, | |
branch: $branch, | |
uri: $uri | |
}') | |
# Save JSON output to a file | |
echo "$OUTPUT" > output.json | |
# Print the contents of the JSON file | |
cat output.json | |
- name: Upload JSON file | |
uses: actions/upload-artifact@v3 | |
with: | |
name: git-details | |
path: output.json | |
last_job: | |
permissions: | |
security-events: write | |
id-token: write | |
contents: read | |
attestations: write | |
packages: write | |
runs-on: ubuntu-latest | |
needs: sign | |
# strategy: | |
# matrix: | |
# java: [ '17' ] | |
steps: | |
- name: Check Out Source Code | |
uses: actions/checkout@v3 | |
- name: Download output.json file artifact | |
uses: actions/download-artifact@v3 | |
with: | |
name: git-details | |
- name: Log in to GitHub Container Registry | |
uses: docker/login-action@v2 | |
with: | |
username: ${{github.actor}} | |
password: ${{secrets.GH_TOKEN}} | |
registry: ghcr.io | |
- name: Install Cosign CLI | |
run: | | |
curl -sSL -o /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.2.3/cosign-linux-amd64 | |
chmod +x /usr/local/bin/cosign | |
cosign version | |
- name: Public Key Creation | |
run: | | |
cosign public-key --key env://COSIGN_KEY > cosign.pub | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Attestation using Predicate | |
run: | | |
# cosign attest --key env://COSIGN_KEY --predicate output.json ghcr.io/saurav631/spring-petclinic:latest --yes | |
cosign attest --key env://COSIGN_KEY --predicate output.json ghcr.io/saurav631/sbom --yes | |
# $(cosign attest --key env://COSIGN_KEY --predicate output.json ghcr.io/saurav631/spring-petclinic:latest --yes 2>&1) || { echo "Failed to attest predicate with the image"; exit 1; } | |
# $(cat output.json | cosign attest --predicate - ghcr.io/saurav631/spring-petclinic:latest --yes 2>&1) || { echo "Failed to attest predicate with the image"; exit 1; } | |
shell: bash | |
env: | |
COSIGN_KEY: ${{secrets.Cosign_Key}} | |
COSIGN_PASSWORD: ${{secrets.Cosign_Key_Password}} | |
- name: Rego Policy | |
run: | | |
cat <<EOF > policy.rego | |
package signature | |
allow[msg] { | |
input.Data.author_name != "saurav631" | |
msg := sprintf("Invalid Git Author: %v", [input.Data]) | |
} | |
allow[msg] { | |
input.Data.branch != "main" | |
msg := sprintf("Invalid branch name: %v", [input.Data]) | |
} | |
- name: Read Rego Policy | |
run: | | |
cat policy.rego | |
- name: Verify Predicate Attestation | |
run: | | |
cosign verify-attestation --policy policy.rego --certificate-github-workflow-name=.github/workflows/maven-build.yml@refs/heads/main --certificate-github-workflow-ref=refs/heads/main --certificate-github-workflow-repository=https://github.com/saurav631/spring-petclinic --certificate-github-workflow-trigger=workflow_dispatch --key cosign.pub ghcr.io/saurav631/sbom 2>&1 #| jq -r .payload | base64 --decode | jq . | |
# cosign verify-attestation --certificate-github-workflow-name=.github/workflows/maven-build.yml@refs/heads/main --certificate-github-workflow-ref=refs/heads/main --certificate-github-workflow-repository=https://github.com/saurav631/spring-petclinic --certificate-github-workflow-trigger=workflow_dispatch --key cosign.pub ghcr.io/saurav631/sbom | jq -r .payload | base64 --decode | jq . | |
# cosign verify-attestation --certificate-github-workflow-name=.github/workflows/maven-build.yml@refs/heads/main --certificate-github-workflow-ref=refs/heads/main --certificate-github-workflow-repository=https://github.com/saurav631/spring-petclinic --certificate-github-workflow-trigger=workflow_dispatch --key cosign.pub ghcr.io/saurav631/spring-petclinic:latest | jq -r .payload | base64 --decode | jq . | |
# $(cosign verify-attestation --key cosign.pub ghcr.io/saurav631/spring-petclinic:latest 2>&1) || { echo "Failed to verify the attested image"; exit 1; } |