├── .github ├── actions │ └── build_image │ │ └── action.yml ├── renovate.json └── workflows │ ├── bats.yml │ ├── main.yml │ ├── release.yml │ └── test-image.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .shellcheckrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASES.md ├── SECURITY.md ├── argo-main.yml ├── catalog-info.yaml ├── deployment └── helm │ ├── cluster-image-scanner-orchestrator-base │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── cleanup-scandata.yml │ │ ├── cleanup-unpacked.yml │ │ ├── delete-test-products-template.yml │ │ ├── email.cm.yml │ │ ├── email.secret.yml │ │ ├── exit-handler.yml │ │ ├── orchestration-job.yml │ │ ├── pvc.yml │ │ ├── rbac.yml │ │ ├── scanjob.yml │ │ ├── servicemonitor.yaml │ │ ├── synchronization.cm.yml │ │ └── test-job.yml │ └── values.yaml │ └── cluster-image-scanner-orchestrator │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ ├── _helpers.tpl │ ├── api.cm.yml │ ├── api.secret.yml │ ├── artifacts.sa.cm.yml │ ├── artifacts.sa.yml │ ├── artifacts.yml │ ├── cron.yml │ ├── defectdojo.cm.yml │ ├── defectdojo.secret.yml │ ├── dependency-track.cm.yml │ ├── dependency-track.secret.yml │ ├── github.secret.yml │ ├── job.exclude-node.yml │ ├── local-minio.auth.yml │ ├── registry.secret.yml │ ├── repolist.yml │ ├── roles-workflow.yml │ ├── roles.yml │ ├── scanjob-parameter.yml │ └── slack.secret.yml │ └── values.yaml ├── docs ├── architecture │ ├── README.md │ ├── collector.md │ ├── comparsion.md │ ├── decisions │ │ ├── architecture-orchestration.md │ │ ├── case-study-image-transfer.md │ │ ├── collector-to-orchestrator-exchange.md │ │ ├── defectdojo-structure.md │ │ └── images │ │ │ ├── fetcher.png │ │ │ ├── file_transfer.png │ │ │ ├── initial_startup.png │ │ │ ├── multi_pv.png │ │ │ ├── one_pv.png │ │ │ └── orchestrate_containers.png │ ├── fetcher.md │ ├── images │ │ ├── collector.png │ │ ├── dfd-clusterimagescanner.dia │ │ ├── dfd-clusterimagescanner.png │ │ ├── dfd-defectdojo.dia │ │ ├── dfd-defectdojo.png │ │ └── technical.png │ └── threat-model.md ├── deployment │ ├── README.md │ ├── clusterscanner-image-collector.md │ ├── deployment-orchestrator.md │ ├── fetcher.md │ ├── multitenant-impl.png │ ├── multitenant.png │ └── repolist.md ├── development │ ├── README.md │ ├── coding-guideline.md │ └── scripts.md ├── dsomm.png ├── images │ ├── logo.png │ ├── overview.png │ └── usecase.png ├── index.md └── user │ ├── README.md │ ├── configuration │ ├── README.md │ ├── inheritance.png │ ├── latest-jenkins.png │ ├── lifetime-scans.png │ └── namespace-filter.md │ ├── defectdojo │ ├── README.md │ ├── defectdojo-findings-filter.png │ └── finding-duplicate.png │ └── scans │ ├── README.md │ ├── baseimage-lifetime.md │ ├── distroless.md │ ├── image-lifetime.md │ ├── images │ ├── accept-add-risk-exception.png │ ├── accept-overview.png │ ├── accept-threat-overview.png │ ├── baseimage-lifetime.png │ └── lifetime-yum.png │ ├── known-vulnerabilities.md │ ├── malware.md │ ├── new-version.md │ └── run-as-root.md ├── icons ├── cis_logo_128.png ├── cis_logo_16.png ├── cis_logo_192.png ├── cis_logo_32.png ├── cis_logo_48.png ├── cis_logo_64.png └── cis_logo_96.png ├── images ├── README.md ├── base │ ├── .gitignore │ ├── README.md │ ├── auth.bash │ ├── build.sh │ ├── cache.bash │ ├── check-required-env.bash │ ├── entrypoint.bash │ ├── env.bash │ ├── git.bash │ ├── scan-common.bash │ └── unpack.bash ├── process │ ├── image-source-fetcher │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.sh │ │ ├── env.bash │ │ ├── module.bash │ │ └── runImageLocally.bash │ ├── imagecollector │ │ ├── .gitconfig │ │ ├── .gitignore │ │ ├── bin │ │ │ ├── jq │ │ │ └── kubectl │ │ ├── build.sh │ │ ├── config │ │ │ ├── imageNegativeList.json │ │ │ ├── namespace-mapping.json │ │ │ ├── namespace-mapping.schema.json │ │ │ └── registry-rename.sample.json │ │ ├── entrypoint.bash │ │ ├── pods.bash │ │ ├── runImageLocally.bash │ │ ├── runLocally.bash │ │ └── test │ │ │ └── pvc.yaml │ ├── notifier │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.sh │ │ ├── module.bash │ │ ├── slack-template-one-block.json │ │ └── slack-template.json │ ├── test-image │ │ ├── build.sh │ │ ├── env.bash │ │ ├── log4j-core-2.14.0.jar │ │ └── module.bash │ └── workflow-runner │ │ ├── build.sh │ │ ├── env.bash │ │ ├── module.bash │ │ └── workflow.template.yml └── scan │ ├── ddTemplate.json │ ├── distroless │ ├── README.md │ ├── build.sh │ ├── env.bash │ └── module.bash │ ├── lifetime │ ├── build.sh │ ├── env.bash │ └── module.bash │ ├── malware │ ├── README.md │ ├── build.sh │ ├── env.bash │ ├── module.bash │ └── runLocally.bash │ ├── new-version │ ├── build.sh │ ├── env.bash │ ├── module.bash │ └── runLocally.bash │ ├── parseMarkdownToCreateDefectDojoText.bash │ ├── runasroot │ ├── build.sh │ ├── env.bash │ └── module.bash │ └── syft │ ├── README.md │ ├── build.sh │ ├── env.bash │ └── module.bash ├── mkdocs.yml ├── test_actions ├── .gitignore ├── argocd.project.yml ├── argocd │ ├── kustomization.yml │ └── namespace.yml ├── argowf │ ├── argowf.yml │ ├── kustomization.yml │ └── namespace.yml ├── base │ ├── auth.yml │ └── kustomization.yml ├── collector │ ├── application │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ └── namespace.yaml │ ├── configmap.yaml │ ├── job.yml │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── secret-volume.yaml │ ├── service-account-authorization.yaml │ └── setup.bash ├── library.bash ├── minio │ ├── kustomization.yml │ ├── minio.yaml │ ├── namespace.yml │ ├── pvc.yml │ └── service.yml ├── secrets.example ├── setup.bash ├── submit-workflow-with-image.bash ├── teardown.bash ├── variables.base.yaml ├── variables.secret.yaml.example └── variables.yaml └── tests ├── clean-image.tar ├── infected-image.tar ├── scan-common.bats └── scan-malware.bats /.github/actions/build_image/action.yml: -------------------------------------------------------------------------------- 1 | name: "Build image" 2 | description: "Builds an publishes an image to quay.io" 3 | inputs: 4 | image-path: 5 | description: "Path in repository to image build dir" 6 | required: true 7 | image-name: 8 | description: "Name of the image to build an publish" 9 | required: true 10 | registry-user: 11 | description: "Username for pushing the image to a registry" 12 | required: true 13 | registry-token: 14 | description: "Token for pushing the image to a registry" 15 | required: true 16 | runs: 17 | using: "composite" 18 | steps: 19 | - name: create and push an image 20 | shell: bash 21 | run: | 22 | source ./test_actions/library.bash 23 | cd ${{ inputs.image-path }} 24 | 25 | find . -type f -name "*.bash" -print0 | xargs -0 sed -i "s#quay.io/sdase/cluster-image-scanner-base:3#quay.io/sdase/cluster-image-scanner-base:${VERSION}#g" 26 | find . -type f -name "*.sh" -print0 | xargs -0 sed -i "s#quay.io/sdase/cluster-image-scanner-base:3#quay.io/sdase/cluster-image-scanner-base:${VERSION}#g" 27 | chmod +x ./build.sh 28 | sudo ./build.sh "quay.io" "sdase" "${{ inputs.image-name }}" "${VERSION}" "${{ inputs.registry-user }}" "${{ inputs.registry-token }}" true 29 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "labels": [ 7 | "dependencies", 8 | "github-actions" 9 | ], 10 | "prConcurrentLimit": 5, 11 | "prHourlyLimit": 2, 12 | "timezone": "UTC", 13 | "schedule": [ 14 | "before 6am on monday" 15 | ], 16 | "github-actions": { 17 | "enabled": true, 18 | "pinDigests": false, 19 | "commitMessageTopic": "GitHub Action {{depName}}", 20 | "commitMessageExtra": "to {{newVersion}}", 21 | "branchTopic": "github-actions-{{depNameSanitized}}", 22 | "groupName": "GitHub Actions", 23 | "automerge": false, 24 | "major": { 25 | "automerge": false 26 | }, 27 | "minor": { 28 | "automerge": false 29 | }, 30 | "patch": { 31 | "automerge": true, 32 | "automergeType": "pr" 33 | } 34 | }, 35 | "packageRules": [ 36 | { 37 | "description": "Group all GitHub Actions updates together", 38 | "matchManagers": [ 39 | "github-actions" 40 | ], 41 | "groupName": "GitHub Actions", 42 | "automerge": false, 43 | "commitMessageTopic": "GitHub Actions", 44 | "commitMessageExtra": "", 45 | "branchTopic": "github-actions-updates", 46 | "schedule": [ 47 | "before 6am on monday" 48 | ] 49 | }, 50 | { 51 | "description": "Auto-merge patch updates for trusted GitHub Actions", 52 | "matchManagers": [ 53 | "github-actions" 54 | ], 55 | "matchPackageNames": [ 56 | "actions/checkout", 57 | "actions/setup-node", 58 | "actions/setup-python", 59 | "actions/cache", 60 | "actions/upload-artifact", 61 | "actions/download-artifact" 62 | ], 63 | "matchUpdateTypes": [ 64 | "patch" 65 | ], 66 | "automerge": true, 67 | "automergeType": "pr" 68 | }, 69 | { 70 | "description": "Convert GitHub Actions from hash to semantic versions", 71 | "matchManagers": [ 72 | "github-actions" 73 | ], 74 | "pinDigests": false, 75 | "separateMajorMinor": true, 76 | "separateMultipleMajor": true 77 | }, 78 | { 79 | "description": "Handle major version updates for GitHub Actions separately", 80 | "matchManagers": [ 81 | "github-actions" 82 | ], 83 | "matchUpdateTypes": [ 84 | "major" 85 | ], 86 | "groupName": "GitHub Actions (Major)", 87 | "automerge": false, 88 | "labels": [ 89 | "dependencies", 90 | "github-actions", 91 | "major-update" 92 | ] 93 | } 94 | ], 95 | "ignoreDeps": [], 96 | "vulnerabilityAlerts": { 97 | "enabled": true, 98 | "labels": [ 99 | "security", 100 | "dependencies" 101 | ] 102 | }, 103 | "osvVulnerabilityAlerts": true, 104 | "customManagers": [ 105 | { 106 | "description": "Update GitHub Actions in composite actions", 107 | "customType": "regex", 108 | "managerFilePatterns": [ 109 | "/\\.yml$/", 110 | "/\\.yaml$/" 111 | ], 112 | "matchStrings": [ 113 | "uses:\\s+(?[^@\\s]+)@(?[^\\s]+)" 114 | ], 115 | "datasourceTemplate": "github-tags", 116 | "packageNameTemplate": "{{depName}}" 117 | } 118 | ], 119 | "prBodyTemplate": "{{{header}}}{{{table}}}{{{notes}}}{{{changelogs}}}", 120 | "commitMessagePrefix": "chore:", 121 | "commitMessageAction": "update", 122 | "commitMessageTopic": "{{depName}}", 123 | "commitMessageExtra": "to {{newVersion}}", 124 | "rebaseWhen": "conflicted" 125 | } 126 | -------------------------------------------------------------------------------- /.github/workflows/bats.yml: -------------------------------------------------------------------------------- 1 | name: Run BATS tests 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | pull_request: 7 | branches: [ '*' ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4.2.2 16 | 17 | - name: Install Bats and ClamAV 18 | run: sudo apt-get install -y clamav npm && sudo npm install -g bats 19 | 20 | - name: Run Bats test 21 | run: bats tests 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Charts 2 | 3 | on: 4 | workflow_dispatch: # for manual release 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | release: 11 | # depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions 12 | # see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token 13 | permissions: 14 | contents: write 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4.2.2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Configure Git 23 | run: | 24 | git config user.name "$GITHUB_ACTOR" 25 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 26 | 27 | - name: Install Helm 28 | uses: azure/setup-helm@v4.3.0 29 | 30 | - name: Run chart-releaser 31 | uses: helm/chart-releaser-action@v1.7.0 32 | with: 33 | charts_dir: deployment/helm 34 | pages_branch: gh-pages 35 | skip_existing: true 36 | env: 37 | CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 38 | -------------------------------------------------------------------------------- /.github/workflows/test-image.yml: -------------------------------------------------------------------------------- 1 | name: Build Test Image 2 | 3 | on: 4 | push: 5 | # branches: 6 | # - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build_test-image: 12 | runs-on: ubuntu-latest 13 | # run on base image change 14 | # do not execute for PRs that origin from forks due to security concerns and missing secrets 15 | # if: | 16 | # (github.event_name == 'schedule') || 17 | # (always() && 18 | # (needs.build_image_base.result == 'success' || 19 | # (needs.build_image_base.result == 'skipped' && needs.changes.outputs.notifier == 'true')) && 20 | # ! (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)) 21 | steps: 22 | - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 23 | - uses: ./.github/actions/build_image 24 | with: 25 | image-path: images/process/test-image 26 | image-name: cluster-image-scanner-test-image 27 | registry-user: ${{ secrets.QUAY_IO_CLUSTERSCANNER_USERNAME }} 28 | registry-token: ${{ secrets.QUAY_IO_CLUSTERSCANNER_TOKEN }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /deployment/test/config-placeholder/defectdojo-test-secret.yml 2 | /defectdojo-test-secret.yml 3 | /.idea 4 | .idea 5 | test/ 6 | /test_actions/ 7 | secrets 8 | .DS_Store 9 | *.bak 10 | *.swp 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: mixed-line-ending 6 | args: [ --fix=lf ] 7 | - id: trailing-whitespace 8 | args: [ --markdown-linebreak-ext=* ] 9 | - id: check-json 10 | - id: check-merge-conflict 11 | - id: detect-aws-credentials 12 | args: [ --allow-missing-credentials ] 13 | - id: double-quote-string-fixer 14 | - id: end-of-file-fixer 15 | 16 | - repo: https://github.com/pre-commit-ci/pre-commit-ci-config 17 | rev: v1.6.1 18 | hooks: 19 | - id: check-pre-commit-ci-config 20 | 21 | - repo: https://github.com/python-jsonschema/check-jsonschema 22 | rev: 0.33.0 23 | hooks: 24 | - id: check-github-workflows 25 | args: ["--verbose"] 26 | 27 | - repo: https://github.com/renovatebot/pre-commit-hooks 28 | rev: 40.26.1 29 | hooks: 30 | - id: renovate-config-validator 31 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | external-sources=true 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | We are looking forward to your contributions. 4 | Please take a moment to read these Contribution Guidelines. 5 | 6 | ## Submitting Pull Requests 7 | 8 | In case you want to submit a small fix, feel free to create a [Pull Request](https://github.com/SDA-SE/clusterscanner-orchestration/pulls). 9 | If you plan bigger changes, prefer to create an [issue](https://github.com/SDA-SE/clusterscanner-orchestration/issues) first to discuss the details. 10 | Make sure that your changes can be build and run on Linux with buildah. 11 | Avoid breaking changes! 12 | 13 | ## Submitting Issues 14 | In case you detected an issue (e.g. a bug), please feel free to create an [issue](https://github.com/SDA-SE/clusterscanner-orchestration/issues). Please provide as much information as possible, e.g. ArgoWorkflow version, steps to reproduce the issue (in the best case, a script based on test/setup-minikube.sh). 15 | 16 | ## Submitting Enhancements 17 | In case you detected a missing feature, please feel free to create an [issue](https://github.com/SDA-SE/clusterscanner-orchestration/issues). 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 SDA SE 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClusterImageScanner 2 | ![Logo](docs/images/logo.png "Logo") 3 | 4 | Discover vulnerabilities and container image misconfiguration in production environments. 5 | 6 | # Introduction 7 | The ClusterImageScanner detects images in a Kubernetes cluster and provides fast feedback based on various security tests. It is recommended to run the ClusterImageScanner in production environments in order to get up-to-date feedback on security issues where they have real impact. 8 | 9 | Since the ClusterImageScanner itself is a service running within your Kubernetes cluster you can re-use your existing deployment procedures. 10 | # Overview 11 | The following figure provides an overview: 12 | ![Overview](docs/images/overview.png) 13 | The following steps are conducted. 14 | 1. The Image Collector, as the name suggests, collects the different images from a container environment like a kubernetes cluster. The Collector creates a JSON file and including information like the cluster, the responsible team, and image. 15 | 2. The Orchestrator (implemented via ArgoWorkflows) starts the workflow periodically (e.g. nightly) 16 | 3. The images from the Collector can be pulled by the Image Fetcher 17 | 4. These files are kept in a separate directory and from there they are passed to the scanner 18 | 5. Multiple scanner are used, e.g. Dependency Track, Lifetime, Malware and further more. 19 | 6. The vulnerability management system (in our case [OWASP DefectDojo](https://github.com/DefectDojo/django-DefectDojo)) then collects the results 20 | 7. Non responded to findings are made available to the developers via a communication channel (Slack/Email). 21 | 22 | ## Documentation Table of Contents 23 | - [User documentation](docs/user) 24 | - [Architecture and Decisions](docs/architecture) 25 | - [Operator documentation](docs/deployment) 26 | 27 | [Video (English): SDA SE CluserImageScanner is going Open Source, 2021-03](https://www.youtube.com/watch?v=_AElSBKSizc&t=2966s) 28 | 29 | # Images 30 | Images to be used by ArgoWorkflows are published in quay.io (2021-06-28): 31 | 32 | - `cluster-image-scanner-scan-runasroot` 33 | - `cluster-image-scanner-scan-distroless` 34 | - `cluster-image-scanner-scan-lifetime` 35 | - `cluster-image-scanner-scan-malware` 36 | - `cluster-image-scanner-scan-new-version` 37 | - `cluster-image-scanner-imagefetcher` 38 | - `cluster-image-scanner-notifier` 39 | - `cluster-image-scanner-imagecollector` 40 | - `cluster-image-scanner-image-source-fetcher` 41 | - `cluster-image-scanner-workflow-runner` 42 | - [quay.io/sdase/image-metadata-collector](https://github.com/SDA-SE/image-metadata-collector) 43 | - [quay.io/sdase/defectdojo-client](https://github.com/SDA-SE/defectdojo-client) 44 | 45 | `cluster-image-scanner-base` is the base for all `cluster-image-scanner-*` images. 46 | 47 | Images are build with [buildah](https://buildah.io/). The env. parameters the image can be started with are documented via --config within the _build.sh_ scripts within the [images](images/). 48 | 49 | # Contributing 50 | We are looking forward to contributions. Take a look at our [Contribution Guidelines](CONTRIBUTING.md) before submitting Pull Requests. 51 | 52 | # Responsible Disclosure and Security 53 | The [SECURITY.md](SECURITY.md) includes information on responsible disclosure and security related topics like security patches. 54 | 55 | # Deployment 56 | ## Test 57 | ```bash 58 | cd test_actions 59 | export IS_MINIKUBE=true # if minikube is used 60 | ./setup.bash 61 | ``` 62 | 63 | ## Production 64 | helm files are in `deployment/helm`. 65 | 66 | # Legal Notice 67 | The purpose of the ClusterImageScanner is not to replace the penetration testers or make them obsolete. We strongly recommend running extensive tests by experienced penetration testers on all your applications. 68 | The ClusterImageScanner is to be used only for testing purpose of your running applications/containers. You need a written agreement of the organization of the _environment under scan_ to scan components with the ClusterScanner. 69 | 70 | # Author Information 71 | This project is developed by [Signal Iduna](https://www.signal-iduna.de) and [SDA SE](https://sda.se/). 72 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | This document describes the release process of SDA ClusterScanner. 4 | 5 | ## Happy Path (WIP) 6 | The release process is automated. 7 | It generates new releases for all (commits/) and performed merges to `master` and uploads all releases as images to quay.io/sdase/cluster-image-scanner-orchestration:x.x.x. 8 | 9 | See also `build.sh` and `.github/workflows/master.yaml`. 10 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Responsible disclosure policy 2 | 3 | ## Introduction 4 | 5 | We take security very seriously at SDA SE. 6 | We welcome any review of the latest release of all our open source code to ensure that these components can not be compromised. 7 | In case you identified a security related issue with severity of _low_ to _medium_, please create a [GitHub issue](https://github.com/SDA-SE/clusterscanner). 8 | 9 | 10 | ## Security related bugs with severity _high_ or _critical_ 11 | 12 | In case you identified a security related issue with severity of _high_ or _critical_, please disclose information about the issue non public via email to `opensource-security@sda.se`. 13 | 14 | We encourage researchers to include a Proof-of-Concept, supported by screenshots or videos. 15 | For each given security related issue with severity _high_ or _critical_ (based on SDA SE own assessment), we will respond within one week. 16 | 17 | # Supported versions and update policy 18 | 19 | Please be aware that only the most recent version will be subject of security patches. 20 | The [changelog](https://github.com/SDA-SE/clusterscanner-orchestration/releases/) provides information about feature and security related fixes like patches. 21 | 22 | # Versioning 23 | 24 | This project uses [Semantic Versioning](https://semver.org/). Images are build nightly and receive automatic update for the operating system components. Images are immutable in this project, so the _patch_ version is increased each night. 25 | 26 | # Known security gaps and future enhancements 27 | There is no format in commits to identify security related fixes and it is not planned yet. 28 | -------------------------------------------------------------------------------- /argo-main.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Workflow 3 | metadata: 4 | generateName: orchestration- 5 | spec: 6 | securityContext: 7 | runAsNonRoot: true 8 | runAsUser: 1000 9 | serviceAccountName: clusterscanner 10 | ttlStrategy: 11 | secondsAfterSuccess: 3600 12 | secondsAfterFailure: 7200 13 | artifactRepositoryRef: 14 | configMap: artifact-repositories 15 | key: default-v1 16 | entrypoint: main 17 | arguments: 18 | parameters: 19 | - name: scanId 20 | value: "{{ workflow.creationTimestamp.Y }}{{ workflow.creationTimestamp.m }}{{ workflow.creationTimestamp.d }}-{{ workflow.creationTimestamp.H }}{{ workflow.creationTimestamp.M }}{{ workflow.creationTimestamp.S }}" 21 | - name: gitSecretName 22 | value: "github" 23 | - name: s3SecretName 24 | value: "api-credentials" 25 | - name: s3ConfigName 26 | value: "api-config" 27 | - name: imageSourceListConfigMapName 28 | value: "image-source-list" 29 | - name: registrySecretName 30 | value: "registry-default" 31 | - name: defectDojoConfigMapName 32 | value: "defectdojo" 33 | - name: slackTokenSecretName 34 | value: "slacktoken" 35 | - name: emailSecretName 36 | value: "email" 37 | - name: enforceSlackChannel 38 | value: "#security-notifications-test" 39 | - name: newVersionImageFilter 40 | value: "quay.io/sdase/|swaggerapi/petstore" 41 | - name: allResultsGitTarget 42 | value: ""# "ssh://git@github.com/SDA-SE/cluster-image-scanner-test-results" 43 | - name: imageRegistryBase 44 | value: "quay.io/sdase" 45 | - name: clusterImageScannerImageTag 46 | value: "###clusterImageScannerImageTag###" 47 | - name: errorTargets 48 | value: '[{ "channel":"#security-notifications-test", "type": "slack"} ]' 49 | templates: 50 | - name: main 51 | steps: 52 | - - name: delete-test-products 53 | templateRef: 54 | name: delete-test-products-template 55 | template: main 56 | - - name: orchestration-job 57 | templateRef: 58 | name: orchestration-job-template 59 | template: main 60 | - - name: test-job 61 | templateRef: 62 | name: test-job-template 63 | template: main 64 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: ClusterImageScanner 5 | description: | 6 | Discover vulnerabilities and container image misconfiguration in production environments. 7 | tags: 8 | - security-stack 9 | annotations: 10 | sda.se/summary: | 11 | Discover vulnerabilities and container image misconfiguration in production environments. 12 | sda.se/federation-scopes: public 13 | backstage.io/techdocs-ref: url:https://github.com/SDA-SE/cluster-image-scanner/tree/master 14 | links: 15 | - url: https://github.com/SDA-SE/cluster-image-scanner/ 16 | title: Github Repository 17 | spec: 18 | type: service 19 | owner: security-journey 20 | system: cluster-image-scanner 21 | lifecycle: production 22 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: cluster-image-scanner-orchestrator-base 3 | description: This chart contains base resources required by the orchestrator. It primarily contains argo workflow templates and their config. 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.4 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.0.0" 25 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/NOTES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/deployment/helm/cluster-image-scanner-orchestrator-base/templates/NOTES.txt -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "cluster-image-scanner-orchestrator-base.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "cluster-image-scanner-orchestrator-base.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "cluster-image-scanner-orchestrator-base.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "cluster-image-scanner-orchestrator-base.labels" -}} 37 | helm.sh/chart: {{ include "cluster-image-scanner-orchestrator-base.chart" . }} 38 | {{ include "cluster-image-scanner-orchestrator-base.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "cluster-image-scanner-orchestrator-base.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "cluster-image-scanner-orchestrator-base.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "cluster-image-scanner-orchestrator-base.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "cluster-image-scanner-orchestrator-base.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/cleanup-scandata.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: CronWorkflow 3 | metadata: 4 | name: cleanup-enforced-delete-pvc-wf-images 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | schedule: "{{ .Values.cleanup.schedule }}" 8 | concurrencyPolicy: "Forbid" 9 | startingDeadlineSeconds: {{ .Values.cleanup.startingDeadlineSeconds }} 10 | timezone: Europe/Berlin 11 | activeDeadlineSeconds: {{ .Values.cleanup.activeDeadlineSeconds }} 12 | workflowSpec: 13 | serviceAccountName: "{{ .Values.serviceAccount.name }}" 14 | artifactRepositoryRef: 15 | configMap: artifact-repositories 16 | key: default-v1 17 | entrypoint: pvc-cleanup 18 | templates: 19 | - name: pvc-cleanup 20 | volumes: 21 | - name: images 22 | persistentVolumeClaim: 23 | claimName: cluster-image-scanner-images 24 | script: 25 | securityContext: 26 | runAsNonRoot: false 27 | runAsUser: 0 28 | image: {{ .Values.cleanup.image.repo }}:{{ .Values.cleanup.image.tag }} 29 | volumeMounts: 30 | - name: images 31 | mountPath: /clusterscanner/images 32 | imagePullPolicy: IfNotPresent 33 | command: [python3] 34 | source: | 35 | import os 36 | import time 37 | import shutil 38 | KEEP_DAYS=7 39 | print('Deleting in /clusterscanner/images') 40 | for root, dirs, files in os.walk('/clusterscanner/images'): 41 | if 'manifest.json' in files: 42 | if time.time() - os.path.getatime(root) > KEEP_DAYS * 24 * 60 * 60: 43 | print(f"Directory {root} has been changed more than {KEEP_DAYS} days ago. Deleting.") 44 | shutil.rmtree(root) 45 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/cleanup-unpacked.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: CronWorkflow 3 | metadata: 4 | name: cleanup-enforced-delete-pvc-wf-scandata 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | schedule: "{{ .Values.cleanup.schedule }}" 8 | concurrencyPolicy: "Forbid" 9 | startingDeadlineSeconds: {{ .Values.cleanup.startingDeadlineSeconds }} 10 | timezone: Europe/Berlin 11 | activeDeadlineSeconds: {{ .Values.cleanup.activeDeadlineSeconds }} 12 | workflowSpec: 13 | serviceAccountName: "{{ .Values.serviceAccount.name }}" 14 | artifactRepositoryRef: 15 | configMap: artifact-repositories 16 | key: default-v1 17 | entrypoint: pvc-cleanup 18 | templates: 19 | - name: pvc-cleanup 20 | volumes: 21 | - name: scandata 22 | persistentVolumeClaim: 23 | claimName: cluster-image-scanner-scandata 24 | script: 25 | securityContext: 26 | runAsNonRoot: false 27 | runAsUser: 0 28 | image: {{ .Values.cleanup.image.repo }}:{{ .Values.cleanup.image.tag }} 29 | volumeMounts: 30 | - name: scandata 31 | mountPath: /clusterscanner/data 32 | imagePullPolicy: IfNotPresent 33 | command: [python3] 34 | source: | 35 | import os 36 | import datetime 37 | 38 | directory = "/clusterscanner/data/" 39 | days = 7 40 | 41 | def find_and_delete_old_files(directory, days): 42 | current_time = datetime.datetime.now() 43 | threshold = current_time - datetime.timedelta(days=days) 44 | 45 | deleted_files = [] 46 | 47 | for root, dirs, files in os.walk(directory, topdown=False): 48 | for file in files: 49 | file_path = os.path.join(root, file) 50 | modified_time = datetime.datetime.fromtimestamp(os.path.getmtime(file_path)) 51 | if modified_time < threshold: 52 | os.remove(file_path) 53 | deleted_files.append(file_path) 54 | 55 | # Delete empty directories 56 | for root, dirs, _ in os.walk(directory, topdown=False): 57 | for dir_name in dirs: 58 | dir_path = os.path.join(root, dir_name) 59 | if not os.listdir(dir_path): 60 | os.rmdir(dir_path) 61 | 62 | return deleted_files 63 | 64 | 65 | deleted_files = find_and_delete_old_files(directory, days) 66 | 67 | print("Deleted files older than 7 days:") 68 | for file in deleted_files: 69 | print(file) 70 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/delete-test-products-template.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: WorkflowTemplate 3 | metadata: 4 | name: delete-test-products-template 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | activeDeadlineSeconds: 3600 8 | entrypoint: main # Entry point for job execution 9 | workflowSpec: 10 | #serviceAccountName: clusterscanner 11 | ttlStrategy: 12 | secondsAfterSuccess: 3600 13 | secondsAfterFailure: 7200 14 | artifactRepositoryRef: 15 | configMap: artifact-repositories 16 | key: default-v1 17 | inputs: 18 | parameters: 19 | - workflow.parameters.defectDojoConfigMapName 20 | - workflow.parameters.scanjobEnvParameter 21 | 22 | templates: 23 | - name: main 24 | steps: 25 | - - name: delete-test-products 26 | template: delete-test-products 27 | 28 | - name: delete-test-products 29 | volumes: 30 | - name: tmp 31 | emptyDir: { } 32 | container: 33 | resources: 34 | limits: 35 | cpu: '1' 36 | memory: 256Mi 37 | requests: 38 | cpu: '0.2' 39 | memory: 64Mi 40 | securityContext: 41 | allowPrivilegeEscalation: false 42 | capabilities: 43 | drop: 44 | - ALL 45 | readOnlyRootFilesystem: true 46 | runAsNonRoot: true 47 | runAsUser: 1001 48 | image: "{{ "{{" }} workflow.parameters.imageRegistryBase {{ "}}" }}/defectdojo-client:4" 49 | imagePullPolicy: Always # TODO IfNotPresent 50 | command: [ "java", "-cp", "@/app/jib-classpath-file", "org.sdase.deleteTestProduct.MainDeleteProduct" ] 51 | volumeMounts: 52 | - name: tmp 53 | mountPath: /tmp 54 | envFrom: 55 | - configMapRef: 56 | name: "{{ "{{" }}workflow.parameters.defectDojoConfigMapName {{ "}}" }}" 57 | - configMapRef: 58 | name: "scanjob-env-parameter" 59 | - secretRef: 60 | name: "{{ "{{" }} workflow.parameters.defectDojoConfigMapName {{ "}}" }}" 61 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/email.cm.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: email-parameter 5 | namespace: {{ .Release.Namespace }} 6 | data: 7 | smtp: "{{ .Values.smtp.smtp }}" 8 | smtp-auth: "{{ .Values.smtp.auth }}" 9 | smtp-auth-user: "{{ .Values.smtp.user }}" 10 | SMTP_START_TLS: "{{ .Values.smtp.starttls }}" 11 | SMTP_MAIL_PARAMETER: "{{ .Values.smtp.smtpMailParameter }}" 12 | SMTP_ENFORCE_MAILTO: "{{ .Values.smtp.smtpEnforceMailTo }}" 13 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/email.secret.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: email 5 | namespace: {{ .Release.Namespace }} 6 | data: 7 | smtp-auth-password: "{{ .Values.smtp.password | b64enc }}" 8 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/exit-handler.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: WorkflowTemplate 3 | metadata: 4 | name: exit-handler 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | activeDeadlineSeconds: 3600 8 | entrypoint: main # Entry point for job execution 9 | workflowSpec: 10 | #serviceAccountName: clusterscanner 11 | ttlStrategy: {{ .Values.exitHandler.ttlStrategy }} 12 | artifactRepositoryRef: 13 | configMap: artifact-repositories 14 | key: default-v1 15 | inputs: 16 | parameters: 17 | - workflow.parameters.defectDojoConfigMapName 18 | - workflow.parameters.scanjobEnvParameter 19 | 20 | templates: 21 | - name: main 22 | steps: 23 | - - name: exit-handler 24 | template: exit-handler 25 | 26 | - name: exit-handler 27 | container: 28 | resources: 29 | {{- toYaml .Values.exitHandler.resources | nindent 10 }} 30 | securityContext: 31 | {{- toYaml .Values.exitHandler.securityContext | nindent 10 }} 32 | image: "{{ "{{" }}workflow.parameters.imageRegistryBase {{ "}}" }}/cluster-image-scanner-base:{{ "{{" }} workflow.parameters.clusterImageScannerImageTag {{ "}}" }}" 33 | imagePullPolicy: IfNotPresent 34 | source: | 35 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/pvc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: cluster-image-scanner-images 6 | namespace: {{ .Release.Namespace }} 7 | spec: 8 | storageClassName: {{ .Values.storage.imagesPvc.storageClass }} 9 | accessModes: 10 | - ReadWriteMany 11 | resources: 12 | requests: 13 | storage: {{ .Values.storage.imagesPvc.capacity }} 14 | --- 15 | apiVersion: v1 16 | kind: PersistentVolumeClaim 17 | metadata: 18 | name: cluster-image-scanner-scandata 19 | namespace: {{ .Release.Namespace }} 20 | spec: 21 | storageClassName: {{ .Values.storage.scandataPvc.storageClass }} 22 | accessModes: 23 | - ReadWriteMany 24 | resources: 25 | requests: 26 | storage: {{ .Values.storage.scandataPvc.capacity }} 27 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/rbac.yml: -------------------------------------------------------------------------------- 1 | # give workflows (as argo:default) permissions to run things 2 | # see https://github.com/argoproj/argo/blob/master/docs/workflow-rbac.md 3 | --- 4 | apiVersion: rbac.authorization.k8s.io/v1 5 | kind: Role 6 | metadata: 7 | name: argo-workflow 8 | namespace: {{ .Release.Namespace }} 9 | rules: 10 | # pod get/watch is used to identify the container IDs of the current pod 11 | # pod patch is used to annotate the step's outputs back to controller (e.g. artifact location) 12 | - apiGroups: 13 | - "" 14 | resources: 15 | - pods 16 | verbs: 17 | - get 18 | - watch 19 | - patch 20 | # logs get/watch are used to get the pods logs for script outputs, and for log archival 21 | - apiGroups: 22 | - "" 23 | resources: 24 | - pods/log 25 | verbs: 26 | - get 27 | - watch 28 | - apiGroups: 29 | - "argoproj.io" 30 | resources: 31 | - workflows 32 | - workflowtaskresults 33 | verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] 34 | --- 35 | {{- if .Values.serviceAccount.create }} 36 | apiVersion: rbac.authorization.k8s.io/v1 37 | kind: RoleBinding 38 | metadata: 39 | name: argo-default-workflow 40 | namespace: {{ .Release.Namespace }} 41 | roleRef: 42 | apiGroup: rbac.authorization.k8s.io 43 | kind: Role 44 | name: argo-workflow 45 | subjects: 46 | - kind: ServiceAccount 47 | name: {{ .Values.serviceAccount.name }} 48 | namespace: {{ .Release.Namespace }} 49 | --- 50 | apiVersion: v1 51 | kind: ServiceAccount 52 | metadata: 53 | name: {{ .Values.serviceAccount.name }} 54 | namespace: {{ .Release.Namespace }} 55 | {{- with .Values.serviceAccount.annotations }} 56 | annotations: 57 | {{ . }} 58 | {{ end }} 59 | {{ end }} 60 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceMonitor.enabled }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: {{ .Release.Name }}-workflow-metrics 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "common.labels" . | nindent 4 }} 9 | release: prometheus-operator 10 | spec: 11 | namespaceSelector: 12 | matchNames: 13 | - argowf # namespace where Argo Workflow controller is installed 14 | selector: 15 | matchLabels: 16 | app: argo-argo-workflows-workflow-controller # matches the Argo Workflow controller service 17 | endpoints: 18 | - port: metrics 19 | path: /metrics 20 | interval: {{ .Values.serviceMonitor.interval | default "30s" }} 21 | jobLabel: workflow-metrics 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/synchronization.cm.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: synchronization 5 | namespace: {{ .Release.Namespace }} 6 | data: 7 | workflow: "12" 8 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator-base/templates/test-job.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: WorkflowTemplate 3 | metadata: 4 | name: test-job-template 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | activeDeadlineSeconds: 3600 8 | entrypoint: main # Entry point for job execution 9 | workflowSpec: 10 | #serviceAccountName: clusterscanner 11 | ttlStrategy: 12 | secondsAfterSuccess: 3600 13 | secondsAfterFailure: 7200 14 | artifactRepositoryRef: 15 | configMap: artifact-repositories 16 | key: default-v1 17 | inputs: 18 | parameters: 19 | - workflow.parameters.defectDojoConfigMapName 20 | - workflow.parameters.scanjobEnvParameter 21 | 22 | templates: 23 | - name: main 24 | steps: 25 | - - name: test-defectdojo-findings 26 | template: test-defectdojo-findings 27 | 28 | - name: test-defectdojo-findings 29 | volumes: 30 | - name: tmp 31 | emptyDir: { } 32 | container: 33 | resources: 34 | limits: 35 | cpu: '1' 36 | memory: 256Mi 37 | requests: 38 | cpu: '0.2' 39 | memory: 64Mi 40 | securityContext: 41 | {{- toYaml .Values.testjob.securityContext | nindent 10 }} 42 | image: "{{ "{{" }} workflow.parameters.imageRegistryBase {{ "}}" }}/defectdojo-client:4" 43 | imagePullPolicy: Always # TODO IfNotPresent 44 | command: [ "java", "-cp", "@/app/jib-classpath-file", "org.sdase.uploadValidation.MainValidation" ] 45 | volumeMounts: 46 | - name: tmp 47 | mountPath: /tmp 48 | envFrom: 49 | - configMapRef: 50 | name: "{{ "{{" }} workflow.parameters.defectDojoConfigMapName {{ "}}" }}" 51 | - configMapRef: 52 | name: "scanjob-env-parameter" 53 | - secretRef: 54 | name: "{{ "{{" }} workflow.parameters.defectDojoConfigMapName {{ "}}" }}" 55 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: cluster-image-scanner-orchestrator 3 | description: A chart that deploys the image metadata orchestrator. 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.5 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.0.0" 25 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "cluster-image-scanner-orchestrator.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "cluster-image-scanner-orchestrator.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "cluster-image-scanner-orchestrator.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "cluster-image-scanner-orchestrator.labels" -}} 37 | helm.sh/chart: {{ include "cluster-image-scanner-orchestrator.chart" . }} 38 | meta.helm.sh/release-name: {{ include "cluster-image-scanner-orchestrator.chart" . }} 39 | meta.helm.sh/release-namespace: {{ .Release.Namespace }} 40 | {{ include "cluster-image-scanner-orchestrator.selectorLabels" . }} 41 | {{- if .Chart.AppVersion }} 42 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 43 | {{- end }} 44 | app.kubernetes.io/managed-by: {{ .Release.Service }} 45 | {{- end }} 46 | 47 | {{/* 48 | Selector labels 49 | */}} 50 | {{- define "cluster-image-scanner-orchestrator.selectorLabels" -}} 51 | app.kubernetes.io/name: {{ include "cluster-image-scanner-orchestrator.name" . }} 52 | app.kubernetes.io/instance: {{ .Release.Name }} 53 | {{- end }} 54 | 55 | {{/* 56 | Create the name of the service account to use 57 | */}} 58 | {{- define "cluster-image-scanner-orchestrator.serviceAccountName" -}} 59 | {{- if .Values.serviceAccount.create }} 60 | {{- default (include "cluster-image-scanner-orchestrator.fullname" .) .Values.serviceAccount.name }} 61 | {{- else }} 62 | {{- default "default" .Values.serviceAccount.name }} 63 | {{- end }} 64 | {{- end }} 65 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/api.cm.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: api-config 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | data: 9 | S3_API_LOCATION: "https://{{ required "An API host is required." .Values.api.host }}{{ .Values.api.pathSeparator }}{{ .Values.api.version }}{{ required "Path is required, should start with a /." .Values.api.path }}" 10 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/api.secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: api-credentials 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | S3_API_KEY: "{{ required "An API key is required." .Values.credentials.apikey | b64enc }}" 11 | S3_API_SIGNATURE: "{{ required "An API signature is required." .Values.credentials.signature | b64enc }}" 12 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/artifacts.sa.cm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # SA to be used in workflow 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: s3-service-account-name 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | app.kubernetes.io/name: clusterscanner 10 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 11 | data: 12 | name: "{{ .Values.serviceAccount.name }}" 13 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/artifacts.sa.yml: -------------------------------------------------------------------------------- 1 | --- 2 | {{- if .Values.serviceAccount.create -}} 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | name: {{ include "cluster-image-scanner-orchestrator.serviceAccountName" . }} 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 10 | annotations: 11 | {{- if .Values.serviceAccount.awsRoleArn}} 12 | eks.amazonaws.com/role-arn: {{ .Values.serviceAccount.awsRoleArn }} 13 | {{- end }} 14 | automountServiceAccountToken: {{ .Values.serviceAccount.automount }} 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: RoleBinding 18 | metadata: 19 | name: argo-default-workflow-artifacts 20 | namespace: {{ .Release.Namespace }} 21 | labels: 22 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 23 | roleRef: 24 | apiGroup: rbac.authorization.k8s.io 25 | kind: Role 26 | name: argo-workflow 27 | subjects: 28 | - kind: ServiceAccount 29 | name: {{ include "cluster-image-scanner-orchestrator.serviceAccountName" . }} 30 | namespace: {{ .Release.Namespace }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/artifacts.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: artifact-repositories 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | annotations: 9 | # v3.0 and after - if you want to use a specific key, put that's key into this annotation 10 | workflows.argoproj.io/default-artifact-repository: default-v1 11 | data: 12 | default-v1: | 13 | s3: 14 | endpoint: {{ .Values.storage.s3.endpoint }} 15 | bucket: {{ required "A bucket for temporary data is required." .Values.storage.s3.bucket }} 16 | insecure: {{ .Values.storage.s3.insecure }} 17 | useSDKCreds: {{ .Values.storage.s3.useSDKCreds }} 18 | {{- if .Values.isLocal }} 19 | accessKeySecret: 20 | name: minio-auth 21 | key: accesskey 22 | secretKeySecret: 23 | name: minio-auth 24 | key: secretkey 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/cron.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: CronWorkflow 3 | metadata: 4 | name: clusterscan-orchestrator 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | spec: 9 | schedule: "{{ .Values.cronSchedule }}" 10 | concurrencyPolicy: Forbid 11 | startingDeadlineSeconds: 0 12 | timezone: Europe/Berlin 13 | suspend: false 14 | workflowSpec: 15 | serviceAccountName: {{ .Values.serviceAccount.name }} 16 | podMetadata: 17 | labels: 18 | sidecar.istio.io/inject: "false" 19 | ttlStrategy: 20 | secondsAfterSuccess: 3600 21 | secondsAfterFailure: 6000 22 | artifactRepositoryRef: 23 | configMap: artifact-repositories 24 | key: default-v1 25 | entrypoint: main 26 | arguments: 27 | parameters: 28 | - name: scanId 29 | value: "{{ "{{" }} workflow.creationTimestamp.Y {{ "}}" }}{{ "{{" }} workflow.creationTimestamp.m {{ "}}" }}{{ "{{" }} workflow.creationTimestamp.d {{ "}}" }}-{{ "{{" }} workflow.creationTimestamp.H {{ "}}" }}{{ "{{" }} workflow.creationTimestamp.M {{ "}}" }}{{ "{{" }} workflow.creationTimestamp.S {{ "}}" }}-sda" 30 | - name: gitSecretName 31 | value: "github" 32 | - name: s3SecretName 33 | value: "api-credentials" 34 | - name: s3ConfigName 35 | value: "api-config" 36 | - name: imageSourceListConfigMapName 37 | value: "image-source-list" 38 | - name: registrySecretName 39 | value: "registry-sda-default" 40 | - name: defectDojoConfigMapName # and secret 41 | value: "defectdojo" 42 | - name: slackTokenSecretName 43 | value: "slacktoken" 44 | - name: emailSecretName 45 | value: "email" 46 | - name: enforceSlackChannel 47 | value: "" 48 | - name: newVersionImageFilter 49 | value: "quay.io/sdase/|dexidp/dex|quay.io/oauth2-proxy/oauth2-proxy|oliver006/redis_exporter|quay.io/prometheuscommunity/postgres-exporter" 50 | - name: allResultsGitTarget 51 | value: "github.com/SDA-SE/cluster-image-scanner-all-results" 52 | - name: imageRegistryBase 53 | value: "quay.io/sdase" 54 | - name: clusterImageScannerImageTag 55 | value: "{{ .Values.image.tag }}" 56 | - name: errorTargets 57 | value: '[{ "channel":"#security-notifications", "type": "slack"} ]' 58 | templates: 59 | - name: main 60 | steps: 61 | - - name: orchestration-job-sda 62 | templateRef: 63 | name: orchestration-job-template 64 | template: main 65 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/defectdojo.cm.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: defectdojo 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | data: 9 | DD_BRANCHES_TO_KEEP: "{{ .Values.defectdojo.branchesToKeep }}" 10 | DD_REPORT_PATH: "{{ .Values.defectdojo.reportPath }}" 11 | DD_LEAD: "{{ .Values.defectdojo.lead }}" 12 | DD_USER: "{{ required "A defectdojo user is required." .Values.defectdojo.user }}" 13 | DEFECTDOJO_URL: "{{ required "A defectdojo url is required." .Values.defectdojo.url }}" 14 | DD_IMPORT_TYPE: "{{ .Values.defectdojo.importType }}" 15 | DD_IS_MARKED_AS_ACTIVE: "{{ .Values.defectdojo.markedAsActive }}" 16 | DD_IS_MARKED_AS_INACTIVE: "{{ .Values.defectdojo.markedAsInactive }}" 17 | DD_DEDUPLICATION_ON_ENGAGEMENT: "{{ .Values.defectdojo.deduplicationOnEngagement }}" 18 | DD_SOURCE_CODE_MANAGEMENT_URI: "{{ .Values.defectdojo.gitUrl }}" 19 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/defectdojo.secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: defectdojo 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | DD_TOKEN: {{ required "A defectdojo access token is required." .Values.defectdojo.token | b64enc }} 11 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/dependency-track.cm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: dependency-track 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | DEPENDENCY_TRACK_URL: "{{ required "A dependencytrack url is required." .Values.dependencytrack.url }}" 11 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/dependency-track.secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: dependency-track 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | DEPENDENCY_TRACK_KEY: {{ required "A dependencytrack access key is required." .Values.dependencytrack.key | b64enc }} 11 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/github.secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: github 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | GH_APP_ID: {{ .Values.github.appId | b64enc }} 11 | GH_APP_LOGIN: {{ .Values.github.appLogin | b64enc }} 12 | GH_INSTALLATION_ID: {{ .Values.github.installationId | b64enc }} 13 | github_private_key.pem: {{ .Values.github.privateKeyPem | b64enc }} 14 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/job.exclude-node.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: WorkflowTemplate 3 | metadata: 4 | # checkov:skip=CKV_K8S_21 The default namespace should not be used 5 | # checkov:skip=CKV_ARGO_1: "Ensure Workflow pods are not using the default ServiceAccount" 6 | # checkov:skip=CKV_ARGO_2: "Ensure Workflow pods are running as non-root user" 7 | name: unused 8 | namespace: {{ .Release.Namespace }} 9 | labels: 10 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 11 | spec: 12 | affinity: 13 | nodeAffinity: 14 | requiredDuringSchedulingIgnoredDuringExecution: 15 | nodeSelectorTerms: 16 | - matchExpressions: 17 | - key: kubernetes.io/hostname 18 | operator: NotIn 19 | values: [] 20 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/local-minio.auth.yml: -------------------------------------------------------------------------------- 1 | {{- if .Values.isLocal }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: minio-auth 6 | namespace: {{ .Release.Namespace }} 7 | stringData: 8 | accesskey: "minioadmin" 9 | secretkey: "minioadmin" 10 | {{- end }} 11 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/registry.secret.yml: -------------------------------------------------------------------------------- 1 | #--- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: registry-sda-default 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | auth.json: {{ .Values.registry.authJson | b64enc }} 11 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/repolist.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: image-source-list 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | {{ .Values.imageSourceList | toYaml }} 11 | #image-lifetime: https://api.github.com/repos/SDA-SE/cluster-scan-test-images/contents/image-lifetime.json 12 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/roles-workflow.yml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: orchestrator-workflow 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | annotations: 9 | checkov.io/skip1: CKV_K8S_21=The default namespace should not be used 10 | checkov.io/skip2: CKV_K8S_49=Minimize wildcard use in Roles and ClusterRoles 11 | rules: 12 | - apiGroups: [""] 13 | resources: ["pods"] 14 | verbs: ["get", "watch", "patch"] 15 | - apiGroups: 16 | - "" 17 | resources: ["pods"] 18 | verbs: ["get", "watch"] 19 | - apiGroups: [ "argoproj.io"] 20 | resources: ["workflows"] 21 | verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] 22 | --- 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | kind: RoleBinding 25 | metadata: 26 | name: s3-account-binding-cis-wf 27 | namespace: {{ .Release.Namespace }} 28 | labels: 29 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 30 | annotations: 31 | checkov.io/skip1: CKV_K8S_21=The default namespace should not be used 32 | roleRef: 33 | apiGroup: rbac.authorization.k8s.io 34 | kind: Role 35 | name: orchestrator-workflow 36 | subjects: 37 | - kind: ServiceAccount 38 | name: {{ .Values.serviceAccount.name }} 39 | namespace: {{ .Release.Namespace }} 40 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/roles.yml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: sda-product-security-role 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | annotations: 9 | checkov.io/skip1: CKV_K8S_21=The default namespace should not be used 10 | checkov.io/skip2: CKV_K8S_49=Minimize wildcard use in Roles and ClusterRoles 11 | rules: 12 | - apiGroups: [""] 13 | resources: ["*"] 14 | verbs: ["*"] 15 | - apiGroups: 16 | - argoproj.io 17 | resources: 18 | - workflows 19 | - workflows/finalizers 20 | - workfloweventbindings 21 | - workfloweventbindings/finalizers 22 | - workflowtemplates 23 | - workflowtemplates/finalizers 24 | - cronworkflows 25 | - cronworkflows/finalizers 26 | - clusterworkflowtemplates 27 | - clusterworkflowtemplates/finalizers 28 | - workflowtaskresults 29 | - workflowtaskresults/finalizers 30 | verbs: 31 | - create 32 | - delete 33 | - deletecollection 34 | - get 35 | - list 36 | - patch 37 | - update 38 | - watch 39 | --- 40 | apiVersion: rbac.authorization.k8s.io/v1 41 | kind: RoleBinding 42 | metadata: 43 | name: sda-product-security-role-binding 44 | labels: 45 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 46 | annotations: 47 | checkov.io/skip1: CKV_K8S_21=The default namespace should not be used 48 | subjects: 49 | - kind: User 50 | name: sda-product-security 51 | apiGroup: rbac.authorization.k8s.io 52 | roleRef: 53 | kind: Role 54 | name: sda-product-security-role 55 | apiGroup: rbac.authorization.k8s.io 56 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/scanjob-parameter.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: scanjob-env-parameter 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 8 | data: 9 | DEPENDENCY_TRACK_DAYS_TO_KEEP_PROJECT_WITHOUT_UPLOAD: '{{ .Values.scanjob.ddDaysToKeepWithoutUpload }}' 10 | ERROR_NOTIFICATION_IGNORE: '{{ .Values.scanjob.errorNotificationIgnore }}' 11 | DEFECTDOJO_URL: '{{ .Values.defectdojo.url }}' 12 | JOB_EXECUTION_NAMESPACE: '{{ .Release.Namespace }}' 13 | DD_PRODUCT_NAME_TEMPLATE: '{{ .Values.scanjob.ddNameTemplate }}' 14 | DEPENDENCY_TRACK_PRODUCT_NAME_TEMPLATE: '{{ .Values.scanjob.dependencyTrackNameTemplate }}' 15 | SERVICE_ACCOUNT_NAME: '{{ .Values.serviceAccount.name }}' 16 | DEPENDENCY_TRACK_NOTIFICATION_THRESHOLDS_THIRD_PARTY: '{{ .Values.scanjob.dependencyTrack.threshold.thirdParty | toJson }}' 17 | DEPENDENCY_TRACK_NOTIFICATION_THRESHOLDS_APPLICATION: '{{ .Values.scanjob.dependencyTrack.threshold.application | toJson }}' 18 | ADDITIONAL_CAS: {{ .Values.scanjob.caBundle | default "" | b64enc | quote }} 19 | 20 | # Registry overrides: 21 | # Any environment variable that starts with REGISTRY_OVERRIDE_ and ends on FROM or TO will 22 | # be parsed. Values in between can be freely chosen as long as they are identical for each pair. 23 | # 24 | # Example: 25 | # 26 | # REGISTRY_OVERRIDE_DOCKER_IO_FROM: 'docker.io' 27 | # REGISTRY_OVERRIDE_DOCKER_IO_TO: 'docker.example.com' 28 | # 29 | # Will override docker.io in a container URI to docker.example.com 30 | # Sed-compatible regexes can be used as long as the delimiter / is escaped 31 | # The tilde (~) character cannot be used. Inputs containing tildes will be skipped. 32 | # 33 | # The first match of _FROM will be applied and follow-ups skipped. Variable pairs are processed 34 | # in alphabetical order. 35 | # 36 | {{- if .Values.scanjob.registryOverride.dockerA.from }} 37 | REGISTRY_OVERRIDE_DOCKER_A_FROM: '{{ .Values.scanjob.registryOverride.dockerA.from }}' 38 | REGISTRY_OVERRIDE_DOCKER_A_TO: '{{ .Values.scanjob.registryOverride.dockerA.to }}' 39 | {{- end }} 40 | 41 | {{- if .Values.scanjob.registryOverride.dockerIo.from }} 42 | REGISTRY_OVERRIDE_DOCKER_IO_FROM: '{{ .Values.scanjob.registryOverride.dockerIo.from }}' 43 | REGISTRY_OVERRIDE_DOCKER_IO_TO: '{{ .Values.scanjob.registryOverride.dockerIo.to }}' 44 | {{- end }} 45 | 46 | {{- if .Values.scanjob.registryOverride.default.from }} 47 | REGISTRY_OVERRIDE_DEFAULT_FROM: '{{ .Values.scanjob.registryOverride.default.from }}' 48 | REGISTRY_OVERRIDE_DEFAULT_TO: '{{ .Values.scanjob.registryOverride.default.to }}' 49 | {{- end }} 50 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/templates/slack.secret.yml: -------------------------------------------------------------------------------- 1 | #--- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: slacktoken 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "cluster-image-scanner-orchestrator.labels" . | nindent 4 }} 9 | data: 10 | SLACK_CLI_TOKEN: {{ .Values.slack.cliToken | b64enc }} 11 | -------------------------------------------------------------------------------- /deployment/helm/cluster-image-scanner-orchestrator/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for cluster-image-scanner-orchestrator. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | serviceAccount: 6 | # Specifies whether a service account should be created 7 | create: true 8 | # Automatically mount a ServiceAccount's API credentials? 9 | automount: true 10 | # Annotations to add to the service account 11 | annotations: {} 12 | # The name of the service account to use. 13 | # If not set and create is true, a name is generated using the fullname template 14 | name: "clusterscanner" 15 | 16 | securityContext: 17 | allowPrivilegeEscalation: false 18 | readOnlyRootFilesystem: true 19 | capabilities: 20 | drop: 21 | - ALL 22 | runAsUser: 56315 23 | 24 | resources: 25 | limits: 26 | memory: 128Mi 27 | requests: 28 | memory: 128Mi 29 | cpu: 50m 30 | 31 | # Additional volumeMounts on the output Deployment definition. 32 | volumeMounts: [] 33 | # - name: foo 34 | # mountPath: "/etc/foo" 35 | # readOnly: true 36 | 37 | cronSchedule: "*/1 * * * 1-5" 38 | 39 | api: 40 | host: "" 41 | version: "v1" 42 | path: "/all-image-collector-reports" 43 | pathSeparator: "/" 44 | 45 | 46 | credentials: 47 | apikey: "" 48 | signature: "" 49 | 50 | storage: 51 | # An S3 bucket through which temporary data is handed over to subflows. 52 | # Usually provided through the argo-wf installation. 53 | s3: 54 | endpoint: "s3.eu-central-1.amazonaws.com" 55 | bucket: "" 56 | insecure: false 57 | useSDKCreds: true 58 | defectdojo: 59 | branchesToKeep: "*" 60 | reportPath: "/tmp/dependency-check-results/dependency-check-report.xml" 61 | lead: "3" 62 | user: "" 63 | url: "" 64 | importType: "import" 65 | markedAsActive: "true" 66 | markedAsInactive: "false" 67 | deduplicationOnEngagement: "false" 68 | gitUrl: "https://github.com/sda-se" 69 | token: "" 70 | 71 | dependencytrack: 72 | url: "" 73 | key: "" 74 | 75 | github: 76 | appId: "" 77 | appLogin: "" 78 | installationId: "" 79 | privateKeyPem: "" 80 | 81 | registry: 82 | authJson: "" 83 | 84 | imageSourceList: 85 | image-lifetime: "https://raw.githubusercontent.com/SDA-SE/cluster-scan-test-images/master/image-lifetime.json" 86 | 87 | scanjob: 88 | ddDaysToKeepWithoutUpload: "0" # 0 disables cleanup 89 | errorNotificationIgnore: 'DiskPressure\|imagefetcher.*sj-tools-kube-system\|sj-si-verzahnung-defectdojo' 90 | ddNameTemplate: "###ENVIRONMENT### | ###NAMESPACE###" 91 | dependencyTrackNameTemplate: "###ENVIRONMENT### | ###NAMESPACE### | ###APP_NAME###" 92 | registryOverride: 93 | dockerA: 94 | from: "" 95 | to: "" 96 | dockerIo: 97 | from: '' 98 | to: "" 99 | default: 100 | from: '' 101 | to: '' 102 | dependencyTrack: 103 | threshold: 104 | application: 105 | maven: 106 | critical: 1 107 | high: 1 108 | medium: 100 109 | golang: 110 | critical: 1 111 | high: 1 112 | medium: 100 113 | npm: 114 | critical: 1 115 | high: 1 116 | medium: 100 117 | deb: 118 | critical: 8 119 | high: 20 120 | medium: 100 121 | rpm: 122 | critical: 8 123 | high: 20 124 | medium: 100 125 | pypi: 126 | critical: 8 127 | high: 20 128 | medium: 100 129 | alpine: 130 | critical: 8 131 | high: 20 132 | medium: 100 133 | thirdParty: 134 | maven: 135 | critical: 99 136 | high: 999 137 | medium: 9999 138 | golang: 139 | critical: 99 140 | high: 999 141 | medium: 9999 142 | npm: 143 | critical: 99 144 | high: 999 145 | medium: 9999 146 | deb: 147 | critical: 30 148 | high: 100 149 | medium: 9999 150 | rpm: 151 | critical: 30 152 | high: 100 153 | medium: 9999 154 | pypi: 155 | critical: 99 156 | high: 999 157 | medium: 9999 158 | alpine: 159 | critical: 30 160 | high: 100 161 | medium: 9999 162 | caBundle: # overwrite /etc/ssl/certs/ca-bundle.crt with the content BASE64 encoded placed here 163 | slack: 164 | cliToken: "" 165 | 166 | image: 167 | tag: "3" 168 | 169 | 170 | isLocal: false 171 | -------------------------------------------------------------------------------- /docs/architecture/README.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | The detailed architecture is depicted in the following dataflow diagram: 3 | ![archicture](images/technical.png) 4 | 5 | A data flow diagram: 6 | ![ClusterImageScanner Flow Diagram](images/dfd-clusterimagescanner.png) 7 | 8 | A data flow diagram of OWASP DefectDojo: 9 | ![ClusterImageScanner Flow Diagram](images/dfd-defectdojo.png) 10 | 11 | The Fetcher is in detail documented in [Fetcher](fetcher.md). 12 | The Collector is in detail documented in [Collector](collector.md). 13 | 14 | The Architectural Decisions are documented: 15 | 16 | - [Orchestration](decisions/architecture-orchestration.md) 17 | - [Image Transfer](decisions/case-study-image-transfer.md) 18 | 19 | The [threat model](threat-model.md) is documented. 20 | 21 | A [comparsion](comparsion.md) of ClusterImageScanner with other tools is documented. 22 | -------------------------------------------------------------------------------- /docs/architecture/collector.md: -------------------------------------------------------------------------------- 1 | # ClusterScanner Image Collector 2 | The _ClusterScanner Image Collector_ collectors images from a cluster. 3 | 4 | ## Solution Overview 5 | ![Overview](images/collector.png) 6 | The Cluster Scan Image Collector gets pods, parses the pod descriptions and, extracts namespaces and images, including image tags and hashes. Because mutable images (e.g. lastest) might be used, the image hash is gathered in case it is available. The image hash is not available for nonrunning cronjobs with history enabled. To get pods from within a container in a cluster, kubectl is used and defined in _entrypoint.bash_ like the following: 7 | ``` 8 | kubectl get pods -A -o json 9 | ``` 10 | 11 | The ClusterScanner Image Collector pushes the used images into the defined target repository. An example entry in the pushed flat JSON is documented in [Github](https://github.com/SDA-SE/cluster-scan-test-images/). 12 | 13 | ## ISMS compliant asset inventory 14 | ### Question: I want to relate: staff->teams->apps->repos->scan results, dependencies, etc. 15 | Let teams add annotations to deployments (e.g. `contact.sdase.org/team`). Teams are also able to add the label `app.kubernetes.io/name`. 16 | So that images for apps are grouped. For source code (repos) annotations can be added 17 | ```scm.sdase.org/source_url="https://github.com/cluster-image-scanner" 18 | scm.sdase.org/source_branch="master" 19 | scm.sdase.org/release="1.0.0" 20 | ``` 21 | We are using the ClusterImageScanner Collector for that https://github.com/SDA-SE/cluster-image-scanner/blob/master/docs/deployment/clusterscanner-image-collector.md 22 | We also use the annoation `sdase.org/description` to get a brief description (might be useful for an auditor who wants to understand what it is used for). 23 | 24 | The outcome is stored in github, here is an example https://github.com/SDA-SE/cluster-scan-test-images/blob/master/output.json. 25 | 26 | This asset catalog doesn't include stuff not in kubernetes, like apps in a google/app store. 27 | 28 | The annotations/labels are often set during build/deployment, fetching it from github-teams or other sources. 29 | 30 | The folder `description/missing-service-description.txt` in the target repo of the Collector contains namespaces with missing annoations. 31 | -------------------------------------------------------------------------------- /docs/architecture/comparsion.md: -------------------------------------------------------------------------------- 1 | # Comparsion with other scanning tools 2 | | Feature | ClusterImageScanner | Clair | AWS Inspector | 3 | |----------------------------------------------|-----------------------|-------|-------------------------| 4 | | Correlation of production images with scan results | Yes | No | Unknown | 5 | | Bill of Materials | Yes (Dependency Track)| No | No | 6 | | Vuln. in OS dependencies | Yes (Dependency Track)| Yes | Yes | 7 | | Vuln. in application dependencies | Yes | No | Yes (Enhanced scanning) | 8 | | New dependency available | Yes (Dependency Track)| No | Yes (Enhanced scanning) | 9 | | Image Lifetime scan | Yes | No | No | 10 | | BaseImage Lifetime scan | Yes | No | No | 11 | | Run as Root scan | Yes | No | No | 12 | | Malware scan | Yes | No | No | 13 | | Distroless scan | Yes | No | No | 14 | | New image version scan | Yes | No | No | 15 | | Notification of teams | Yes | No | Unknown | 16 | 17 | ## Correlation of production images with scan results 18 | The ClusterImageScanner scans images in production. Images in test envirnoments are marked as such or not scanned. 19 | 20 | ## Bill of Materials 21 | A Software Bill of Materials including all libraries/packages in the image to be scanned. 22 | 23 | ## Vuln. in OS dependencies 24 | Vulnerabilities in Operating System dependencies, like glibc, are scanned for vulnerabilities. 25 | 26 | ## Vuln. in application dependencies 27 | Vulnerabilities in application dependencies, like apache-commons in java,are scanned for vulnerabilities. 28 | 29 | ## New dependency available 30 | Scanning for new versions for dependencies. 31 | 32 | ## Image Lifetime scan 33 | See [Image Lifetime](../user/scans/image-lifetime.md). 34 | 35 | ## Base Image Lifetime scan 36 | See [BaseImage Lifetime](../user/scans/baseimage-lifetime.md). 37 | 38 | ## Run as Root scan 39 | See [Run as Root](../user/scans/run-as-root.md). 40 | 41 | ## Malware scan 42 | See [Malware](../user/scans/malware.md). 43 | 44 | ## Distroless scan 45 | See [Distroless](../user/scans/distroless.md). 46 | 47 | ## New image version scan 48 | See [New Version](../user/scans/new-version.md). 49 | 50 | ## Notification of teams 51 | The team responsibile for a namespace/container is getting notified. 52 | -------------------------------------------------------------------------------- /docs/architecture/decisions/collector-to-orchestrator-exchange.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision for the exchange of the Images (currently output.json) from the Collector to the Orchestrator 2 | 3 | ## Git 4 | - Often used technology 5 | - Easy to configure across different seperated clusters (when one cluster can not access components of another) and UI 6 | - Versioning is given (easy to browse through) 7 | - Sometimes declared as not production environment 8 | 9 | Comments: 10 | - git libraries (like https://github.com/go-git/go-git/tree/master/_examples/clone/auth/basic/access_token) exist for go, but they do not implement the fetching of a github token. This step needs to be done manually. 11 | 12 | ## S3 13 | - Often used technology 14 | - Might be hard to access the s3 of another cluster for restricted clusters 15 | - Versioning is given (harder to browse through history than git) 16 | - Production ready 17 | 18 | ## HTTP 19 | - Often used technology 20 | - Easy to configure across different seperated clusters (when one cluster can not access components of another) 21 | - No versioning 22 | - Production ready 23 | 24 | Comments: 25 | - The new collector needs 0.5 minutes for a cluster which needs over 70 minutes with the bash collector 26 | - Access control (e.g. API key via mounted secret would be used to restrict access) 27 | 28 | ## DynamoDB 29 | - Used rarely 30 | - No Versioning 31 | - Depending on the customer, it might be ready to be used or needs to be approved 32 | 33 | # Decision 34 | SDA SE woud like to use S3. S3 has derivates for different cloud providers. 35 | S3 could have a UI with minio. Could be used on premises. 36 | S3 can be easy automated with terraform. 37 | -------------------------------------------------------------------------------- /docs/architecture/decisions/defectdojo-structure.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision of DefectDojo Structure DRAFT 2 | 3 | Two strcutures are evaluated: 4 | * Grouped by organizations' product 5 | * Grouped by namespace 6 | * image/service 7 | 8 | ## Grouped by organizations' product: 9 | ### Structure 10 | * Product, | e.g. two-towers 11 | * Engagement, | , e.g. Dependency Track | quay.io/sdase/cluster-image-scanner-base 12 | * Test, | | (), 2022-12-04 21:21:49 | 2cbc6c9.47 (Dependency Track Import) 13 | 14 | ### Pro 15 | * DefectDojo statistics for products are easier to understand for humans because they are visible in an aggregated form. 16 | * Very flexible for teams 17 | 18 | ### Con 19 | * Effort to implement it for teams 20 | 21 | ## Grouped by namespace: 22 | ### Structure 23 | * Product, | e.g. two-towers 24 | * Engagement, | , e.g. Dependency Track | quay.io/sdase/cluster-image-scanner-base 25 | * Test, | | (), 2022-12-04 21:21:49 | 2cbc6c9.47 (Dependency Track Import) 26 | 27 | ### Pro 28 | * DefectDojo statistics for products are easier to understand for humans because they are visible in an aggregated form. 29 | * No effort to implement it for teams 30 | 31 | ### Con 32 | * Effort to integrate for the security scanner operators 33 | 34 | 35 | ## image/service: 36 | ### Structure 37 | * Product, | | e.g. quay.io/sdase/cluster-image-scanner-base 38 | * Engagement, | , e.g. Dependency Track | quay.io/sdase/cluster-image-scanner-base 39 | * Test, | | (), 2022-12-04 21:21:49 | 2cbc6c9.47 (Dependency Track Import) 40 | 41 | ### Pro 42 | * Current default status (no extra work) 43 | * Might be easier to understand 44 | ### Con 45 | 46 | ## Conclusion 47 | A combination of _grouped by organizations' product_ and _grouped by namespace_ is prefered. By default, _grouped by namespace_ is used. In case a label _kubernetes.io/name_ exists, the value is used instead of the namespace. 48 | -------------------------------------------------------------------------------- /docs/architecture/decisions/images/fetcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/decisions/images/fetcher.png -------------------------------------------------------------------------------- /docs/architecture/decisions/images/file_transfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/decisions/images/file_transfer.png -------------------------------------------------------------------------------- /docs/architecture/decisions/images/initial_startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/decisions/images/initial_startup.png -------------------------------------------------------------------------------- /docs/architecture/decisions/images/multi_pv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/decisions/images/multi_pv.png -------------------------------------------------------------------------------- /docs/architecture/decisions/images/one_pv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/decisions/images/one_pv.png -------------------------------------------------------------------------------- /docs/architecture/decisions/images/orchestrate_containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/decisions/images/orchestrate_containers.png -------------------------------------------------------------------------------- /docs/architecture/fetcher.md: -------------------------------------------------------------------------------- 1 | # ClusterScanner Image Fetcher 2 | The Image Fetcher fetches images from the registry and creates a tar file to distribute it. 3 | -------------------------------------------------------------------------------- /docs/architecture/images/collector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/images/collector.png -------------------------------------------------------------------------------- /docs/architecture/images/dfd-clusterimagescanner.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/images/dfd-clusterimagescanner.dia -------------------------------------------------------------------------------- /docs/architecture/images/dfd-clusterimagescanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/images/dfd-clusterimagescanner.png -------------------------------------------------------------------------------- /docs/architecture/images/dfd-defectdojo.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/images/dfd-defectdojo.dia -------------------------------------------------------------------------------- /docs/architecture/images/dfd-defectdojo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/images/dfd-defectdojo.png -------------------------------------------------------------------------------- /docs/architecture/images/technical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/architecture/images/technical.png -------------------------------------------------------------------------------- /docs/architecture/threat-model.md: -------------------------------------------------------------------------------- 1 | # Rudimentary Threat Model 2 | To satisfy the high protection requirement, rudimentary threat modeling has been conducted and documented. The measures can also be used to understand the architecture of the cluster scan. 3 | 4 | Attack: As an adversary, I compromise the cluster-scan collector image or container and try to access other projects on GitHub\ 5 | Measure: The GitHub token is restricted to access the cluster-image repository only. (HOWTO documentation outstanding) 6 | Hint: The compromise of an image might be done through different attack vectors like taking over quay.io image or the GitHub account with the corresponding infrastructure as code (Dockerfile). The compromise of a container might be performed from an other compromised container. 7 | 8 | Attack: As an adversary, I compromise the GitHub repository or organization and remove all entries to make sure that malware distributed in images is not getting detected.\ 9 | Measure: The access to the GitHub repository is restricted. (HOWTO documentation outstanding) 10 | 11 | Attack: As an adversary, I compromise the _clusterscan image collector_ image or container and want to get as much information from other pods as possible.\ 12 | Measure: The container is limited within its own namespace. (unknown) 13 | 14 | Attack: As an adversary, I compromise the cluster-scan collector image or container and want to get as much information from the API as possible.\ 15 | Measure: The service-account-token is limited to get pod manifest, only (least privileges). (done) 16 | 17 | Attack: As an adversary, I compromise the cluster-scan collector image or container and connect to other containers.\ 18 | Measure 1: The service-account-token is limited to get pod manifest, only (least privileges).\ 19 | Measure 2: The container should be able to connect to the api only and not be able to connect to other containers. (outstanding) 20 | -------------------------------------------------------------------------------- /docs/deployment/README.md: -------------------------------------------------------------------------------- 1 | This folder contains: 2 | 3 | - [Deployment of the ClusterScanner Orchestrator](deployment-orchestrator.md) 4 | - [Configuration of the source for the scan process, the repolists](repolist.md) 5 | - [Fetcher](fetcher.md) 6 | 7 | ``` 8 | argo submit --from cronwf/test-cluster-image-scanner-main -n clusterscanner 9 | ``` 10 | 11 | # Access logs in OpenSearch 12 | https://logging.XXXX/_dashboards/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(columns:!(log,stream),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:logstash,key:kubernetes.namespace_name.keyword,negate:!f,params:(query:clusterscanner),type:phrase),query:(match_phrase:(kubernetes.namespace_name.keyword:clusterscanner)))),index:logstash,interval:auto,query:(language:kuery,query:''),sort:!()) 13 | -------------------------------------------------------------------------------- /docs/deployment/fetcher.md: -------------------------------------------------------------------------------- 1 | # ClusterScanner Image Fetcher 2 | See [ClusterScanner Image Fetcher](../architecture/fetcher.md) 3 | -------------------------------------------------------------------------------- /docs/deployment/multitenant-impl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/deployment/multitenant-impl.png -------------------------------------------------------------------------------- /docs/deployment/multitenant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/deployment/multitenant.png -------------------------------------------------------------------------------- /docs/deployment/repolist.md: -------------------------------------------------------------------------------- 1 | # Image-Source-List 2 | 3 | The image-source-list configmap (often configured via _repolist.yml_) has a list of configured repositories for images. 4 | The list will most likly be created by the _ClusterScanner Image Collector_. As an alternative, it can be manually created or via build pipelines like Jenkins. 5 | 6 | The image-source-list can have entries in the following pattern: 7 | `: `. The source is a link to a specific file or to a repository with multiple files. 8 | The `` is only used for a documentation. Please make sure that `` is only used once. 9 | Example: 10 | ``` 11 | testSpecificPublicFile: https://raw.githubusercontent.com/SDA-SE/cluster-scan-test-images/master/output-short.json 12 | testSpecificProtectedFile: https://api.github.com/repos/SDA-SE/cluster-scan-test-images/contents/output.json 13 | test-alltests: https://api.github.com/repos/SDA-SE/cluster-scan-test-images/tarball 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/development/README.md: -------------------------------------------------------------------------------- 1 | # Developer 2 | ## Project Structure 3 | For each component (different scanner, orchestrator, ...) one repository is created. 4 | They follow the naming convention _clusterscanner-\[-\]_. 5 | For example: 6 | * clusterscanner-orchestrator 7 | * clusterscanner-scan-malware 8 | 9 | ## Coding Guideline 10 | The [Coding Guideline](coding-guideline.md) describes quality criteria and naming conventions. 11 | 12 | ## Testing 13 | # TODO 14 | -------------------------------------------------------------------------------- /docs/development/coding-guideline.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /docs/development/scripts.md: -------------------------------------------------------------------------------- 1 | # Helper Scripts for Development / Operations 2 | ## ClusterImageScanner Orchestration 3 | ``` 4 | # Delete all jobs 5 | argo delete --all -n clusterscanner 6 | 7 | # Delete all jobs, but not the orchestration 8 | argo delete --prefix jobs -n clusterscanner 9 | 10 | # Perform changes and submit a local job with argo-main 11 | argo delete --all -n clusterscanner ; kubectl apply -f ../deployment/base-template/scanjob.yml ; argo submit ../argo-main.yml -n clusterscanner 12 | 13 | # As alternativ to argo-main, use an existing cronjob 14 | argo submit --from cronwf/XXX-clusterscanner-main -n clusterscanner 15 | 16 | ``` 17 | ## ClusterImageScanner Collector 18 | ``` 19 | # Re-Start a cluster-scan-collector job 20 | kubectl delete job cluster-scan-collector-manual -n cluster-scan; kubectl create job --from=cronjob/cluster-scan-collector -n cluster-scan cluster-scan-collector-manual``` 21 | -------------------------------------------------------------------------------- /docs/dsomm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/dsomm.png -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/images/overview.png -------------------------------------------------------------------------------- /docs/images/usecase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/images/usecase.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # ClusterScanner 2 | ![Logo](images/logo.png) 3 | 4 | Discover vulnerabilities and container image misconfiguration in production environments. 5 | 6 | The ClusterImageScanner detects images in Kubernetes clusters and provides fast feedback based on security tests. It is recommended to run the Cluster Scanner in production environments. 7 | As a developer, the Cluster Scanner works out of the box. As a system operator, I just have to add the ClusterImageScanner in my deployment configuration, for example Argo CD. The benefit is to get feedback on what is in production and not what should be in production. For example, due to waiting for approval or due to a failing build. 8 | 9 | The use case is shown in the following figure: 10 | ![UseCase](images/usecase.png) 11 | 12 | An overview is depicted in the following figure: 13 | ![Overview](images/overview.png) 14 | 15 | 16 | * The Image Collector, as the name suggests, collects the different images. 17 | * These images can be passed to the Fetcher via the Cluster Scan, via the GitOps process, or manually. The Fetcher then converts the CSV files into JSON files and provides additional fields with information about clusters, teams and images. 18 | * These files are kept in a separate directory and from there they are passed to the scanner. 19 | * Multiple scanner are used, e.g. Dependency Track, Lifetime, Malware and further more. 20 | * The vulnerability management tool then collects the results and makes them available to the developers via a communication channel like Slack. 21 | 22 | ## Why using ClusterScanner 23 | ### Scanner 24 | The various scans help to enhance the security of images. This is required by GDPR. 25 | In addition, the ClusterScanner helps to be compliant with regulations like [DRAFT Digital Operational Resilience Act (short DORA)](https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:52020PC0595&from=EN). 26 | Some samples of what DORA (2021-07) requires and how the ClusterScanner addresses it: 27 | - Article 6: _[...] Financial entities shall use and maintain updated ICT systems, protocols and tools, which fulfil the following conditions [...]_ which the lifetime check addresses 28 | - Article 7: _[...]Financial entities shall on a continuous basis identify all sources of ICT risk, in particular the risk exposure to and from other financial entities, and assess cyber threats and ICT vulnerabilities relevant to their ICT-related business functions and information assets.[...]_, which the continuously/periodically running ClusterScanner addresses 29 | 30 | The [OWASP DevSecOps Maturity Model](https://dsomm.timo-pagel.de) provides a guideline on what security activities to perform at which organization maturity. 31 | The ClusterScanner solves the following activities directly (via tests on the left side) and enforces indirectly that an organization or team enhances the maturity. For example enforces the lifetime check, that a defined maximum image lifetime has to be set. 32 | ![DSOMM](dsomm.png) 33 | 34 | See also [scans](user/scans/README.md) for details about the scans itself. 35 | 36 | ### ClusterScanner Image Collector 37 | The _ClusterScanner Image Collector_ provides the following features: 38 | - Version of the images in production to know what was in production in case of an incident and discovery later (compliance) 39 | - Up to date asset inventory (like required by ISO 27001 A.8), specially by providing a brief description on namespaces 40 | -------------------------------------------------------------------------------- /docs/user/README.md: -------------------------------------------------------------------------------- 1 | The subfolders describe the following: 2 | - [configuration](configuration) Configuration of the ClusterScanner as a project team 3 | - [scans](scans) Response to findings from the ClusterScanner 4 | - [defectdojo](defectdojo) Response to findings from the ClusterScanner in OWASP DefectDojo 5 | -------------------------------------------------------------------------------- /docs/user/configuration/inheritance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/configuration/inheritance.png -------------------------------------------------------------------------------- /docs/user/configuration/latest-jenkins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/configuration/latest-jenkins.png -------------------------------------------------------------------------------- /docs/user/configuration/lifetime-scans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/configuration/lifetime-scans.png -------------------------------------------------------------------------------- /docs/user/configuration/namespace-filter.md: -------------------------------------------------------------------------------- 1 | # Usecases for the `namespace_filter` 2 | By using one target environment, _release_ (branch _master_) and _develop_ (branch _develop_) will be in the same environment and should be scanned. 3 | 4 | In this scenario, a release is deployed separately, resulting in the following structure in a _kustomize_ deployment: 5 | * base 6 | * overlay/release 7 | 8 | For _release_, the annotation `clusterscanner.sdase.org/skip: "false"` can be added. 9 | _develop_ is often treated as a normal branch, so that no difference between develop and an other PR is given. 10 | For such cases a `namespace_filter` with a regex is needed to give teams the flexibility to include namespaces they want. 11 | 12 | Sample Namespaces: 13 | * master: fellowship-ring-release 14 | * Develop: fellowship-ring-develop 15 | * PR 1: master: fellowship-ring-pr1 16 | * PR 2: master: fellowship-ring-pr2 17 | 18 | An example filter: 19 | ``` 20 | clusterscanner.sdase.org/namespace_filter: "^fellowship-ring-release$\|^fellowship-ring-develop$" 21 | ``` 22 | As a result, fellowship-ring-release and fellowship-ring-develop will be scanned, but not other namespaces like fellowship-ring-pr1 or fellowship-ring-pr2 with this annotation. 23 | 24 | To negate a namespace_filter (because it is not possible in regex), the annotation `clusterscanner.sdase.org/negated_namespace_filter` can be used: 25 | ``` 26 | clusterscanner.sdase.org/negated_namespace_filter: "\\-pr\\-" # one \ is needed for grep, double escpe for Kubernetes 27 | ``` 28 | As a result, fellowship-ring-release and fellowship-ring-develop will be scanned, but not other namespaces like fellowship-ring-pr1 or fellowship-ring-pr2 with this annotation. 29 | 30 | While production is important for security metrics, the branch develop is important during the development process. 31 | Therefore, we need a way distinguish them in DefectDojo. DefectDojo offers tags, so that we can add tags. 32 | 33 | A way to do it is use the existing functionality of 34 | `scm.sdase.org/source_branch="master"` 35 | which will create an engagement with the scan type and append the branch. Otherwise, the image tag (e.g. 1.2.3) is used. 36 | -------------------------------------------------------------------------------- /docs/user/defectdojo/README.md: -------------------------------------------------------------------------------- 1 | # Handling of Findings in DefectDojo 2 | 3 | Each time the ClusterImageScanner runs, findings will be uploaded to OWASP DefectDojo. 4 | The first time a finding is found, DefectDojo will create the finding as active. 5 | Each time afterwards, OWASP DefectDojo detects the finding as duplicate and creates a references. 6 | 7 | # Filter for active findings 8 | ## Open Findings View 9 | To accept a finding or to mark it as false positive, the first active finding must be chosen. 10 | 11 | To handle all open findings, go to "Findings" -> "Open Findings" and filter for non duplicates like: 12 | ![Filter](defectdojo-findings-filter.png) 13 | 14 | ## Finding View 15 | In case you are in the finding view at a duplicate, click on original and handle the original finding. 16 | ![Duplicate](finding-duplicate.png) 17 | -------------------------------------------------------------------------------- /docs/user/defectdojo/defectdojo-findings-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/defectdojo/defectdojo-findings-filter.png -------------------------------------------------------------------------------- /docs/user/defectdojo/finding-duplicate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/defectdojo/finding-duplicate.png -------------------------------------------------------------------------------- /docs/user/scans/README.md: -------------------------------------------------------------------------------- 1 | Software and infrastructure are potential points of attack. Such attacks can lead to damages, financial losses due to failures or recourse claims from customers, and more. There are various forms of investigations that protect against such threats. Manual searches for these vulnerabilities and points of attack require a great deal of effort and tie up staff in the execution. Automated scanning reduces effort and minimizes errors such as forgotten tests. The different types of scanning used, their significance, and possible reactions to the results are listed below. 2 | 3 | # Types of Scans 4 | There are a number of different types of scans that can produce different findings. Each check is introduced by a short summary of the relevance and different response possibilities. 5 | 6 | ## Misconfigurations 7 | 8 | - [Distroless](distroless.md) 9 | - [Root-User](run-as-root.md) 10 | 11 | ## Known Vulnerabilities and Malware 12 | 13 | - [Known Vulnerabilities](known-vulnerabilities.md) 14 | - [Malware](malware.md) 15 | 16 | ## Patch Management 17 | 18 | - [BaseImage Lifetime](baseimage-lifetime.md) 19 | - [Image Lifetime](image-lifetime.md) 20 | - [New version](new-version.md) 21 | 22 | # Responses to Findings 23 | When a finding is detected, the first step is to perform an analysis. The following treatments can be used for this: 24 | 25 | - Avoidance: Remove the component with the threat/vulnerability. 26 | - Mitigation: Patch a vulnerable component. In DefectDojo, vulnerabilities are automatically closed after the next scan report. 27 | - Acceptance: Accept a vulnerability. In DefectDojo, the vulnerability needs to be manually marked as Accepted. 28 | - False Positive: The finding doesn't apply. In DefectDojo, the vulnerability can be manually marked as False Positive. 29 | 30 | Not all responses apply to all scans. In the scan descriptions, the possible responses are described. 31 | We use OWASP DefectDojo as vulnerability management system to combine the findings of different tools. All described, scans are implemented by the _Cluster Scanner_. 32 | -------------------------------------------------------------------------------- /docs/user/scans/baseimage-lifetime.md: -------------------------------------------------------------------------------- 1 | # Scan BaseImage Lifetime 2 | The baseimage lifetime scan has the implication that images based on the respective container baseimage should only run for a certain period of time (e.g. 5 days) in the cluster, otherwise libraries contained in the baseimage might be outdated. 3 | 4 | ## Relevance 5 | The baseimage lifetime scan inspects the build time of first image layer (called base image) of the image under scan. In case the baseimage is older than the defined lifetime, the baseimage and therefore the image might run with outdated components. 6 | By using an update to date base image, it minimizes the potential vulnerabilities, since a newly build image is up to date with the latest software components. 7 | 8 | Container images consists of: 9 | - Application and the application dependencies 10 | - Operating system packages 11 | 12 | The BaseImage Lifetime scan checks the history for package updates like `yum.*update` or `apt.*upgrade`. In case a package update has been performed in one of the layers (determined from the image history), the latest layer will be used: 13 | ![BaseImageLifetime Scans](images/baseimage-lifetime.png) 14 | In case no update has been performed, the first layer will be used: 15 | ![BaseImageLifetime Yum Scans](images/lifetime-yum.png) 16 | 17 | ## Response 18 | Use the following threat treatments on issues: 19 | 20 | ### Avoidance 21 | Do not use the image in production. 22 | 23 | ### Mitigation 24 | Update the base image. In a Dockerfile, the image in the first line `FROM` should be checked for new versions. 25 | 26 | In case the image is a third party image, consider to build the image on your own and using the latest available application/service version. 27 | 28 | ### Acceptance 29 | The runtime of the image over the recommended time span cannot have any comprehensible reasons. Even if the application contained in the image has not been updated at all, it is recommended to start a new image build after the maximum allowed time period. 30 | 31 | Hint: The maximum defined lifetime your team agreed on can be configured. 32 | 33 | ### False Positive 34 | A false positive case would only occur if the number of days was incorrect. Since the scan does not assume that this can happen, a possible action can be neglected. 35 | -------------------------------------------------------------------------------- /docs/user/scans/distroless.md: -------------------------------------------------------------------------------- 1 | # Scan Distroless 2 | The distroless scan aims to ensure that the container image contains only the necessary parts. All other programs like shells, packet manager etc. have been removed. 3 | 4 | ## Relevance 5 | A normal container image contains, in addition to the actual application, many components that ensure the functionality. However, these components also contain parts that are not necessary for the operation of the application. These parts may contain potential vulnerabilities or could otherwise be used for abuse. In order to avoid the possible threats, the images are checked for components that are not required and a warning is given. 6 | 7 | ## Response 8 | Use the following treatments on issues: 9 | 10 | ### Avoidance 11 | Do not use the image in production. 12 | 13 | ### Mitigation 14 | If the scan detects that the image has not been cleaned of parts that are unnecessary for the operation of the image, it is recommended to build container images according to distroless. 15 | 16 | ### Acceptance 17 | As the risk owner of the application, you can accept the risks coming by the usage of a non-distroless image. A temporary acceptance is recommended, to be reminded of the risk. 18 | 19 | ### False Positive 20 | In case a shell is needed for the application and distroless is used, the finding can be marked as false positive. 21 | -------------------------------------------------------------------------------- /docs/user/scans/image-lifetime.md: -------------------------------------------------------------------------------- 1 | # Scan Image Lifetime 2 | The image lifetime scan has the implication that the respective container image should only run for a certain period of time (e.g. 5 days) in the cluster, otherwise libraries contained in the image might be outdated. 3 | 4 | ## Relevance 5 | The lifetime scan is used as an indicator for missing patch management. So that no image runs too long without updated components being included in the images. 6 | By using an update to date base image, it minimizes the potential vulnerabilities, since a newly build image is up to date with the latest software components. 7 | 8 | Container images consists of: 9 | - Application and the application dependencies 10 | - Operating system packages 11 | 12 | The creation date of the first layer is used for the Image Lifetime Scan: 13 | ![BaseImageLifetime Scans](images/baseimage-lifetime.png) 14 | 15 | ## Response 16 | Use the following threat treatments on issues: 17 | 18 | ### Avoidance 19 | Do not use the image in production. 20 | 21 | ### Mitigation 22 | In case the scanned container image exceeds the specified period of time, it is urged to create and deploy a new container image. The creation of a new container image reduces the risk of having outdated software with vulnerabilities in operation. 23 | 24 | In case the image is a third party image, consider to build the image on your own and using the latest available application/service version. 25 | 26 | ### Acceptance 27 | The runtime of the image over the recommended time span cannot have any comprehensible reasons. Even if the application contained in the image has not been updated at all, it is recommended to start a new image build after the maximum allowed time period. 28 | 29 | Hint: The maximum defined lifetime your team agreed on can be configured. 30 | 31 | ### False Positive 32 | A false positive case would only occur if the number of days was incorrect. Since the scan does not assume that this can happen, a possible action can be neglected. 33 | -------------------------------------------------------------------------------- /docs/user/scans/images/accept-add-risk-exception.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/scans/images/accept-add-risk-exception.png -------------------------------------------------------------------------------- /docs/user/scans/images/accept-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/scans/images/accept-overview.png -------------------------------------------------------------------------------- /docs/user/scans/images/accept-threat-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/scans/images/accept-threat-overview.png -------------------------------------------------------------------------------- /docs/user/scans/images/baseimage-lifetime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/scans/images/baseimage-lifetime.png -------------------------------------------------------------------------------- /docs/user/scans/images/lifetime-yum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/docs/user/scans/images/lifetime-yum.png -------------------------------------------------------------------------------- /docs/user/scans/known-vulnerabilities.md: -------------------------------------------------------------------------------- 1 | # Known Vulnerabilities via OWASP Dependency Track 2 | 3 | ## Relevance 4 | Services and artifacts often rely on external dependencies for all sorts of reasons. These dependencies may have known vulnerabilities. 5 | 6 | ## Response 7 | Use the following vulnerability treatments on issues: 8 | 9 | ### Avoidance 10 | Elimination of the vulnerability by replacing the dependency containing the vulnerability with another dependency. 11 | 12 | ### Mitigation 13 | In case a patch for the dependency containing the vulnerability is available which fixes the vulnerability, the usage of the patched version is highly recommended. After the patch is in production, the vulnerability management system OWASP DefectDojo will detect that the vulnerability is not getting reported and therefore automatically closes the vulnerability. 14 | 15 | The use of pinning a transitive dependency version and/or exclusion of a transitive dependencies MAY be performed to mitigate vulnerabilities. As pinning and exclusions come with high risks to the stability of the system, advanced tests of the whole product need to be conducted. It is not a recommended way to handle vulnerabilities, but is better than the acceptance of a vulnerability. 16 | 17 | ### Acceptance 18 | In case no patch for the direct or transitive dependency is available, or the mitigation is very cost intensive (e.g. pinning of a core library without having tests), acceptance is the last option. 19 | 20 | A simple assessment MUST be performed. The calculation SHOULD be performed with the BASE SCORE of the CVSS Calculator. The OWASP Risk Rating Methodology in this wiki might also help in case there are further questions raised during the assessment. 21 | 22 | Vulnerabilities SHOULD only be accepted temporary by setting a due date after the vulnerability is reported again in order to be able to check possible treatments again. 23 | 24 | ### False Positive 25 | Mark the finding as false positive in DefectDojo. 26 | -------------------------------------------------------------------------------- /docs/user/scans/malware.md: -------------------------------------------------------------------------------- 1 | # Scan Malware 2 | The malware scan makes sure that no malware has been introduced in the image. 3 | An Example: In case an adversary compromises a used third party component maintainer account of an image, the adversary can 4 | compromise the component. For example, a [cryptominer](https://www.csoonline.com/article/3253572/what-is-cryptojacking-how-to-prevent-detect-and-recover-from-it.html) can be introduced. Cryptominers in images are [rising](https://www.infoq.com/news/2020/12/dockerhub-image-vulnerabilities/). 5 | 6 | ## Relevance 7 | The scanning of images for viruses is a mandatory process nowadays. Various possibilities of introducing viruses cannot be completely excluded, so it is only possible to scan the images with the help of virus scanners. One technique is described here as an example: It is possible that libraries are loaded from a wrong domain due to the so-called Typosquatting. Thus, an account on a website could be taken over undetected and an – at first glance correct – URL could be given, which at second glance might be different, but which in turn offers a library that is infected with a virus. 8 | Malware, download during runtime, is not scanned. THis is one of the reasons to deny (or restrict) outgoing traffic. 9 | 10 | ## Response 11 | Use the following treatments on issues: 12 | 13 | ### Avoidance 14 | If the virus scan for an image indicates that a virus was found, the image must be analysed for it, if after the assessment it is confirmed that it is a virus, the image must be deleted immediately and a new image must be created. Before the new image is build, it is thoroughly checked that the virus is not reintroduced into the image. 15 | 16 | ### Acceptance 17 | Accepting a virus is not an option and will not be pursued further. A workaround must be found in any case. 18 | 19 | ### False Positive 20 | If the result of the check is that the scan incorrectly reports a virus, then this scan will be marked as false positive. 21 | -------------------------------------------------------------------------------- /docs/user/scans/new-version.md: -------------------------------------------------------------------------------- 1 | # Scan New Version 2 | The new version scan tests for a new version in the defined registry for the defined filter (e.g. an organization in quay.io). 3 | It searches for (simple) a new image in the registry. (simple) semantic versioning (e.g. 1.1.1 or v1.1.1) must be used by the repository. 4 | 5 | ## Relevance 6 | A new version might fix security issues, even without announcing it. 7 | 8 | ## Response 9 | Use the following treatments on issues: 10 | 11 | ### Mitigation 12 | Usage the new image version. 13 | 14 | ### Acceptance 15 | Temporary acceptance, for example because the new version will be deployed in within the next spring, is an option. 16 | Do not accept this with for a too long time (or even forever). 17 | 18 | ### False Positive 19 | If the result is a false positive, in addition to mark it as such in DefectDojo, please create an [Issue](https://github.com/SDA-SE/cluster-image-scanner/issues/new). 20 | False Positives shouldn't happen. 21 | -------------------------------------------------------------------------------- /docs/user/scans/run-as-root.md: -------------------------------------------------------------------------------- 1 | # Scan Run As Root 2 | 3 | The Root Account scan checks if the user root is found during a scan of the container images. The root user is the administrator account that can be used to exploit any rights. 4 | 5 | ## Relevance 6 | Containers running as root user are running with higher privileges than necessary. Container images are using isolation and do not have the same security level given by virtualization with hypervisor type I. 7 | 8 | ## Response 9 | Use the following treatments on issues: 10 | 11 | ### Avoidance 12 | Do not use the image. 13 | 14 | ### Acceptance 15 | After an assessment, the risk owner accept the risk. This might be the case for infrastructure related container images. In case the cluster deployment configuration enforces using non-root images, a temporary acceptance is recommended. 16 | 17 | ### False Positive 18 | In case the image runs as root, but you enforce a user via the deployment manifest in kubernetes, you might mark this as false positive. 19 | In all other cases, please inform the security team in order to adjust the check. 20 | -------------------------------------------------------------------------------- /icons/cis_logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_128.png -------------------------------------------------------------------------------- /icons/cis_logo_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_16.png -------------------------------------------------------------------------------- /icons/cis_logo_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_192.png -------------------------------------------------------------------------------- /icons/cis_logo_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_32.png -------------------------------------------------------------------------------- /icons/cis_logo_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_48.png -------------------------------------------------------------------------------- /icons/cis_logo_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_64.png -------------------------------------------------------------------------------- /icons/cis_logo_96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/icons/cis_logo_96.png -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | # Clusterscanner images 2 | 3 | This folder contains the build instructions for all container images used by the clusterscanner. 4 | 5 | ## Folder structure 6 | 7 | ```sh 8 | ├─ base # base image used for building other images 9 | ├─ process # contains images aiding in the scanning process 10 | │ ├─ imagecollector # collects information about images running in a Kubernetes cluster 11 | │ ├─ imagefetcher # fetches image contents for scanning 12 | │ ├─ image-source-fetcher # fetches and preprocesses image lists generated by the imagecollector 13 | │ └─ notifier # notifies targets of scan results 14 | └─ scan # contains all scanner modules 15 | ├─ distroless # image for distroless check 16 | ├─ lifetime # image for lifetime check 17 | ├─ malware # image for malware scan 18 | └─ runasroot # image for runasroot check 19 | ``` 20 | -------------------------------------------------------------------------------- /images/base/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .idea 3 | .idea 4 | -------------------------------------------------------------------------------- /images/base/README.md: -------------------------------------------------------------------------------- 1 | Base for the Cluster Scanner. 2 | 3 | # Usage 4 | Each child image (e.g. scanner) has to place 5 | - a module/scan name in _/clusterscanner/module-name_ with the name 6 | - the code to be executed in _/clusterscanner/module.bash_ with the code to be executed 7 | -------------------------------------------------------------------------------- /images/base/auth.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | createJWT() { 5 | # Static header fields. 6 | header='{"alg": "RS256"}' 7 | 8 | payload=$( 9 | cat </dev/null 2>&1 && pwd )" 28 | build_dir="${dir}/build" 29 | 30 | 31 | base_image="registry.access.redhat.com/ubi9/ubi-init" # minimal doesn't have useradd 32 | ctr_tools="$(buildah from --pull --quiet ${base_image})" 33 | 34 | base_image="quay.io/sdase/cluster-image-scanner-base:3" 35 | ctr="$( buildah from --pull --quiet "${base_image}")" 36 | mnt="$( buildah mount "${ctr}" )" 37 | 38 | cp -a *.bash "${mnt}/clusterscanner/" 39 | 40 | dnf_opts=( 41 | "--installroot=/mnt" 42 | "--assumeyes" 43 | "--setopt=install_weak_deps=false" 44 | "--releasever=9" 45 | "--setopt=tsflags=nocontexts,nodocs" 46 | "--quiet" 47 | ) 48 | buildah run --volume "${mnt}:/mnt" "${ctr_tools}" -- /usr/bin/dnf install -y "${dnf_opts[@]}" curl git openssl openssh 49 | buildah run --volume "${mnt}:/mnt" "${ctr_tools}" -- /usr/bin/dnf clean "${dnf_opts[@]}" all 50 | rm -rf "${mnt}"/var/{cache,log}/* "${mnt}"/tmp/* 51 | find $mnt 52 | 53 | 54 | mkdir "${mnt}/clusterscanner/.aws" 55 | chown 1001:1001 "${mnt}/clusterscanner/.aws" 56 | curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 57 | unzip awscliv2.zip 58 | ./aws/install --install-dir "${mnt}/usr/local/aws-cli" --bin-dir "${mnt}/usr/local/bin" 59 | 60 | 61 | 62 | # Get a bill of materials 63 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 64 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 65 | bill_of_materials_hash="$( ( cat "${0}"; 66 | echo "${base_bill_of_materials_hash}"; \ 67 | find ${mnt}; \ 68 | cat ./*; \ 69 | ) | sha256sum | awk '{ print $1 }' )" 70 | echo "bill_of_materials: $bill_of_materials_hash"; 71 | buildah config \ 72 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-${MODULE_NAME}" \ 73 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-${MODULE_NAME}" \ 74 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 75 | --label "${oci_prefix}.version=${VERSION}" \ 76 | --label "${oci_prefix}.title=${TITLE}" \ 77 | --label "${oci_prefix}.description=${DESCRIPTION}" \ 78 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 79 | "${ctr}" 80 | 81 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 82 | 83 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 84 | then 85 | mkdir --parent "${build_dir}" 86 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 87 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 88 | 89 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 90 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 91 | 92 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 93 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 94 | 95 | buildah rmi "${IMAGE_NAME}:${VERSION}" 96 | fi 97 | 98 | cleanup 99 | -------------------------------------------------------------------------------- /images/process/image-source-fetcher/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="image-source-fetcher" 4 | export TITLE="ClusterScanner Image Source Fetcher" 5 | export DESCRIPTION="Fetches the environment from the configmap" 6 | -------------------------------------------------------------------------------- /images/process/image-source-fetcher/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | echo "source auth.bash" 5 | source auth.bash # > /dev/null 2>&1 6 | echo "calling sp_authorize" 7 | sp_authorize || echo "Couldn't authorize, assuming the image-source-repo is accessible by anonymous." #> /dev/null 2>&1 8 | 9 | mkdir -p /clusterscanner/out/merged 10 | 11 | # TODO implement https://github.com/SDA-SE/cluster-image-scanner-aws-api?tab=readme-ov-file#request-images-list-of-a-single-cluster to overcome 6mb AWS lamba limit 12 | if [ "${S3_API_LOCATION}" != "" ]; then 13 | curl --http1.1 --location "${S3_API_LOCATION}" \ 14 | --header "x-api-key: ${S3_API_KEY}" \ 15 | --header "x-api-signature: ${S3_API_SIGNATURE}" \ 16 | | jq '( .[] | select(.team == "") ).team |= "nobody"' \ 17 | > /clusterscanner/out/metadata-api.json 18 | # test for valid JSON 19 | jq empty < /clusterscanner/out/metadata-api.json > /dev/null 20 | # test for object/array in case of not authorized 21 | [ $(jq 'type=="array"' < /clusterscanner/out/metadata-api.json) == "true" ] 22 | fi 23 | 24 | mkdir -p /clusterscanner/out/tmp 25 | i=0 26 | if [ $(ls -l /clusterscanner/image-source-list/ | grep -v total | wc -l) -eq 0 ]; then 27 | echo "No image-source-list found" 28 | else 29 | for repofile in /clusterscanner/image-source-list/*; do 30 | repourl=$(cat "${repofile}") 31 | echo "${i}: ${repofile} ${repourl}" 32 | if [ "$(echo "${repourl}" | grep -c "json")" -eq 1 ]; then # do not check for end because the branch might be in the URL 33 | sp_getfile "${repourl}" "/clusterscanner/out/${i}.json" #> /dev/null 2>&1# 34 | if [ "$(grep -c 'image' "/clusterscanner/out/${i}.json")" -eq 0 ]; then 35 | echo "Could not get repo ${repourl} from (${repofile}) or the repos doesn't include images" 36 | ls -la /clusterscanner/out/${i}.json 37 | echo "Content of /clusterscanner/out/${i}.json" 38 | cat /clusterscanner/out/${i}.json 39 | exit 1 40 | fi 41 | elif [ $(echo "${repourl}" | grep -c "ssh://") -eq 1 ]; then 42 | source git.bash # > /dev/null 2>&1 43 | GIT_REPOSITORY_PATH=$(echo ${repourl} | sed 's#.*@##g') 44 | GIT_REPOSITORY_PATH=$(echo ${GIT_REPOSITORY_PATH#*/}) 45 | GIT_SSH_REPOSITORY_HOST=$(echo ${repourl} | sed 's#.*@##g' | sed 's#/.*##g') 46 | gitAuth 47 | git clone "${repourl}" /tmp/${i} 48 | echo "Will delete service-description.json" 49 | find /tmp/${i} -type f -name ".*service-description.json" -exec rm -rf {} + || true 50 | find /tmp/${i} -type f -name "service-description.json" -exec rm -rf {} + || true 51 | find /tmp/${i} -name "*.json" -exec mv "{}" /clusterscanner/out/ \; 52 | rm -Rf /tmp/${i} 53 | else 54 | dest="/clusterscanner/out/${i}.tar" 55 | sp_getfile "${repourl}" "${dest}" "application/vnd.github.v3+json" #> /dev/null 2>&1# 56 | cd /clusterscanner/out/tmp/ 57 | tar xfv "${dest}" 58 | echo "Will delete service-description.json" 59 | find . -type f -name "service-description.json" -exec rm -rf {} + || true 60 | find . -name "*.json" -exec mv "{}" /clusterscanner/out/ \; 61 | cd - 62 | rm -Rf /clusterscanner/out/tmp/* || true 63 | rm "${dest}" 64 | fi 65 | ((i=i+1)) 66 | done 67 | fi 68 | 69 | mkdir -p /clusterscanner/out/merged 70 | echo "Will flatten JSONs" 71 | jq -s 'flatten | sort_by(.image, .namespace)' /clusterscanner/out/*.json > /clusterscanner/out/merged/merged.json 72 | sed -i 's#"scm_source_branch": null#"scm_source_branch": "notset"#g' /clusterscanner/out/merged/merged.json 73 | 74 | ls -la /clusterscanner/out/merged/ 75 | # test for valid JSON 76 | jq empty < /clusterscanner/out/merged/merged.json > /dev/null 77 | # test for object/array in case of not authorized 78 | [ $(jq 'type=="array"' < /clusterscanner/out/merged/merged.json) == "true" ] 79 | 80 | exit 0 81 | -------------------------------------------------------------------------------- /images/process/image-source-fetcher/runImageLocally.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ ! -f /tmp/private ]]; then 3 | ssh-keygen -f /tmp/private -N "" 4 | chmod 777 /tmp/private 5 | fi 6 | docker pull quay.io/sdase/cluster-image-scanner-image-source-fetcher:featcollector-test 7 | docker run -v /tmp/private:/.ssh/id_rsa/ssh-privatekey \ 8 | -v $(pwd)/../../base/auth.bash:/clusterscanner/auth.bash \ 9 | -v $(pwd)/../../base/git.bash:/clusterscanner/git.bash \ 10 | -v /tmp/cluster-image-scanner-test.2022-08-21.private-key.pem:/clusterscanner/github/github_private_key.pem \ 11 | --env GH_KEY_FILE_PATH="/clusterscanner/github/github_private_key.pem" \ 12 | --env GH_APP_LOGIN="SDA-SE" \ 13 | --env GH_APP_ID="" \ 14 | --env GH_INSTALLATION_ID="" \ 15 | --env GIT_REPOSITORY="https://api.github.com/repos/SDA-SE/cluster-image-scanner-sda-internal-test-images/tarball" \ 16 | -ti --rm quay.io/sdase/cluster-image-scanner-image-so urce-fetcher:featcollector-test 17 | -------------------------------------------------------------------------------- /images/process/imagecollector/.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | email = cluster-scan@sda-se.io 3 | name = ClusterImageScanner Bot 4 | -------------------------------------------------------------------------------- /images/process/imagecollector/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /images/process/imagecollector/bin/jq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/images/process/imagecollector/bin/jq -------------------------------------------------------------------------------- /images/process/imagecollector/bin/kubectl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/images/process/imagecollector/bin/kubectl -------------------------------------------------------------------------------- /images/process/imagecollector/config/imageNegativeList.json: -------------------------------------------------------------------------------- 1 | [ 2 | "eu-central-1.amazonaws.com/", 3 | "kiwigrid/k8s-sidecar" 4 | ] 5 | -------------------------------------------------------------------------------- /images/process/imagecollector/config/namespace-mapping.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /images/process/imagecollector/config/namespace-mapping.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "teams": { 6 | "type": "array", 7 | "items": [ 8 | { 9 | "type": "object", 10 | "properties": { 11 | "namespaces": { 12 | "type": "array", 13 | "items": [ 14 | { 15 | "type": "object", 16 | "properties": { 17 | "namespace_filter": { 18 | "type": "string" 19 | }, 20 | "description": { 21 | "type": "string" 22 | } 23 | }, 24 | "required": [ 25 | "namespace_filter", 26 | "description" 27 | ] 28 | } 29 | ] 30 | }, 31 | "configurations": { 32 | "type": "object", 33 | "properties": { 34 | "scan_lifetime_max_days": { 35 | "type": "string" 36 | }, 37 | "is_scan_lifetime": { 38 | "type": "string" 39 | }, 40 | "is_scan_baseimage_lifetime": { 41 | "type": "string" 42 | }, 43 | "is_scan_distroless": { 44 | "type": "string" 45 | }, 46 | "is_scan_malware": { 47 | "type": "string" 48 | }, 49 | "is_scan_runasroot": { 50 | "type": "string" 51 | }, 52 | "slack": { 53 | "type": "string" 54 | }, 55 | "team": { 56 | "type": "string" 57 | } 58 | }, 59 | "required": [ 60 | "team" 61 | ] 62 | } 63 | }, 64 | "required": [ 65 | "configurations" 66 | ] 67 | } 68 | ] 69 | } 70 | }, 71 | "required": [ 72 | "teams" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /images/process/imagecollector/config/registry-rename.sample.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "original": "myexample.quay.io", 4 | "replacement": "myexample.quay-proxy.sda-se.io" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /images/process/imagecollector/entrypoint.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | export HOME=/home/code 5 | id || true 6 | 7 | cd $HOME 8 | source pods.bash 9 | source git.bash 10 | IMAGE_FILENAME_JSON=/tmp/cluster-scan/output.json 11 | 12 | echo "gitAuth1" 13 | gitAuth # test that it works 14 | 15 | echo "getPods" 16 | if [ "${CLUSTER_NAME}" != "" ]; then 17 | ENVIRONMENT_NAME="${CLUSTER_NAME}" 18 | fi 19 | getPods "${IMAGE_FILENAME_JSON}" "${ENVIRONMENT_NAME}" 20 | 21 | echo "gitAuth2" 22 | gitAuth 23 | echo "gitFetch" 24 | gitFetch 25 | cp -a /tmp/cluster-scan/* /tmp/clusterscanner-remote 26 | 27 | cd /tmp/clusterscanner-remote 28 | git add "output.json" || true 29 | git commit -m "update file" "output.json" || true 30 | 31 | if [ "${IS_FETCH_DESCRIPTION}" == "true" ]; then 32 | git add description/ || true 33 | git commit -m "update file" description/ || true 34 | fi 35 | 36 | TZ="Europe/Berlin" date > lastScan 37 | git add lastScan || true 38 | 39 | echo "Old json is not to be used anymore" # TODO: Remove 40 | git rm imagesAndCluster.json || true 41 | 42 | git commit -m "update lastscan" lastScan || true 43 | git push -f origin master || true 44 | -------------------------------------------------------------------------------- /images/process/imagecollector/runImageLocally.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ ! -f /tmp/private ]]; then 3 | ssh-keygen -f /tmp/private -N "" 4 | chmod 777 /tmp/private 5 | fi 6 | docker pull quay.io/sdase/cluster-image-scanner-imagecollector:2 7 | docker run -v /tmp/private:/.ssh/id_rsa/ssh-privatekey \ 8 | -v $(pwd)/../../base/git.bash:/home/code/git.bash \ 9 | -v $(pwd)/../../base/auth.bash:/home/code/auth.bash \ 10 | -v $(pwd)/entrypoint.sh:/home/code/entrypoint.sh \ 11 | -v $(pwd)/pods.bash:/home/code/pods.bash \ 12 | --env GIT_SSH_REPOSITORY_HOST=github.com/SDA-SE/cluster-scan-test-images.git \ 13 | -user=2300:5555 -ti --rm quay.io/sdase/cluster-image-scanner-imagecollector:2 14 | -------------------------------------------------------------------------------- /images/process/imagecollector/runLocally.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export ENVIRONMENT_NAME="minikube" 4 | export IMAGE_JSON_FILE=/tmp/cluster-scan/output.json 5 | export IS_FETCH_DESCRIPTION="true" 6 | export TEAM_MAPPING='[{ "namespace_filter": "verdaccio-", "team": "operations", "description": "verdaccio is about..." }]' 7 | 8 | DESCRIPTION_ANNOTATION="sdase.org/description" 9 | export NAMESPACE_TO_SCAN_ANNOTATION="clusterscanner.sdase.org/namespace_filter" 10 | export DEFAULT_SKIP="false" 11 | export DEFAULT_SCAN_LIFETIME="true" 12 | export DEFAULT_SCAN_DISTROLESS="true" 13 | export DEFAULT_SCAN_MALWARE="false" 14 | export DEFAULT_SCAN_RUNASROOT="true" 15 | export DEFAULT_SCAN_BASEIMAGE_LIFETIME="true" 16 | export DEFAULT_SCAN_DEPENDENCY_TRACK="false" 17 | export DEFAULT_CONTAINER_TYPE="application" 18 | export SCAN_LIFETIME_ANNOTATION="clusterscanner.sdase.org/is-scan-lifetime" 19 | export SCAN_DISTROLESS_ANNOTATION="clusterscanner.sdase.org/is-scan-distroless" 20 | export SCAN_MALWARE_ANNOTATION="clusterscanner.sdase.org/is-scan-malware" 21 | export SCAN_RUNASROOT_ANNOTATION="clusterscanner.sdase.org/is-scan-runasroot" 22 | #export NAMESPACE_SKIP_REGEX="\-pr\-" 23 | export DEFAULT_TEAM_NAME="test" 24 | export DEFAULT_SLACK_POSTFIX="-security" 25 | mkdir /tmp/cluster-scan 26 | export DEFAULT_SCAN_LIFETIME_MAX_DAYS=14 27 | export NAMESPACE_MAPPINGS='{ 28 | "teams": [ 29 | { 30 | "namespaces": [ 31 | {"namespace_filter": "argo", "description": "used for deployment"}, 32 | {"namespace_filter": "kube-", "description": "kube-system is the namespace for objects created by the Kubernetes system, containing services which are needed to run Kubernetes"} 33 | ], 34 | "configurations": { 35 | "team": "operations", 36 | "is_scan_lifetime": "true", 37 | "is_scan_baseimage_lifetime": "false", 38 | "is_scan_distroless": "false", 39 | "is_scan_malware": "false", 40 | "is_scan_runasroot": "false" 41 | } 42 | } 43 | ] 44 | }' 45 | 46 | source "pods.bash" 47 | 48 | getPods "${IMAGE_JSON_FILE}" "${ENVIRONMENT_NAME}" ${DESCRIPTION_JSON_FILE} 49 | -------------------------------------------------------------------------------- /images/process/imagecollector/test/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: clusterscanner-staging-images 6 | namespace: cluster-scan 7 | annotations: 8 | app.kubernetes.io/name: cluster-scan 9 | app.kubernetes.io/instance: staging 10 | spec: 11 | accessModes: 12 | - ReadWriteMany 13 | resources: 14 | requests: 15 | storage: 10Gi 16 | --- 17 | -------------------------------------------------------------------------------- /images/process/notifier/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /images/process/notifier/README.md: -------------------------------------------------------------------------------- 1 | # ClusterImageScanner Notifier 2 | 3 | Installs slack and email binaries. 4 | 5 | The logic is in the _deployment/base/orchestration-job.yml_. 6 | -------------------------------------------------------------------------------- /images/process/notifier/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | # not used, see orchestration.yml 4 | -------------------------------------------------------------------------------- /images/process/notifier/slack-template-one-block.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "header", 4 | "text": { 5 | "type": "plain_text", 6 | "text": "###TITLE###", 7 | "emoji": true 8 | } 9 | }, 10 | { 11 | "type": "section", 12 | "fields": [ 13 | { 14 | "type": "mrkdwn", 15 | "text": "_INFO_" 16 | } 17 | ], 18 | "accessory": { 19 | "type": "image", 20 | "image_url": "https://api.slack.com/img/blocks/bkb_template_images/notificationsWarningIcon.png", 21 | "alt_text": "Warning" 22 | } 23 | 24 | }, 25 | { 26 | "type": "section", 27 | "fields": [ 28 | { 29 | "type": "mrkdwn", 30 | "text": "*image:*\n###IMAGE###" 31 | }, 32 | { 33 | "type": "mrkdwn", 34 | "text": "*application:*\n###APP_NAME###" 35 | } 36 | ] 37 | }, 38 | { 39 | "type": "section", 40 | "fields": [ 41 | { 42 | "type": "mrkdwn", 43 | "text": "*namespace:*\n###NAMESPACE###" 44 | }, 45 | { 46 | "type": "mrkdwn", 47 | "text": "*environment:*\n###ENVIRONMENT###" 48 | } 49 | ] 50 | }, 51 | { 52 | "type": "section", 53 | "fields": [ 54 | { 55 | "type": "mrkdwn", 56 | "text": "*severity:*\n###SEVERITY###" 57 | }, 58 | { 59 | "type": "mrkdwn", 60 | "text": "*scan job status:*\n###STATUS###" 61 | } 62 | ] 63 | }, 64 | { 65 | "type": "actions", 66 | "elements": [ 67 | { 68 | "type": "button", 69 | "text": { 70 | "type": "plain_text", 71 | "emoji": true, 72 | "text": "Handle potential vulnerabilities" 73 | }, 74 | "url": "###DD_LINK###", 75 | "style": "primary" 76 | } 77 | ] 78 | } 79 | ] 80 | -------------------------------------------------------------------------------- /images/process/notifier/slack-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "channel": "###SLACK_CHANNEL###", 3 | "author_name": "ClusterImageScanner", 4 | "blocks": [] 5 | } 6 | -------------------------------------------------------------------------------- /images/process/test-image/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source ./env.bash 4 | if [ $# -ne 7 ]; then 5 | echo "Parameters are not set correctly" 6 | exit 1 7 | fi 8 | 9 | REGISTRY=$1 10 | ORGANIZATION=$2 11 | IMAGE_NAME=$3 12 | VERSION=$4 13 | REGISTRY_USER=$5 14 | REGISTRY_TOKEN=$6 15 | BUILD_EXPORT_OCI_ARCHIVES=$7 16 | 17 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 18 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 19 | 20 | oci_prefix="org.opencontainers.image" 21 | 22 | trap cleanup INT EXIT 23 | cleanup() { 24 | test -n "${ctr}" && buildah rm "${ctr}" || true 25 | } 26 | 27 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 28 | build_dir="${dir}/build" 29 | 30 | 31 | base_image="registry.access.redhat.com/ubi9/ubi-init" # minimal doesn't have useradd 32 | ctr_tools="$(buildah from --pull --quiet ${base_image})" 33 | mnt_tools="$( buildah mount "${ctr_tools}" )" 34 | 35 | base_image="scratch" 36 | ctr="$( buildah from --pull --quiet "${base_image}")" 37 | mnt="$( buildah mount "${ctr}" )" 38 | 39 | dnf_opts=( 40 | "--installroot=/mnt" 41 | "--assumeyes" 42 | "--setopt=install_weak_deps=false" 43 | "--releasever=9" 44 | "--setopt=tsflags=nocontexts,nodocs" 45 | "--quiet" 46 | ) 47 | 48 | buildah run --volume "${mnt}":/mnt "${ctr_tools}" -- /usr/bin/dnf install "${dnf_opts[@]}" bash coreutils-single 49 | buildah run --volume "${mnt}:/mnt" "${ctr_tools}" -- /usr/bin/dnf clean "${dnf_opts[@]}" all 50 | rm -rf "${mnt}"/var/{cache,log}/* "${mnt}"/tmp/* 51 | mkdir "${mnt}/vulnerable-files/" 52 | cp log4j-core-2.14.0.jar "${mnt}/vulnerable-files/log4j-core-2.14.0.jar" 53 | cp module.bash "${mnt}/module.bash" 54 | 55 | echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > "${mnt}/vulnerable-files/eicar.txt" 56 | find $mnt 57 | 58 | # Get a bill of materials 59 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 60 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 61 | bill_of_materials_hash="$( ( cat "${0}"; 62 | echo "${base_bill_of_materials_hash}"; \ 63 | find ${mnt}; \ 64 | cat ./*; \ 65 | ) | sha256sum | awk '{ print $1 }' )" 66 | echo "bill_of_materials: $bill_of_materials_hash"; 67 | buildah config \ 68 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-${MODULE_NAME}" \ 69 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-${MODULE_NAME}" \ 70 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 71 | --label "${oci_prefix}.version=${VERSION}" \ 72 | --label "${oci_prefix}.title=${TITLE}" \ 73 | --label "${oci_prefix}.description=${DESCRIPTION}" \ 74 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 75 | --entrypoint "/module.bash" \ 76 | "${ctr}" 77 | 78 | buildah commit --timestamp "1655294536" --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 79 | 80 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 81 | then 82 | mkdir --parent "${build_dir}" 83 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 84 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 85 | 86 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 87 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 88 | 89 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 90 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 91 | 92 | buildah rmi "${IMAGE_NAME}:${VERSION}" 93 | fi 94 | 95 | cleanup 96 | -------------------------------------------------------------------------------- /images/process/test-image/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="test-image" 4 | export TITLE="ClusterScanner Test Image" 5 | export DESCRIPTION="Provides an Images with vulnerabilities and misconfigurations" 6 | -------------------------------------------------------------------------------- /images/process/test-image/log4j-core-2.14.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/images/process/test-image/log4j-core-2.14.0.jar -------------------------------------------------------------------------------- /images/process/test-image/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "This image will fall asleep now to be able to test it as a deployment" 5 | sleep 99999 6 | -------------------------------------------------------------------------------- /images/process/workflow-runner/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ./env.bash 5 | if [ $# -ne 7 ]; then 6 | echo "Parameters are not set correctly" 7 | exit 1 8 | fi 9 | 10 | REGISTRY=$1 11 | ORGANIZATION=$2 12 | IMAGE_NAME=$3 13 | VERSION=$4 14 | REGISTRY_USER=$5 15 | REGISTRY_TOKEN=$6 16 | BUILD_EXPORT_OCI_ARCHIVES=$7 17 | 18 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 19 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 20 | 21 | oci_prefix="org.opencontainers.image" 22 | 23 | trap cleanup INT EXIT 24 | cleanup() { 25 | test -n "${ctr}" && buildah rm "${ctr}" || true 26 | } 27 | 28 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 29 | build_dir="${dir}/build" 30 | 31 | base_image="quay.io/sdase/cluster-image-scanner-base:3" 32 | ctr="$( buildah from --pull --quiet ${base_image} )" 33 | mnt="$( buildah mount "${ctr}" )" 34 | 35 | cp module.bash "${mnt}/clusterscanner/" 36 | cp env.bash "${mnt}/clusterscanner/" 37 | cp workflow.template.yml "${mnt}/clusterscanner/" 38 | touch "${mnt}/clusterscanner/template.yml" 39 | chmod 777 "${mnt}/clusterscanner/template.yml" 40 | 41 | # set argo version 42 | export ARGO_VERSION=$(curl --silent https://api.github.com/repos/argoproj/argo-workflows/releases/latest | jq -r '.tag_name' | sed 's/"//g') 43 | # Download archive 44 | echo "Will download from https://github.com/argoproj/argo-workflows/releases/download/${ARGO_VERSION}/argo-linux-amd64.gz" 45 | curl -sLO "https://github.com/argoproj/argo-workflows/releases/download/${ARGO_VERSION}/argo-linux-amd64.gz" 46 | # Unzip 47 | gunzip argo-linux-amd64.gz 48 | # Make binary executable 49 | chmod +x argo-linux-amd64 50 | # Move binary to path 51 | mv ./argo-linux-amd64 "${mnt}/usr/local/bin/argo" 52 | # Test installation not working, as kubectl context is not given 53 | # ${mnt}/usr/local/bin/argo version 54 | 55 | curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" 56 | chmod +x ./kubectl 57 | mv ./kubectl "${mnt}/usr/local/bin" 58 | 59 | 60 | # Get a bill of materials 61 | base_bill_of_materials_hash=$(buildah inspect --type image ${base_image} | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 62 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 63 | bill_of_materials_hash="$( ( cat "${0}"; 64 | echo "${base_bill_of_materials_hash}"; \ 65 | cat ./*; \ 66 | ) | sha256sum | awk '{ print $1 }' )" 67 | echo "bill_of_materials: ${bill_of_materials_hash}"; 68 | buildah config \ 69 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-${MODULE_NAME}" \ 70 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-${MODULE_NAME}" \ 71 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 72 | --label "${oci_prefix}.version=${VERSION}" \ 73 | --label "${oci_prefix}.title=${TITLE}" \ 74 | --label "${oci_prefix}.description=${DESCRIPTION}" \ 75 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 76 | --env "GH_KEY_FILE_PATH=/clusterscanner/github/github_private_key.pem" \ 77 | --env "MAX_RUNNING_JOBS_IN_QUEUE=5" \ 78 | --env "JOB_EXECUTION_NAMESPACE=clusterscanner" \ 79 | --env DEPENDENCY_TRACK_NOTIFICATION_THRESHOLDS_DEFAULT='[{"maven": {"critical": 1, "high": 1, "medium": 100}},{"npm": {"critical": 1, "high": 1, "medium": 100}}, {"deb": {"critical": 1, "high": 10, "medium": 100}},{"rpm": {"critical": 1, "high": 10, "medium": 100}}, {"alpine": {"critical": 1, "high": 10, "medium": 100}} ]' \ 80 | "${ctr}" 81 | 82 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 83 | 84 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ]; then 85 | mkdir --parent "${build_dir}" 86 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 87 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 88 | 89 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 90 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 91 | 92 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 93 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 94 | 95 | buildah rmi "${IMAGE_NAME}:${VERSION}" 96 | fi 97 | 98 | cleanup 99 | -------------------------------------------------------------------------------- /images/process/workflow-runner/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="workflow-runner" 4 | export TITLE="ClusterImageScanner Workflow Runner" 5 | export DESCRIPTION="Starts job workflows to scan images" 6 | -------------------------------------------------------------------------------- /images/process/workflow-runner/workflow.template.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Workflow 3 | metadata: 4 | generateName: "###workflow_name###" 5 | labels: 6 | clusterscanner.sda.org/scan-id: "###SCAN_ID###" 7 | spec: 8 | serviceAccountName: ###SERVICE_ACCOUNT_NAME### 9 | synchronization: 10 | semaphore: 11 | configMapKeyRef: 12 | name: synchronization 13 | key: workflow 14 | arguments: 15 | parameters: 16 | - name: REGISTRY_SECRET 17 | value: "###REGISTRY_SECRET###" 18 | - name: DEPENDENCY_SCAN_CM 19 | value: "###DEPENDENCY_SCAN_CM###" 20 | - name: DEFECTDOJO_CM 21 | value: "###DEFECTDOJO_CM###" 22 | - name: DEFECTDOJO_SECRETS 23 | value: "###DEFECTDOJO_SECRETS###" 24 | - name: SCAN_ID 25 | value: "###SCAN_ID###" 26 | - name: team 27 | value: "###team###" 28 | - name: appname 29 | value: "###appname###" 30 | - name: appversion 31 | value: "###appversion###" 32 | - name: environment 33 | value: "###environment###" 34 | - name: namespace 35 | value: "###namespace###" 36 | - name: scm_source_branch 37 | value: "###scm_source_branch###" 38 | - name: image 39 | value: "###image###" 40 | - name: image_id 41 | value: "###image_id###" 42 | - name: slack 43 | value: "###slack###" 44 | - name: email 45 | value: "###email###" 46 | - name: is_scan_baseimage_lifetime 47 | value: "###is_scan_baseimage_lifetime###" 48 | - name: is_scan_lifetime 49 | value: "###is_scan_lifetime###" 50 | - name: is_scan_distroless 51 | value: "###is_scan_distroless###" 52 | - name: is_scan_malware 53 | value: "###is_scan_malware###" 54 | - name: is_scan_runasroot 55 | value: "###is_scan_runasroot###" 56 | - name: is_scan_new_version 57 | value: "###is_scan_new_version###" 58 | - name: is_scan_dependency_track 59 | value: "###is_scan_dependency_track###" 60 | - name: scan_lifetime_max_days 61 | value: "###scan_lifetime_max_days###" 62 | - name: new_version_image_filter 63 | value: "###new_version_image_filter###" 64 | - name: imageRegistryBase 65 | value: "###imageRegistryBase###" 66 | - name: scanjobEnvParameter 67 | value: "scanjob-env-parameter" 68 | - name: "containerType" 69 | value: "###containerType###" 70 | - name: "clusterImageScannerImageTag" 71 | value: "###clusterImageScannerImageTag###" 72 | - name: "slackTokenSecretName" 73 | value: "###slackTokenSecretName###" 74 | - name: "errorTargets" 75 | value: '###errorTargets###' 76 | 77 | 78 | workflowTemplateRef: 79 | name: scan-image-job-template 80 | -------------------------------------------------------------------------------- /images/scan/ddTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "findings": [ 3 | { 4 | "date": "", 5 | "title": "", 6 | "severity": "", 7 | "description": { 8 | "infoText": "", 9 | "image": "", 10 | "cluster": "", 11 | "namespace": "" 12 | }, 13 | "mitigation": "", 14 | "impact": "", 15 | "references": "" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /images/scan/distroless/README.md: -------------------------------------------------------------------------------- 1 | # Clusterscanner Scan distroless 2 | 3 | This repository contains the image configuration for the distroless scanner, used in the containerized clusterscan. 4 | 5 | The purpose of this image is to scan container image tarballs for shells, indicating a non-distroless image. 6 | -------------------------------------------------------------------------------- /images/scan/distroless/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 7 ]; then 5 | echo "Parameters are not set correctly" 6 | exit 1 7 | fi 8 | 9 | REGISTRY=$1 10 | ORGANIZATION=$2 11 | IMAGE_NAME=$3 12 | VERSION=$4 13 | REGISTRY_USER=$5 14 | REGISTRY_TOKEN=$6 15 | BUILD_EXPORT_OCI_ARCHIVES=$7 16 | 17 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 18 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 19 | 20 | oci_prefix="org.opencontainers.image" 21 | descr="Clusterscan Scanner Distroless" 22 | 23 | # shellcheck source=env.bash 24 | source env.bash 25 | 26 | trap cleanup INT EXIT 27 | cleanup() { 28 | test -n "${ctr}" && buildah rm "${ctr}" || true 29 | } 30 | 31 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 32 | build_dir="${dir}/build" 33 | 34 | base_image="quay.io/sdase/cluster-image-scanner-base:3" 35 | ctr="$( buildah from --pull --quiet "${base_image}")" 36 | mnt="$( buildah mount "${ctr}" )" 37 | 38 | # shellcheck source=../../base/scan-common.bash 39 | source "${mnt}/clusterscanner/scan-common.bash" 40 | 41 | JSONFILE="${mnt}/clusterscanner/distroless.json" 42 | 43 | cp module.bash "${mnt}/clusterscanner/" 44 | cp env.bash "${mnt}/clusterscanner/" 45 | cp ../ddTemplate.json "$JSONFILE" 46 | 47 | JSON=$(<"$JSONFILE") 48 | JSON=$(add_json_field infoText "$infoText" "$JSON" description) #infoText comes from env.bash 49 | JSON=$(add_json_field title "No distroless used" "$JSON") 50 | JSON=$(add_json_field severity "Medium" "$JSON") 51 | 52 | if [ -z "$JSON" ]; then 53 | echo "failed to prepare JSON template" 54 | exit 1 55 | else 56 | echo "$JSON" > "$JSONFILE" 57 | fi 58 | 59 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/distroless.md Relevance "$JSONFILE" 60 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/distroless.md Response "$JSONFILE" 61 | 62 | # Get a bill of materials 63 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 64 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 65 | bill_of_materials_hash="$( ( cat "${0}"; 66 | echo "${base_bill_of_materials_hash}"; \ 67 | cat ./*; \ 68 | ) | sha256sum | awk '{ print $1 }' )" 69 | echo "bill_of_materials: $bill_of_materials_hash"; 70 | buildah config \ 71 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-distroless" \ 72 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-distroless" \ 73 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 74 | --label "${oci_prefix}.version=${VERSION}" \ 75 | --label "${oci_prefix}.title=Clusterscanner Scanner Distroless" \ 76 | --label "${oci_prefix}.description=${descr}" \ 77 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 78 | --env 'RESULT_CACHING_HOURS=168' \ 79 | "${ctr}" 80 | 81 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 82 | 83 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 84 | then 85 | mkdir --parent "${build_dir}" 86 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 87 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 88 | 89 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 90 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 91 | 92 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 93 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 94 | 95 | buildah rmi "${IMAGE_NAME}:${VERSION}" 96 | fi 97 | 98 | cleanup 99 | -------------------------------------------------------------------------------- /images/scan/distroless/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="scan-distroless" 4 | export infoText="Image has shell executables and therefore most likely is not distroless" 5 | -------------------------------------------------------------------------------- /images/scan/distroless/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | # checks if an image appears to be distroless 5 | # by looking for a shell 6 | # 7 | # Usage example 8 | # export IMAGE_TAR_PATH=/path/to/image.tar" 9 | # export ARTIFACTS_PATH=/path/to/output/dir" 10 | # ./module.bash 11 | 12 | # shellcheck source=../../base/scan-common.bash 13 | source /clusterscanner/scan-common.bash 14 | 15 | scan_result_pre 16 | 17 | _shell_found=0 18 | 19 | for sh in "bin/sh$" "bin/bash$" "bin/dash$" "bin/zsh$" "bin/ash$"; do 20 | if tar -tf "${IMAGE_TAR_PATH}" | grep -q ${sh}; then 21 | _sh_name=$(echo "${sh}" | rev | cut -c2- | rev) 22 | echo "${_sh_name} found" 23 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ".shells += [\"${_sh_name}\"]") 24 | _shell_found=1 25 | fi 26 | done 27 | 28 | if [[ ${_shell_found} -eq 1 ]]; then 29 | cp /clusterscanner/distroless.json "${ARTIFACTS_PATH}/distroless.json" 30 | #infoText should be part of the environment at this point. Failing otherwise 31 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": true, \"infoText\": \"${infoText:?}\"}") 32 | else 33 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": false}") 34 | fi 35 | 36 | scan_result_post 37 | 38 | exit 0 39 | -------------------------------------------------------------------------------- /images/scan/lifetime/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 7 ]; then 5 | echo "Parameters are not set correctly" 6 | exit 1 7 | fi 8 | 9 | REGISTRY=$1 10 | ORGANIZATION=$2 11 | IMAGE_NAME=$3 12 | VERSION=$4 13 | REGISTRY_USER=$5 14 | REGISTRY_TOKEN=$6 15 | BUILD_EXPORT_OCI_ARCHIVES=$7 16 | 17 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 18 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 19 | 20 | oci_prefix="org.opencontainers.image" 21 | descr="Clusterscan Scanner Lifetime" 22 | 23 | trap cleanup INT EXIT 24 | cleanup() { 25 | test -n "${ctr}" && buildah rm "${ctr}" || true 26 | } 27 | 28 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 29 | build_dir="${dir}/build" 30 | 31 | base_image="quay.io/sdase/cluster-image-scanner-base:3" 32 | ctr="$( buildah from --pull --quiet "${base_image}")" 33 | mnt="$( buildah mount "${ctr}" )" 34 | 35 | # shellcheck source=../../base/scan-common.bash 36 | source "${mnt}/clusterscanner/scan-common.bash" 37 | 38 | JSONFILE="${mnt}/clusterscanner/lifetime.json" 39 | 40 | cp module.bash "${mnt}/clusterscanner/" 41 | cp env.bash "${mnt}/clusterscanner/" 42 | cp ../ddTemplate.json "$JSONFILE" 43 | 44 | JSON=$(<"$JSONFILE") 45 | JSON=$(add_json_field severity "Medium" "$JSON") 46 | 47 | if [ -z "$JSON" ]; then 48 | echo "failed to prepare JSON template" 49 | exit 1 50 | else 51 | echo "$JSON" > "$JSONFILE" 52 | fi 53 | 54 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/image-lifetime.md Relevance "$JSONFILE" 55 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/image-lifetime.md Response "$JSONFILE" 56 | 57 | # Get a bill of materials 58 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 59 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 60 | bill_of_materials_hash="$( ( cat "${0}"; 61 | echo "${base_bill_of_materials_hash}"; \ 62 | cat ./*; \ 63 | ) | sha256sum | awk '{ print $1 }' )" 64 | echo "bill_of_materials: $bill_of_materials_hash"; 65 | buildah config \ 66 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-scan-lifetime" \ 67 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-scan-lifetime" \ 68 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 69 | --label "${oci_prefix}.version=${VERSION}" \ 70 | --label "${oci_prefix}.title=Clusterscanner Scanner Lifetime" \ 71 | --label "${oci_prefix}.description=${descr}" \ 72 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 73 | --env 'MAX_IMAGE_LIFETIME_IN_DAYS=14' \ 74 | --env 'MIN_IMAGE_LIFETIME_IN_YEARS_TO_BE_REPRODUCIBLE=30' \ 75 | --env 'IS_BASE_IMAGE_LIFETIME_SCAN=false' \ 76 | --env 'RESULT_CACHING_HOURS=20' \ 77 | "${ctr}" 78 | 79 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 80 | 81 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 82 | then 83 | mkdir --parent "${build_dir}" 84 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 85 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 86 | 87 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 88 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 89 | 90 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 91 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 92 | 93 | buildah rmi "${IMAGE_NAME}:${VERSION}" 94 | fi 95 | 96 | cleanup 97 | -------------------------------------------------------------------------------- /images/scan/lifetime/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="scan-lifetime" 4 | 5 | if [ "${IS_BASE_IMAGE_LIFETIME_SCAN}" == "true" ]; then 6 | export MODULE_NAME="scan-baseimage-lifetime" 7 | fi 8 | -------------------------------------------------------------------------------- /images/scan/malware/README.md: -------------------------------------------------------------------------------- 1 | # ClusterScanner Scan malware 2 | 3 | ## Development 4 | On an ubuntu:20.10 with buildah the image is not getting build due to freshclam failure: 5 | ``` 6 | can't open/parse the config file /etc/freshclam.conf 7 | ``` 8 | On github, it works. 9 | -------------------------------------------------------------------------------- /images/scan/malware/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="scan-malware" 4 | -------------------------------------------------------------------------------- /images/scan/malware/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # shellcheck source=../../base/scan-common.bash 6 | source ./scan-common.bash 7 | 8 | # Initialize variables with default values 9 | MAX_FILESIZE="4000M" 10 | # IMAGE_BY_HASH="" 11 | # IMAGE_TAR_PATH="" 12 | # ARTIFACTS_PATH="" 13 | # 14 | # while getopts "i:t:s:a:" opt; do 15 | # case $opt in 16 | # i) 17 | # IMAGE_BY_HASH="$OPTARG" 18 | # ;; 19 | # t) 20 | # IMAGE_TAR_PATH="$OPTARG" 21 | # ;; 22 | # s) 23 | # MAX_FILESIZE="$OPTARG" 24 | # ;; 25 | # a) 26 | # ARTIFACTS_PATH="$OPTARG" 27 | # ;; 28 | # \?) 29 | # echo "Invalid option: -$OPTARG" >&2 30 | # exit 1 31 | # ;; 32 | # :) 33 | # echo "Option -$OPTARG requires an argument." >&2 34 | # exit 1 35 | # ;; 36 | # esac 37 | # done 38 | # 39 | # if [[ -z "$IMAGE_BY_HASH" || -z "$IMAGE_TAR_PATH" || -z "$ARTIFACTS_PATH" ]]; then 40 | # echo "Error: one or more parameters is empty" >&2 41 | # exit 1 42 | # fi 43 | # 44 | # >&3 echo "i: $IMAGE_BY_HASH, t: $IMAGE_TAR_PATH, s: $MAX_FILESIZE, a: $ARTIFACTS_PATH" 45 | 46 | scan_result_pre 47 | 48 | JSONFILE="${ARTIFACTS_PATH}/malware.json" 49 | 50 | echo "Scanning ${IMAGE_BY_HASH} ${IMAGE} in ${IMAGE_TAR_PATH}" 51 | LOGFILE="${ARTIFACTS_PATH}/clamav-$(date +%s).log" 52 | echo "CLAMAV_SIGNATURE_IGNORE: ${CLAMAV_SIGNATURE_IGNORE}" 53 | echo "Tar-size: " 54 | ls -la "${IMAGE_TAR_PATH}" 55 | set +e 56 | clamscan --max-filesize="${MAX_FILESIZE}" --max-scansize="${MAX_FILESIZE}" --infected --recursive --log="${LOGFILE}" "${IMAGE_TAR_PATH}" 57 | CLAM_RETURN_CODE=$? 58 | echo "CLAM_RETURN_CODE: ${CLAM_RETURN_CODE}" 59 | set -e 60 | 61 | if [ ${CLAM_RETURN_CODE} -eq 1 ]; then 62 | echo "ClamAV found malware, checking for ignored signatures" 63 | set +e # grep -v returns 1 if no lines are found 64 | detectedMalware=$(cat "${LOGFILE}" | grep " FOUND" | awk '{print $2}' | grep -v "${CLAMAV_SIGNATURE_IGNORE}") 65 | echo "$detectedMalware" > /tmp/detectedMalware.txt 66 | detectedMalwareCount=$(grep -c -v '^$' /tmp/detectedMalware.txt) 67 | set -e 68 | echo "detectedMalwareCountFiltered=${detectedMalwareCount}" # number of malware without signatures that should be ignored 69 | 70 | fi 71 | 72 | if [ ${CLAM_RETURN_CODE} -eq 0 ] || [ ${detectedMalwareCount} -eq 0 ]; then 73 | echo "JSON_RESULT: ${JSON_RESULT}" 74 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": false}") 75 | elif [ ${CLAM_RETURN_CODE} -eq 1 ]; then 76 | infoText="Potential malware found in ${IMAGE_BY_HASH}\nSignature(s): ${detectedMalware}" 77 | echo "${infoText}" 78 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": true, \"infoText\": \"${infoText}\"}") 79 | cp /clusterscanner/malware.json "$JSONFILE" 80 | sanitizedInfoText=$(echo "${infoText}" | sed 's#{#\\\\{#g' | sed 's#}#\\\\}#g' | tr -d '|') 81 | JSON=$(<"$JSONFILE") 82 | JSON=$(add_json_field infoText "$sanitizedInfoText" "$JSON" description) 83 | 84 | if [ -z "$JSON" ]; then 85 | echo "error creating result JSON" 86 | exit 1 87 | else 88 | echo "$JSON" > "$JSONFILE" 89 | fi 90 | else 91 | infoText="Malware exited with the unexpected code ${CLAM_RETURN_CODE} in ${IMAGE_BY_HASH}, log: ${LOGFILE}" 92 | echo "${infoText}" 93 | cat "${LOGFILE}" 94 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"failed\", \"infoText\": \"${infoText}\"}") 95 | fi 96 | 97 | scan_result_post 98 | 99 | if [ ${CLAM_RETURN_CODE} -eq 0 ] || [ "${detectedMalwareCount}" -eq 0 ]; then 100 | exit 0 101 | fi 102 | exit ${CLAM_RETURN_CODE} 103 | -------------------------------------------------------------------------------- /images/scan/malware/runLocally.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # mkdir $(pwd)/test/image.tar 4 | # docker save -o $(pwd)/test/image.tar 5 | docker pull quay.io/sdase/cluster-image-scanner-scan-malware:2 # make sure to have latest malware signatures 6 | docker run -e CLAMAV_SIGNATURE_IGNORE="EICAR2" -v "${PWD}/module.bash:/clusterscanner/module.bash" -v /tmp/log:/clusterscanner/data/ -v /tmp/image.tar:/clusterscanner/images/image.tar quay.io/sdase/cluster-image-scanner-scan-malware:2 7 | -------------------------------------------------------------------------------- /images/scan/new-version/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | if [ $# -ne 7 ]; then 5 | echo "Parameters are not set correctly" 6 | exit 1 7 | fi 8 | 9 | REGISTRY=$1 10 | ORGANIZATION=$2 11 | IMAGE_NAME=$3 12 | VERSION=$4 13 | REGISTRY_USER=$5 14 | REGISTRY_TOKEN=$6 15 | BUILD_EXPORT_OCI_ARCHIVES=$7 16 | 17 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 18 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 19 | 20 | oci_prefix="org.opencontainers.image" 21 | descr="Check for new version of a semantic image tag in registry" 22 | 23 | trap cleanup INT EXIT 24 | cleanup() { 25 | test -n "${ctr}" && buildah rm "${ctr}" || true 26 | } 27 | 28 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 29 | build_dir="${dir}/build" 30 | 31 | base_image="quay.io/sdase/cluster-image-scanner-base:3" 32 | ctr="$( buildah from --pull --quiet "${base_image}")" 33 | mnt="$( buildah mount "${ctr}" )" 34 | 35 | # shellcheck source=../../base/scan-common.bash 36 | source "${mnt}/clusterscanner/scan-common.bash" 37 | 38 | JSONFILE="${mnt}/clusterscanner/new-version.json" 39 | 40 | cp module.bash "${mnt}/clusterscanner/" 41 | cp env.bash "${mnt}/clusterscanner/" 42 | cp ../ddTemplate.json "$JSONFILE" 43 | 44 | JSON=$(<"$JSONFILE") 45 | JSON=$(add_json_field severity "Medium" "$JSON") 46 | if [ -z "$JSON" ]; then 47 | echo "error generating JSON file" 48 | exit 1 49 | else 50 | echo "$JSON" > "$JSONFILE" 51 | fi 52 | 53 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/new-version.md Relevance "$JSONFILE" 54 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/new-version.md Response "$JSONFILE" 55 | 56 | # Get a bill of materials 57 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 58 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 59 | bill_of_materials_hash="$( ( cat "${0}"; 60 | echo "${base_bill_of_materials_hash}"; \ 61 | cat ./*; \ 62 | ) | sha256sum | awk '{ print $1 }' )" 63 | echo "bill_of_materials: $bill_of_materials_hash"; 64 | buildah config \ 65 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-scan-new-version" \ 66 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-scan-new-version" \ 67 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 68 | --label "${oci_prefix}.version=${VERSION}" \ 69 | --label "${oci_prefix}.title=Clusterscanner Scanner New Version" \ 70 | --label "${oci_prefix}.description=${descr}" \ 71 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 72 | --env 'IMAGE=' \ 73 | --env 'IMAGE_SCAN_POSITIVE_FILTER=^quay.io/sdase/.*:(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' \ 74 | --env 'RESULT_CACHING_HOURS=4' \ 75 | "${ctr}" 76 | 77 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 78 | 79 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 80 | then 81 | mkdir --parent "${build_dir}" 82 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 83 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 84 | 85 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 86 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 87 | 88 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 89 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 90 | 91 | buildah rmi "${IMAGE_NAME}:${VERSION}" 92 | fi 93 | 94 | cleanup 95 | -------------------------------------------------------------------------------- /images/scan/new-version/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="scan-new-version" 4 | -------------------------------------------------------------------------------- /images/scan/new-version/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | # checks if an image a new images exists 5 | 6 | # shellcheck source=../../base/scan-common.bash 7 | source ./scan-common.bash 8 | 9 | JSONFILE="${ARTIFACTS_PATH}/new-version.json" 10 | 11 | scan_result_pre 12 | 13 | function testNewImageAndReport { 14 | imageToTest=$1 15 | 16 | 17 | imageExists=true 18 | skopeo inspect "docker://${imageToTest}" > /dev/null || imageExists=false 19 | if [ ${imageExists} == false ]; then return ; fi 20 | 21 | infoText="Image has a new tag, at least ${imageToTest}" 22 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": true, \"infoText\": \"${infoText}\", \"newVersion\": \"${imageToTest}\"}") 23 | 24 | cp /clusterscanner/new-version.json "${JSONFILE}" 25 | JSON=$(<"${JSONFILE}") 26 | JSON=$(add_json_field infoText "$infoText" "$JSON" description) 27 | JSON=$(add_json_field title "Image Has a New Version" "$JSON") 28 | JSON=$(add_json_field severity "Low" "$JSON") 29 | if [ -z "$JSON" ]; then 30 | echo "JSON generation with base data failed" 31 | exit 1 32 | else 33 | echo "$JSON" > "$JSONFILE" 34 | fi 35 | 36 | scan_result_post 37 | exit 0 38 | } 39 | 40 | if ! [[ ${IMAGE} =~ ${IMAGE_SCAN_POSITIVE_FILTER} ]]; then 41 | echo "Image ${IMAGE} is not whitelisted via '${IMAGE_SCAN_POSITIVE_FILTER}', no scan" 42 | exit 0 43 | fi 44 | 45 | if [[ "${IMAGE}" =~ "@sha256" ]]; then 46 | echo "Image ${IMAGE} is an image with a hash, no scan" 47 | exit 0 48 | fi 49 | 50 | 51 | 52 | # Is image tag (simple) semantic (normally, - would be allowed) 53 | imageTag=$(echo "${IMAGE}" | sed 's#.*/.*:##') 54 | echo "Analysing IMAGE: ${IMAGE} with tag ${imageTag}" 55 | if ! [[ "${imageTag}" =~ ^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then 56 | echo "${IMAGE} has tag ${imageTag} which is not (simple) semantic, stopping test (exit code 0)" 57 | exit 0 58 | fi 59 | echo "Image Tag: ${imageTag}" 60 | MAJOR=$(echo "${imageTag}" | tr '.' "\n" | sed -n 1p) 61 | MINOR=$(echo "${imageTag}" | tr '.' "\n" | sed -n 2p) 62 | PATCH=$(echo "${imageTag}" | tr '.' "\n" | sed -n 3p) 63 | IMAGE_WITHOUT_TAG=$(echo "${IMAGE}" | sed 's#:.*##') 64 | echo "IMAGE_WITHOUT_TAG: ${IMAGE_WITHOUT_TAG}" 65 | 66 | (( patchPlusOne = PATCH + 1 )) 67 | testNewImageAndReport "${IMAGE_WITHOUT_TAG}:${MAJOR}.${MINOR}.${patchPlusOne}" 68 | 69 | (( minorPlusOne = MINOR + 1 )) 70 | testNewImageAndReport "${IMAGE_WITHOUT_TAG}:${MAJOR}.${minorPlusOne}.0" 71 | if [[ "${MAJOR}" =~ ^v ]]; then 72 | let majorPlusOne=$(echo ${MAJOR} | sed 's#v##g')+1 73 | majorPlusOne="v${majorPlusOne}" 74 | else let majorPlusOne=${MAJOR}+1 75 | fi 76 | 77 | 78 | testNewImageAndReport "${IMAGE_WITHOUT_TAG}:${majorPlusOne}.0.0" 79 | 80 | echo "No new version available" 81 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": false}") 82 | 83 | scan_result_post 84 | 85 | echo "result file:" 86 | cat "$JSONFILE" 87 | 88 | exit 0 89 | -------------------------------------------------------------------------------- /images/scan/new-version/runLocally.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export IMAGE=quay.io/sdase/cluster-image-scanner-imagecollector:2.0.339 # last Version 4 | export IMAGE=quay.io/sdase/cluster-image-scanner-imagecollector:1.9.9 5 | export IMAGE=quay.io/sdase/cluster-image-scanner-imagecollector:2.0.338 6 | export IMAGE=dexidp/dex:v2.29.0 7 | export IMAGE=quay.io/sdase/cluster-image-scanner-test-image:2.0.22 8 | export IMAGE_SCAN_POSITIVE_FILTER="quay.io/sdase/|dexidp/dex" 9 | 10 | docker run -v $(pwd)/module.bash:/clusterscanner/module.bash --env "IMAGE=${IMAGE}" -ti quay.io/sdase/cluster-image-scanner-scan-new-version:2 11 | -------------------------------------------------------------------------------- /images/scan/parseMarkdownToCreateDefectDojoText.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SOURCE_FILE=$1 # md from doc 4 | TARGET=$2 # What to extract Response or Relevance 5 | TARGET_FILE=$3 # Target JSON file 6 | TEMP_FILE="/tmp/tmp-file-to-create-markdown" 7 | heading="##" 8 | 9 | if [ "${TARGET}" != "Response" ] && [ "${TARGET}" != "Relevance" ]; then 10 | echo "TARGET not set correctly" 11 | exit 1 12 | fi 13 | 14 | cp "${SOURCE_FILE}" "${TEMP_FILE}" 15 | 16 | sed -i.bak "/${heading} ${TARGET}/,\$!d" "${TEMP_FILE}" 17 | sed -i.bak "s/${heading} ${TARGET}/${TARGET}/g" ${TEMP_FILE} 18 | sed -i.bak "/^${heading} [a-zA-Z]/,\$d" ${TEMP_FILE} 19 | sed -i.bak "s/##/#/g" ${TEMP_FILE} 20 | sed -i.bak "s/# //g" ${TEMP_FILE} 21 | sed -i.bak "s/#/\n/g" ${TEMP_FILE} 22 | 23 | extract=$(jq -R -r -s -c '.' ${TEMP_FILE}) 24 | references=$(jq -r '.findings[].references' "${TARGET_FILE}") 25 | echo "references: ${references}" 26 | 27 | # heredoc for correctly-parsed newlines 28 | extract=$(cat < "${TARGET_FILE}" 35 | -------------------------------------------------------------------------------- /images/scan/runasroot/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 7 ]; then 5 | echo "Parameters are not set correctly" 6 | exit 1 7 | fi 8 | 9 | REGISTRY=$1 10 | ORGANIZATION=$2 11 | IMAGE_NAME=$3 12 | VERSION=$4 13 | REGISTRY_USER=$5 14 | REGISTRY_TOKEN=$6 15 | BUILD_EXPORT_OCI_ARCHIVES=$7 16 | 17 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 18 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 19 | 20 | oci_prefix="org.opencontainers.image" 21 | descr="Clusterscan Scanner run as root" 22 | 23 | trap cleanup INT EXIT 24 | cleanup() { 25 | test -n "${ctr}" && buildah rm "${ctr}" || true 26 | } 27 | 28 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 29 | build_dir="${dir}/build" 30 | 31 | base_image="quay.io/sdase/cluster-image-scanner-base:3" 32 | ctr="$( buildah from --pull --quiet "${base_image}")" 33 | mnt="$( buildah mount "${ctr}" )" 34 | 35 | # shellcheck source=../../base/scan-common.bash 36 | source "${mnt}/clusterscanner/scan-common.bash" 37 | 38 | JSONFILE="${mnt}/clusterscanner/runAsRoot.json" 39 | 40 | cp module.bash "${mnt}/clusterscanner/" 41 | cp env.bash "${mnt}/clusterscanner/" 42 | cp ../ddTemplate.json "$JSONFILE" 43 | 44 | JSON=$(<"$JSONFILE") 45 | JSON=$(add_json_field severity "Medium" "$JSON") 46 | JSON=$(add_json_field title "Image Potentially Running as Root" "$JSON") 47 | 48 | if [ -z "$JSON" ]; then 49 | echo "error creating JSON template" 50 | exit 1 51 | else 52 | echo "$JSON" > "$JSONFILE" 53 | fi 54 | 55 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/run-as-root.md Relevance "$JSONFILE" 56 | ../parseMarkdownToCreateDefectDojoText.bash ../../../docs/user/scans/run-as-root.md Response "$JSONFILE" 57 | 58 | # Get a bill of materials 59 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 60 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 61 | bill_of_materials_hash="$( ( cat "${0}"; 62 | echo "${base_bill_of_materials_hash}"; \ 63 | cat ./*; \ 64 | ) | sha256sum | awk '{ print $1 }' )" 65 | echo "bill_of_materials: $bill_of_materials_hash"; 66 | buildah config \ 67 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-scan-runasroot" \ 68 | --label "${oci_prefix}.source=https://github.com/SDA-SE/clusterscanner-scan-runasroot" \ 69 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 70 | --label "${oci_prefix}.version=${VERSION}" \ 71 | --label "${oci_prefix}.title=Clusterscanner Scanner run as root" \ 72 | --label "${oci_prefix}.description=${descr}" \ 73 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 74 | --env 'RESULT_CACHING_HOURS=168' \ 75 | "${ctr}" 76 | 77 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 78 | 79 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 80 | then 81 | mkdir --parent "${build_dir}" 82 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 83 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 84 | 85 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 86 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 87 | 88 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 89 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 90 | 91 | buildah rmi "${IMAGE_NAME}:${VERSION}" 92 | fi 93 | 94 | cleanup 95 | -------------------------------------------------------------------------------- /images/scan/runasroot/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="scan-runasroot" 4 | -------------------------------------------------------------------------------- /images/scan/runasroot/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # shellcheck source=../../base/scan-common.bash 6 | source /clusterscanner/scan-common.bash 7 | 8 | scan_result_pre 9 | echo "Checking image exists ${IMAGE_BY_HASH}" 10 | if [ "${SKOPEO_INSPECT_PARAMETER}" != "" ]; then 11 | SKOPEO_CONFIG="--config \"${SKOPEO_INSPECT_PARAMETER}\"" 12 | else 13 | SKOPEO_CONFIG="" 14 | fi 15 | skopeo inspect ${SKOPEO_CONFIG} "docker://${IMAGE_BY_HASH}" > /dev/null || exit="true" 16 | if [ "${exit}" == "true" ]; then 17 | echo "skopeo inspect ${SKOPEO_CONFIG} \"docker://${IMAGE_BY_HASH}\"" 18 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"failed\"}") 19 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ".errors += [{\"errorText\": \"skopeo inspect failed for image\", \"command\": \"skopeo inspect docker://${IMAGE_BY_HASH}\"}]") 20 | scan_result_post 21 | exit 1 22 | fi 23 | 24 | echo "get User from docker manifest" 25 | _imageUser=$(skopeo inspect "${SKOPEO_CONFIG}" docker://"${IMAGE_BY_HASH}" | jq '.config.User // "ROOT"' | tr -d \") 26 | 27 | if [[ "xX${_imageUser,,}" =~ ^xX(root|0) ]]; then 28 | cp /clusterscanner/runAsRoot.json "${ARTIFACTS_PATH}/runAsRoot.json" 29 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": true, \"infoText\": \"Image is potentially running as root\"}") 30 | else 31 | JSON_RESULT=$(echo "${JSON_RESULT}" | jq -Sc ". += {\"status\": \"completed\", \"finding\": false}") 32 | fi 33 | 34 | scan_result_post 35 | 36 | exit 0 37 | -------------------------------------------------------------------------------- /images/scan/syft/README.md: -------------------------------------------------------------------------------- 1 | # Cluster Scan SBOM Generation 2 | Generation of SBOM with Syft 3 | -------------------------------------------------------------------------------- /images/scan/syft/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 7 ]; then 5 | echo "Parameters are not set correctly" 6 | exit 1 7 | fi 8 | 9 | REGISTRY=$1 10 | ORGANIZATION=$2 11 | IMAGE_NAME=$3 12 | VERSION=$4 13 | REGISTRY_USER=$5 14 | REGISTRY_TOKEN=$6 15 | BUILD_EXPORT_OCI_ARCHIVES=$7 16 | MAJOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 1p) 17 | MINOR=$(echo "${VERSION}" | tr '.' "\n" | sed -n 2p) 18 | 19 | oci_prefix="org.opencontainers.image" 20 | descr="ClusterImageScanner Syft" 21 | 22 | 23 | 24 | trap cleanup INT EXIT 25 | cleanup() { 26 | test -n "${ctr}" && buildah rm "${ctr}" || true 27 | } 28 | 29 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 30 | build_dir="${dir}/build" 31 | 32 | base_image="anchore/syft:v1.26.1" 33 | #base_image="quay.io/sdase/cluster-image-scanner-scan-syft:cataloger" 34 | 35 | ctr="$( buildah from --pull --quiet "${base_image}")" 36 | mnt="$( buildah mount "${ctr}" )" 37 | 38 | base_image_base="quay.io/sdase/cluster-image-scanner-base:3" 39 | ctr_base="$( buildah from --pull --quiet $base_image_base )" 40 | mnt_base="$( buildah mount "${ctr_base}" )" 41 | 42 | rsync -a --exclude="/etc/ssl/certs" "${mnt_base}/" "${mnt}" # overrides stuff from syft, hopefully it works} 43 | 44 | cp module.bash "${mnt}/clusterscanner/" 45 | cp env.bash "${mnt}/clusterscanner/" 46 | 47 | # Get a bill of materials 48 | base_bill_of_materials_hash=$(buildah inspect --type image "${base_image}" | jq '.OCIv1.config.Labels."io.sda-se.image.bill-of-materials-hash"') 49 | #echo "base_bill_of_materials_hash $base_bill_of_materials_hash" 50 | bill_of_materials_hash="$( ( cat "${0}"; 51 | echo "${base_bill_of_materials_hash}"; \ 52 | cat ./*; \ 53 | ) | sha256sum | awk '{ print $1 }' )" 54 | echo "bill_of_materials: $bill_of_materials_hash"; 55 | buildah config \ 56 | --label "${oci_prefix}.url=https://quay.io/sdase/cluster-image-scanner-scan-syft" \ 57 | --label "${oci_prefix}.source=https://github.com/SDA-SE/cluster-image-scanner" \ 58 | --label "${oci_prefix}.revision=$( git rev-parse HEAD )" \ 59 | --label "${oci_prefix}.version=${VERSION}" \ 60 | --label "${oci_prefix}.title=ClusterImageScanner SBOM Generation" \ 61 | --label "${oci_prefix}.description=${descr}" \ 62 | --label "io.sda-se.image.bill-of-materials-hash=${bill_of_materials_hash}" \ 63 | --env "IMAGE_TAR_FOLDER_PATH=/clusterscanner/images" \ 64 | --env "IMAGE_TAR_PATH=/clusterscanner/images/image.tar" \ 65 | --env "ARTIFACTS_PATH=/clusterscanner/data" \ 66 | --env "SCAN_ID=2021-01-29" \ 67 | --env "IMAGE_UNPACKED_DIRECTORY=/tmp/image-unpacked" \ 68 | --env 'RESULT_CACHING_HOURS=4' \ 69 | --entrypoint "/clusterscanner/entrypoint.bash" \ 70 | --user clusterscanner \ 71 | --workingdir "/clusterscanner" \ 72 | "${ctr}" 73 | # entrypoint from base needs to be overwritten 74 | 75 | buildah commit --quiet "${ctr}" "${IMAGE_NAME}:${VERSION}" && ctr= 76 | 77 | if [ -n "${BUILD_EXPORT_OCI_ARCHIVES}" ] 78 | then 79 | mkdir --parent "${build_dir}" 80 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${VERSION}" 81 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 82 | 83 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}.${MINOR}" 84 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 85 | 86 | image="docker://${REGISTRY}/${ORGANIZATION}/${IMAGE_NAME}:${MAJOR}" 87 | buildah push --quiet --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "${IMAGE_NAME}:${VERSION}" "${image}" 88 | 89 | buildah rmi "${IMAGE_NAME}:${VERSION}" 90 | fi 91 | 92 | cleanup 93 | -------------------------------------------------------------------------------- /images/scan/syft/env.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MODULE_NAME="syft" 4 | -------------------------------------------------------------------------------- /images/scan/syft/module.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # shellcheck source=../../base/scan-common.bash 6 | source /clusterscanner/scan-common.bash 7 | 8 | scan_result_pre 9 | 10 | echo "Starting Syft with parameter '$*'" 11 | /syft "$@" 12 | ls -la /clusterscanner/data 13 | if ! [ -f /clusterscanner/data/bom.json ]; then 14 | echo "/clusterscanner/data/bom.json doesn't exists" 15 | exit 2 16 | fi 17 | scan_result_post 18 | 19 | exit 0 20 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: ClusterScanner 2 | 3 | # These files are relative to the /docs repository 4 | # Learn more on https://www.mkdocs.org/user-guide/writing-your-docs/ 5 | nav: 6 | - Home: index.md 7 | - User: 8 | - Configuration for Teams: user/configuration/README.md 9 | - Repolists: deployment/repolist.md 10 | - DefectDojo: user/defectdojo/README.md 11 | - Deployment: 12 | - Orchestrator: deployment/deployment-orchestrator.md 13 | - Collector: deployment/clusterscanner-image-collector.md 14 | - Repolists: deployment/repolist.md 15 | - Scans: 16 | - Overview: user/scans/README.md 17 | - Distroless: user/scans/distroless.md 18 | - BaseImage Lifetime: user/scans/baseimage-lifetime.md 19 | - Image Lifetime: user/scans/image-lifetime.md 20 | - Known Vulnerabilities: user/scans/known-vulnerabilities.md 21 | - Malware: user/scans/malware.md 22 | - New-Version: user/scans/new-version.md 23 | - Root-User: user/scans/run-as-root.md 24 | - Architecture: 25 | - Overview: architecture/README.md 26 | - Image Fetcher: architecture/fetcher.md 27 | - Decision Orchestration: architecture/decisions/architecture-orchestration.md 28 | - Decision Image Transfer: architecture/decisions/case-study-image-transfer.md 29 | - Threat Model: architecture/threat-model.md 30 | 31 | plugins: 32 | - techdocs-core 33 | -------------------------------------------------------------------------------- /test_actions/.gitignore: -------------------------------------------------------------------------------- 1 | secrets/ 2 | -------------------------------------------------------------------------------- /test_actions/argocd.project.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: AppProject 3 | metadata: 4 | name: clusterscanner 5 | namespace: argocd 6 | spec: 7 | clusterResourceWhitelist: 8 | - group: "*" 9 | kind: "*" 10 | destinations: 11 | - namespace: clusterscanner 12 | server: https://kubernetes.default.svc 13 | - namespace: minio-operator 14 | server: https://kubernetes.default.svc 15 | - namespace: default 16 | server: https://kubernetes.default.svc 17 | sourceRepos: 18 | - "*" 19 | status: {} 20 | -------------------------------------------------------------------------------- /test_actions/argocd/kustomization.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: argocd 5 | resources: 6 | - namespace.yml 7 | - https://raw.githubusercontent.com/argoproj/argo-cd/v2.1.2/manifests/install.yaml 8 | 9 | patches: 10 | - patch: |- 11 | - op: add 12 | path: "/spec/template/spec/containers/0/command/-" 13 | value: "--disable-auth" 14 | target: 15 | kind: Deployment 16 | name: argocd-server 17 | -------------------------------------------------------------------------------- /test_actions/argocd/namespace.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: argocd 5 | # annotations: 6 | # team: "operations" 7 | # slack: "#security-notifications-test" 8 | # clusterscanner.sdase.org/skip: "false" 9 | # clusterscanner.sdase.org/is-scan-baseimage-lifetime: "true" # Boolean 10 | # clusterscanner.sdase.org/is-scan-lifetime: "true" # Boolean 11 | # clusterscanner.sdase.org/is-scan-distroless: "true" # Boolean 12 | # clusterscanner.sdase.org/is-scan-dependency-track: "true" # Boolean 13 | -------------------------------------------------------------------------------- /test_actions/argowf/argowf.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: argo-workflow 5 | namespace: argocd 6 | spec: 7 | destination: 8 | namespace: clusterscanner 9 | server: https://kubernetes.default.svc 10 | source: 11 | repoURL: https://github.com/argoproj/argo-workflows.git 12 | targetRevision: v3.4.4 13 | path: manifests/namespace-install 14 | project: clusterscanner 15 | syncPolicy: 16 | automated: {} 17 | syncOptions: 18 | - CreateNamespace=true 19 | -------------------------------------------------------------------------------- /test_actions/argowf/kustomization.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - namespace.yml 6 | # - argowf.yml 7 | -------------------------------------------------------------------------------- /test_actions/argowf/namespace.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: clusterscanner 5 | -------------------------------------------------------------------------------- /test_actions/base/auth.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | auth.json: e30K 4 | kind: Secret 5 | metadata: 6 | name: registry-default 7 | type: kubernetes.io/secret 8 | -------------------------------------------------------------------------------- /test_actions/base/kustomization.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: clusterscanner 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | resources: 8 | - auth.yml 9 | 10 | commonLabels: 11 | app.kubernetes.io/name: clusterscanner 12 | app.kubernetes.io/instance: org.sda 13 | 14 | #patches: 15 | # - path: artifacts.yml 16 | -------------------------------------------------------------------------------- /test_actions/collector/application/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: "test-deployment" 5 | namespace: "shire" 6 | labels: 7 | app: test-deployment 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: test-app 13 | template: 14 | metadata: 15 | annotations: 16 | team: "fellowship-of-the-ring" 17 | slack: "#security-notifications-test" 18 | clusterscanner.sdase.org/skip: "false" 19 | clusterscanner.sdase.org/is-scan-baseimage-lifetime: "true" # Boolean 20 | clusterscanner.sdase.org/is-scan-lifetime: "true" # Boolean 21 | clusterscanner.sdase.org/is-scan-distroless: "true" # Boolean 22 | clusterscanner.sdase.org/is-scan-dependency-track: "false" # Boolean 23 | clusterscanner.sdase.org/is-scan-runasroot: "true" # Boolean 24 | clusterscanner.sdase.org/is-scan-new-version: "false" # Boolean 25 | clusterscanner.sdase.org/is-scan-malware: "true" 26 | labels: 27 | app: test-app 28 | spec: 29 | containers: 30 | - name: test-image 31 | image: quay.io/sdase/cluster-image-scanner-test-image:feathelm-test 32 | imagePullPolicy: "Always" 33 | -------------------------------------------------------------------------------- /test_actions/collector/application/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: shire 4 | 5 | resources: 6 | - namespace.yaml 7 | - deployment.yaml 8 | -------------------------------------------------------------------------------- /test_actions/collector/application/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: shire 5 | -------------------------------------------------------------------------------- /test_actions/collector/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: collector-upload-repository 5 | namespace: cluster-image-scanner-image-collector 6 | data: 7 | collector.upload.github.appid: "GH_APP_ID_PLACEHOLDER" 8 | collector.upload.github.installlationid: "GH_INSTALLATION_ID_PLACEHOLDER" 9 | collector.namespacemapping: | 10 | { 11 | "teams": [ 12 | { 13 | "namespaces": [ 14 | {"namespace_filter": "argo", "description": "used for deployment"}, 15 | {"namespace_filter": "kube-", "description": "kube-system is the namespace for objects created by the Kubernetes system, containing services which are needed to run Kubernetes"}, 16 | {"namespace_filter": "kube-", "description": "kube-system is the namespace for objects created by the Kubernetes system, containing services which are needed to run Kubernetes"}, 17 | {"namespace_filter": "minio", "description": "minio to store workflow artifacts"} 18 | 19 | ], 20 | "configurations": { 21 | "skip": "true", 22 | "scan_lifetime_max_days": "90", 23 | "is_scan_lifetime": "true", 24 | "is_scan_baseimage_lifetime": "true", 25 | "is_scan_distroless": "true", 26 | "is_scan_malware": "false", 27 | "is_scan_runasroot": "true", 28 | "slack": "#security-notifications-test", 29 | "container_type": "third-party", 30 | "team": "operations" 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /test_actions/collector/job.yml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: cluster-scan-collector 5 | namespace: cluster-image-scanner-image-collector 6 | spec: 7 | template: 8 | spec: 9 | activeDeadlineSeconds: 3600 10 | serviceAccountName: image-collector 11 | automountServiceAccountToken: true # mount cluster-scan 12 | restartPolicy: Never 13 | containers: 14 | - name: cluster-scan-image-collector 15 | securityContext: 16 | runAsNonRoot: true 17 | allowPrivilegeEscalation: false 18 | runAsUser: 1001 19 | image: "quay.io/sdase/cluster-image-scanner-imagecollector:###VERSION###" 20 | imagePullPolicy: Always 21 | env: 22 | - name: CLUSTER_NAME 23 | value: "lord-of-the-rings" 24 | - name: DEFAULT_SKIP 25 | value: "true" 26 | - name: GIT_REPOSITORY 27 | value: "###GIT_COLLECTOR_REPOSITORY###" 28 | - name: GH_APP_ID 29 | valueFrom: 30 | configMapKeyRef: 31 | name: collector-upload-repository 32 | key: collector.upload.github.appid 33 | - name: GH_INSTALLATION_ID 34 | valueFrom: 35 | configMapKeyRef: 36 | name: collector-upload-repository 37 | key: collector.upload.github.installlationid 38 | - name: NAMESPACE_MAPPINGS 39 | valueFrom: 40 | configMapKeyRef: 41 | name: collector-upload-repository 42 | key: collector.namespacemapping 43 | resources: 44 | limits: 45 | cpu: 2000m 46 | memory: 124Mi 47 | volumes: 48 | - name: collector-upload-repository 49 | configMap: 50 | name: collector-upload-repository 51 | -------------------------------------------------------------------------------- /test_actions/collector/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: cluster-image-scanner-image-collector 4 | 5 | resources: 6 | - namespace.yaml 7 | - configmap.yaml 8 | - job.yml 9 | - service-account-authorization.yaml 10 | 11 | patchesStrategicMerge: 12 | - secret-volume.yaml 13 | -------------------------------------------------------------------------------- /test_actions/collector/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cluster-image-scanner-image-collector 5 | annotations: 6 | contact.sdase.org/team: "security-journey" 7 | app.kubernetes.io/name: "cluster-image-scanner" 8 | clusterscanner.sdase.org/namespace_filter: "^cluster-image-scanner-image-collector$" # a better option is skip 9 | -------------------------------------------------------------------------------- /test_actions/collector/secret-volume.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: cluster-scan-collector 5 | namespace: cluster-image-scanner-image-collector 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: cluster-scan-image-collector 11 | volumeMounts: 12 | - name: github 13 | mountPath: "/etc/github" 14 | readOnly: true 15 | volumes: 16 | - name: github 17 | secret: 18 | secretName: github 19 | items: 20 | - key: keyfile 21 | path: keyfile 22 | -------------------------------------------------------------------------------- /test_actions/collector/service-account-authorization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: image-collector 5 | namespace: cluster-image-scanner-image-collector 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: pod-reader-global 11 | rules: 12 | - apiGroups: [""] # "" indicates the core API group 13 | resources: ["pods", "namespaces"] 14 | verbs: ["get", "list"] 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRoleBinding 18 | metadata: 19 | name: read-pods-global 20 | subjects: 21 | - kind: ServiceAccount 22 | name: image-collector 23 | namespace: cluster-image-scanner-image-collector 24 | roleRef: 25 | kind: ClusterRole 26 | name: pod-reader-global 27 | apiGroup: rbac.authorization.k8s.io 28 | --- 29 | -------------------------------------------------------------------------------- /test_actions/collector/setup.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ "${SECRETS_PATH}" != "" ]; then 5 | source ${SECRETS_PATH} 6 | fi 7 | source ${DEPLOYMENT_PATH}/../test_actions/library.bash 8 | 9 | sed -i.bak "s~###VERSION###~2.0.225~g" application/deployment.yaml # test image has separate build with extra version 10 | cat application/deployment.yaml 11 | sed -i.bak "s~###VERSION###~${VERSION}~g" job.yml 12 | sed -i.bak "s~###GIT_COLLECTOR_REPOSITORY###~${GIT_COLLECTOR_REPOSITORY}~g" job.yml 13 | 14 | kubectl apply -k ./application 15 | wait_for_pods_ready "test deployment of image" "shire" 1 10 120 16 | 17 | sed -i.bak "s#GH_APP_ID_PLACEHOLDER#${GH_APP_ID}#" configmap.yaml 18 | sed -i.bak "s#GH_APP_LOGIN_PLACEHOLDER#${GH_APP_LOGIN}#" configmap.yaml 19 | sed -i.bak "s#GH_INSTALLATION_ID_PLACEHOLDER#${GH_INSTALLATION_ID}#" configmap.yaml 20 | 21 | kubectl apply -k . 22 | kubectl delete secret github --ignore-not-found=true -n cluster-image-scanner-image-collector 23 | if [ -f "${GH_PRIVATE_KEY_PATH}" ] && [ "${GH_PRIVATE_KEY_PATH}" != "" ]; then 24 | echo "Creating github secret from GH_PRIVATE_KEY_PATH ${GH_PRIVATE_KEY_PATH}" 25 | kubectl create secret generic github --from-file="keyfile=${GH_PRIVATE_KEY_PATH}" -n cluster-image-scanner-image-collector 26 | else 27 | echo "GH_PRIVATE_KEY_PATH ${GH_PRIVATE_KEY_PATH} not existing!" 28 | kubectl create secret generic github --from-literal="keyfile=not-existing" -n cluster-image-scanner-image-collector 29 | fi 30 | 31 | 32 | #wait_for_pods_completed "collector" "cluster-image-scanner-image-collector" 1 10 120 33 | # 34 | #if [ $(kubectl get pods -n cluster-image-scanner-image-collector | grep -c Completed) -lt 1 ]; then 35 | # kubectl get pods -n cluster-image-scanner-image-collector 36 | # for pod in $(kubectl get pods -n cluster-image-scanner-image-collector | grep -v NAME | awk '{print $1}'); do 37 | # kubectl logs ${pod} -n cluster-image-scanner-image-collector 38 | # done 39 | # echo "Collector is broken" 40 | # exit 1 41 | #fi 42 | 43 | #kubectl delete namespace shire 44 | -------------------------------------------------------------------------------- /test_actions/library.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wait_for_pods_ready () { 4 | local name="${1}"; shift 5 | local namespace="${1}"; shift 6 | local count="${1}"; shift 7 | local sleep="${1}"; shift 8 | local max_attempts="${1}" 9 | local attempt_num=0 10 | 11 | until [[ $(kubectl -n "${namespace}" get pods -o json | jq '.items | length') -ge "${count}" ]] 12 | do 13 | if [[ $(( attempt_num++ )) -ge "${max_attempts}" ]] 14 | then 15 | echo "max_attempts ${max_attempts} reached, aborting" 16 | debug_pods_in_namespace "${namespace}" 17 | exit 1 18 | fi 19 | echo "waiting for ${name} to be created" 20 | sleep "${sleep}" 21 | done 22 | until [[ $(kubectl -n "${namespace}" get pods -o json | jq '.items[].status.conditions[].status=="True"' | grep -c false) -eq "0" ]] 23 | do 24 | if [[ $(( attempt_num++ )) -ge "${max_attempts}" ]] 25 | then 26 | echo "max_attempts ${max_attempts} reached, aborting" 27 | debug_pods_in_namespace "${namespace}" 28 | exit 1 29 | fi 30 | echo "waiting for ${name} to be up" 31 | sleep "${sleep}" 32 | done 33 | } 34 | debug_pods_in_namespace() { 35 | namespace=$1 36 | kubectl get pods -A 37 | for pod in $(kubectl get pods -n ${namespace} | grep -v NAME | awk '{print $1}'); do 38 | echo "######################### ${pod}:" 39 | kubectl get pod -n ${namespace} ${pod} -o yaml 40 | kubectl logs -n ${namespace} ${pod} 41 | echo "#########################" 42 | done 43 | } 44 | wait_for_pods_completed () { 45 | local name="${1}"; shift 46 | local namespace="${1}"; shift 47 | local count="${1}"; shift 48 | local sleep="${1}"; shift 49 | local max_attempts="${1}" 50 | local attempt_num=0 51 | 52 | until [[ $(kubectl get pods -n ${namespace} | grep -c Running) -eq ${count} ]] 53 | do 54 | if [[ $(( attempt_num++ )) -ge "${max_attempts}" ]] 55 | then 56 | echo "max_attempts ${max_attempts} reached, aborting" 57 | debug_pods_in_namespace "${namespace}" 58 | exit 1 59 | fi 60 | echo "waiting for ${name} to be created" 61 | sleep "${sleep}" 62 | done 63 | until [[ $(kubectl get pods -n ${namespace} | grep -c Running) -eq 0 ]] 64 | do 65 | if [[ $(( attempt_num++ )) -ge "${max_attempts}" ]] 66 | then 67 | echo "max_attempts ${max_attempts} reached, aborting" 68 | debug_pods_in_namespace "${namespace}" 69 | exit 1 70 | fi 71 | echo "waiting for ${name} to be done" 72 | sleep "${sleep}" 73 | done 74 | } 75 | 76 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 77 | export BRANCH 78 | #export BRANCH="master" 79 | export MAJOR="3" 80 | export MINOR="0" 81 | export PATCH="${GITHUB_RUN_NUMBER}" 82 | export VERSION="${MAJOR}.${MINOR}.${PATCH}" 83 | if [ "${BRANCH}" != "master" ] && [ "${BRANCH}" != "head" ]; then 84 | export MAJOR=$(echo ${BRANCH} | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]//g') 85 | export PATCH="" 86 | if [ "${GITHUB_RUN_NUMBER}" != "" ]; then 87 | echo "Detected GITHUB_RUN_NUMBER" 88 | export MINOR="${GITHUB_RUN_NUMBER}" 89 | if [ "${GITHUB_HEAD_REF}" != "" ]; then 90 | echo "Detected GITHUB_HEAD_REF" 91 | export MAJOR=$(echo ${GITHUB_HEAD_REF} | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]//g') 92 | fi 93 | else 94 | export MINOR="" 95 | fi 96 | export VERSION="${MAJOR}.${MINOR}.${PATCH}" 97 | fi 98 | VERSION=$(echo ${VERSION} | sed 's#\.$##') 99 | VERSION=$(echo ${VERSION} | sed 's#\.$##') # to heal two . 100 | echo "VERSION: ${VERSION}" 101 | -------------------------------------------------------------------------------- /test_actions/minio/kustomization.yml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: clusterscanner 5 | 6 | resources: 7 | - namespace.yml 8 | - pvc.yml 9 | - minio.yaml 10 | - service.yml 11 | -------------------------------------------------------------------------------- /test_actions/minio/minio.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | # This name uniquely identifies the Deployment 6 | name: minio-deployment 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: minio 11 | strategy: 12 | type: Recreate 13 | template: 14 | metadata: 15 | labels: 16 | # Label is used as selector in the service. 17 | app: minio 18 | spec: 19 | # Refer to the PVC created earlier 20 | volumes: 21 | - name: storage 22 | persistentVolumeClaim: 23 | # Name of the PVC created earlier 24 | claimName: minio-pv-claim 25 | containers: 26 | - name: minio 27 | # Pulls the default Minio image from Docker Hub 28 | image: minio/minio:latest 29 | args: 30 | - server 31 | - /storage 32 | env: 33 | # Minio access key and secret key 34 | - name: MINIO_ACCESS_KEY 35 | value: "minioadmin" 36 | - name: MINIO_SECRET_KEY 37 | value: "minioadmin" 38 | ports: 39 | - containerPort: 9000 40 | hostPort: 9000 41 | # Mount the volume into the pod 42 | volumeMounts: 43 | - name: storage # must match the volume name, above 44 | mountPath: "/storage" 45 | --- 46 | 47 | --- 48 | -------------------------------------------------------------------------------- /test_actions/minio/namespace.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: clusterscanner 5 | -------------------------------------------------------------------------------- /test_actions/minio/pvc.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: minio-pv-claim 5 | labels: 6 | app: minio-storage-claim 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | storageClassName: standard 11 | resources: 12 | # This is the request for storage. Should be available in the cluster. 13 | requests: 14 | storage: 3Gi 15 | -------------------------------------------------------------------------------- /test_actions/minio/service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: minio-service 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - port: 9000 9 | targetPort: 9000 10 | protocol: TCP 11 | selector: 12 | app: minio 13 | -------------------------------------------------------------------------------- /test_actions/secrets.example: -------------------------------------------------------------------------------- 1 | # to be placed in $HOME/.clusterscanner/secrets 2 | DOCKER_SECRET="" 3 | DOCKER_USER="" 4 | -------------------------------------------------------------------------------- /test_actions/submit-workflow-with-image.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKFLOW_FILE=/tmp/image-workflow.yml 4 | cp ../images/process/workflow-runner/workflow.template.yml ${WORKFLOW_FILE} 5 | 6 | IMAGE="quay.io/sdase/cluster-image-scanner-test-image:2" 7 | IMAGE_ID="${IMAGE}" 8 | 9 | currentBranch=$(git branch --show-current) 10 | echo "currentBranch ${currentBranch}" 11 | if [ "${currentBranch}" == "master" ];then 12 | currentBranch="2" 13 | fi 14 | 15 | sed -i.bak 's|###workflow_name###|test-scan|g' ${WORKFLOW_FILE} 16 | 17 | sed -i.bak 's|###REGISTRY_SECRET###|registry-default|g' ${WORKFLOW_FILE} 18 | sed -i.bak 's|###DEFECTDOJO_CM###|defectdojo|g' ${WORKFLOW_FILE} 19 | sed -i.bak 's|###DEFECTDOJO_SECRETS###|defectdojo|g' ${WORKFLOW_FILE} 20 | sed -i.bak 's|###SCAN_ID###|test-workflow|g' ${WORKFLOW_FILE} 21 | sed -i.bak 's|###team###|test|g' ${WORKFLOW_FILE} 22 | sed -i.bak 's|###appname###|test|g' ${WORKFLOW_FILE} 23 | sed -i.bak 's|###appversion###|0.1|g' ${WORKFLOW_FILE} 24 | sed -i.bak 's|###environment###|test|g' ${WORKFLOW_FILE} 25 | sed -i.bak 's|###namespace###|test|g' ${WORKFLOW_FILE} 26 | sed -i.bak 's|###scm_source_branch###||g' ${WORKFLOW_FILE} 27 | sed -i.bak "s|###image###|${IMAGE}|g" ${WORKFLOW_FILE} 28 | sed -i.bak "s|###image_id###|${IMAGE_ID}|g" ${WORKFLOW_FILE} 29 | sed -i.bak 's|###slack###|#security-notifications-test|g' ${WORKFLOW_FILE} 30 | sed -i.bak 's|###email###||g' ${WORKFLOW_FILE} 31 | sed -i.bak 's|###is_scan_baseimage_lifetime###|true|g' ${WORKFLOW_FILE} 32 | sed -i.bak 's|###is_scan_lifetime###|true|g' ${WORKFLOW_FILE} 33 | sed -i.bak 's|###is_scan_distroless###|true|g' ${WORKFLOW_FILE} 34 | sed -i.bak 's|###is_scan_malware###|true|g' ${WORKFLOW_FILE} 35 | sed -i.bak 's|###is_scan_runasroot###|true|g' ${WORKFLOW_FILE} 36 | sed -i.bak 's|###is_scan_new_version###|true|g' ${WORKFLOW_FILE} 37 | sed -i.bak 's|###is_scan_dependency_track###|true|g' ${WORKFLOW_FILE} 38 | sed -i.bak 's|###scan_lifetime_max_days###|14|g' ${WORKFLOW_FILE} 39 | sed -i.bak 's|###dependencyCheckSuppressionsConfigMapName###|suppressions-sda|g' ${WORKFLOW_FILE} 40 | sed -i.bak 's|###new_version_image_filter###|.*|g' ${WORKFLOW_FILE} 41 | sed -i.bak 's|###imageRegistryBase###|quay.io/sdase|g' ${WORKFLOW_FILE} 42 | sed -i.bak 's|###containerType###|application|g' ${WORKFLOW_FILE} 43 | sed -i.bak "s|###clusterImageScannerImageTag###|${currentBranch}|g" ${WORKFLOW_FILE} 44 | sed -i.bak 's|###slackTokenSecretName###|slacktoken|g' ${WORKFLOW_FILE} 45 | sed -i.bak 's|###errorTargets###|[{ "channel":"#security-notifications-test", "type": "slack"} ]|g' ${WORKFLOW_FILE} 46 | 47 | argo submit -n clusterscanner ${WORKFLOW_FILE} 48 | -------------------------------------------------------------------------------- /test_actions/teardown.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kubectl delete ns argocd 4 | kubectl delete ns minio-operator 5 | kubectl delete ns clusterscanner 6 | kubectl delete ns cluster-image-scanner-image-collector 7 | -------------------------------------------------------------------------------- /test_actions/variables.base.yaml: -------------------------------------------------------------------------------- 1 | serviceAccount: 2 | name: "argo-workflows" 3 | -------------------------------------------------------------------------------- /test_actions/variables.secret.yaml.example: -------------------------------------------------------------------------------- 1 | credentials: 2 | apikey: "x" 3 | signature: "x" 4 | 5 | defectdojo: 6 | lead: "3" 7 | user: "cluster-image-scanner" 8 | url: "http://192.168.178.23:8085/" 9 | token: "" # asd123asdASD12,3 10 | 11 | dependencytrack: 12 | url: "http://192.168.178.23:8081" 13 | key: "" # Administration -> Teams 14 | 15 | github: 16 | appId: "" 17 | appLogin: "" 18 | installationId: "" 19 | privateKeyPem: "" 20 | 21 | registry: 22 | authJson: "" 23 | 24 | slack: 25 | cliToken: "" 26 | 27 | smtp: 28 | smtp: "" 29 | auth: "" 30 | user: "" 31 | password: "" 32 | starttls: "" 33 | -------------------------------------------------------------------------------- /test_actions/variables.yaml: -------------------------------------------------------------------------------- 1 | storage: 2 | # An S3 bucket through which temporary data is handed over to subflows. 3 | # Usually provided through the argo-wf installation. 4 | s3: 5 | endpoint: "minio-service.clusterscanner.svc:9000" 6 | bucket: "local" 7 | insecure: true 8 | useSDKCreds: false 9 | 10 | cronSchedule: "1 1 1 1 9" # close to never 11 | 12 | api: 13 | host: "raw.githubusercontent.com" 14 | version: "" 15 | path: "/SDA-SE/cluster-scan-test-images/master/output.json" 16 | pathSeparator: "" 17 | 18 | imageSourceList: 19 | 20 | orchestrationJob: 21 | imagePullPolicy: Always 22 | 23 | scanjob: 24 | imagePullPolicy: Always 25 | 26 | 27 | image: 28 | tag: "###VERSION###" 29 | 30 | isLocal: true 31 | 32 | smtp: 33 | smtpMailParameter: "" # e.g. -v for debugging 34 | smtpEnforceMailTo: "" 35 | -------------------------------------------------------------------------------- /tests/clean-image.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/tests/clean-image.tar -------------------------------------------------------------------------------- /tests/infected-image.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SDA-SE/cluster-image-scanner/5455b5e2eb43290abd5d25a362001d380d1d578a/tests/infected-image.tar -------------------------------------------------------------------------------- /tests/scan-malware.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | bats_require_minimum_version 1.5.0 4 | 5 | load "${BATS_TEST_DIRNAME}/../images/base/scan-common.bash" 6 | 7 | setup_file () { 8 | export FRESHCLAM_DATA_DIR=$(mktemp -d) 9 | FRESHCLAMCONF="${FRESHCLAM_DATA_DIR}/freshclam.conf" 10 | 11 | echo "DatabaseMirror db.de.clamav.net" > "$FRESHCLAMCONF" 12 | 13 | freshclam --config-file="$FRESHCLAMCONF" --datadir="$FRESHCLAM_DATA_DIR" 14 | } 15 | 16 | teardown_file () { 17 | rm -rf "$FRESHCLAM_DATA_DIR" 18 | } 19 | 20 | setup () { 21 | INFECTED_IMAGE_LOCATION=$(realpath "${BATS_TEST_DIRNAME}/infected-image.tar") 22 | INFECTED_IMAGE_HASH="066b4af1f7fb365d2475baadf4ec42446eff11a56a3c8f7f7e4bcb2e7b58b649" 23 | 24 | CLEAN_IMAGE_LOCATION=$(realpath "${BATS_TEST_DIRNAME}/clean-image.tar") 25 | CLEAN_IMAGE_HASH="995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089" 26 | 27 | TEST_ARTIFACTS_PATH=$(mktemp -d) 28 | MODULE_PATH=$(realpath "${BATS_TEST_DIRNAME}/../images/scan/malware") 29 | export RESOURCE_PATH=$(mktemp -d) 30 | 31 | MODULE_NAME="malware_scan" 32 | IS_SCAN="true" 33 | 34 | source () { 35 | return 0; 36 | } 37 | 38 | scan_result_pre () { 39 | return 0; 40 | } 41 | scan_result_post () { 42 | return 0; 43 | } 44 | 45 | clamscan () { 46 | command clamscan -d "$FRESHCLAM_DATA_DIR" "$@" 47 | } 48 | } 49 | 50 | teardown() { 51 | rm -rf "$TEST_ARTIFACTS_PATH" 52 | rm -rf "$RESOURCE_PATH" 53 | } 54 | 55 | #@test "Scanning an infected image smaller than MAX_FILESIZE should return an error 1" { 56 | # cd "$MODULE_PATH" 57 | # run -1 builtin source module.bash \ 58 | # -i $INFECTED_IMAGE_HASH \ 59 | # -t $INFECTED_IMAGE_LOCATION \ 60 | # -a $TEST_ARTIFACTS_PATH \ 61 | # &>/dev/null 62 | #} 63 | # 64 | #@test "Scanning a clean image smaller than MAX_FILESIZE should return 0" { 65 | # cd "$MODULE_PATH" 66 | # run -0 builtin source module.bash \ 67 | # -i $CLEAN_IMAGE_HASH \ 68 | # -t $CLEAN_IMAGE_LOCATION \ 69 | # -a $TEST_ARTIFACTS_PATH \ 70 | # &>/dev/null 71 | #} 72 | # 73 | #@test "Scanning an infected image larger than MAX_FILESIZE should return 0" { 74 | # cd "$MODULE_PATH" 75 | # run -0 builtin source module.bash \ 76 | # -i $INFECTED_IMAGE_HASH \ 77 | # -t $INFECTED_IMAGE_LOCATION \ 78 | # -a $TEST_ARTIFACTS_PATH \ 79 | # -s 30000 \ 80 | # &>/dev/null 81 | #} 82 | # 83 | #@test "Scanning a clean image larger than MAX_FILESIZE should return 0" { 84 | # cd "$MODULE_PATH" 85 | # run -0 builtin source module.bash \ 86 | # -i $CLEAN_IMAGE_HASH \ 87 | # -t $CLEAN_IMAGE_LOCATION \ 88 | # -a $TEST_ARTIFACTS_PATH \ 89 | # -s 10 \ 90 | # &>/dev/null 91 | #} 92 | --------------------------------------------------------------------------------