Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
permissions:
contents: read
id-token: write
security-events: write

env:
PROJECT_ID: casecomp-495718
Expand All @@ -16,6 +17,8 @@ env:
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.digest.outputs.digest }}
steps:
- uses: actions/checkout@v5

Expand Down Expand Up @@ -44,11 +47,87 @@ jobs:
esac
done

- name: Get image digest
id: digest
run: |
DIGEST=$(gcloud container images describe ${{ env.IMAGE }}:latest \
--project ${{ env.PROJECT_ID }} \
--format='value(image_summary.digest)')
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
echo "Image digest: $DIGEST"

- uses: sigstore/cosign-installer@v3

- name: Sign image (keyless)
run: |
cosign sign --yes \
--oidc-issuer=https://token.actions.githubusercontent.com \
"${{ env.IMAGE }}@${{ steps.digest.outputs.digest }}"

- name: Deploy to Cloud Run
run: |
gcloud run deploy ${{ env.SERVICE }} \
--image ${{ env.IMAGE }} \
--image "${{ env.IMAGE }}@${{ steps.digest.outputs.digest }}" \
--region ${{ env.REGION }} \
--project ${{ env.PROJECT_ID }} \
--port 3000 \
--allow-unauthenticated

- name: Verify signature
run: |
cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity-regexp="github.com/Pyronewbic/casecomp" \
"${{ env.IMAGE }}@${{ steps.digest.outputs.digest }}" || true

scan:
needs: deploy
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/auth@v3
with:
workload_identity_provider: projects/129850122606/locations/global/workloadIdentityPools/github-pool/providers/github-provider
service_account: casecomp-deploy@casecomp-495718.iam.gserviceaccount.com

- uses: google-github-actions/setup-gcloud@v3

- name: Pull image
run: |
gcloud auth configure-docker --quiet
docker pull "${{ env.IMAGE }}@${{ needs.deploy.outputs.digest }}"

- name: Generate SBOM (Syft)
uses: anchore/sbom-action@v0
with:
image: "${{ env.IMAGE }}@${{ needs.deploy.outputs.digest }}"
format: spdx-json
output-file: sbom.spdx.json

- name: Vulnerability scan (Grype)
uses: anchore/scan-action@v6
id: grype
with:
sbom: sbom.spdx.json
fail-build: false
severity-cutoff: critical

- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom-${{ github.sha }}
path: sbom.spdx.json
retention-days: 90

- name: Upload Grype report
if: always()
uses: actions/upload-artifact@v4
with:
name: grype-report-${{ github.sha }}
path: ${{ steps.grype.outputs.sarif }}
retention-days: 90

- name: Upload SARIF to GitHub Security
if: always() && steps.grype.outputs.sarif != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.grype.outputs.sarif }}
5 changes: 4 additions & 1 deletion cloudbuild.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
steps:
- name: gcr.io/kaniko-project/executor:latest
- name: gcr.io/kaniko-project/executor:v1.23.2
args:
- --destination=gcr.io/$PROJECT_ID/casecomp-api:latest
- --destination=gcr.io/$PROJECT_ID/casecomp-api:$SHORT_SHA
- --cache=true
- --cache-ttl=168h
- --reproducible
- --image-name-with-digest-file=/workspace/digest.txt
41 changes: 38 additions & 3 deletions terraform/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ resource "google_project_service" "cloudbuild" {
disable_on_destroy = false
}

resource "google_project_service" "binaryauthorization" {
service = "binaryauthorization.googleapis.com"
disable_on_destroy = false
}

resource "google_project_service" "containeranalysis" {
service = "containeranalysis.googleapis.com"
disable_on_destroy = false
}

# ── Binary Authorization ──────────────────────────────────────

resource "google_binary_authorization_policy" "default" {
global_policy_evaluation_mode = "ENABLE"

default_admission_rule {
evaluation_mode = "ALWAYS_ALLOW"
enforcement_mode = "DRYRUN_AUDIT_LOG_ONLY"
}

depends_on = [google_project_service.binaryauthorization]
}

# ── Firestore ─────────────────────────────────────────────────

resource "google_firestore_database" "default" {
Expand Down Expand Up @@ -137,9 +160,14 @@ resource "google_cloud_run_v2_service" "api" {
}
}

binary_authorization {
use_default = true
}

depends_on = [
google_project_service.run,
google_secret_manager_secret_iam_member.cloud_run_access,
google_binary_authorization_policy.default,
]
}

Expand Down Expand Up @@ -222,7 +250,14 @@ resource "google_cloud_run_v2_service" "site" {
}
}

depends_on = [google_project_service.run]
binary_authorization {
use_default = true
}

depends_on = [
google_project_service.run,
google_binary_authorization_policy.default,
]
}

resource "google_cloud_run_v2_service_iam_member" "site_public" {
Expand Down Expand Up @@ -300,8 +335,8 @@ resource "google_compute_managed_ssl_certificate" "site_cert" {
}

resource "google_compute_target_https_proxy" "api_proxy" {
name = "cardscrapebot-https-proxy"
url_map = google_compute_url_map.api_urlmap.id
name = "cardscrapebot-https-proxy"
url_map = google_compute_url_map.api_urlmap.id
ssl_certificates = [
google_compute_managed_ssl_certificate.api_cert.id,
google_compute_managed_ssl_certificate.site_cert.id,
Expand Down
Loading