Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Access Control to Dispatchable Workflows #217

Merged
merged 36 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b40299c
feat(ci): add access checker
zhijie-yang Aug 7, 2024
0051543
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 7, 2024
2a09f58
chore(ci): update comments and retrigger wf
zhijie-yang Aug 7, 2024
906339a
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 7, 2024
56e8647
feat(ci): use custom action to validate access
zhijie-yang Aug 8, 2024
0163833
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 8, 2024
3ce593b
fix: force grep return 0 to avoid pipefail
zhijie-yang Aug 8, 2024
35d8bc3
ci: add .github/actions to _test
zhijie-yang Aug 8, 2024
7c889f7
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 8, 2024
8049c70
TEST: SHOULD FAIL: remove zhijie-yang from CODEOWNERS
zhijie-yang Aug 8, 2024
dc804d1
TEST: SHOULD PASS: add zhijie-yang as mock-rock maintainer
zhijie-yang Aug 8, 2024
0cc5c76
Revert tests: SHOULD PASS
zhijie-yang Aug 8, 2024
f885a37
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 8, 2024
ef52911
feat(ci): update access control action condition
zhijie-yang Aug 9, 2024
cc9c627
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 9, 2024
bb687ad
feat(ci): move action out as shell script
zhijie-yang Aug 13, 2024
295b95f
test(ci): add tests for validate-access action
zhijie-yang Aug 14, 2024
a2e47a7
chore: update _test workflow trigger path
zhijie-yang Aug 14, 2024
3062782
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 14, 2024
1233b66
chore: change to code reviews
zhijie-yang Aug 20, 2024
b5493b1
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 20, 2024
351cf4d
feat: add repository as condition
zhijie-yang Aug 21, 2024
0df4aa2
fix: validate for build rock
zhijie-yang Aug 21, 2024
a5bb198
fix: preserve original CODEOWNER
zhijie-yang Aug 21, 2024
a6f1f39
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Aug 21, 2024
8e8da80
Merge branch 'main' into ROCKS-1340/access-control
zhijie-yang Sep 10, 2024
df28bae
feat: add bats test
zhijie-yang Sep 10, 2024
2a45d7f
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Sep 10, 2024
1bf39e1
fix: pass token to bats test
zhijie-yang Sep 10, 2024
dba17b3
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Sep 10, 2024
af929ac
feat: use cancel to explicit cancel the run when actor is not permitted
zhijie-yang Sep 12, 2024
f999261
TEST: SHOULD CANCEL: remove zhijie-yang from CODEOWNERS
zhijie-yang Sep 12, 2024
defb751
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Sep 12, 2024
953262a
Revert test: SHOULD PASS
zhijie-yang Sep 12, 2024
bdbd5eb
Merge branch 'main' into ROCKS-1340/access-control
zhijie-yang Sep 12, 2024
7f05d9b
ci: automatically update oci/mock-rock/_releases.json, from https://g…
Sep 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/actions/validate-actor/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Validate Access
description: 'Check if the workflow is triggered by an admin user'

# This callable workflow checks if the workflow is triggered by
# a code owner or an image maintainer by testing the github.actor
# variable against the CODEOWNERS file and the contacts.yaml file
# under oci/* path

inputs:
admin-only:
description: 'The protected workflow should only be triggered as a code owner or an image maintainer'
required: true
default: 'false'
image-path:
description: 'The path to the image to be built'
required: true
github-token:
description: 'The GITHUB_TOKEN for the GitHub CLI'
required: true

runs:
using: "composite"
steps:
- name: Check if the workflow is triggered by an admin user
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
run: ./.github/actions/validate-actor/validate-actor.sh ${{ github.actor }} ${{ inputs.admin-only }} ${{ github.workspace }} ${{ inputs.image-path }}
46 changes: 46 additions & 0 deletions .github/actions/validate-actor/test-validate-actor.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bats

SOURCE_DIR=$(dirname -- "${BASH_SOURCE[0]}")

setup() {
workdir=$(mktemp -d)
mkdir -p $workdir/img
echo -n "* @code-owner" > $workdir/CODEOWNERS
echo -e "maintainers:\n - maintainer" > $workdir/img/contacts.yaml
}

@test "blocks non-code-owner-non-maintainer user" {
{
output=$(${BATS_TEST_DIRNAME}/validate-actor.sh "random" "true" $workdir "img" 2>&1)
exit_status=$?
} || true
[[ $exit_status -eq 1 ]]
[[ $(echo "${output}"| tail -n 1) = "The workflow is triggered by a user neither as a code owner nor a maintainer of the image img" ]]
}

@test "allows code owner" {
output=$(${BATS_TEST_DIRNAME}/validate-actor.sh "code-owner" "true" $workdir "img" 2>&1)
[[ $(echo "${output}"| tail -n 1) = "The workflow is triggered by code-owner as the code owner" ]]
}

@test "allows image maintainer" {
output=$(${BATS_TEST_DIRNAME}/validate-actor.sh "maintainer" "true" $workdir "img")
[[ $(echo "${output}"| tail -n 1) = "The workflow is triggered by maintainer as a maintainer of the image img" ]]
}

@test "allows non-code-owner-non-maintainer user" {
output=$(${BATS_TEST_DIRNAME}/validate-actor.sh "random" "false" $workdir "img")
[[ $(echo "${output}"| tail -n 1) = "The workflow is not restricted to non-code-owner or non-maintainer users" ]]
}

@test "user as both code-owner and maintainer is triggered as code owner" {
echo -n " @maintainer" >> $workdir/CODEOWNERS
output=$(${BATS_TEST_DIRNAME}/validate-actor.sh "maintainer" "true" $workdir "img")
[[ $(echo "${output}"| tail -n 1) = "The workflow is triggered by maintainer as the code owner" ]]
}

@test "teams are expanded to team members" {
echo -n "@canonical/rocks" >> $workdir/CODEOWNERS
output=$(${BATS_TEST_DIRNAME}/validate-actor.sh "ROCKsBot" "true" $workdir "img")
[[ $(echo "${output}"| tail -n 1) = "The workflow is triggered by ROCKsBot as the code owner" ]]
}
35 changes: 35 additions & 0 deletions .github/actions/validate-actor/validate-actor.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash -e

actor=$1
admin_only=$2
workspace=$3
image_path=$4

echo "github.actor: ${actor}"
echo "admin-only: ${admin_only}"
if [[ ${admin_only} == true ]]; then
echo "Expanding team mentions in the CODEOWNERS file"
cp ${workspace}/CODEOWNERS ${workspace}/CODEOWNERS.bak
teams=$(grep -oE '@[[:alnum:]_.-]+\/[[:alnum:]_.-]+' ${workspace}/CODEOWNERS || true | sort | uniq)

for team in ${teams}; do
org=$(echo ${team} | cut -d'/' -f1 | sed 's/@//')
team_name=$(echo ${team} | cut -d'/' -f2)
members=$(gh api "/orgs/${org}/teams/${team_name}/members" | jq -r '.[].login')
replacement=$(echo "${members}" | xargs -I {} echo -n "@{} " | awk '{$1=$1};1')
sed -i "s|${team}|${replacement}|g" ${workspace}/CODEOWNERS
done

if grep -wq "@${actor}" ${workspace}/CODEOWNERS; then
echo "The workflow is triggered by ${actor} as the code owner"
elif cat ${workspace}/${image_path}/contacts.yaml | yq ".maintainers" | grep "\- " | grep -wq "${actor}"; then
echo "The workflow is triggered by ${actor} as a maintainer of the image ${image_path}"
else
echo "The workflow is triggered by a user neither as a code owner nor a maintainer of the image ${image_path}"
exit 1
fi
else
echo "The workflow is not restricted to non-code-owner or non-maintainer users"
fi

mv ${workspace}/CODEOWNERS.bak ${workspace}/CODEOWNERS
9 changes: 8 additions & 1 deletion .github/workflows/Announcements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ jobs:
fi
done

- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: "oci/${{ steps.get-image-name.outputs.img-name }}"
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

- name: Get contacts for ${{ steps.get-image-name.outputs.img-name }}
id: get-contacts
working-directory: oci/${{ steps.get-image-name.outputs.img-name }}
Expand Down Expand Up @@ -104,4 +112,3 @@ jobs:
do
MM_CHANNEL_ID="${channel}" ./src/notifications/send_to_mattermost.sh
done

10 changes: 10 additions & 0 deletions .github/workflows/Build-Rock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,17 @@ jobs:
if: ${{ steps.clone-image-repo.outcome == 'failure' }}
run: |
git clone ${{ inputs.rock-repo }} .

- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: ${{ inputs.oci-factory-path }}
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

- run: git checkout ${{ inputs.rock-repo-commit }}

- run: sudo snap install yq --channel=v4/stable
- name: Validate image naming and base
working-directory: ${{ inputs.rockfile-directory }}
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/Documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
push:
paths:
- "oci/*/documentation.y*ml"
branches:
- main
workflow_dispatch:
inputs:
oci-image-name:
Expand Down Expand Up @@ -40,6 +42,14 @@ jobs:

- uses: actions/checkout@v4

- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: "oci/${{ inputs.oci-image-name }}"
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

- name: Infer images to document
uses: tj-actions/changed-files@v35
id: changed-files
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/Image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ jobs:
echo "img-name=$(basename ${img_path})" >> "$GITHUB_OUTPUT"
echo "img-path=${img_path}" >> "$GITHUB_OUTPUT"

- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: ${{ steps.validate-image.outputs.img-path }}
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

- name: Use custom image trigger
if: ${{ inputs.b64-image-trigger != '' }}
run: echo ${{ inputs.b64-image-trigger }} | base64 -d > ${{ steps.validate-image.outputs.img-path }}/image.yaml
Expand All @@ -113,7 +121,7 @@ jobs:

./src/image/prepare_single_image_build_matrix.py \
--oci-path ${{ steps.validate-image.outputs.img-path }} \
--revision-data-dir ${{ env.DATA_DIR }} \
--revision-data-dir ${{ env.DATA_DIR }}

run-build:
needs: [prepare-build]
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/Release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ jobs:

- uses: actions/checkout@v4

- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: "oci/${{ inputs.oci-image-name }}"
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

- name: Infer number of image triggers
uses: tj-actions/changed-files@v35
id: changed-files
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/Tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,22 @@ env:
DIVE_IMAGE: 'wagoodman/dive:v0.12'

jobs:
access-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: ${{ inputs.oci-image-path }}
github-token: ${{ secrets.ROCKSBOT_TOKEN }}
cjdcordeiro marked this conversation as resolved.
Show resolved Hide resolved

fetch-oci-image:
runs-on: ubuntu-22.04
name: Fetch OCI image for testing
needs: [access-check]
outputs:
test-cache-key: ${{ steps.cache.outputs.key }}
steps:
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/Vulnerability-Scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Validate access to triggered image
uses: ./.github/actions/validate-actor
if: ${{ github.repository == 'canonical/oci-factory' }}
with:
admin-only: true
image-path: ${{ inputs.oci-image-path }}
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

- id: vulnerability-report
run: |
full_name="${{ inputs.oci-image-name }}${{ inputs.vulnerability-report-suffix }}"
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/_Test-OCI-Factory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
paths:
- ".github/workflows/*"
- ".github/actions/**"
- "!.github/workflows/CLA-Check.yaml"
- "!.github/workflows/PR-Validator.yaml"
- "!.github/workflows/_Auto-updates.yaml"
Expand All @@ -16,8 +17,20 @@ on:
- "!src/cli-client/**"

jobs:
access-check:
name: Validate access to mock-rock
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/validate-actor
with:
admin-only: true
image-path: "oci/mock-rock"
github-token: ${{ secrets.ROCKSBOT_TOKEN }}

test-workflows:
name: Trigger internal tests for mock-rock
needs: [access-check]
uses: ./.github/workflows/Image.yaml
with:
oci-image-name: "mock-rock"
Expand Down
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @canonical/rocks
20 changes: 10 additions & 10 deletions oci/mock-rock/_releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
},
"1.0-22.04": {
"candidate": {
"target": "397"
"target": "433"
},
"beta": {
"target": "397"
"target": "433"
},
"edge": {
"target": "397"
"target": "433"
},
"end-of-life": "2025-05-01T00:00:00Z"
},
Expand All @@ -35,31 +35,31 @@
"1.1-22.04": {
"end-of-life": "2025-05-01T00:00:00Z",
"candidate": {
"target": "398"
"target": "434"
},
"beta": {
"target": "398"
"target": "434"
},
"edge": {
"target": "398"
"target": "434"
}
},
"1-22.04": {
"end-of-life": "2025-05-01T00:00:00Z",
"candidate": {
"target": "398"
"target": "434"
},
"beta": {
"target": "398"
"target": "434"
},
"edge": {
"target": "398"
"target": "434"
}
},
"1.2-22.04": {
"end-of-life": "2025-05-01T00:00:00Z",
"beta": {
"target": "399"
"target": "435"
},
"edge": {
"target": "1.2-22.04_beta"
Expand Down