Skip to content

Commit

Permalink
Merge pull request #4 from xordataexchange/unencrypted
Browse files Browse the repository at this point in the history
Unencrypted
  • Loading branch information
bketelsen committed Oct 24, 2014
2 parents 2d89461 041620b commit b47b0c5
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 11 deletions.
12 changes: 12 additions & 0 deletions bin/crypt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 59,15 @@ Example:
```
crypt get -secret-keyring secring.gpg /app/config
```

### Support for unencrypted values
```
crypt setplain ...
crypt getplain ...
```
Crypt now has support for getting and setting plain unencrypted values, as
a convenience. This was added to the backend libraries so it could be exposed
in spf13/viper. The subcommands `setplain` and `getplain` are tentatively named
right now, in the absence of another name that makes it clear that they're
not encrypting the values. Comments welcomed.

80 changes: 69 additions & 11 deletions bin/crypt/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 12,7 @@ import (
"github.com/xordataexchange/crypt/backend/consul"
"github.com/xordataexchange/crypt/backend/etcd"
"github.com/xordataexchange/crypt/encoding/secconf"
"github.com/xordataexchange/crypt/encoding/standard"
)

func getCmd(flagset *flag.FlagSet) {
Expand All @@ -30,20 31,52 @@ func getCmd(flagset *flag.FlagSet) {
if err != nil {
log.Fatal(err)
}
kr, err := os.Open(secretKeyring)
if plaintext {
value, err := getPlain(key, backendStore)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", value)
return
}
value, err := getEncrypted(key, secretKeyring, backendStore)

if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", value)
}

func getEncrypted(key, keyring string, store backend.Store) ([]byte, error) {
var value []byte
kr, err := os.Open(secretKeyring)
if err != nil {
return value, err
}
defer kr.Close()
data, err := backendStore.Get(key)
data, err := store.Get(key)
if err != nil {
log.Fatal(err)
return value, err
}
value, err := secconf.Decode(data, kr)
value, err = secconf.Decode(data, kr)
if err != nil {
log.Fatal(err)
return value, err
}
fmt.Printf("%s\n", value)
return value, err

}

func getPlain(key string, store backend.Store) ([]byte, error) {
var value []byte
data, err := store.Get(key)
if err != nil {
return value, err
}
value, err = standard.Decode(data)
if err != nil {
return value, err
}
return value, err
}

func setCmd(flagset *flag.FlagSet) {
Expand Down Expand Up @@ -71,18 104,43 @@ func setCmd(flagset *flag.FlagSet) {
if err != nil {
log.Fatal(err)
}
kr, err := os.Open(keyring)

if plaintext {
err := setPlain(key, backendStore, d)
if err != nil {
log.Fatal(err)
return
}
}
err = setEncrypted(key, keyring, d, backendStore)
if err != nil {
log.Fatal(err)
}
return

}
func setPlain(key string, store backend.Store, d []byte) error {
value, err := standard.Encode(d)
if err != nil {
return err
}
err = store.Set(key, value)
return err

}

func setEncrypted(key, keyring string, d []byte, store backend.Store) error {
kr, err := os.Open(keyring)
if err != nil {
return err
}
defer kr.Close()
secureValue, err := secconf.Encode(d, kr)
if err != nil {
log.Fatal(err)
}
if err := backendStore.Set(key, secureValue); err != nil {
log.Fatal(err)
return err
}
err = store.Set(key, secureValue)
return err
}

func getBackendStore(provider string, endpoint string) (backend.Store, error) {
Expand Down
4 changes: 4 additions & 0 deletions bin/crypt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 16,7 @@ var (
keyring string
endpoint string
secretKeyring string
plaintext bool
machines []string
)

Expand Down Expand Up @@ -46,5 47,8 @@ func help() {
fmt.Fprintf(os.Stderr, "commands:\n")
fmt.Fprintf(os.Stderr, " get retrieve the value of a key\n")
fmt.Fprintf(os.Stderr, " set set the value of a key\n")
fmt.Fprintf(os.Stderr, "\n\n")
fmt.Fprintf(os.Stderr, "-plaintext don't encrypt or decrypt the values before storage or retrieval\n")

os.Exit(1)
}
56 changes: 56 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 9,7 @@ import (
"github.com/xordataexchange/crypt/backend/consul"
"github.com/xordataexchange/crypt/backend/etcd"
"github.com/xordataexchange/crypt/encoding/secconf"
"github.com/xordataexchange/crypt/encoding/standard"
)

// A ConfigManager retrieves and decrypts configuration from a key/value store.
Expand All @@ -22,7 23,32 @@ type configManager struct {
store backend.Store
}

type standardConfigManager struct {
store backend.Store
}

// NewStandardEtcdConfigManager returns a new ConfigManager backed by etcd.
// Data will be base64 encoded but unencrypted.
func NewStandardEtcdConfigManager(machines []string) (ConfigManager, error) {
store, err := etcd.New(machines)
if err != nil {
return nil, err
}
return standardConfigManager{store}, nil
}

// NewStandardConsulConfigManager returns a new ConfigManager backed by consul.
// Data will be base64 encoded but unencrypted.
func NewStandardConsulConfigManager(machines []string) (ConfigManager, error) {
store, err := consul.New(machines)
if err != nil {
return nil, err
}
return standardConfigManager{store}, nil
}

// NewEtcdConfigManager returns a new ConfigManager backed by etcd.
// Data will be encrypted.
func NewEtcdConfigManager(machines []string, keystore io.Reader) (ConfigManager, error) {
store, err := etcd.New(machines)
if err != nil {
Expand Down Expand Up @@ -57,6 83,15 @@ func (c configManager) Get(key string) ([]byte, error) {
return secconf.Decode(value, bytes.NewBuffer(c.keystore))
}

// Get retrieves and decodes a value stored at key.
func (c standardConfigManager) Get(key string) ([]byte, error) {
value, err := c.store.Get(key)
if err != nil {
return nil, err
}
return standard.Decode(value)
}

type Response struct {
Value []byte
Error error
Expand All @@ -82,3 117,24 @@ func (c configManager) Watch(key string, stop chan bool) <-chan *Response {
}()
return resp
}

func (c standardConfigManager) Watch(key string, stop chan bool) <-chan *Response {
resp := make(chan *Response, 0)
backendResp := c.store.Watch(key, stop)
go func() {
for {
select {
case <-stop:
return
case r := <-backendResp:
if r.Error != nil {
resp <- &Response{nil, r.Error}
continue
}
value, err := standard.Decode(r.Value)
resp <- &Response{value, err}
}
}
}()
return resp
}
30 changes: 30 additions & 0 deletions encoding/standard/standard.go
Original file line number Diff line number Diff line change
@@ -0,0 1,30 @@
// Package standard implements base64 encoding in the following format:
//
// base64(data)
//
package standard

import (
"bytes"
"encoding/base64"
"io/ioutil"
)

// Deocde decodes data using the standard codec.
func Decode(data []byte) ([]byte, error) {
decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(data))
bytes, err := ioutil.ReadAll(decoder)
if err != nil {
return nil, err
}
return bytes, nil
}

// Encode encodes data to a base64 encoded using the standard codec.
func Encode(data []byte) ([]byte, error) {
buffer := new(bytes.Buffer)
encoder := base64.NewEncoder(base64.StdEncoding, buffer)
encoder.Write(data)
encoder.Close()
return buffer.Bytes(), nil
}
26 changes: 26 additions & 0 deletions encoding/standard/standard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 1,26 @@
package standard

import "testing"

var encodingTests = []struct {
in, out string
}{
{"opentext", "opentext"},
{"\nblue\t63", "\nblue\t63"},
}

func TestEncoding(t *testing.T) {
for _, tt := range encodingTests {
encoded, err := Encode([]byte(tt.in))
if err != nil {
t.Errorf(err.Error())
}
decoded, err := Decode(encoded)
if err != nil {
t.Errorf(err.Error())
}
if tt.out != string(decoded) {
t.Errorf("want %s, got %s", tt.out, decoded)
}
}
}

0 comments on commit b47b0c5

Please sign in to comment.