/drone-secrets-sync

Tool to idempotently synchronise Drone CI secrets

Primary LanguageGoGNU General Public License v3.0GPL-3.0

Build Status Code Coverage

About

drone-secrets-sync is able to idempotently synchronise Drone CI repository and organisation secrets.

# Synchronise repository secrets from JSON file
drone-secrets-sync repo octocat/hello-world secrets.json

# Synchronise organisation secrets from JSON file
drone-secrets-sync org octocat secrets.json
# Synchronise multiple repository secrets from JSON map on stdin
echo '{"some-secret": "example", "other-secret": "value"}' \
    | drone-secrets-sync repo octocat/hello-world

# Synchronise multiple organisation secrets from JSON map on stdin
echo '{"some-secret": "example", "other-secret": "value"}' \
    | drone-secrets-sync org octocat

The tool will output what secrets have changed, e.g.

["some-secret","other-secret"]

The Drone CI API does not provide access to secret values. Therefore, to allow the determination as to whether a secret already contains the required value, two secrets are created:

  1. The requested secret with the name, and value supplied.
  2. A corresponding "hash secret", with a name that contains a salted hash of the secret value.
drone secret ls --format '{{ .Name }}' octocat/hello-world
secret
secret___e861b26001c00803bb492889c1cf3faaf5a093ebc59f2c6838c7e10edfae4d0a

Be aware that exposing hashes makes it possible for an attacker that has gained access to the Drone API to brute force secret values offline. Hashes are generated using Argon2 to make attacks more difficult. The memory and compute required to generate hashes can be configured.

Installation

Docker

docker run --rm colinnolan/drone-secrets-sync --help

GitHub Releases

Release builds for various architectures can be downloaded from GitHub, e.g.

curl -fsL https://github.com/colin-nolan/drone-secrets-sync/releases/latest/drone-secrets-sync_linux-amd64 /usr/local/bin/drone-secrets-sync
chmod +x /usr/local/bin/drone-secrets-sync

From GitHub

go install github.com/colin-nolan/drone-secrets-sync/cmd/drone-secrets-sync@latest

From Source

make install

Usage

The tool uses the Drone API via the official drone-go library. It requires DRONE_TOKEN and DRONE_SERVER environment variables to be set, e.g.

# Configure environment - see: https://docs.drone.io/cli/configure
export DRONE_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
export DRONE_SERVER=http://drone.mycompany.com
Usage: drone-secrets-sync [--argon2-iterations ARGON2-ITERATIONS] [--argon2-length ARGON2-LENGTH] [--argon2-memory ARGON2-MEMORY] [--argon2-parallelism ARGON2-PARALLELISM] [--dry-run] [--droneserver DRONESERVER] [--dronetoken DRONETOKEN] [--verbose] <command> [<args>]

Options:
  --argon2-iterations ARGON2-ITERATIONS, -i ARGON2-ITERATIONS
                         number of argon2 iterations to create corresponding hash secret name [default: 32]
  --argon2-length ARGON2-LENGTH, -l ARGON2-LENGTH
                         length of argon2 hash used in corresponding hash secret name [default: 32]
  --argon2-memory ARGON2-MEMORY, -m ARGON2-MEMORY
                         memory for argon2 to use when creating corresponding hash secret name [default: 65536]
  --argon2-parallelism ARGON2-PARALLELISM, -p ARGON2-PARALLELISM
                         parallelism used when creating argon2 hash [default: 4]
  --dry-run, -d          indicate only what secrets would be updated; does not update secrets
  --droneserver DRONESERVER [env: DRONE_SERVER]
  --dronetoken DRONETOKEN [env: DRONE_TOKEN]
  --verbose, -v          enable verbose logging
  --help, -h             display this help and exit
  --version              display version and exit

Commands:
  repository             sync secrets for a repository
  repo                   sync secrets for a repository
  organisation           sync secrets for an organisation
  org                    sync secrets for an organisation
Usage: drone-secrets-sync repo REPOSITORY [SECRETSFILE]

Positional arguments:
  REPOSITORY             repository to sync secrets for, e.g. octocat/hello-world
  SECRETSFILE            location to read secrets from (default: - (stdin))

Global options:
  --argon2-iterations ARGON2-ITERATIONS, -i ARGON2-ITERATIONS
                         number of argon2 iterations to create corresponding hash secret name [default: 32]
  --argon2-length ARGON2-LENGTH, -l ARGON2-LENGTH
                         length of argon2 hash used in corresponding hash secret name [default: 32]
  --argon2-memory ARGON2-MEMORY, -m ARGON2-MEMORY
                         memory for argon2 to use when creating corresponding hash secret name [default: 65536]
  --argon2-parallelism ARGON2-PARALLELISM, -p ARGON2-PARALLELISM
                         parallelism used when creating argon2 hash [default: 4]
  --dry-run, -d          indicate only what secrets would be updated; does not update secrets
  --droneserver DRONESERVER [env: DRONE_SERVER]
  --dronetoken DRONETOKEN [env: DRONE_TOKEN]
  --verbose, -v          enable verbose logging
  --help, -h             display this help and exit
  --version              display version and exit
Usage: drone-secrets-sync org NAMESPACE [SECRETSFILE]

Positional arguments:
  NAMESPACE              name of organisation to sync secrets for, e.g. octocat
  SECRETSFILE            location to read secrets from (default: - (stdin))

Global options:
  --argon2-iterations ARGON2-ITERATIONS, -i ARGON2-ITERATIONS
                         number of argon2 iterations to create corresponding hash secret name [default: 32]
  --argon2-length ARGON2-LENGTH, -l ARGON2-LENGTH
                         length of argon2 hash used in corresponding hash secret name [default: 32]
  --argon2-memory ARGON2-MEMORY, -m ARGON2-MEMORY
                         memory for argon2 to use when creating corresponding hash secret name [default: 65536]
  --argon2-parallelism ARGON2-PARALLELISM, -p ARGON2-PARALLELISM
                         parallelism used when creating argon2 hash [default: 4]
  --dry-run, -d          indicate only what secrets would be updated; does not update secrets
  --droneserver DRONESERVER [env: DRONE_SERVER]
  --dronetoken DRONETOKEN [env: DRONE_TOKEN]
  --verbose, -v          enable verbose logging
  --help, -h             display this help and exit
  --version              display version and exit

Development

Build and Run

Executable

# Compile for GOOS and GOARCH of build machine
make build

# Compile for multiple targets
make build TARGET_BUILDS="linux/amd64 linux/arm64 linux/arm darwin/amd64 darwin/arm64"

To run after building:

./bin/drone-secrets-sync --help

Docker Image

make build-image-and-load

To run after building:

docker run --rm --pull never -e DRONE_SERVER -e DRONE_TOKEN "colin-nolan/drone-secrets-sync:$(make version)" --help

Test

make test

Linting

make lint

Requires:

Apply Format

make format

Requires: (see Linting)

CI

To run a Drone CI step manually:

drone exec --pipeline=lint <(drone jsonnet --stream --stdout)

Clear Secrets

When testing against a Drone CI installation, to clear all secrets on a repository:

repository=octocat/hello-world
drone secret ls --format '{{ .Name }}' "${repository}" \
    | xargs -I {} drone secret rm --name {} "${repository}"

Or on an organisation:

namespace=octocat
drone orgsecret ls --format '{{ .Name }}' "${namespace}" \
    | xargs -n 1 drone orgsecret rm "${namespace}"

Requires:

Alternatives

  • drone-secret-sync can synchronise secrets across multiple orgs/repositories. It is not idempotent though, meaning it will update all secrets, every time it is ran.

Legal

GPL v3 (contact for other licencing). Copyright 2023 Colin Nolan.

This work is in no way related to any company that I may work for.