Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

feat: Add github-upload-public-key module #241

Merged
merged 23 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
1 change: 1 addition & 0 deletions .icons/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions github-upload-public-key/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
display_name: Github Upload Public Key
description: Automates uploading Coder public key to Github so users don't have to.
icon: ../.icons/github.svg
maintainer_github: coder
verified: true
tags: [helper, git]
---

# github-upload-public-key

Templates that utilize Github External Auth can automatically ensure that the Coder public key is uploaded to Github so that users can clone repositories without needing to upload the public key themselves.

```tf
module "github-upload-public-key" {
source = "registry.coder.com/modules/github-upload-public-key/coder"
version = "1.0.13"
agent_id = coder_agent.example.id
}
```

# Requirements

This module requires `curl` and `jq` to be installed inside your workspace.

Github External Auth must be enabled in the workspace for this module to work. The Github app that is configured for external auth must have both read and write permissions to "Git SSH keys" in order to upload the public key. Additionally, a Coder admin must also have the `admin:public_key` scope added to the external auth configuration of the Coder deployment. For example:

```
CODER_EXTERNAL_AUTH_0_ID="USER_DEFINED_ID"
CODER_EXTERNAL_AUTH_0_TYPE=github
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
CODER_EXTERNAL_AUTH_0_SCOPES="repo,workflow,admin:public_key"
```

Note that the default scopes if not provided are `repo,workflow`. If the module is failing to complete after updating the external auth configuration, instruct users of the module to "Unlink" and "Link" their Github account in the External Auth user settings page to get the new scopes.

# Example

Using a coder github external auth with a non-default id: (default is `github`)

```tf
data "coder_external_auth" "github" {
id = "myauthid"
}

module "github-upload-public-key" {
source = "registry.coder.com/modules/github-upload-public-key/coder"
version = "1.0.13"
agent_id = coder_agent.example.id
external_auth_id = data.coder_external_auth.github.id
}
```
35 changes: 35 additions & 0 deletions github-upload-public-key/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
terraform {
required_version = ">= 1.0"

required_providers {
coder = {
source = "coder/coder"
version = ">= 0.12"
}
}
}

variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}

variable "external_auth_id" {
type = string
description = "The ID of the GitHub external auth."
default = "github"
}

data "coder_workspace" "me" {}

resource "coder_script" "github_upload_public_key" {
agent_id = var.agent_id
script = templatefile("${path.module}/run.sh", {
CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token,
CODER_ACCESS_URL : data.coder_workspace.me.access_url,
GITHUB_EXTERNAL_AUTH_ID : var.external_auth_id,
})
display_name = "Github Upload Public Key"
icon = "/icon/github.svg"
run_on_start = true
}
108 changes: 108 additions & 0 deletions github-upload-public-key/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env bash

set -e

CODER_ACCESS_URL="${CODER_ACCESS_URL}"
CODER_OWNER_SESSION_TOKEN="${CODER_OWNER_SESSION_TOKEN}"
GITHUB_EXTERNAL_AUTH_ID="${GITHUB_EXTERNAL_AUTH_ID}"

if [ -z "$CODER_ACCESS_URL" ]; then
echo "No coder access url specified!"
exit 1
fi

if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then
echo "No coder owner session token specified!"
exit 1
fi

if [ -z "$GITHUB_EXTERNAL_AUTH_ID" ]; then
echo "No GitHub external auth id specified!"
exit 1
fi

echo "Fetching GitHub token..."
GITHUB_TOKEN=$(coder external-auth access-token $GITHUB_EXTERNAL_AUTH_ID)
if [ $? -ne 0 ]; then
echo "Failed to fetch GitHub token!"
exit 1
fi
if [ -z "$GITHUB_TOKEN" ]; then
echo "No GitHub token found!"
exit 1
fi
echo "GitHub token found!"

echo "Fetching Coder public SSH key..."
PUBLIC_KEY_RESPONSE=$(
curl -L -s \
-w "%%{http_code}" \
-H 'accept: application/json' \
-H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \
"$CODER_ACCESS_URL/api/v2/users/me/gitsshkey"
)
PUBLIC_KEY_RESPONSE_STATUS=$(tail -n1 <<< "$PUBLIC_KEY_RESPONSE")
PUBLIC_KEY_BODY=$(sed \$d <<< "$PUBLIC_KEY_RESPONSE")

if [ "$PUBLIC_KEY_RESPONSE_STATUS" -ne 200 ]; then
echo "Failed to fetch Coder public SSH key with status code $PUBLIC_KEY_RESPONSE_STATUS!"
echo "$PUBLIC_KEY_BODY"
exit 1
fi

PUBLIC_KEY=$(jq -r '.public_key' <<< "$PUBLIC_KEY_BODY")
echo "Coder public SSH key found!"

if [ -z "$PUBLIC_KEY" ]; then
echo "No Coder public SSH key found!"
exit 1
fi

echo "Fetching GitHub public SSH keys..."
GITHUB_KEYS_RESPONSE=$(
curl -L -s \
-w "%%{http_code}" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/user/keys
)
GITHUB_KEYS_RESPONSE_STATUS=$(tail -n1 <<< "$GITHUB_KEYS_RESPONSE")
GITHUB_KEYS_RESPONSE_BODY=$(sed \$d <<< "$GITHUB_KEYS_RESPONSE")

if [ "$GITHUB_KEYS_RESPONSE_STATUS" -ne 200 ]; then
echo "Failed to fetch Coder public SSH key with status code $GITHUB_KEYS_RESPONSE_STATUS!"
echo "$GITHUB_KEYS_RESPONSE_BODY"
exit 1
fi

GITHUB_MATCH=$(jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' <<< "$GITHUB_KEYS_RESPONSE_BODY")

if [ "$PUBLIC_KEY" = "$GITHUB_MATCH" ]; then
echo "Coder public SSH key is already uploaded to GitHub!"
exit 0
fi

echo "Coder public SSH key not found in GitHub keys!"
echo "Uploading Coder public SSH key to GitHub..."
CODER_PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces"
UPLOAD_RESPONSE=$(
curl -L -s \
-X POST \
-w "%%{http_code}" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/user/keys \
-d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}"
)
UPLOAD_RESPONSE_STATUS=$(tail -n1 <<< "$UPLOAD_RESPONSE")
UPLOAD_RESPONSE_BODY=$(sed \$d <<< "$UPLOAD_RESPONSE")

if [ "$UPLOAD_RESPONSE_STATUS" -ne 201 ]; then
echo "Failed to upload Coder public SSH key with status code $UPLOAD_RESPONSE_STATUS!"
echo "$UPLOAD_RESPONSE_BODY"
exit 1
fi

echo "Coder public SSH key uploaded to GitHub!"