Last active
January 8, 2023 10:59
-
-
Save lispyclouds/7752a72f388ad5136f3a1d3843ceb9e8 to your computer and use it in GitHub Desktop.
Babashka script to rotate all CircleCI deploy keys in GitHub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(require '[babashka.deps :as deps]) | |
(deps/add-deps '{:deps {org.babashka/http-client {:mvn/version "0.0.2"}}}) | |
(require '[babashka.http-client :as http] | |
'[cheshire.core :as json]) | |
(import '[java.net URL]) | |
(defn get-projects | |
"Returns the projects followed by the creator of the token as org/repo" | |
[token] | |
(let [urls (-> (http/get "https://circleci.com/api/v1.1/me" | |
{:headers {"Circle-Token" token | |
"Accept" "application/json"}}) | |
:body | |
json/parse-string | |
(get "projects") | |
keys)] | |
(map #(-> % | |
URL. | |
.getPath | |
(subs 1)) | |
urls))) | |
(defn get-gh-deploy-keys | |
"Returns a map of the SSH key to its id in a org/repo" | |
[token project] | |
(let [dkeys (-> (http/get (format "https://api.github.com/repos/%s/keys" project) | |
{:headers {"Authorization" (str "Bearer " token)}}) | |
:body | |
(json/parse-string true))] | |
(->> dkeys | |
(map #(vector (% :key) | |
(% :id))) | |
(into {})))) | |
(defn get-circle-checkout-keys | |
"Returns a map of the SSH key to its fingerprint in a org/repo" | |
[token project] | |
(let [pkeys (-> (http/get (format "https://circleci.com/api/v2/project/gh/%s/checkout-key" project) | |
{:headers {"Circle-Token" token}}) | |
:body | |
(json/parse-string true) | |
:items)] | |
(->> pkeys | |
(filter #(= "deploy-key" (% :type))) | |
(map #(vector (% :public_key) | |
(% :fingerprint))) | |
(into {})))) | |
(defn delete-gh-key | |
"Deletes a key in GitHub" | |
[token project id] | |
(http/delete (format "https://api.github.com/repos/%s/keys/%d" | |
project | |
id) | |
{:headers {"Authorization" (str "Bearer " token)}})) | |
(defn delete-circle-key | |
"Deletes a key in CircleCI" | |
[token project fingerprint] | |
(http/delete (format "https://circleci.com/api/v2/project/gh/%s/checkout-key/%s" | |
project | |
fingerprint) | |
{:headers {"Circle-Token" token}})) | |
(defn create-deploy-key | |
"Creates a new deploy key in Circle" | |
[token project] | |
(http/post (format "https://circleci.com/api/v2/project/gh/%s/checkout-key" project) | |
{:headers {"Circle-Token" token | |
"Content-Type" "application/json"} | |
:body (json/generate-string {"type" "deploy-key"})})) | |
(defn make-deletion-data | |
"Given a map of circle and github tokens, returns a list of matching key info in a org/repo" | |
[project circle-tokens gh-tokens] | |
(->> circle-tokens | |
(map (fn [[k f]] | |
(when (contains? gh-tokens k) | |
{:project project | |
:id (gh-tokens k) | |
:fingerprint f}))) | |
(filter some?))) | |
(defn rotate-deploy-keys | |
"Driver fn, pass dry-run as true to avoid the rotations" | |
([gh-token circle-token] | |
(rotate-deploy-keys gh-token circle-token false)) | |
([gh-token circle-token dry-run] | |
(let [projects (get-projects circle-token)] | |
(->> projects | |
(mapcat #(make-deletion-data % | |
(get-circle-checkout-keys circle-token %) | |
(get-gh-deploy-keys gh-token %))) | |
(run! (fn [{:keys [fingerprint id project]}] | |
(println (format "Rotating key id: %s fingerprint: %s for %s" | |
id | |
fingerprint | |
project)) | |
(when-not dry-run | |
(delete-circle-key circle-token project fingerprint) | |
(println "circle deleted") | |
(delete-gh-key gh-token project id) | |
(println "gh deleted") | |
(create-deploy-key circle-token project) | |
(println "new key generated on circle")))))))) | |
(when (= *file* (System/getProperty "babashka.file")) | |
(let [github-token (System/getenv "GITHUB_TOKEN") | |
circle-token (System/getenv "CIRCLE_TOKEN")] | |
(when (or (nil? github-token) (nil? circle-token)) | |
(println "Please set GITHUB_TOKEN and CIRCLE_TOKEN env vars") | |
(System/exit 1))) | |
(rotate-deploy-keys (System/getenv "GITHUB_TOKEN") (System/getenv "CIRCLE_TOKEN"))) | |
(comment | |
(rotate-deploy-keys (System/getenv "GITHUB_TOKEN") (System/getenv "CIRCLE_TOKEN") true)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment