token/disk: implement unencrypted disk store

This commit is contained in:
Mitchell Hashimoto 2015-03-30 09:21:59 -07:00
parent 62e36ecb68
commit 27bc188758
8 changed files with 227 additions and 31 deletions

View File

@ -0,0 +1,89 @@
package disk
import (
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/mitchellh/go-homedir"
)
// DefaultPath is the default path where the Vault token is stored.
const DefaultPath = "~/.vault-token"
type Command struct{}
func (c *Command) Run(args []string) int {
var path string
f := flag.NewFlagSet("token-disk", flag.ContinueOnError)
f.StringVar(&path, "path", DefaultPath, "")
f.Usage = func() { fmt.Fprintf(os.Stderr, c.Help()+"\n") }
if err := f.Parse(args); err != nil {
fmt.Fprintf(os.Stderr, "\n%s\n", err)
return 1
}
path, err := homedir.Expand(path)
if err != nil {
fmt.Fprintf(os.Stderr, "Error expanding directory: %s\n", err)
return 1
}
switch args[0] {
case "get":
f, err := os.Open(path)
if os.IsNotExist(err) {
return 0
}
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
return 1
}
defer f.Close()
if _, err := io.Copy(os.Stdout, f); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
return 1
}
case "store":
f, err := os.Create(path)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
return 1
}
defer f.Close()
if _, err := io.Copy(f, os.Stdin); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
return 1
}
case "erase":
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "%s\n", err)
return 1
}
}
return 0
}
func (c *Command) Synopsis() string {
return "Stores Vault tokens on disk"
}
func (c *Command) Help() string {
helpText := `
Usage: vault token-disk [options] [operation]
Vault token helper (see vault config "token_helper") that writes
authenticated tokens to disk unencrypted.
Options:
-path=path Path to store the token.
`
return strings.TrimSpace(helpText)
}

View File

@ -0,0 +1,15 @@
package disk
import (
"testing"
"github.com/hashicorp/vault/command/token"
)
func TestCommand(t *testing.T) {
token.TestProcess(t)
}
func TestHelperProcess(t *testing.T) {
token.TestHelperProcessCLI(t, new(Command))
}

11
command/config.go Normal file
View File

@ -0,0 +1,11 @@
package command
// Config is the CLI configuration for Vault that can be specified via
// a `$HOME/.vault` file which is HCL-formatted (therefore HCL or JSON).
type Config struct {
// TokenHelper is the executable/command that is executed for storing
// and retrieving the authentication token for the Vault CLI. If this
// is not specified, then vault token-disk will be used, which stores
// the token on disk unencrypted.
TokenHelper string `hcl:"token_helper"`
}

View File

@ -2,6 +2,7 @@ package token
import (
"bytes"
"fmt"
"os/exec"
)
@ -24,16 +25,23 @@ type Helper struct {
// Erase deletes the contents from the helper.
func (h *Helper) Erase() error {
cmd := h.cmd("erase")
return cmd.Run()
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf(
"Error: %s\n\n%s", err, string(output))
}
return nil
}
// Get gets the token value from the helper.
func (h *Helper) Get() (string, error) {
var buf bytes.Buffer
var buf, stderr bytes.Buffer
cmd := h.cmd("get")
cmd.Stdout = &buf
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return "", err
return "", fmt.Errorf(
"Error: %s\n\n%s", err, stderr.String())
}
return buf.String(), nil
@ -44,7 +52,12 @@ func (h *Helper) Store(v string) error {
buf := bytes.NewBufferString(v)
cmd := h.cmd("store")
cmd.Stdin = buf
return cmd.Run()
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf(
"Error: %s\n\n%s", err, string(output))
}
return nil
}
func (h *Helper) cmd(op string) *exec.Cmd {

View File

@ -11,31 +11,7 @@ import (
func TestHelper(t *testing.T) {
h := testHelper(t)
if err := h.Store("foo"); err != nil {
t.Fatalf("err: %s", err)
}
v, err := h.Get()
if err != nil {
t.Fatalf("err: %s", err)
}
if v != "foo" {
t.Fatalf("bad: %#v", v)
}
if err := h.Erase(); err != nil {
t.Fatalf("err: %s", err)
}
v, err = h.Get()
if err != nil {
t.Fatalf("err: %s", err)
}
if v != "" {
t.Fatalf("bad: %#v", v)
}
Test(t, h.Path)
}
func testHelper(t *testing.T) *Helper {

77
command/token/testing.go Normal file
View File

@ -0,0 +1,77 @@
package token
import (
"fmt"
"os"
"strings"
"testing"
"github.com/mitchellh/cli"
)
// Test is a public function that can be used in other tests to
// test that a helper is functioning properly.
func Test(t *testing.T, path string) {
h := &Helper{Path: path}
if err := h.Store("foo"); err != nil {
t.Fatalf("err: %s", err)
}
v, err := h.Get()
if err != nil {
t.Fatalf("err: %s", err)
}
if v != "foo" {
t.Fatalf("bad: %#v", v)
}
if err := h.Erase(); err != nil {
t.Fatalf("err: %s", err)
}
v, err = h.Get()
if err != nil {
t.Fatalf("err: %s", err)
}
if v != "" {
t.Fatalf("bad: %#v", v)
}
}
// TestProcess is used to re-execute this test in order to use it as the
// helper process. For this to work, the TestHelperProcess function must
// exist.
func TestProcess(t *testing.T, s ...string) {
// Build the path to the CLI to execute
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
path := fmt.Sprintf(
"GO_WANT_HELPER_PROCESS=1 %s %s",
os.Args[0],
strings.Join(cs, " "))
// Run the tests
Test(t, path)
}
// TestHelperProcessCLI can be called to implement TestHelperProcess
// for TestProcess that just executes a CLI command.
func TestHelperProcessCLI(t *testing.T, cmd cli.Command) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
args := os.Args
for len(args) > 0 {
if args[0] == "--" {
args = args[1:]
break
}
args = args[1:]
}
os.Exit(cmd.Run(args))
}

View File

@ -5,13 +5,16 @@ import (
"github.com/hashicorp/vault/builtin/logical/aws"
"github.com/hashicorp/vault/builtin/logical/consul"
tokenDisk "github.com/hashicorp/vault/builtin/token/disk"
"github.com/hashicorp/vault/command"
"github.com/hashicorp/vault/logical"
"github.com/mitchellh/cli"
)
// Commands is the mapping of all the available Consul commands.
// Commands is the mapping of all the available Vault commands. CommandsInclude
// are the commands to include for help.
var Commands map[string]cli.CommandFactory
var CommandsInclude []string
func init() {
ui := &cli.BasicUi{
@ -97,4 +100,15 @@ func init() {
}, nil
},
}
// Build the commands to include in the help now
CommandsInclude = make([]string, 0, len(Commands))
for k, _ := range Commands {
CommandsInclude = append(CommandsInclude, k)
}
// The commands below are hidden from the help output
Commands["token-disk"] = func() (cli.Command, error) {
return &tokenDisk.Command{}, nil
}
}

View File

@ -28,7 +28,8 @@ func realMain() int {
cli := &cli.CLI{
Args: args,
Commands: Commands,
HelpFunc: cli.BasicHelpFunc("vault"),
HelpFunc: cli.FilteredHelpFunc(
CommandsInclude, cli.BasicHelpFunc("vault")),
}
exitCode, err := cli.Run()