merging with master

This commit is contained in:
Vishal Nayak 2015-06-18 20:51:11 -04:00
commit 5cd9b7a7d8
24 changed files with 600 additions and 75 deletions

View File

@ -15,11 +15,13 @@ func Backend() *framework.Backend {
PathsSpecial: &logical.Paths{
Root: []string{
"keys/*",
"raw/*",
},
},
Paths: []*framework.Path{
pathKeys(),
pathRaw(),
pathEncrypt(),
pathDecrypt(),
},

View File

@ -21,10 +21,27 @@ func TestBackend_basic(t *testing.T) {
Steps: []logicaltest.TestStep{
testAccStepWritePolicy(t, "test"),
testAccStepReadPolicy(t, "test", false),
testAccStepReadRaw(t, "test", false),
testAccStepEncrypt(t, "test", testPlaintext, decryptData),
testAccStepDecrypt(t, "test", testPlaintext, decryptData),
testAccStepDeletePolicy(t, "test"),
testAccStepReadPolicy(t, "test", true),
testAccStepReadRaw(t, "test", true),
},
})
}
func TestBackend_upsert(t *testing.T) {
decryptData := make(map[string]interface{})
logicaltest.Test(t, logicaltest.TestCase{
Backend: Backend(),
Steps: []logicaltest.TestStep{
testAccStepReadPolicy(t, "test", true),
testAccStepEncrypt(t, "test", testPlaintext, decryptData),
testAccStepReadPolicy(t, "test", false),
testAccStepDecrypt(t, "test", testPlaintext, decryptData),
testAccStepDeletePolicy(t, "test"),
testAccStepReadPolicy(t, "test", true),
},
})
}
@ -65,6 +82,43 @@ func testAccStepReadPolicy(t *testing.T, name string, expectNone bool) logicalte
return err
}
if d.Name != name {
return fmt.Errorf("bad: %#v", d)
}
if d.CipherMode != "aes-gcm" {
return fmt.Errorf("bad: %#v", d)
}
// Should NOT get a key back
if d.Key != nil {
return fmt.Errorf("bad: %#v", d)
}
return nil
},
}
}
func testAccStepReadRaw(t *testing.T, name string, expectNone bool) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.ReadOperation,
Path: "raw/" + name,
Check: func(resp *logical.Response) error {
if resp == nil && !expectNone {
return fmt.Errorf("missing response")
} else if expectNone {
if resp != nil {
return fmt.Errorf("response when expecting none")
}
return nil
}
var d struct {
Name string `mapstructure:"name"`
Key []byte `mapstructure:"key"`
CipherMode string `mapstructure:"cipher_mode"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
return err
}
if d.Name != name {
return fmt.Errorf("bad: %#v", d)
}

View File

@ -5,6 +5,7 @@ import (
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
@ -56,7 +57,10 @@ func pathEncryptWrite(
// Error if invalid policy
if p == nil {
return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest
p, err = generatePolicy(req.Storage, name)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("failed to upsert policy: %v", err)), logical.ErrInvalidRequest
}
}
// Guard against a potentially invalid cipher-mode

View File

@ -45,6 +45,41 @@ func getPolicy(req *logical.Request, name string) (*Policy, error) {
return p, nil
}
// generatePolicy is used to create a new named policy with
// a randomly generated key
func generatePolicy(storage logical.Storage, name string) (*Policy, error) {
// Create the policy object
p := &Policy{
Name: name,
CipherMode: "aes-gcm",
}
// Generate a 256bit key
p.Key = make([]byte, 32)
_, err := rand.Read(p.Key)
if err != nil {
return nil, err
}
// Encode the policy
buf, err := p.Serialize()
if err != nil {
return nil, err
}
// Write the policy into storage
err = storage.Put(&logical.StorageEntry{
Key: "policy/" + name,
Value: buf,
})
if err != nil {
return nil, err
}
// Return the policy
return p, nil
}
func pathKeys() *framework.Path {
return &framework.Path{
Pattern: `keys/(?P<name>\w+)`,
@ -79,34 +114,9 @@ func pathPolicyWrite(
return nil, nil
}
// Create the policy object
p := &Policy{
Name: name,
CipherMode: "aes-gcm",
}
// Generate a 256bit key
p.Key = make([]byte, 32)
_, err = rand.Read(p.Key)
if err != nil {
return nil, err
}
// Encode the policy
buf, err := p.Serialize()
if err != nil {
return nil, err
}
// Write the policy into storage
err = req.Storage.Put(&logical.StorageEntry{
Key: "policy/" + name,
Value: buf,
})
if err != nil {
return nil, err
}
return nil, nil
// Generate the policy
_, err = generatePolicy(req.Storage, name)
return nil, err
}
func pathPolicyRead(
@ -124,7 +134,6 @@ func pathPolicyRead(
resp := &logical.Response{
Data: map[string]interface{}{
"name": p.Name,
"key": p.Key,
"cipher_mode": p.CipherMode,
},
}

View File

@ -0,0 +1,54 @@
package transit
import (
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
func pathRaw() *framework.Path {
return &framework.Path{
Pattern: `raw/(?P<name>\w+)`,
Fields: map[string]*framework.FieldSchema{
"name": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Name of the key",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: pathRawRead,
},
HelpSynopsis: pathPolicyHelpSyn,
HelpDescription: pathPolicyHelpDesc,
}
}
func pathRawRead(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
name := d.Get("name").(string)
p, err := getPolicy(req, name)
if err != nil {
return nil, err
}
if p == nil {
return nil, nil
}
// Return the response
resp := &logical.Response{
Data: map[string]interface{}{
"name": p.Name,
"key": p.Key,
"cipher_mode": p.CipherMode,
},
}
return resp, nil
}
const pathRawHelpSyn = `Fetch raw keys for named encrption keys`
const pathRawHelpDesc = `
This path is used to get the underlying encryption keys used for the
named keys that are available.
`

View File

@ -2,6 +2,8 @@ package cli
import (
"os"
"os/signal"
"syscall"
auditFile "github.com/hashicorp/vault/builtin/audit/file"
auditSyslog "github.com/hashicorp/vault/builtin/audit/syslog"
@ -70,6 +72,7 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
"mysql": mysql.Factory,
"ssh": ssh.Factory,
},
ShutdownCh: makeShutdownCh(),
}, nil
},
@ -79,8 +82,8 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
}, nil
},
"help": func() (cli.Command, error) {
return &command.HelpCommand{
"path-help": func() (cli.Command, error) {
return &command.PathHelpCommand{
Meta: meta,
}, nil
},
@ -276,3 +279,20 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
},
}
}
// makeShutdownCh returns a channel that can be used for shutdown
// notifications for commands. This channel will send a message for every
// interrupt or SIGTERM received.
func makeShutdownCh() <-chan struct{} {
resultCh := make(chan struct{})
signalCh := make(chan os.Signal, 4)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
go func() {
for {
<-signalCh
resultCh <- struct{}{}
}
}()
return resultCh
}

View File

@ -13,14 +13,14 @@ import (
// HelpFunc is a cli.HelpFunc that can is used to output the help for Vault.
func HelpFunc(commands map[string]cli.CommandFactory) string {
commonNames := map[string]struct{}{
"delete": struct{}{},
"help": struct{}{},
"read": struct{}{},
"renew": struct{}{},
"revoke": struct{}{},
"write": struct{}{},
"server": struct{}{},
"status": struct{}{},
"delete": struct{}{},
"path-help": struct{}{},
"read": struct{}{},
"renew": struct{}{},
"revoke": struct{}{},
"write": struct{}{},
"server": struct{}{},
"status": struct{}{},
}
// Determine the maximum key length, and classify based on type

View File

@ -114,6 +114,15 @@ func (c *AuthCommand) Run(args []string) int {
return 0
}
// Warn if the VAULT_TOKEN environment variable is set, as that will take
// precedence
if os.Getenv("VAULT_TOKEN") != "" {
c.Ui.Output("==> WARNING: VAULT_TOKEN environment variable set!\n")
c.Ui.Output(" The environment variable takes precedence over the value")
c.Ui.Output(" set by the auth command. Either update the value of the")
c.Ui.Output(" environment variable or unset it to use the new token.\n")
}
var vars map[string]string
if len(args) > 0 {
builder := kvbuilder.Builder{Stdin: os.Stdin}

View File

@ -5,12 +5,12 @@ import (
"strings"
)
// HelpCommand is a Command that lists the mounts.
type HelpCommand struct {
// PathHelpCommand is a Command that lists the mounts.
type PathHelpCommand struct {
Meta
}
func (c *HelpCommand) Run(args []string) int {
func (c *PathHelpCommand) Run(args []string) int {
flags := c.Meta.FlagSet("help", FlagSetDefault)
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
@ -35,8 +35,15 @@ func (c *HelpCommand) Run(args []string) int {
help, err := client.Help(path)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error reading help: %s", err))
if strings.Contains(err.Error(), "Vault is sealed") {
c.Ui.Error(`Error: Vault is sealed.
The path-help command requires the Vault to be unsealed so that
mount points of secret backends are known.`)
} else {
c.Ui.Error(fmt.Sprintf(
"Error reading help: %s", err))
}
return 1
}
@ -44,13 +51,13 @@ func (c *HelpCommand) Run(args []string) int {
return 0
}
func (c *HelpCommand) Synopsis() string {
func (c *PathHelpCommand) Synopsis() string {
return "Look up the help for a path"
}
func (c *HelpCommand) Help() string {
func (c *PathHelpCommand) Help() string {
helpText := `
Usage: vault help [options] path
Usage: vault path-help [options] path
Look up the help for a path.
@ -58,6 +65,9 @@ Usage: vault help [options] path
providers provide built-in help. This command looks up and outputs that
help.
The command requires that the Vault be unsealed, because otherwise
the mount points of the backends are unknown.
General Options:
-address=addr The address of the Vault server.

View File

@ -14,7 +14,7 @@ func TestHelp(t *testing.T) {
defer ln.Close()
ui := new(cli.MockUi)
c := &HelpCommand{
c := &PathHelpCommand{
Meta: Meta{
ClientToken: token,
Ui: ui,

View File

@ -22,12 +22,16 @@ func (c *ReadCommand) Run(args []string) int {
}
args = flags.Args()
if len(args) < 1 || len(args) > 2 {
c.Ui.Error("read expects one or two arguments")
if len(args) != 1 {
c.Ui.Error("read expects one argument")
flags.Usage()
return 1
}
path := args[0]
if path[0] == '/' {
path = path[1:]
}
client, err := c.Client()
if err != nil {
@ -98,7 +102,7 @@ Read Options:
-format=table The format for output. By default it is a whitespace-
delimited table. This can also be json.
-field=field If included, the raw value of the specified field
-field=field If included, the raw value of the specified field
will be output raw to stdout.
`

View File

@ -32,6 +32,7 @@ type ServerCommand struct {
CredentialBackends map[string]logical.Factory
LogicalBackends map[string]logical.Factory
ShutdownCh <-chan struct{}
Meta
}
@ -154,7 +155,7 @@ func (c *ServerCommand) Run(args []string) int {
"immediately begin using the Vault CLI.\n\n"+
"The only step you need to take is to set the following\n"+
"environment variables:\n\n"+
" export VAULT_ADDR='http://127.0.0.1:8200'\n"+
" export VAULT_ADDR='http://127.0.0.1:8200'\n\n"+
"The unseal key and root token are reproduced below in case you\n"+
"want to seal/unseal the Vault or play with authentication.\n\n"+
"Unseal Key: %s\nRoot Token: %s\n",
@ -237,7 +238,14 @@ func (c *ServerCommand) Run(args []string) int {
// Release the log gate.
logGate.Flush()
<-make(chan struct{})
// Wait for shutdown
select {
case <-c.ShutdownCh:
c.Ui.Output("==> Vault shutdown triggered")
if err := core.Shutdown(); err != nil {
c.Ui.Error(fmt.Sprintf("Error with core shutdown: %s", err))
}
}
return 0
}
@ -407,8 +415,8 @@ General Options:
specified multiple times. If it is a directory, all
files with a ".hcl" or ".json" suffix will be loaded.
-dev Enables Dev mode. In this mode, Vault is completely
in-memory and unsealed. Do not run the Dev server in
-dev Enables Dev mode. In this mode, Vault is completely
in-memory and unsealed. Do not run the Dev server in
production!
-log-level=info Log verbosity. Defaults to "info", will be outputted

View File

@ -19,15 +19,18 @@ type WriteCommand struct {
func (c *WriteCommand) Run(args []string) int {
var format string
var force bool
flags := c.Meta.FlagSet("write", FlagSetDefault)
flags.StringVar(&format, "format", "table", "")
flags.BoolVar(&force, "force", false, "")
flags.BoolVar(&force, "f", false, "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
args = flags.Args()
if len(args) < 2 {
if len(args) < 2 && !force {
c.Ui.Error("write expects at least two arguments")
flags.Usage()
return 1
@ -117,6 +120,12 @@ General Options:
not recommended. This is especially not recommended
for unsealing a vault.
Write Options:
-f | -force Force the write to continue without any data values
specified. This allows writing to keys that do not
need or expect any fields to be specified.
`
return strings.TrimSpace(helpText)
}

View File

@ -246,3 +246,26 @@ func TestWrite_Output(t *testing.T) {
t.Fatalf("bad: %s", string(ui.OutputWriter.Bytes()))
}
}
func TestWrite_force(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
ui := new(cli.MockUi)
c := &WriteCommand{
Meta: Meta{
ClientToken: token,
Ui: ui,
},
}
args := []string{
"-address", addr,
"-force",
"sys/rotate",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
}

175
physical/mysql.go Normal file
View File

@ -0,0 +1,175 @@
package physical
import (
"database/sql"
"fmt"
"sort"
"strings"
"time"
"github.com/armon/go-metrics"
_ "github.com/go-sql-driver/mysql"
)
// MySQLBackend is a physical backend that stores data
// within MySQL database.
type MySQLBackend struct {
dbTable string
client *sql.DB
statements map[string]*sql.Stmt
}
// newMySQLBackend constructs a MySQL backend using the given API client and
// server address and credential for accessing mysql database.
func newMySQLBackend(conf map[string]string) (Backend, error) {
// Get the MySQL credentials to perform read/write operations.
username, ok := conf["username"]
if !ok || username == "" {
return nil, fmt.Errorf("missing username")
}
password, ok := conf["password"]
if !ok || username == "" {
return nil, fmt.Errorf("missing password")
}
// Get or set MySQL server address. Defaults to localhost and default port(3306)
address, ok := conf["address"]
if !ok {
address = "127.0.0.1:3306"
}
// Get the MySQL database and table details.
database, ok := conf["database"]
if !ok {
database = "vault"
}
table, ok := conf["table"]
if !ok {
table = "vault"
}
dbTable := database + "." + table
// Create MySQL handle for the database.
dsn := username + ":" + password + "@tcp(" + address + ")/"
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, fmt.Errorf("failed to connect to mysql: %v", err)
}
// Create the required database if it doesn't exists.
if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + database); err != nil {
return nil, fmt.Errorf("failed to create mysql database: %v", err)
}
// Create the required table if it doesn't exists.
create_query := "CREATE TABLE IF NOT EXISTS " + dbTable +
" (vault_key varchar(512), vault_value mediumblob, PRIMARY KEY (vault_key))"
if _, err := db.Exec(create_query); err != nil {
return nil, fmt.Errorf("failed to create mysql table: %v", err)
}
// Setup the backend.
m := &MySQLBackend{
dbTable: dbTable,
client: db,
statements: make(map[string]*sql.Stmt),
}
// Prepare all the statements required
statements := map[string]string{
"put": "INSERT INTO " + dbTable +
" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE vault_value=VALUES(vault_value)",
"get": "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
"list": "SELECT vault_key FROM " + dbTable + " WHERE vault_key LIKE ?",
}
for name, query := range statements {
if err := m.prepare(name, query); err != nil {
return nil, err
}
}
return m, nil
}
// prepare is a helper to prepare a query for future execution
func (m *MySQLBackend) prepare(name, query string) error {
stmt, err := m.client.Prepare(query)
if err != nil {
return fmt.Errorf("failed to prepare '%s': %v", name, err)
}
m.statements[name] = stmt
return nil
}
// Put is used to insert or update an entry.
func (m *MySQLBackend) Put(entry *Entry) error {
defer metrics.MeasureSince([]string{"mysql", "put"}, time.Now())
_, err := m.statements["put"].Exec(entry.Key, entry.Value)
if err != nil {
return err
}
return nil
}
// Get is used to fetch and entry.
func (m *MySQLBackend) Get(key string) (*Entry, error) {
defer metrics.MeasureSince([]string{"mysql", "get"}, time.Now())
var result []byte
err := m.statements["get"].QueryRow(key).Scan(&result)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
ent := &Entry{
Key: key,
Value: result,
}
return ent, nil
}
// Delete is used to permanently delete an entry
func (m *MySQLBackend) Delete(key string) error {
defer metrics.MeasureSince([]string{"mysql", "delete"}, time.Now())
_, err := m.statements["delete"].Exec(key)
if err != nil {
return err
}
return nil
}
// List is used to list all the keys under a given
// prefix, up to the next prefix.
func (m *MySQLBackend) List(prefix string) ([]string, error) {
defer metrics.MeasureSince([]string{"mysql", "list"}, time.Now())
// Add the % wildcard to the prefix to do the prefix search
likePrefix := prefix + "%"
rows, err := m.statements["list"].Query(likePrefix)
var keys []string
for rows.Next() {
var key string
err = rows.Scan(&key)
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
key = strings.TrimPrefix(key, prefix)
if i := strings.Index(key, "/"); i == -1 {
// Add objects only from the current 'folder'
keys = append(keys, key)
} else if i != -1 {
// Add truncated 'folder' paths
keys = appendIfMissing(keys, string(key[:i+1]))
}
}
sort.Strings(keys)
return keys, nil
}

53
physical/mysql_test.go Normal file
View File

@ -0,0 +1,53 @@
package physical
import (
"os"
"testing"
_ "github.com/go-sql-driver/mysql"
)
func TestMySQLBackend(t *testing.T) {
address := os.Getenv("MYSQL_ADDR")
if address == "" {
t.SkipNow()
}
database := os.Getenv("MYSQL_DB")
if database == "" {
database = "test"
}
table := os.Getenv("MYSQL_TABLE")
if table == "" {
table = "test"
}
username := os.Getenv("MYSQL_USERNAME")
password := os.Getenv("MYSQL_PASSWORD")
// Run vault tests
b, err := NewBackend("mysql", map[string]string{
"address": address,
"database": database,
"table": table,
"username": username,
"password": password,
})
if err != nil {
t.Fatalf("Failed to create new backend: %v", err)
}
defer func() {
mysql := b.(*MySQLBackend)
_, err := mysql.client.Exec("DROP TABLE " + mysql.dbTable)
if err != nil {
t.Fatalf("Failed to drop table: %v", err)
}
}()
testBackend(t, b)
testBackend_ListPrefix(t, b)
}

View File

@ -84,4 +84,5 @@ var BuiltinBackends = map[string]Factory{
"file": newFileBackend,
"s3": newS3Backend,
"etcd": newEtcdBackend,
"mysql": newMySQLBackend,
}

View File

@ -328,6 +328,21 @@ func NewCore(conf *CoreConfig) (*Core, error) {
return c, nil
}
// Shutdown is invoked when the Vault instance is about to be terminated. It
// should not be accessible as part of an API call as it will cause an availability
// problem. It is only used to gracefully quit in the case of HA so that failover
// happens as quickly as possible.
func (c *Core) Shutdown() error {
c.stateLock.Lock()
defer c.stateLock.Unlock()
if c.sealed {
return nil
}
// Seal the Vault, causes a leader stepdown
return c.sealInternal()
}
// HandleRequest is used to handle a new incoming request
func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err error) {
c.stateLock.RLock()
@ -930,6 +945,14 @@ func (c *Core) Seal(token string) error {
return err
}
// Seal the Vault
return c.sealInternal()
}
// sealInternal is an internal method used to seal the vault.
// It does not do any authorization checking. The stateLock must
// be held prior to calling.
func (c *Core) sealInternal() error {
// Enable that we are sealed to prevent furthur transactions
c.sealed = true

View File

@ -348,6 +348,17 @@ func TestCore_SealUnseal(t *testing.T) {
}
}
// Attempt to shutdown after unseal
func TestCore_Shutdown(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
if err := c.Shutdown(); err != nil {
t.Fatalf("err: %v", err)
}
if sealed, err := c.Sealed(); err != nil || !sealed {
t.Fatalf("err: %v", err)
}
}
// Attempt to seal bad token
func TestCore_Seal_BadToken(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)

View File

@ -635,7 +635,7 @@ func (l *leaseEntry) encode() ([]byte, error) {
func (le *leaseEntry) renewable() error {
// If there is no entry, cannot review
if le == nil || le.ExpireTime.IsZero() {
return fmt.Errorf("lease not found")
return fmt.Errorf("lease not found or lease is not renewable")
}
// Determine if the lease is expired

View File

@ -152,7 +152,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) {
// Should not be able to renew, no expiration
_, err = exp.RenewToken("auth/github/login", root.ID, 0)
if err.Error() != "lease not found" {
if err.Error() != "lease not found or lease is not renewable" {
t.Fatalf("err: %v", err)
}

View File

@ -202,7 +202,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) {
if err != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp.Data["error"] != "lease not found" {
if resp.Data["error"] != "lease not found or lease is not renewable" {
t.Fatalf("bad: %v", resp)
}
}
@ -250,7 +250,7 @@ func TestSystemBackend_revoke(t *testing.T) {
if err != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp3.Data["error"] != "lease not found" {
if resp3.Data["error"] != "lease not found or lease is not renewable" {
t.Fatalf("bad: %v", resp)
}
}
@ -312,7 +312,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) {
if err != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp3.Data["error"] != "lease not found" {
if resp3.Data["error"] != "lease not found or lease is not renewable" {
t.Fatalf("bad: %v", resp)
}
}

View File

@ -76,6 +76,8 @@ durability, etc.
* `s3` - Store data within an S3 bucket [S3](http://aws.amazon.com/s3/).
This backend does not support HA.
* `mysql` - Store data within MySQL. This backend does not support HA.
* `inmem` - Store data in-memory. This is only really useful for
development and experimentation. Data is lost whenever Vault is
restarted.
@ -143,6 +145,21 @@ For S3, the following options are supported:
* `region` (optional) - The AWS region. It can be sourced from the AWS_DEFAULT_REGION environment variable and will default to "us-east-1" if not specified.
#### Backend Reference: MySQL
The MySQL backend has the following options:
* `username` (required) - The MySQL username to connect with.
* `password` (required) - The MySQL password to connect with.
* `address` (optional) - The address of the MySQL host. Defaults to
"127.0.0.1:3306.
* `database` (optional) - The name of the database to use. Defaults to "vault".
* `table` (optional) - The name of the table to use. Defaults to "vault".
#### Backend Reference: Inmem
The in-memory backend has no configuration options.

View File

@ -54,6 +54,15 @@ $ vault read transit/keys/foo
Key Value
name foo
cipher_mode aes-gcm
````
We can read from the `raw/` endpoint to see the encryption key itself:
```
$ vault read transit/raw/foo
Key Value
name foo
cipher_mode aes-gcm
key PhKFTALCmhAhVQfMBAH4+UwJ6J2gybapUH9BsrtIgR8=
````
@ -114,17 +123,7 @@ only encrypt or decrypt using the named keys they need access to.
<dt>Returns</dt>
<dd>
```javascript
{
"data": {
"name": "foo",
"cipher_mode": "aes-gcm",
"key": "PhKFTALCmhAhVQfMBAH4+UwJ6J2gybapUH9BsrtIgR8="
}
}
```
A `204` response code.
</dd>
</dl>
@ -156,7 +155,6 @@ only encrypt or decrypt using the named keys they need access to.
"data": {
"name": "foo",
"cipher_mode": "aes-gcm",
"key": "PhKFTALCmhAhVQfMBAH4+UwJ6J2gybapUH9BsrtIgR8="
}
}
```
@ -196,7 +194,9 @@ only encrypt or decrypt using the named keys they need access to.
<dl class="api">
<dt>Description</dt>
<dd>
Encrypts the provided plaintext using the named key.
Encrypts the provided plaintext using the named key. If the named key
does not already exist, it will be automatically generated for the given
name with the default parameters.
</dd>
<dt>Method</dt>
@ -269,3 +269,42 @@ only encrypt or decrypt using the named keys they need access to.
</dd>
</dl>
### /transit/raw/
#### GET
<dl class="api">
<dt>Description</dt>
<dd>
Returns raw information about a named encryption key,
Including the underlying encryption key. This is a root protected endpoint.
</dd>
<dt>Method</dt>
<dd>GET</dd>
<dt>URL</dt>
<dd>`/transit/raw/<name>`</dd>
<dt>Parameters</dt>
<dd>
None
</dd>
<dt>Returns</dt>
<dd>
```javascript
{
"data": {
"name": "foo",
"cipher_mode": "aes-gcm",
"key": "PhKFTALCmhAhVQfMBAH4+UwJ6J2gybapUH9BsrtIgR8="
}
}
```
</dd>
</dl>