~egtann/shh

53855162b1d602869563a470d26daa9688bccfc0 — Evan Tann 4 months ago 2f8159f v1.9.0
update install instructions, add man page
4 files changed, 156 insertions(+), 321 deletions(-)

A Makefile
M README.md
M main.go
A man/man1/shh.1
A Makefile => Makefile +20 -0
@@ 0,0 1,20 @@
BINDIR?=/usr/local/bin
MANDIR?=/usr/local/man

shh:
	go build .

install: shh
	mkdir -m755 -p $(BINDIR) $(MANDIR)/man1
	cp man/man1/shh.1 $(MANDIR)/man1/
	mv shh /usr/local/bin/shh
.PHONY: install

clean:
	rm -f shh
.PHONY: clean

uninstall:
	rm -f $(BINDIR)/shh
	rm -f $(MANDIR)/man1/shh.1
.PHONY: uninstall

M README.md => README.md +14 -293
@@ 1,9 1,9 @@
# shh
# shh(1)

shh manages secrets for projects and small teams. Secrets are encrypted and
shh(1) manages secrets for projects and small teams. Secrets are encrypted and
safe to commit to version control software like git.

Unlike Hashicorp Vault, shh requires no infrastructure. There's no server to
Unlike Hashicorp Vault, shh(1) requires no infrastructure. There's no server to
manage and secure -- just a single file.

> **NOTE:** This code has not been audited, and was not written by an expert


@@ 18,298 18,19 @@ manage and secure -- just a single file.
> feedback, please reach out on the [mailing
> list](mailto:~egtann/shh@lists.sr.ht).

## Install

```
$ go get -u egt.run/shh
```

## Getting started

First, generate encryption keys:

```
shh gen-keys
```

You'll be asked to choose a username and set a password. Be sure to remember
your password.

`gen-keys` places keys in `~/.config/shh`. Back up your id_rsa private key; if
lost there's no way to recover it!

Next, initialize a new project:

```
shh init
```

This creates a `.shh` file in your current working directory which will be used
to store your secrets. Your public key was automatically added to the `.shh`
file, so you can now add secrets, like so:

```
shh set staging/env "$(cat staging.env)"
```

Where `staging/env` is the name of the secret and the content of the file
`staging.env` is the secret itself.

You can retrieve that secret with `get` like this:

```
shh get staging/env
```

You'll have to enter your password to retrieve the secret.

> **NOTE:** There's no concept in shh of directories or `/`, but it's useful to
> namespace your secrets for glob matches as described later.
>
> Also note that secrets belong to a global namespace. If Bob creates secret
> "A", but Alice doesn't have access, Alice trying to create a secret named "A"
> will result in an error. The same name resolves to the same secret for every
> user in the project, regardless of whether or not that user has access.

If you need to edit your secret, `shh edit staging/env` can do it. That uses
your `$EDITOR` of choice. Note that `$EDITOR` should be an absolute path. Save
and quit to re-encrypt the updated version without ever saving an unencrypted
version to disk.

You can rename a secret with `rename` like this:

```
shh rename old-name new-name
```

If you want to start a new secret using another secret as a starting point and
maintain all teammates' access:

```
shh copy old-name new-name
```

This may be useful when you're setting up a new environment, such as:

```
shh copy production/env staging/env
```

## Team management

You can grant and revoke access to secrets among teammates at any time. First
ensure they're added to the project, which will require their public key
generated via `gen-key`:

```
shh add-user alice@example.com "$(cat alice_id_rsa.pub)"
```

Now they're added to the project, but they don't have access to any keys:

```
shh allow alice@example.com staging/env
```

You can only allow access to keys which you, yourself, have access to.

Save time by using glob matches like this to grant access to an entire
namespace:

```
shh allow alice@example.com staging/*
```

You can revoke access to individual or globbed keys, like this:

```
shh deny alice@example.com staging/env
```

Or you can remove a user from a project entirely, which will remove all of
their secrets and delete their public key from `.shh`:

```
shh rm-user alice@example.com
```

## Project-level settings
See this article on [Secret
Management](https://www.egt.run/~egtann/better-secret-management) to understand
the rational for this as well as example uses of the command.

If you want to change your username on a per-project basis, create a
`.shhconfig` file next to `.shh` at the project's root. The format is the same
as ~/.config/shh/config:

```
username=bob@example.com
```

## Advanced usage

### Serve and login

If you're using shh, you'll probably need to retrieve secrets during deploys
and other scripts. That's why there's `shh serve`, which saves your password in
memory for 1 hour.

You can set the port in your `~/.config/shh/config` file like this:

```
username=bob@example.com
port=4850
```

Then run `shh serve` and from another terminal run `shh login` to set your
password in memory. Now you can run `get` or `allow` without needing to enter
your password each time -- especially useful during deploy scripts.

### Rotate

If your private key is compromised or you need to change your password, you can
easily change your keys:

```
shh rotate
```

This will ask for a new password, generate new keys and re-encrypt all secrets
using that new password.

### Using the command line

See the difference in secrets granted between two users:

```
diff -y <(shh show alice@example.com) <(shh show bob@example.com)
```

Edit all files containing a regular expression:

```
shh search "\d{8,}" | xargs -I % -o shh edit %
```

Count the number of secrets to which a user has access:

```
shh show bob@example.com | wc -l
```

See the changes in an shh file across commits:

```
vimdiff \
	<(shh -f <(git show commit_1:.shh) get my_secret) \
	<(shh -f <(git show commit_2:.shh) get my_secret)
```


## Key commands
## Install

```
shh init                        # initialize project, creating .shh file
shh gen-keys                    # generate keys
shh get $secret_name            # get secret or secrets
shh set $secret_name $value     # set value
shh del $secret_name            # delete secret
shh allow $user $secret         # allow access to secret
shh deny $user $secret          # deny access to secret
shh add-user [$user $pubkey]    # add user to project, default self
shh rm-user $user               # remove user from project
shh show [$user]                # show user's allowed and denied keys
shh search $regex               # list all secrets containing the regex
shh edit                        # edit secret using $EDITOR
shh rotate                      # rotate your key
shh serve                       # start server to maintain password in memory
shh login                       # login to server
shh version                     # version info
shh help                        # usage info
```

## Example usage:

$ git clone git@git.sr.ht:~egtann/shh && cd shh
$ make
$ sudo make install
```
# Create secret file and keys.
shh init
> creating new .shh
>
> username (usually email): alice@example.com
> password:
> confirm password:
>
> generated ~/.config/shh/config
> generated ~/.config/shh/id_rsa
> generated ~/.config/shh/id_rsa.pub
> created .shh
>
> be sure to back up ~/.config/shh/id_rsa and remember your password, or you
> may lose access to your secrets!

# Add user to an existing project (on a different computer than above)
shh init
> adding user to existing .shh
>
> your username (usually email): bob@example.com
> password:
> confirm password:
>
> generated ~/.config/shh/config
> generated ~/.config/shh/id_rsa
> generated ~/.config/shh/id_rsa.pub
> added bob@example.com to .ssh
>
> be sure to back up ~/.config/shh/id_rsa and remember your password, or you
> may lose access to your secrets!

# Create a secret named "database_url"
shh set database_url $DATABASE_URL

# An alternative syntax to set a secret from a file
shh set very_secret "$(< secret.txt)"

# You can also namespace the secrets like a filesystem. There's no built-in
# support for this, but it makes it easy to support different projects/repos
# within a single project.
shh set my_project/staging/database_url "127.0.0.1:3304"

# Allow a user to access a secret
shh allow bob@example.com database_url

# Deny a user from accessing a secret
shh deny bob@example.com database_url

# Deny a user from accessing all secrets. The quotes are necessary
shh deny bob@example.com "*"

# Deny a user from accessing any secrets matching a glob pattern
shh deny bob@example.com staging/*

# Show your accessible keys and meta info
shh show

# Show Bob's keys and meta info
shh show user bob@example.com

# Show all user keys and meta info
shh show user "*"

# Show all secrets containing the regular expression
shh search "example.(com|net)"

# In case of stolen key, you can regenerate/rotate your key
shh rotate
> old password:
> new password:
> confirm password:
>
> generated ~/.config/shh/id_rsa
> generated ~/.config/shh/id_rsa.pub
>
> be sure to back up ~/.config/shh/id_rsa and remember your password, or you
> may lose access to your secrets!

# Stream staging secrets to server.env on deploy
shh get staging/env | ssh alice@staging "cat > server.env"
```
After installation, check the man pages for usage information for shh(1).

## Encryption details



@@ 332,10 53,10 @@ As of v1.8.0, the following security vulnerability is fixed:
  prevents this attack. You should regenerate any keys that were stored in .shh
  and shared.

## Project Contact
## Contact

The best way to communicate bugs or provide patches is through the [mailing
list](https://lists.sr.ht/~egtann/shh).
To report bugs, discuss functionality or submit patches, email [this
list](mailto:~egtann/all@lists.sr.ht).

## Future improvements


M main.go => main.go +1 -28
@@ 1239,34 1239,7 @@ func copyFile(dst, src string) error {
}

func usage() {
	fmt.Println(`usage:

	shh [flags] [command]

global commands:
	init                    initialize store or add self to existing store
	get $name               get secret
	set $name $val          set secret
	del $name               delete a secret
	copy $old $new          copy a secret, maintaining the same team access
	rename $old $new        rename a secret
	allow $user $secret     allow user access to a secret
	deny $user $secret      deny user access to a secret
	add-user $user $pubkey  add user to project given their public key
	rm-user $user           remove user from project
	search $regex           list all secrets containing the regex
	show [$user]            show user's allowed and denied keys
	edit                    edit a secret using $EDITOR
	rotate                  rotate key
	serve                   start server to maintain password in memory
	gen-keys                generate global keys and configuration files
	login                   login to server to maintain password in memory
	version                 version information
	help                    usage info

flags:
	-n                      Non-interactive mode. Fail if shh would prompt for the password
	-f                      shh filename. Defaults to .shh`)
	fmt.Println(`usage: shh [-f file] [-n] command ...`)
}

type configOpt bool

A man/man1/shh.1 => man/man1/shh.1 +121 -0
@@ 0,0 1,121 @@
.Dd $Mdocdate$
.Dt SHH 1
.Os
.Sh NAME
.Nm shh
.Nd secret manager
.Sh SYNOPSIS
.Nm shh
.Bk -words
.Op Fl f Ar file
.Op Fl n
.Cm command ...
.Ek
.Sh DESCRIPTION
The
.Nm
utility encrypts project secrets to a single file.  It produces an encrypted
file that is safe to commit to version control and share among a team.
.Pp
In commands below, you may substitute names of secrets and users either
partially or completely for a glob
.Sq \(** .
Take care to quote the name to prevent shell expansion.  See the EXAMPLES
section below.
.Pp
.Nm
is controlled through the following commands:
.Pp
.Bl -tag -width Ds
.It Cm init
Initialize the shh file, or if one already exists, add your public key to the
existing file.
.It Cm get Ar name
Get secret.
.It Cm set Ar name Ar val
Set secret to value for all users.
.It Cm del Ar name
Delete secret for all users.
.It Cm copy Ar old-name Ar new-name
Copy a secret, preserving team access.
.It Cm rename Ar old-name Ar new-name
Rename a secret.
.It Cm allow Ar user Ar secret
Allow user access to a secret.  You must also have access.
.It Cm deny Ar user Ar secret
Deny user access to a secret.
.It Cm add-user Ar user Ar pubkey
Add user to the project given their pubkey.
.It Cm rm-user Ar user
Remove user from the project.
.It Cm search Ar regex
List all secrets containing the regular expression.
.It Cm show Bq Ar user
Show all allowed keys, or show a specific user's allowed keys.
.It Cm edit Ar name
Edit a secret using $EDITOR.
.It Cm rotate
Rotate your public and private keys and re-encrypt all secrets.
.It Cm serve
Start server to maintain password in memory for one hour, which is useful when
running scripts.  This server is often backgrounded and used in conjunction
with Cm login.
.It Cm gen-keys
Generate global keys and configuration files.
.It Cm login
Login to server from Cm serve to maintain password in memory.
.It Cm version
Version information.
.It Cm help
Usage information.  Equivalent to -h.
.El
.Pp
The options are as follows:
.Pp
.Bl -tag -width xxxxxxx -compact
.It Fl f Ar file
Path to the shh file (default ".shh")
.It Fl h
Help.
.It Fl n
Non-interactive mode.  Fail if shh would prompt for the password.
.El
.Sh EXAMPLES
.Pp
Add Alice to the project and grant access to all secrets namespaced under
"staging":
.Bd -literal -offset indent
shh add-user alice@example.com "$(cat alice_id_rsa.pub)"
shh allow alice@example.com "staging/*"
.Ed
.Pp
See the difference in secrets granted between two users:
.Bd -literal -offset indent
diff -y <(shh show alice@example.com) <(shh show bob@example.com)
.Ed
.Pp
Edit all files containing a regular expression:
.Bd -literal -offset indent
shh search "\d{8,}" | xargs -I % -o shh edit %
.Ed
.Pp
Count the number of secrets to which a user has access:
.Bd -literal -offset indent
shh show bob@example.com | wc -l
.Ed
.Pp
See the changes in a secret across git commits:
.Bd -literal -offset indent
vimdiff \e
	<(shh -f <(git show commit_1:.shh) get my_secret) \e
	<(shh -f <(git show commit_2:.shh) get my_secret)
.Ed
.Pp
Stream a secret from the encrypted file directly to a server:
.Bd -literal -offset indent
shh get staging/env | ssh alice@staging "cat > server.env"
.Ed
.Sh EXIT STATUS
.Ex -std
.Sh AUTHORS
.An Evan Tann Aq Mt os@evantann.com