Skip to content

Commit

Permalink
Merge pull request #147 from jku/add-token-arg
Browse files Browse the repository at this point in the history
Support custom GitHub tokens
  • Loading branch information
jku authored Dec 18, 2023
2 parents 867fd5c + 3b0a239 commit f64f2d4
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 37 deletions.
10 changes: 9 additions & 1 deletion actions/create-signing-events/action.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
name: 'Create signing events'
description: 'Create signing events for offline signed metadata that is about to expire'

inputs:
token:
description: 'GitHub token'
required: true

runs:
using: "composite"
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
token: ${{ inputs.token }}
fetch-depth: 0

- uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c
Expand All @@ -29,11 +35,13 @@ runs:
shell: bash

- name: Dispatch signing event workflow
if: steps.create-signing-events.outputs.events != ''
# dispatch if using default token: otherwise create-signing-events step has already triggered push events
if: inputs.token == github.token && steps.create-signing-events.outputs.events != ''
env:
EVENTS: ${{ steps.create-signing-events.outputs.events }}
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
with:
github-token: ${{ inputs.token }}
script: |
console.log('Dispatching events: ', process.env.EVENTS)
process.env.EVENTS.trim().split(' ').forEach(event => {
Expand Down
5 changes: 5 additions & 0 deletions actions/online-sign/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: "Online sign"
description: "Creates a snapshot and timestamp if needed, moves publish branch if needed"

inputs:
token:
description: 'GitHub token'
required: true
gcp_workload_identity_provider:
description: "Google Cloud workload identity provider"
required: false
Expand All @@ -24,6 +27,7 @@ runs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
token: ${{ inputs.token }}
fetch-depth: 0

- name: Authenticate to Google Cloud
Expand Down Expand Up @@ -76,6 +80,7 @@ runs:
if: github.event_name != 'schedule' || env.ONLINE_SIGNED == 'true'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ inputs.token }}
script: |
console.log('Dispatching publish workflow')
github.rest.actions.createWorkflowDispatch({
Expand Down
50 changes: 50 additions & 0 deletions actions/signing-event/action.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
name: 'Signing event'
description: 'TUF-on-CI Signing event management'

# This action is called from signing-event workflow, which is dispatched in multiple ways
# depending on the type of token provided in inputs.
#
# 1. create-signing-events action creates a new signing event branch
# * When using a custom token this triggers push event handler
# * When using the default GitHub token the action calls createWorkflowDispatch()
# 2. A signer pushes artifact changes to a signing event branch
# * This triggers push event handler
# 3. This action (signing-event) makes a metadata change in update_targets step as a result of an artifact change
# * When using a custom token this triggers push event handler
# * When using the default GitHub token the action calls createWorkflowDispatch()
#
# Cases 1 & 3 lead to status step running. Case 2 leads to skipping status step, but
# triggering case 3 immediately afterwards.

inputs:
token:
description: 'GitHub token'
required: true

runs:
using: "composite"
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
token: ${{ inputs.token }}
fetch-depth: 0

- uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c
Expand All @@ -14,7 +36,19 @@ runs:
- run: pip install $GITHUB_ACTION_PATH/../../repo/
shell: bash

- id: update_targets
run: |
if tuf-on-ci-update-targets >> status-output; then
echo "targets_updated=true" >> $GITHUB_OUTPUT
else
echo "targets_updated=false" >> $GITHUB_OUTPUT
fi
cat status-output
cat status-output >> "$GITHUB_STEP_SUMMARY"
shell: bash

- id: status
if: steps.update_targets.outputs.targets_updated != 'true'
run: |
if tuf-on-ci-status >> status-output; then
echo "status=success" >> $GITHUB_OUTPUT
Expand All @@ -30,6 +64,7 @@ runs:
env:
STATUS: ${{ steps.status.outputs.status }}
with:
github-token: ${{ inputs.token }}
script: |
const fs = require('fs')
message = fs.readFileSync('./status-output').toString()
Expand Down Expand Up @@ -77,3 +112,18 @@ runs:
})
await core.summary.addHeading(summary).write()
- name: Dispatch another signing event workflow
# dispatch if using default token: otherwise update_targets step has already triggered a push event
if: inputs.token == github.token && steps.update_targets.outputs.targets_updated == 'true'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
with:
github-token: ${{ inputs.token }}
script: |
console.log('Dispatching another signing event workflow after a targets metadata update')
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'signing-event.yml',
ref: process.env.GITHUB_REF_NAME,
})
14 changes: 14 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## Unreleased

NOTE: This is a major Actions API break, users should **not** just upgrade the action
versions but should instead replace `create-signing-events.yml`, `online-sign.yml` and
`signing-event.yml` with workflows from tuf-on-ci-template.

Changes
* Support for custom GitHub tokens: see [REPOSITORY-MAINTENANCE.md].

Upgrade instructions from v0.3.0:
* We recommend replacing `create-signing-events.yml`, `online-sign.yml` and
`signing-event.yml` with workflows from tuf-on-ci-template to ensure workflows
stay compatible with the actions

## v0.3.0

NOTE: This is a major API break, users should **not** just upgrade the action versions but
Expand Down
8 changes: 4 additions & 4 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ manually after inspection and it will work as if the push was done by the tool i

### Debugging repository tools

The same tool (`tuf-on-ci-status`) that runs during the signing event automation
can be run locally to inspect the current status of the signing event branch. Note
that the repository tools only operate on current commit (unlike the signing tools
that always checkout the remote branch)
The same tools (`tuf-on-ci-status`, `tuf-on-ci-update-targets`) that run during the
signing event automation can be run locally to inspect the current status of the signing
event branch. Note that the repository tools only operate on current commit (unlike the
signing tools that always checkout the remote branch).

As an example, this would be the markdown output when an open invitation exists
for a new user to become a root key holder:
Expand Down
34 changes: 34 additions & 0 deletions docs/REPOSITORY-MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,37 @@ use.
After this the delegating role signers (in this case root signers) accept
the new key by signing the delegating metadata version.
</details>

## Configuration and modifying workflows

tuf-on-ci workflows (with the exception of `publish`) are written in a way to minimize
need to modify the workflows: It may be useful to consider the workflows part of the
tuf-on-ci application. The intention with this is to make workflow upgrades easier:
tuf-on-ci release notes will mention when workflows change and typically the suggested
upgrade mechanism is to copy the modified workflows from tuf-on-ci-template.

Supported ways to configure and modify tuf-on-ci workflows:
* online signing is configured using signing method specific _Repository Variables_,
see [ONLINE-SIGNING-SETUP.md](ONLINE-SIGNING-SETUP.md) for details
* A custom GitHub token can be optionally configured with _Repository Secret_
`TUF_ON_CI_TOKEN`, see details below
* The `publish` workflow can be customized to publish to a destination that is not
the default GitHub Pages

### Custom GitHub token

tuf-on-ci uses GITHUB_TOKEN by default but supports using a custom fine-grained Github
token. This allows the GitHub organization to limit the default GITHUB_TOKEN permissions
(in practice this means other workflows in the repository can operate with this lower
permission default token while tuf-on-ci workflows still have higher permissions).

The custom token needs the following repository permissions:
* `Contents: write` to create online signing commits, and to create targets metadata
change commits in signing event
* `Issues: write` to create comments in signing events
* `Actions: write` to dispatch other workflows when needed

To use a custom token, define a _repository secret_ `TUF_ON_CI_TOKEN` with a fine grained
token as the secrets value. No workflow changes are needed. Note that all automated comments
in signing event issues will be seemingly made by the account that created the custom
token: Creating the token on a "bot" account is sensible for this reason.
1 change: 1 addition & 0 deletions repo/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ tuf-on-ci-build-repository = "tuf_on_ci:build_repository"
tuf-on-ci-create-signing-events = "tuf_on_ci:create_signing_events"
tuf-on-ci-online-sign = "tuf_on_ci:online_sign"
tuf-on-ci-status = "tuf_on_ci:status"
tuf-on-ci-update-targets = "tuf_on_ci:update_targets"

[[tool.mypy.overrides]]
module = [
Expand Down
10 changes: 8 additions & 2 deletions repo/tuf_on_ci/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from tuf_on_ci.build_repository import build_repository
from tuf_on_ci.create_signing_events import create_signing_events
from tuf_on_ci.online_sign import online_sign
from tuf_on_ci.status import status
from tuf_on_ci.signing_event import status, update_targets

__all__ = ["build_repository", "create_signing_events", "online_sign", "status"]
__all__ = [
"build_repository",
"create_signing_events",
"online_sign",
"status",
"update_targets",
]
29 changes: 27 additions & 2 deletions repo/tuf_on_ci/_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,34 @@ def _validate_role(
if md.signed.expires > datetime.utcnow() + timedelta(days=days):
return False, f"Expiry date is further than expected {days} days ahead"

if isinstance(md.signed, Root):
# tuf-on-ci is always consistent_snapshot
if not md.signed.consistent_snapshot:
return False, "Consistent snapshot is not enabled"

# Specification: root version must be x+1, not just larger
if prev_md and prev_md.signed != md.signed:
if md.signed.version != prev_md.signed.version + 1:
return False, f"Version {md.signed.version} is not valid for root"

# tuf-on-ci online signer must be the same for both roles
ts_role = md.signed.get_delegated_role(Timestamp.type)
sn_role = md.signed.get_delegated_role(Snapshot.type)
if (
ts_role.keyids != sn_role.keyids
or ts_role.threshold != sn_role.threshold
):
return False, "Timestamp and Snapshot signers differ"

# Check expiry and signing period sanity
for role in [ts_role, sn_role]:
expiry_days = role.unrecognized_fields["x-tuf-on-ci-expiry-period"]
signing_days = role.unrecognized_fields["x-tuf-on-ci-signing-period"]
if signing_days < 1 or expiry_days <= signing_days:
return False, "Online signing or expiry period failed sanity check"

# TODO for root:
# * check version is prev_version + 1
# * check delegations are correct, consistent_snapshot is on
# * check delegations are correct

# TODO for top-level targets:
# * check delegations are expected
Expand Down
Loading

0 comments on commit f64f2d4

Please sign in to comment.