diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml deleted file mode 100644 index ab92a443..00000000 --- a/.github/workflows/scan.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: scan-images - -on: - schedule: - - cron: "0 12 * * 1" - -# Remove all permissions from GITHUB_TOKEN except metadata. -permissions: {} - -jobs: - scan: - name: Trivy - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # tag=v3.1.0 - - name: Setup go - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # tag=v6.0.0 - with: - go-version: 1.23 - - name: Run verify container script - run: make verify-container-images diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 00000000..8cc34757 --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,39 @@ +name: Weekly security scan + +on: + schedule: + # Cron for every Monday at 12:00 UTC. + - cron: "0 12 * * 1" + workflow_dispatch: + inputs: + branch: + description: 'Branch to scan' + required: false + default: 'main' + type: string + +# Remove all permissions from GITHUB_TOKEN except metadata. +permissions: {} + +jobs: + scan: + strategy: + fail-fast: false + matrix: + branch: [ ${{ inputs.branch || 'main' }} ] + name: Trivy + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2 + with: + ref: ${{ matrix.branch }} + - name: Calculate go version + id: vars + run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0 + with: + go-version: ${{ steps.vars.outputs.go_version }} + - name: Run verify security target + run: make verify-security diff --git a/Makefile b/Makefile index 7fdccc4a..292cdeab 100644 --- a/Makefile +++ b/Makefile @@ -174,6 +174,8 @@ RELEASE_NOTES_VER := v0.18.0 RELEASE_NOTES_BIN := release-notes RELEASE_NOTES := $(TOOLS_BIN_DIR)/$(RELEASE_NOTES_BIN)-$(RELEASE_NOTES_VER) +TRIVY_VER := 0.64.0 + YQ_VER := v4.35.2 YQ_BIN := yq YQ := $(abspath $(TOOLS_BIN_DIR)/$(YQ_BIN)-$(YQ_VER)) @@ -203,6 +205,11 @@ TILT_PREPARE := $(abspath $(TOOLS_BIN_DIR)/$(TILT_PREPARE_BIN)) GOLANGCI_LINT_BIN := golangci-lint GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)) +GOVULNCHECK_BIN := govulncheck +GOVULNCHECK_VER := v1.1.4 +GOVULNCHECK := $(abspath $(TOOLS_BIN_DIR)/$(GOVULNCHECK_BIN)-$(GOVULNCHECK_VER)) +GOVULNCHECK_PKG := golang.org/x/vuln/cmd/govulncheck + HELM_VER := $(call get_go_version,helm.sh/helm/v3) HELM_BIN := helm HELM := $(TOOLS_BIN_DIR)/$(HELM_BIN)-$(HELM_VER) @@ -262,6 +269,9 @@ all: test manager help: # Display this help @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[0-9A-Za-z_-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 } /^\$$\([0-9A-Za-z_-]+\):.*?##/ { gsub("_","-", $$1); printf " \033[36m%-45s\033[0m %s\n", tolower(substr($$1, 3, length($$1)-7)), $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) +go-version: ## Print the go version we use to compile our binaries and images + @echo $(GO_VERSION) + ## -------------------------------------- ## Generate / Manifests ## -------------------------------------- @@ -384,7 +394,26 @@ verify-shellcheck: ## Verify shell files .PHONY: verify-container-images verify-container-images: ## Verify container images - TRACE=$(TRACE) ./hack/verify-container-images.sh + TRACE=$(TRACE) ./hack/verify-container-images.sh $(TRIVY_VER) + +.PHONY: verify-govulncheck +verify-govulncheck: $(GOVULNCHECK) ## Verify code for vulnerabilities + $(GOVULNCHECK) ./... && R1=$$? || R1=$$?; \ + $(GOVULNCHECK) -C "$(TOOLS_DIR)" ./... && R2=$$? || R2=$$?; \ + $(GOVULNCHECK) -C "$(TEST_DIR)" ./... && R3=$$? || R3=$$?; \ + if [ "$$R1" -ne "0" ] || [ "$$R2" -ne "0" ] || [ "$$R3" -ne "0" ]; then \ + exit 1; \ + fi + +.PHONY: verify-security +verify-security: ## Verify code and images for vulnerabilities + $(MAKE) verify-container-images && R1=$$? || R1=$$?; \ + $(MAKE) verify-govulncheck && R2=$$? || R2=$$?; \ + if [ "$$R1" -ne "0" ] || [ "$$R2" -ne "0" ]; then \ + echo "Check for vulnerabilities failed! There are vulnerabilities to be fixed"; \ + exit 1; \ + fi + ## -------------------------------------- ## Binaries @@ -759,6 +788,12 @@ $(TILT_PREPARE_BIN): $(TILT_PREPARE) ## Build a local copy of tilt-prepare. .PHONY: $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_BIN): $(GOLANGCI_LINT) ## Build a local copy of golangci-lint +.PHONY: $(GOVULNCHECK_BIN) +$(GOVULNCHECK_BIN): $(GOVULNCHECK) ## Build a local copy of govulncheck. + +$(GOVULNCHECK): # Build govulncheck. + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOVULNCHECK_PKG) $(GOVULNCHECK_BIN) $(GOVULNCHECK_VER) + .PHONY: $(GINKGO_BIN) $(GINKGO_BIN): $(GINKGO) ## Build a local copy of ginkgo diff --git a/hack/ensure-trivy.sh b/hack/ensure-trivy.sh new file mode 100755 index 00000000..ac26d973 --- /dev/null +++ b/hack/ensure-trivy.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Copyright 2025 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset +set -o pipefail + +if [[ "${TRACE-0}" == "1" ]]; then + set -o xtrace +fi + +VERSION=${1} + +GO_OS="$(go env GOOS)" +if [[ "${GO_OS}" == "linux" ]]; then + TRIVY_OS="Linux" +elif [[ "${GO_OS}" == "darwin"* ]]; then + TRIVY_OS="macOS" +fi + +GO_ARCH="$(go env GOARCH)" +if [[ "${GO_ARCH}" == "amd" ]]; then + TRIVY_ARCH="32bit" +elif [[ "${GO_ARCH}" == "amd64"* ]]; then + TRIVY_ARCH="64bit" +elif [[ "${GO_ARCH}" == "arm" ]]; then + TRIVY_ARCH="ARM" +elif [[ "${GO_ARCH}" == "arm64" ]]; then + TRIVY_ARCH="ARM64" +fi + +TOOL_BIN=hack/tools/bin +mkdir -p ${TOOL_BIN} + +TRIVY="${TOOL_BIN}/trivy/${VERSION}/trivy" + +# Downloads trivy scanner +if [ ! -f "$TRIVY" ]; then + curl -L -o "${TOOL_BIN}/trivy.tar.gz" "https://github.com/aquasecurity/trivy/releases/download/v${VERSION}/trivy_${VERSION}_${TRIVY_OS}-${TRIVY_ARCH}.tar.gz" + mkdir -p "${TOOL_BIN}/trivy/${VERSION}" + tar -xf "${TOOL_BIN}/trivy.tar.gz" -C "${TOOL_BIN}/trivy/${VERSION}" trivy + chmod +x "${TOOL_BIN}/trivy/${VERSION}/trivy" + rm "${TOOL_BIN}/trivy.tar.gz" +fi diff --git a/hack/verify-container-images.sh b/hack/verify-container-images.sh index 694412da..a1203b9f 100755 --- a/hack/verify-container-images.sh +++ b/hack/verify-container-images.sh @@ -22,51 +22,29 @@ if [[ "${TRACE-0}" == "1" ]]; then set -o xtrace fi -TRIVY_VERSION=0.34.0 - -GO_OS="$(go env GOOS)" -if [[ "${GO_OS}" == "linux" ]]; then - TRIVY_OS="Linux" -elif [[ "${GO_OS}" == "darwin"* ]]; then - TRIVY_OS="macOS" -fi - +VERSION=${1} GO_ARCH="$(go env GOARCH)" -if [[ "${GO_ARCH}" == "amd" ]]; then - TRIVY_ARCH="32bit" -elif [[ "${GO_ARCH}" == "amd64"* ]]; then - TRIVY_ARCH="64bit" -elif [[ "${GO_ARCH}" == "arm" ]]; then - TRIVY_ARCH="ARM" -elif [[ "${GO_ARCH}" == "arm64" ]]; then - TRIVY_ARCH="ARM64" -fi - -TOOL_BIN=hack/tools/bin -mkdir -p ${TOOL_BIN} -# Downloads trivy scanner -curl -L -o ${TOOL_BIN}/trivy.tar.gz "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_${TRIVY_OS}-${TRIVY_ARCH}.tar.gz" +REPO_ROOT=$(git rev-parse --show-toplevel) +"${REPO_ROOT}/hack/ensure-trivy.sh" "${VERSION}" -tar -xf "${TOOL_BIN}/trivy.tar.gz" -C "${TOOL_BIN}" trivy -chmod +x ${TOOL_BIN}/trivy -rm ${TOOL_BIN}/trivy.tar.gz +TRIVY="${REPO_ROOT}/hack/tools/bin/trivy/${VERSION}/trivy" # Builds all the container images to be scanned and cleans up changes to ./*manager_image_patch.yaml ./*manager_pull_policy.yaml. make REGISTRY=gcr.io/k8s-staging-cluster-api-helm PULL_POLICY=IfNotPresent TAG=dev docker-build make clean-release-git # Scan the images -${TOOL_BIN}/trivy image -q --exit-code 1 --ignore-unfixed --severity MEDIUM,HIGH,CRITICAL gcr.io/k8s-staging-cluster-api-helm/cluster-api-helm-controller-"${GO_ARCH}":dev && R5=$? || R5=$? +"${TRIVY}" image -q --exit-code 1 --ignore-unfixed --severity MEDIUM,HIGH,CRITICAL gcr.io/k8s-staging-cluster-api-helm/cluster-api-helm-controller-"${GO_ARCH}":dev && R1=$? || R1=$? echo "" BRed='\033[1;31m' BGreen='\033[1;32m' NC='\033[0m' # No -if [ "$R1" -ne "0" ] || [ "$R2" -ne "0" ] || [ "$R3" -ne "0" ] || [ "$R4" -ne "0" ] || [ "$R5" -ne "0" ] || [ "$R6" -ne "0" ] +if [ "$R1" -ne "0" ] then - echo -e "${BRed}Check container images failed! There are vulnerability to be fixed${NC}" + echo -e "${BRed}Check container images failed! There are vulnerabilities to be fixed${NC}" exit 1 fi