From 5e7f7686ac8f1b24152c509babc0a932613d9e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98ystein=20Thuen?= Date: Fri, 1 Mar 2024 11:53:14 +0100 Subject: [PATCH] copy --- .github/workflows/cdeploy.yaml | 181 ++++++++++++++++++++------------- .github/workflows/deploy.yaml | 134 ++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 70 deletions(-) create mode 100644 .github/workflows/deploy.yaml diff --git a/.github/workflows/cdeploy.yaml b/.github/workflows/cdeploy.yaml index fda298b3..ccfef944 100644 --- a/.github/workflows/cdeploy.yaml +++ b/.github/workflows/cdeploy.yaml @@ -1,4 +1,4 @@ -name: Deploy to kubernetes +name: Build docker image, run CodeQL and scan for vulnerabilities on: workflow_call: @@ -12,43 +12,84 @@ on: required: true type: string environment: - description: 'Environment to deploy to. Must be from dev, test or prod' + description: 'Github Environment.' required: true type: string - AZURE_CLIENT_ID: - description: 'ClientId of a service principal that can deploy to AKS.' + dockerfile: + description: 'Path to Dockerfile.' + required: true + type: string + dockerBuildContext: + description: 'Docker build context. It is the working directory needed to build the dockerfile. Defaults to the directory with the Dockerfile.' required: false - default: '' type: string - AZURE_TENANT_ID: - description: 'TenantId of a service principal that can deploy to AKS.' + languages: + description: 'List of languages to run CodeQL on, As JSON array. Defaults to ["csharp"]. The supported languages are c-cpp, csharp, go, java-kotlin, javascript-typescript, python, ruby, swift.' required: false - default: '' + default: '["csharp"]' type: string - AKS_SUBSCRIPTION_ID: - description: 'Subscription ID of AKS to deploy to.' + severity: + description: 'Severity levels to scan for. See https://github.com/aquasecurity/trivy-action?tab=readme-ov-file#inputs for more information.' + required: false + default: 'CRITICAL,HIGH' + type: string + AZURE_CLIENT_ID: + description: 'ClientId of a service principal that can push to Container Registry.' required: false default: '' type: string - AKS_CLUSTER_NAME: - description: 'Subscription ID of AKS to deploy to.' + AZURE_TENANT_ID: + description: 'TenantId of a service principal that can push to Azure Container Registry.' required: false default: '' type: string - AKS_RESOURCE_GROUP: - description: 'Subscription ID of AKS to deploy to.' + ACR_SUBSCRIPTION_ID: + description: 'Subscription ID of the Azure Container Registry to push to.' required: false default: '' type: string + ACR_NAME: + description: 'Name of the Azure Container Registry to push to.' + required: false + default: containerregistryelvia + type: string env: image_tag: ${{ github.sha }}-${{ github.run_number }} + image_full_name: containerregistryelvia.azurecr.io/${{ inputs.namespace }}-${{ inputs.name }}:${{ github.sha }}-${{ github.run_number }} jobs: - deploy_kubernetes: - name: Deploy to ${{ inputs.environment}} + analyze: + name: CodeQL Analyze + runs-on: 'ubuntu-latest' + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + language: ${{ fromJSON(inputs.languages) }} + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + + build: + name: Build and Scan runs-on: ubuntu-latest - environment: ${{ inputs.environment}} + environment: ${{ inputs.environment }} steps: - name: Parse input run: | @@ -64,71 +105,71 @@ jobs: else echo "AZURE_TENANT_ID=${{ inputs.AZURE_TENANT_ID}}" >> "$GITHUB_ENV" fi - if [ -z "${{ inputs.AKS_SUBSCRIPTION_ID}}" ] - then - echo "AKS_SUBSCRIPTION_ID=${{ vars.AKS_SUBSCRIPTION_ID}}" >> "$GITHUB_ENV" - else - echo "AKS_SUBSCRIPTION_ID=${{ inputs.AKS_SUBSCRIPTION_ID}}" >> "$GITHUB_ENV" - fi - if [ -z "${{ inputs.AKS_CLUSTER_NAME}}" ] + if [ -z "${{ inputs.ACR_SUBSCRIPTION_ID}}" ] then - echo "AKS_CLUSTER_NAME=${{ vars.AKS_CLUSTER_NAME}}" >> "$GITHUB_ENV" + echo "ACR_SUBSCRIPTION_ID=${{ vars.ACR_SUBSCRIPTION_ID}}" >> "$GITHUB_ENV" else - echo "AKS_CLUSTER_NAME=${{ inputs.AKS_CLUSTER_NAME}}" >> "$GITHUB_ENV" + echo "ACR_SUBSCRIPTION_ID=${{ inputs.ACR_SUBSCRIPTION_ID}}" >> "$GITHUB_ENV" fi - if [ -z "${{ inputs.AKS_RESOURCE_GROUP}}" ] + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build image + run: | + if [ -z "${{ inputs.dockerBuildContext}}" ] then - echo "AKS_RESOURCE_GROUP=${{ vars.AKS_RESOURCE_GROUP}}" >> "$GITHUB_ENV" + dir=`dirname ${{ inputs.dockerfile}}` # default to the directory of the Dockerfile else - echo "AKS_RESOURCE_GROUP=${{ inputs.AKS_RESOURCE_GROUP}}" >> "$GITHUB_ENV" + dir=${{ inputs.dockerBuildContext}} fi - - name: checkout repository - uses: actions/checkout@v4 + docker build --tag ${{ env.image_full_name}} -f ${{ inputs.dockerfile}} $dir - - name: authenticate with Azure - uses: azure/login@v1 + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master with: - client-id: ${{ env.AZURE_CLIENT_ID}} - tenant-id: ${{ env.AZURE_TENANT_ID}} - subscription-id: ${{ env.AKS_SUBSCRIPTION_ID}} + image-ref: ${{ env.image_full_name}} + format: 'table' + exit-code: '1' + ignore-unfixed: true + severity: ${{ inputs.severity }} + github-pat: ${{ secrets.GITHUB_TOKEN }} + if: github.event_name == 'pull_request' - - name: setup kubelogin - uses: azure/use-kubelogin@v1 + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master with: - kubelogin-version: 'v0.0.24' + image-ref: ${{ env.image_full_name}} + format: 'sarif' + template: '@/contrib/sarif.tpl' + output: trivy.sarif + severity: ${{ inputs.severity }} + github-pat: ${{ secrets.GITHUB_TOKEN }} + ignore-unfixed: true + if: github.event_name == 'push' - - name: set AKS context - uses: azure/aks-set-context@v3 - with: - cluster-name: ${{ env.AKS_CLUSTER_NAME }} - resource-group: ${{ env.AKS_RESOURCE_GROUP }} - admin: 'false' - use-kubelogin: 'true' - subscription: ${{ env.AKS_SUBSCRIPTION_ID }} + # GitHub Security tab does not support SARIF files with `git::` or `https:/` URL's: + # https://github.com/aquasecurity/trivy/issues/5003#issuecomment-1780415058 + - name: Fix Trivy output + run: sed -i 's#git::https:/##g' "trivy.sarif" + if: github.event_name == 'push' - - name: helm deploy - run: | - helm repo add elvia-charts https://raw.githubusercontent.com/3lvia/kubernetes-charts/master - helm repo update - helm upgrade --debug --install -n ${{ inputs.namespace}} -f CI/values.yaml ${{ inputs.name}} elvia-charts/elvia-deployment \ - --set environment=dev --set image.tag=${{ env.image_tag }} --set labels.repositoryName=${{ github.repository }} --set labels.commitHash=${{ github.sha }} + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: trivy.sarif + category: 'Trivy' + if: github.event_name == 'push' - - name: rollout status - run: | - kubectl -n ${{ inputs.namespace}} rollout status deploy ${{ inputs.name}} + - name: Authenticate with Azure + uses: azure/login@v1 + with: + client-id: ${{ env.AZURE_CLIENT_ID }} + tenant-id: ${{ env.AZURE_TENANT_ID }} + subscription-id: ${{ env.ACR_SUBSCRIPTION_ID }} - - name: get events - continue-on-error: true - run: | - kubectl -n ${{ inputs.namespace}} get events --sort-by='.lastTimestamp' |grep ${{ inputs.name}} || true - if: always() + - name: Login ACR + run: az acr login --name ${{ inputs.ACR_NAME }} - - name: set grafana annotations - uses: 3lvia/core-github-actions-templates/post-grafana-annotations.yaml - with: - title: 'Event - Deploy ${{job.status}}' - description: 'description' - app: '${{input.name}}' - system: '${{input.namespace}}' - env: 'dev' - event: 'deploy' + - name: Push image + run: docker push ${{ env.image_full_name}} diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 00000000..fda298b3 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,134 @@ +name: Deploy to kubernetes + +on: + workflow_call: + inputs: + name: + description: 'Name of application. Do not include namespace.' + required: true + type: string + namespace: + description: 'Namespace or system of the application.' + required: true + type: string + environment: + description: 'Environment to deploy to. Must be from dev, test or prod' + required: true + type: string + AZURE_CLIENT_ID: + description: 'ClientId of a service principal that can deploy to AKS.' + required: false + default: '' + type: string + AZURE_TENANT_ID: + description: 'TenantId of a service principal that can deploy to AKS.' + required: false + default: '' + type: string + AKS_SUBSCRIPTION_ID: + description: 'Subscription ID of AKS to deploy to.' + required: false + default: '' + type: string + AKS_CLUSTER_NAME: + description: 'Subscription ID of AKS to deploy to.' + required: false + default: '' + type: string + AKS_RESOURCE_GROUP: + description: 'Subscription ID of AKS to deploy to.' + required: false + default: '' + type: string + +env: + image_tag: ${{ github.sha }}-${{ github.run_number }} + +jobs: + deploy_kubernetes: + name: Deploy to ${{ inputs.environment}} + runs-on: ubuntu-latest + environment: ${{ inputs.environment}} + steps: + - name: Parse input + run: | + if [ -z "${{ inputs.AZURE_CLIENT_ID}}" ] + then + echo "AZURE_CLIENT_ID=${{ vars.AZURE_CLIENT_ID}}" >> "$GITHUB_ENV" + else + echo "AZURE_CLIENT_ID=${{ inputs.AZURE_CLIENT_ID}}" >> "$GITHUB_ENV" + fi + if [ -z "${{ inputs.AZURE_TENANT_ID}}" ] + then + echo "AZURE_TENANT_ID=${{ vars.AZURE_TENANT_ID}}" >> "$GITHUB_ENV" + else + echo "AZURE_TENANT_ID=${{ inputs.AZURE_TENANT_ID}}" >> "$GITHUB_ENV" + fi + if [ -z "${{ inputs.AKS_SUBSCRIPTION_ID}}" ] + then + echo "AKS_SUBSCRIPTION_ID=${{ vars.AKS_SUBSCRIPTION_ID}}" >> "$GITHUB_ENV" + else + echo "AKS_SUBSCRIPTION_ID=${{ inputs.AKS_SUBSCRIPTION_ID}}" >> "$GITHUB_ENV" + fi + if [ -z "${{ inputs.AKS_CLUSTER_NAME}}" ] + then + echo "AKS_CLUSTER_NAME=${{ vars.AKS_CLUSTER_NAME}}" >> "$GITHUB_ENV" + else + echo "AKS_CLUSTER_NAME=${{ inputs.AKS_CLUSTER_NAME}}" >> "$GITHUB_ENV" + fi + if [ -z "${{ inputs.AKS_RESOURCE_GROUP}}" ] + then + echo "AKS_RESOURCE_GROUP=${{ vars.AKS_RESOURCE_GROUP}}" >> "$GITHUB_ENV" + else + echo "AKS_RESOURCE_GROUP=${{ inputs.AKS_RESOURCE_GROUP}}" >> "$GITHUB_ENV" + fi + - name: checkout repository + uses: actions/checkout@v4 + + - name: authenticate with Azure + uses: azure/login@v1 + with: + client-id: ${{ env.AZURE_CLIENT_ID}} + tenant-id: ${{ env.AZURE_TENANT_ID}} + subscription-id: ${{ env.AKS_SUBSCRIPTION_ID}} + + - name: setup kubelogin + uses: azure/use-kubelogin@v1 + with: + kubelogin-version: 'v0.0.24' + + - name: set AKS context + uses: azure/aks-set-context@v3 + with: + cluster-name: ${{ env.AKS_CLUSTER_NAME }} + resource-group: ${{ env.AKS_RESOURCE_GROUP }} + admin: 'false' + use-kubelogin: 'true' + subscription: ${{ env.AKS_SUBSCRIPTION_ID }} + + - name: helm deploy + run: | + helm repo add elvia-charts https://raw.githubusercontent.com/3lvia/kubernetes-charts/master + helm repo update + helm upgrade --debug --install -n ${{ inputs.namespace}} -f CI/values.yaml ${{ inputs.name}} elvia-charts/elvia-deployment \ + --set environment=dev --set image.tag=${{ env.image_tag }} --set labels.repositoryName=${{ github.repository }} --set labels.commitHash=${{ github.sha }} + + - name: rollout status + run: | + kubectl -n ${{ inputs.namespace}} rollout status deploy ${{ inputs.name}} + + - name: get events + continue-on-error: true + run: | + kubectl -n ${{ inputs.namespace}} get events --sort-by='.lastTimestamp' |grep ${{ inputs.name}} || true + if: always() + + - name: set grafana annotations + uses: 3lvia/core-github-actions-templates/post-grafana-annotations.yaml + with: + title: 'Event - Deploy ${{job.status}}' + description: 'description' + app: '${{input.name}}' + system: '${{input.namespace}}' + env: 'dev' + event: 'deploy'