diff --git a/.github/workflows/updatecli.yml b/.github/workflows/updatecli.yml new file mode 100644 index 0000000000..bd121eca64 --- /dev/null +++ b/.github/workflows/updatecli.yml @@ -0,0 +1,36 @@ +name: "Updatecli: Dependency Management" + +on: + schedule: + # Runs at 06 PM UTC + - cron: '0 18 * * *' + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + updatecli: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: '1.19.4' + + - name: Install Updatecli + uses: updatecli/updatecli-action@v2 + + - name: Apply Updatecli + # Never use '--debug' option, because it might leak the access tokens. + run: "updatecli apply --clean --config ./updatecli/updatecli.d/ --values ./updatecli/values.yaml" + env: + UPDATECLI_GITHUB_ACTOR: ${{ github.actor }} + UPDATECLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/updatecli/README.md b/updatecli/README.md new file mode 100644 index 0000000000..c269ca97d2 --- /dev/null +++ b/updatecli/README.md @@ -0,0 +1,61 @@ +# Updatecli automation + +*Note:* This automation is still work in progress and subject to change. For more information, please consult [PR #6559](https://github.com/k3s-io/k3s/pull/6559). + +This project uses [Updatecli](https://github.com/updatecli/updatecli) to automate and orchestrate security related updates and versions bumps in K3s. + +## Tool + +We use Updatecli for this automation, instead of Dependabot or Renovate, because of its extensibility and multiple [plugins resources](https://www.updatecli.io/docs/prologue/introduction/) that allow greater flexibility when automating sequences of conditional update steps across multiple repos. + +For detailed information on how to use Updatecli, please consult its [documentation](https://www.updatecli.io/docs/prologue/introduction/) page. + +## Scope + +The main usage of Updatecli is for: + +* Bumping versions in unstructured formats, e.g., environment variables in Dockerfiles and by matching regular expressions. +* Scripting the automation process, e.g., update package A in repo B after package X in repo Y matches a pre-defined version criteria. + +### Not in scope + +* Updatecli will only open a pull request in the targeted repo. It's not responsible for approving and merging the PR. +* The resulting PR must still follow the rules of the targeted repo, e.g., passing checks, QA testing, review process etc. + +## Project organization + +A manifest or pipeline consists of three stages - source, condition and target - that define how to apply the update strategy. + +When adding a new manifest, please follow the example structure defined below. + +``` +. +└── updatecli + ├── scripts # Contains the auxiliary scripts used in the manifests + ├── updatecli.d + │   ├── golang-alpine.yaml # Ideally each pipeline file corresponds to a dependency update + │   ├── helm-controller.yaml + │   ├── klipper.yaml + └── values.yaml # Configuration values +``` + +## Local testing + +Local testing of manifests require: + +1. Updatecli binary that can be download from [updatecli/updatecli#releases](https://github.com/updatecli/updatecli/releases). Test only with the latest stable version. + 1. Always run locally with the command `diff`, that will show the changes without actually applying them. +2. A GitHub [PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) (personal access token). The only required permission scope for Updatecli to work, when targeting only public repos, is `public_repo`. + 1. For obvious security reasons and to avoid leaking your GH PAT, export it as a local environment variable. + +```shell +export UPDATECLI_GITHUB_TOKEN="your GH PAT" +updatecli diff --clean --config updatecli/updatecli.d/ --values updatecli/values.yaml +``` + +## Contributing + +Everyone is free to contribute with new manifests and pipelines for security version bumps targeting Rancher owned repos. + +Before contributing, please follow the guidelines provided in this readme and make sure to test locally your changes before opening a PR. + diff --git a/updatecli/scripts/run-go-mod-update.sh b/updatecli/scripts/run-go-mod-update.sh new file mode 100755 index 0000000000..5fb41d0f37 --- /dev/null +++ b/updatecli/scripts/run-go-mod-update.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -eux + +go get "${1}" >&2 +go mod tidy >&2 +git diff + +exit 0 + diff --git a/updatecli/updatecli.d/golang-alpine.yaml b/updatecli/updatecli.d/golang-alpine.yaml new file mode 100644 index 0000000000..d62a6fe34f --- /dev/null +++ b/updatecli/updatecli.d/golang-alpine.yaml @@ -0,0 +1,77 @@ +--- +name: "Bump Golang Alpine version" +scms: + k3s: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .k3s.repo }}" + branch: "{{ .k3s.branch }}" + commitmessage: + title: "Bump golang:alpine version" + +actions: + github: + title: "Bump golang:alpine image version" + kind: "github/pullrequest" + scmid: "k3s" + spec: + automerge: false + +sources: + # Find Alpine latest semver version in DockerHub + alpine-docker-image: + name: "Check Alpine image version in DockerHub" + kind: "dockerimage" + spec: + image: "alpine" + versionfilter: + kind: "semver" + strict: true + # We want only the major and minor version, because it's the format + # used in golang:alpine version. + # Example: Alpine latest version is alpine:3.17.0, so we want only + # 3.17 to then check for golang:X.Y-alpine3.17 . + transformers: + - find: '\d+\.\d+' + # Dockerfile.dapper is considered the base for the Golang version that we + # must use. + dockerfile-dapper: + name: "Retrieve golang image version used in Dockerfile.dapper" + kind: "file" + scmid: "k3s" + disablesourceinput: true + spec: + file: "Dockerfile.dapper" + matchpattern: 'golang:\S+-alpine(\S+)?' + # Example: if the version found is golang:1.19.3-alpine3.16, then + # we extract only 1.19.3-alpine . + transformers: + - find: 'v?\d+\.\d+\.\d+-alpine' + +conditions: + docker-image: + name: "Check golang:alpine latest image version in DockerHub" + kind: "dockerimage" + disablesourceinput: true + spec: + image: "golang" + tag: '{{ source "dockerfile-dapper" }}{{ source "alpine-docker-image" }}' + +targets: + dockerfiles: + name: "Bump golang:alpine image version in Dockerfiles" + kind: "file" + scmid: "k3s" + disablesourceinput: true + spec: + files: + - "Dockerfile.dapper" + - "Dockerfile.test" + - "Dockerfile.manifest" + matchpattern: 'golang:\S+-alpine(\S+)?' + replacepattern: 'golang:{{ source "dockerfile-dapper" }}{{ source "alpine-docker-image" }}' diff --git a/updatecli/updatecli.d/helm-controller.yaml b/updatecli/updatecli.d/helm-controller.yaml new file mode 100644 index 0000000000..12ec68e051 --- /dev/null +++ b/updatecli/updatecli.d/helm-controller.yaml @@ -0,0 +1,68 @@ +--- +name: "Bump Helm Controller version" +scms: + k3s: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .k3s.repo }}" + branch: "{{ .k3s.branch }}" + commitmessage: + title: "Bump Helm Controller version" + +actions: + github: + title: "Bump Helm Controller version" + kind: "github/pullrequest" + scmid: "k3s" + spec: + automerge: false + mergemethod: "squash" + usetitleforautomerge: true + +sources: + helm-controller: + name: "Get Helm Controller latest release version" + kind: "githubrelease" + spec: + owner: "{{ .helm_controller.org }}" + repository: "{{ .helm_controller.repo }}" + branch: "{{ .helm_controller.branch }}" + token: "{{ requiredEnv .github.token }}" + versionfilter: + kind: "latest" + get-pwd: + name: "Run Updatecli execution directory" + kind: "shell" + disablesourceinput: true + spec: + command: 'pwd' + environments: + - name: PATH + +conditions: + helm-controller: + name: "Check Helm Controller usage in go.mod" + kind: "file" + scmid: "k3s" + disablesourceinput: true + spec: + file: "go.mod" + matchpattern: 'github.com/k3s-io/helm-controller' + +targets: + go-mod: + name: "Run go mod update" + kind: "shell" + scmid: "k3s" + disablesourceinput: true + spec: + command: '{{ source "get-pwd" }}/updatecli/scripts/run-go-mod-update.sh github.com/k3s-io/helm-controller@{{ source "helm-controller" }}' + environments: + - name: PATH + - name: HOME + diff --git a/updatecli/updatecli.d/klipper.yaml b/updatecli/updatecli.d/klipper.yaml new file mode 100644 index 0000000000..915d77839e --- /dev/null +++ b/updatecli/updatecli.d/klipper.yaml @@ -0,0 +1,103 @@ +--- +name: "Bump Klipper Helm and LB versions" +scms: + k3s: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .k3s.repo }}" + branch: "{{ .k3s.branch }}" + commitmessage: + title: "Bump Klipper version" + klipper-helm: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .klipper_helm.repo }}" + branch: "{{ .klipper_helm.branch }}" + klipper-lb: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .klipper_lb.repo }}" + branch: "{{ .klipper_lb.branch }}" + +actions: + github: + title: "Bump Klipper Helm and LB versions" + kind: "github/pullrequest" + scmid: "k3s" + spec: + automerge: false + mergemethod: "squash" + usetitleforautomerge: true + +sources: + klipper-helm: + name: "Get Klipper Helm latest release version" + kind: "githubrelease" + spec: + owner: "{{ .klipper_helm.org }}" + repository: "{{ .klipper_helm.repo }}" + branch: "{{ .klipper_helm.branch }}" + token: "{{ requiredEnv .github.token }}" + versionfilter: + kind: "latest" + klipper-lb: + name: "Get Klipper LB latest release version" + kind: "githubrelease" + spec: + owner: "{{ .klipper_helm.org }}" + repository: "{{ .klipper_lb.repo }}" + branch: "{{ .klipper_lb.branch }}" + token: "{{ requiredEnv .github.token }}" + versionfilter: + kind: "latest" + +conditions: + klipper-helm: + name: "Check rancher/klipper-helm image version in DockerHub" + kind: "dockerimage" + sourceid: "klipper-helm" + spec: + image: "rancher/klipper-helm" + klipper-lb: + name: "Check rancher/klipper-lb image version in DockerHub" + kind: "dockerimage" + sourceid: "klipper-lb" + spec: + image: "rancher/klipper-lb" + +targets: + klipper-lb: + name: "Update rancher/klipper-lb image versions" + kind: "file" + scmid: "k3s" + sourceid: "klipper-lb" + spec: + files: + - "pkg/cloudprovider/servicelb.go" + - "scripts/airgap/image-list.txt" + matchpattern: 'rancher/klipper-lb:v\d+\.\d+\.\d+(-\w+)?' + replacepattern: 'rancher/klipper-lb:{{ source "klipper-lb" }}' + klipper-helm: + name: "Update rancher/klipper-helm image versions" + kind: "file" + scmid: "k3s" + sourceid: "klipper-helm" + spec: + file: "scripts/airgap/image-list.txt" + matchpattern: 'rancher/klipper-helm:v\d+\.\d+\.\d+(-\w+)?' + replacepattern: 'rancher/klipper-helm:{{ source "klipper-helm" }}' diff --git a/updatecli/updatecli.d/local-path-provisioner.yaml b/updatecli/updatecli.d/local-path-provisioner.yaml new file mode 100644 index 0000000000..401c472623 --- /dev/null +++ b/updatecli/updatecli.d/local-path-provisioner.yaml @@ -0,0 +1,68 @@ +--- +name: "Bump Local Path Provisioner version" +scms: + k3s: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .k3s.repo }}" + branch: "{{ .k3s.branch }}" + commitmessage: + title: "Bump Local Path Provisioner version" + local-path-provisioner: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .local_path_provisioner.org }}" + repository: "{{ .local_path_provisioner.repo }}" + branch: "{{ .local_path_provisioner.branch }}" + +actions: + github: + title: "Bump Local Path Provisioner version" + kind: "github/pullrequest" + scmid: "k3s" + spec: + automerge: false + mergemethod: "squash" + usetitleforautomerge: true + +sources: + local-path-provisioner: + name: "Get Local Path Provisioner latest release version" + kind: "githubrelease" + spec: + owner: "{{ .local_path_provisioner.org }}" + repository: "{{ .local_path_provisioner.repo }}" + branch: "{{ .local_path_provisioner.branch }}" + token: "{{ requiredEnv .github.token }}" + versionfilter: + kind: "latest" + +conditions: + local-path-provisioner: + name: "Check rancher/local-path-provisioner image version in DockerHub" + kind: "dockerimage" + sourceid: "local-path-provisioner" + spec: + image: "rancher/local-path-provisioner" + +targets: + local-path-provisioner: + name: "Update rancher/local-path-provisioner image version" + kind: "file" + scmid: "k3s" + sourceid: "local-path-provisioner" + spec: + files: + - "manifests/local-storage.yaml" + - "scripts/airgap/image-list.txt" + matchpattern: 'rancher/local-path-provisioner:v\d+\.\d+\.\d+(-\w+)?' + replacepattern: 'rancher/local-path-provisioner:{{ source `local-path-provisioner` }}' diff --git a/updatecli/updatecli.d/sonobuoy.yaml b/updatecli/updatecli.d/sonobuoy.yaml new file mode 100644 index 0000000000..51758d717e --- /dev/null +++ b/updatecli/updatecli.d/sonobuoy.yaml @@ -0,0 +1,68 @@ +--- +name: "Bump Sonobuoy version" +scms: + k3s: + kind: "github" + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + username: "{{ .github.username }}" + token: "{{ requiredEnv .github.token }}" + owner: "{{ .k3s.org }}" + repository: "{{ .k3s.repo }}" + branch: "{{ .k3s.branch }}" + +actions: + github: + title: "Bump Sonobuoy version" + kind: "github/pullrequest" + scmid: "k3s" + spec: + automerge: false + +sources: + sonobuoy: + name: "Get Sonobuoy latest release version" + kind: "githubrelease" + spec: + owner: "vmware-tanzu" + repository: "sonobuoy" + token: "{{ requiredEnv .github.token }}" + branch: "main" + versionfilter: + kind: "latest" + transformers: + - trimprefix: "v" + +conditions: + docker-image: + name: "Check sonobuoy/sonobuoy image version in DockerHub" + kind: "dockerimage" + sourceid: "sonobuoy" + spec: + image: "sonobuoy/sonobuoy" + transformers: + - addprefix: "v" + dockerfiles: + name: "Check if 'ENV SONOBUOY_VERSION' is set in Dockerfiles" + kind: "file" + scmid: "k3s" + disablesourceinput: true + spec: + files: + - "Dockerfile.test" + - "conformance/Dockerfile" + matchpattern: 'ENV SONOBUOY_VERSION \d+\.\d+\.\d+(-\w+)?' + +targets: + sonobuoy: + name: "Update sonobuoy image versions" + kind: "file" + scmid: "k3s" + sourceid: "sonobuoy" + spec: + files: + - "Dockerfile.test" + - "conformance/Dockerfile" + matchpattern: 'ENV SONOBUOY_VERSION \d+\.\d+\.\d+(-\w+)?' + replacepattern: 'ENV SONOBUOY_VERSION {{ source "sonobuoy" }}' diff --git a/updatecli/values.yaml b/updatecli/values.yaml new file mode 100644 index 0000000000..5b46fade56 --- /dev/null +++ b/updatecli/values.yaml @@ -0,0 +1,25 @@ +github: + user: "github-actions[bot]" + email: "41898282+github-actions[bot]@users.noreply.github.com" + username: "UPDATECLI_GITHUB_ACTOR" + token: "UPDATECLI_GITHUB_TOKEN" +k3s: + org: "k3s-io" + repo: "k3s" + branch: "master" +klipper_helm: + org: "k3s-io" + repo: "klipper-helm" + branch: "master" +klipper_lb: + org: "k3s-io" + repo: "klipper-lb" + branch: "master" +local_path_provisioner: + org: "rancher" + repo: "local-path-provisioner" + branch: "master" +helm_controller: + org: "k3s-io" + repo: "helm-controller" + branch: "master"