Skip to content

Commit

Permalink
Merge branch 'release/0.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
aschmidt75 committed Dec 15, 2021
2 parents 92731c0 + a68ecc0 commit 0b5016c
Show file tree
Hide file tree
Showing 60 changed files with 4,705 additions and 1 deletion.
38 changes: 38 additions & 0 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Go

on:
push:
branches:
- main
- develop
- feature/*
pull_request:
branches: [ main, develop ]

jobs:

build:
name: Build
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.16
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Test
run: go test -v ./... -coverpkg ./...
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@

# Dependency directories (remove the comment below to include it)
# vendor/

BACKLOG.md
.idea/
dist/
*.dat

tests-internal/
21 changes: 21 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
before:
hooks:
- go mod download
builds:
- main: ./cmd/cmd.go
archives:
- replacements:
linux: Linux
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^tests:'
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
SOURCES ?= $(shell find . -name "*.go" -type f)
BINARY_NAME = go-secretshelper
MAIN_GO_PATH=cmd/cmd.go
COMMIT:=$(shell git describe --tags 2>/dev/null || git rev-parse --short HEAD)
NOW:=$(shell date +"%Y-%m-%d_%H-%M-%S")

all: clean lint build

.PHONY: build
build:
CGO_ENABLED=0 go build -v -ldflags "-X main.commit=$(COMMIT) -X main.date=$(NOW)" -o dist/${BINARY_NAME} ${MAIN_GO_PATH}

.PHONY: staticcheck
staticcheck:
staticcheck ./...

.PHONY: lint
lint:
@for file in ${SOURCES} ; do \
golint $$file ; \
done

.PHONY: test
test:
@go test -coverprofile=cover.out -coverpkg=./... ./...
@go tool cover -func=cover.out

.PHONY: gen
gen:
go generate -v ./...

.PHONY: release
release:
goreleaser --snapshot --rm-dist

.PHONY: clean
clean:
rm -rf dist/*
rm -f cover.out
go clean -testcache
107 changes: 106 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,107 @@
# go-secretshelper
CLI for accessing cloud-based vaults and secrets managers

`go-secretshelper` is both a library and a CLI to access secrets stored in vaults such as Cloud-based secrets managers, transform them and store them in files.

[![Go](https://github.com/aschmidt75/go-secretshelper/actions/workflows/go.yaml/badge.svg)](https://github.com/aschmidt75/go-secretshelper/actions/workflows/go.yaml)

## Usage

`go-secretshelper` expects a yaml-based configuration file, which it processes. The configuration contains four major elements:

* **Vaults** specify, where secrets are stored. Examples are Azure Key Vault or AWS Secrets Manager
* **Secrets** define, what data is read from which vault.
* **Transformation** describe,how secrets are modified, e.g. to decode base64 or render a template
* **Sinks** specify where and how secrets are written. At present, only files are supported as sinks.

To run a configuration, use:

```bash
$ go-secretshelper run -c <config file>
```

Sample configuration file:
```yaml
vaults:
- name: myvault
type: aws-secretsmanager
spec:
region: us-east-2

secrets:
- type: secret
vault: myvault
name: sample

transformations:
- type: template
in:
- sample
out: sample-ini
spec:
template: |
thesecret={{ .sample }}
sinks:
- type: file
var: sample-ini
spec:
path: ./sample.ini
mode: 400
```
The above configuration defines a secret named `sample`, which is read from the AWS Secrets Manager instance in `us-east-2`. The secret is then transformed by the
template and stored in a new secret named `sample-ini`. The new secret is written to a file named `./sample.ini` with file mode 400. Such a configuration may define
multiple vaults, secrets, multiple transformations and sinks.

See [docs/](docs/README.md) for more details. A configuration file may contain environment variables, which are expanded before processing by using the `-e` switch, e.g.:

```yaml
secrets:
- type: secret
vault: ${VAULT_NAME}
name: sample
```

This will expand the vault name of the environment variable `VAULT_NAME` and continue. This makes it possible to use the same configuration
file for multiple environments.

## Building

The Makefile's `build` target builds an executable in `dist/`.

```bash
$ make build
```

To build exectuables for several platforms, the `release` target uses [goreleaser](https://goreleaser.com/):

```bash
$ make release
```


## Testing

### Unit tests

```bash
$ go test -v ./...
```

### CLI tests

CLI tests are shell-based and written using bats. The executable is expected to be present in `dist/`. so `make build`
is necessary before. To run the tests:

```bash
$ cd tests
$ bats .
```

## Contributing

Pull requests are welcome!

## License

(C) 2021 @aschmidt75, MIT License
130 changes: 130 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Package main contains the main command
//
// MIT License
//
// Copyright (c) 2021 Andreas Schmidt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
"context"
"flag"
"fmt"
"github.com/spf13/afero"
"go-secretshelper/pkg/adapters"
"go-secretshelper/pkg/core"
"io/ioutil"
"log"
"os"
)

var (
commit = "none"
date = "unknown"
)

const (
// ExitCodeOk is ok
ExitCodeOk = 0

// ExitCodeNoOrUnknownCommand we're not able to run the command
ExitCodeNoOrUnknownCommand = 1

// ExitCodeInvalidConfig something wrong with config
ExitCodeInvalidConfig = 2
)

func usage() {
fmt.Println("Usage: go-secretshelper [-v] [-e] [-c config] <command>")
fmt.Println("where commands are")
fmt.Println(" version print out version")
fmt.Println(" run run specified config")
}

func main() {

verboseFlag := flag.Bool("v", false, "Enables verbose output")
envFlag := flag.Bool("e", false, "Enables environment variable substitution")
flag.Parse()

var l *log.Logger
if *verboseFlag {
l = log.New(os.Stderr, "", log.LstdFlags)
} else {
l = log.New(ioutil.Discard, "", 0)
}

values := flag.Args()
if len(values) == 0 {
usage()
os.Exit(ExitCodeNoOrUnknownCommand)
}

switch values[0] {
case "version":
fmt.Printf("%s (%s)\n", commit, date)
os.Exit(ExitCodeOk)

case "run":

fs := flag.NewFlagSet("run", flag.ExitOnError)
configFlag := fs.String("c", "", "configuration file")

if err := fs.Parse(values[1:]); err != nil {
fmt.Fprintf(os.Stderr, "error parsind commands: %s\n", err)
os.Exit(ExitCodeNoOrUnknownCommand)
}

// read config
config, err := core.NewConfigFromFile(*configFlag, *envFlag)
if err != nil {
fmt.Printf("Unable to read config from file %s: %s\n", *configFlag, err)
os.Exit(ExitCodeInvalidConfig)
}

// validate
f := adapters.NewBuiltinFactory(l, afero.NewOsFs())
if err := config.Validate(f); err != nil {
fmt.Fprintf(os.Stderr, "Error validating configuration: %s\n", err)
os.Exit(ExitCodeInvalidConfig)
}

// run
cmd := core.NewMainUseCaseImpl(l)

err = cmd.Process(context.Background(), f,
&config.Defaults,
&config.Vaults,
&config.Secrets,
&config.Transformations,
&config.Sinks)

if err != nil {
fmt.Println(err)
os.Exit(4)
}
os.Exit(ExitCodeOk)
default:
fmt.Fprintf(os.Stderr, "unknown command: %s\n", values[0])
usage()
os.Exit(ExitCodeNoOrUnknownCommand)
}

}
30 changes: 30 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Documentation

## Overview

* [Vaults](/docs/vaults.md) describes valid vault types and their configuration
* [Transformations](/docs/transformations.md) is about transformation steps.
* [Sinks](/docs/sinks.md)describe the use of sinks.

## Running

```bash
$ go-secretshelper
Usage: go-secretshelper [-v] [-e] <command>
where commands are
version print out version
run run specified config
```

Global flags are:
* -v: be more verbose
* -e: substitute environment variables when processing configuration files

```bash
$ go-secretshelper run -h
Usage of run:
-c string
configuration file
```

The `run` command takes the name of a yaml-based configuration file in `-c`, and starts processing the file.
Loading

0 comments on commit 0b5016c

Please sign in to comment.