Crate rejson

source ·
Expand description

(r)EJSON

CI Latest version Docs

rejson is a utility for managing a collection of secrets in source control. The secrets are encrypted using public key, elliptic curve cryptography (NaCl Box: Curve25519 + Salsa20 + Poly1305-AES). Secrets are collected in a JSON file, in which all the string values are encrypted. Public keys are embedded in the file, and the decrypter looks up the corresponding private key from its local filesystem.

This is a rust port of EJSON with a few extra bells and whistles. Full credit should go to the team that made EJSON. No innovation here other than needing Rust bindings and wanting a few extra features I’m not sure belonged upstream.

Usage

CLI

See rejson -h or (cargo run -- -h) for usage details.

A command line utility for managing secrets

Usage: rejson <COMMAND>

Commands:
  encrypt  Encrypt one or more EJSON files
  decrypt  Decrypt an EJSON file
  keygen   Generate a new EJSON key pair
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

Docker

A docker image is published for each release of rEJSON. Usage is similar to using the binary, only the /keys and /files volumes are required for encrypt/decrypt functionality.

docker run --rm -it rejson keygen

docker run --rm -it \
  -v $(pwd)/keys:/keys \
  -v $(pwd)/secrets:/files \
  rejson encrypt /files/secrets.ejson

docker run --rm -it \
  -v $(pwd)/keys:/keys \
  -v $(pwd)/secrets:/files \
  rejson decrypt /files/secrets.ejson

Code

use std::fs;

use rejson::{KeyPair, SecretsFile};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = "build/secrets.ejson";
    let mut secrets_file = SecretsFile::load(file).expect("failed to load file");
    secrets_file.transform(rejson::compact()?)?;
    secrets_file.transform(rejson::encrypt(&secrets_file)?)?;

    let json = secrets_file.to_string();
    let data = json.as_bytes();
    fs::write(file, data)?;

    println!("Wrote {} bytes to {}", data.len(), file);
    Ok(())
}

Development

Local Setup

  • Ensure you’re using nightly (currently required only for Rustfmt and the trait_alias feature).
  • Add pre-commit to avoid committing malformatted code
ln -s -f ../../build/pre-commit .git/hooks/pre-commit

Cutting a New release

Run build/release. This will:

  • Update version in Cargo.toml
  • Create a new commit with the message “Release v
  • git tag -sm "Release v<version" v<version>
  • git push --tags

From there, the release pipeline will publish the crate and the corresponding docker image.

Structs

  • A newtype representing an encryption key (32-byte array)
  • A struct representing a Curve25519 key pair (public and private).

Functions

  • Returns a Transform function that compacts multiline strings into single lines with line break characters. This is useful when adding something like a service account in the EJSON file and having the encrypt function compact it before encryption.
  • Returns a Transform that will decrypt incoming values from the supplied secrets file. This is done by creating a KeyPair consisting of the public key from the file and the supplied private key.
  • Returns a Transform function for use with [Parser::transform] that will encrypt all eligible values (that aren’t already encrypted).
  • Loads the private key from disk, searching for a file named as the public key defined in the secrets file.

Trait Aliases

  • A trait alias for Fn(String) -> Result.