Skip to content

Commit

Permalink
feat: first cut of credential helper plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
jamestelfer committed Apr 11, 2024
1 parent 6aa82f6 commit 953a0ce
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 1 deletion.
21 changes: 21 additions & 0 deletions .github/workflows/bash-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: bash-checks

on:
pull_request:

push:
branches:
- main

jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Lint
run: docker-compose run --rm lint

- name: Test
run: docker-compose run --rm tests
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
# github-app-auth-buildkite-plugin
Combines a Git credential helper with a separate helper agent to allow Buildkite agents securely authorize Github repository access..

Combines a Git credential helper with a separate helper agent to allow Buildkite agents securely authorize Github repository access.

## Example

Add the following to your `pipeline.yml`:

```yml
steps:
- command: ls
plugins:
- jamestelfer/github-app-auth#v0.1.0:
vendor-url: "https://your-vendor-agent"
audience: "github-app-auth:your-buildkite-organization"
```
## Configuration
### `vendor-url` (Required, string)

The URL of the helper agent that vends a token for a pipeline. This is a
separate (as yet unreleased) agent that is accessible to your Buildkite agents.

### `audience` (string)

**Default:** `github-app-auth:default`

The value of the `aud` claim of the OIDC JWT that will be sent to the helper
agent. This must correlate with the value configured in the vendor agent
settings.

A recommendation: `github-app-auth:your-github-organization`. This is specific
to the purpose of the token, and also scoped to the GitHub organization that
tokens will be vended for. The agent's GitHub app is configured for a particular
GitHub organization/user, so if you have multiple organizations, multiple agents
will need to be running.

## Developing

To run the tests:

```shell
docker-compose run --rm tests
```

## Contributing

1. Fork the repo
2. Make the changes
3. Run the tests
4. Commit and push your changes
5. Send a pull request
26 changes: 26 additions & 0 deletions credential-helper/buildkite-connector-credential-helper
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/bash
set -eou pipefail

url="${1:?url parameter required}"
audience="${2:?audience parameter required}"
action="${2:?action parameter required}"

# ignore unsupported actions without error
if [[ "${action}" != "get" ]]; then
exit 0
fi

# read credential helper input from stdin
args="$(< /dev/stdin)"

oidc_auth_token="$(buildkite-agent oidc request-token --audience "${audience}")"

# Request a token for the given repository from the remote server, using the
# OIDC JWT from the agent. The output of this request is in the expected format,
# so is sent to stdout to be read by git.
curl --silent --show-error --fail \
--request POST "${url}/git-credentials" \
--data "${args}" \
--header "Authorization: Bearer ${oidc_auth_token}" \
--header "Content-Type: text/plain" \
--header "Accept: text/plain"
11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
services:
lint:
image: buildkite/plugin-linter
command: ['--id', 'jamestelfer/github-app-auth']
volumes:
- ".:/plugin:ro"

tests:
image: buildkite/plugin-tester
volumes:
- ".:/plugin:ro"
31 changes: 31 additions & 0 deletions hooks/environment
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
set -euo pipefail

vendor_url="${BUILDKITE_PLUGIN_GITHUB_APP_AUTH_VENDOR_URL:?vendor-url property required}"
audience="${BUILDKITE_PLUGIN_GITHUB_APP_AUTH_AUDIENCE:-github-app-auth:default}"

echo "~~~ :git: :github: Configuring git to authenticate via the vendor agent"

plugin_root="$(cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd)"

echo "Credential helper will run from plugin root ${plugin_root}"

# set up the helper using environment variables for git config, as defined at
# https://git-scm.com/docs/git-config#ENVIRONMENT

git_config_add() {
local key="$1"
local value="$2"

count="${GIT_CONFIG_COUNT:-0}"

# count is incremented each time a setting is added
(( count++ ))

export GIT_CONFIG_COUNT="${count}"
export "GIT_CONFIG_KEY_${count}=${key}"
export "GIT_CONFIG_VALUE_${count}=${value}"
}

git_config_add "credential.https://github.com.usehttppath" "true"
git_config_add "credential.https://github.com.helper" "${plugin_root}/credential-helper/buildkite-connector-credential-helper ${vendor_url} ${audience}"
18 changes: 18 additions & 0 deletions plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Github App Auth
description: |
Adds a Git credential helper that authorizes a pipeline to access its Github
using HTTPS and a time-limited token.
The helper agent (separate) is accessed via HTTP, using the Buildkite Agent
OIDC token as its authorization.
author: https://github.com/jamestelfer
requirements: []
configuration:
properties:
vendor-url:
type: string
description: The URL of the helper agent that vends a token for a pipeline.
audience:
type: string
description: (Default `github-app-auth:default`.) The audience to use for the Buildkite OIDC JWT that is sent to the vendor agent. Must match the setting in the vendor agent.
additionalProperties: false
92 changes: 92 additions & 0 deletions tests/environment.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bats

load "${BATS_PLUGIN_PATH}/load.bash"

#
# Tests for pre-command hook
#

# Uncomment the following line to debug stub failures
# export [stub_command]_STUB_DEBUG=/dev/tty
#export DOCKER_STUB_DEBUG=/dev/tty

clear_git_config() {
if [[ -n "${GIT_CONFIG_COUNT}" ]]; then
for i in $(seq 1 "${GIT_CONFIG_COUNT}"); do
unset "GIT_CONFIG_KEY_$i"
unset "GIT_CONFIG_VALUE_$i"
done

unset ${GIT_CONFIG_COUNT}
fi
}

setup() {
clear_git_config
}

teardown() {
unset BUILDKITE_PLUGIN_GITHUB_APP_AUTH_VENDOR_URL
unset BUILDKITE_PLUGIN_GITHUB_APP_AUTH_AUDIENCE

clear_git_config
}

run_environment() {
run bash -c "source $* && (env | grep GIT_)"
}

@test "Fails without configuration" {
# export BUILDKITE_COMMAND_EXIT_STATUS=0

run "$PWD/hooks/environment"

assert_failure
assert_line --partial "vendor-url property required"
}

@test "Adds config for default audience" {
export BUILDKITE_PLUGIN_GITHUB_APP_AUTH_VENDOR_URL=http://test-location

run_environment "${PWD}/hooks/environment"

assert_success
assert_line "GIT_CONFIG_COUNT=2"
assert_line "GIT_CONFIG_KEY_1=credential.https://github.com.usehttppath"
assert_line "GIT_CONFIG_VALUE_1=true"
assert_line "GIT_CONFIG_KEY_2=credential.https://github.com.helper"
assert_line --regexp "GIT_CONFIG_VALUE_2=/.*/credential-helper/buildkite-connector-credential-helper http://test-location github-app-auth:default"
}

@test "Adds config for non-default audience" {
export BUILDKITE_PLUGIN_GITHUB_APP_AUTH_VENDOR_URL=http://test-location
export BUILDKITE_PLUGIN_GITHUB_APP_AUTH_AUDIENCE=test-audience

run_environment "${PWD}/hooks/environment"

assert_success
assert_line "GIT_CONFIG_COUNT=2"
assert_line "GIT_CONFIG_KEY_1=credential.https://github.com.usehttppath"
assert_line "GIT_CONFIG_VALUE_1=true"
assert_line "GIT_CONFIG_KEY_2=credential.https://github.com.helper"
assert_line --regexp "GIT_CONFIG_VALUE_2=/.*/credential-helper/buildkite-connector-credential-helper http://test-location test-audience"
}

@test "Adds to existing configuration if present" {
export BUILDKITE_PLUGIN_GITHUB_APP_AUTH_VENDOR_URL=http://test-location

export GIT_CONFIG_COUNT="3"
export GIT_CONFIG_KEY_3="key-3"
export GIT_CONFIG_VALUE_3="value-3"

run_environment "${PWD}/hooks/environment"

assert_success
assert_line "GIT_CONFIG_COUNT=5"
assert_line "GIT_CONFIG_KEY_3=key-3"
assert_line "GIT_CONFIG_VALUE_3=value-3"
assert_line "GIT_CONFIG_KEY_4=credential.https://github.com.usehttppath"
assert_line "GIT_CONFIG_VALUE_4=true"
assert_line "GIT_CONFIG_KEY_5=credential.https://github.com.helper"
assert_line --regexp "GIT_CONFIG_VALUE_5=/.*/credential-helper/buildkite-connector-credential-helper http://test-location github-app-auth:default"
}

0 comments on commit 953a0ce

Please sign in to comment.