c349e97168
/cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
357 lines
6.4 KiB
Go
357 lines
6.4 KiB
Go
package vault
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/logical"
|
|
"github.com/hashicorp/vault/physical"
|
|
)
|
|
|
|
var (
|
|
// invalidKey is used to test Unseal
|
|
invalidKey = []byte("abcdefghijklmnopqrstuvwxyz")[:17]
|
|
)
|
|
|
|
func TestCore_Init(t *testing.T) {
|
|
inm := physical.NewInmem()
|
|
conf := &CoreConfig{Physical: inm}
|
|
c, err := NewCore(conf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
init, err := c.Initialized()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if init {
|
|
t.Fatalf("should not be init")
|
|
}
|
|
|
|
// Check the seal configuration
|
|
outConf, err := c.SealConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if outConf != nil {
|
|
t.Fatalf("bad: %v", outConf)
|
|
}
|
|
|
|
sealConf := &SealConfig{
|
|
SecretShares: 1,
|
|
SecretThreshold: 1,
|
|
}
|
|
res, err := c.Initialize(sealConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if len(res.SecretShares) != 1 {
|
|
t.Fatalf("Bad: %v", res)
|
|
}
|
|
|
|
_, err = c.Initialize(sealConf)
|
|
if err != ErrAlreadyInit {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
init, err = c.Initialized()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if !init {
|
|
t.Fatalf("should be init")
|
|
}
|
|
|
|
// Check the seal configuration
|
|
outConf, err = c.SealConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(outConf, sealConf) {
|
|
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
|
}
|
|
|
|
// New Core, same backend
|
|
c2, err := NewCore(conf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
_, err = c2.Initialize(sealConf)
|
|
if err != ErrAlreadyInit {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
init, err = c2.Initialized()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if !init {
|
|
t.Fatalf("should be init")
|
|
}
|
|
|
|
// Check the seal configuration
|
|
outConf, err = c2.SealConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(outConf, sealConf) {
|
|
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
|
}
|
|
}
|
|
|
|
func TestCore_Init_MultiShare(t *testing.T) {
|
|
c := TestCore(t)
|
|
sealConf := &SealConfig{
|
|
SecretShares: 5,
|
|
SecretThreshold: 3,
|
|
}
|
|
res, err := c.Initialize(sealConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if len(res.SecretShares) != 5 {
|
|
t.Fatalf("Bad: %v", res)
|
|
}
|
|
|
|
// Check the seal configuration
|
|
outConf, err := c.SealConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(outConf, sealConf) {
|
|
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
|
}
|
|
}
|
|
|
|
func TestCore_Unseal_MultiShare(t *testing.T) {
|
|
c := TestCore(t)
|
|
|
|
_, err := c.Unseal(invalidKey)
|
|
if err != ErrNotInit {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
sealConf := &SealConfig{
|
|
SecretShares: 5,
|
|
SecretThreshold: 3,
|
|
}
|
|
res, err := c.Initialize(sealConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
sealed, err := c.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !sealed {
|
|
t.Fatalf("should be sealed")
|
|
}
|
|
|
|
if prog := c.SecretProgress(); prog != 0 {
|
|
t.Fatalf("bad progress: %d", prog)
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
unseal, err := c.Unseal(res.SecretShares[i])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ignore redundant
|
|
_, err = c.Unseal(res.SecretShares[i])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if i >= 2 {
|
|
if !unseal {
|
|
t.Fatalf("should be unsealed")
|
|
}
|
|
if prog := c.SecretProgress(); prog != 0 {
|
|
t.Fatalf("bad progress: %d", prog)
|
|
}
|
|
} else {
|
|
if unseal {
|
|
t.Fatalf("should not be unsealed")
|
|
}
|
|
if prog := c.SecretProgress(); prog != i+1 {
|
|
t.Fatalf("bad progress: %d", prog)
|
|
}
|
|
}
|
|
}
|
|
|
|
sealed, err = c.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if sealed {
|
|
t.Fatalf("should not be sealed")
|
|
}
|
|
|
|
err = c.Seal()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ignore redundant
|
|
err = c.Seal()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
sealed, err = c.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !sealed {
|
|
t.Fatalf("should be sealed")
|
|
}
|
|
}
|
|
|
|
func TestCore_Unseal_Single(t *testing.T) {
|
|
c := TestCore(t)
|
|
|
|
_, err := c.Unseal(invalidKey)
|
|
if err != ErrNotInit {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
sealConf := &SealConfig{
|
|
SecretShares: 1,
|
|
SecretThreshold: 1,
|
|
}
|
|
res, err := c.Initialize(sealConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
sealed, err := c.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !sealed {
|
|
t.Fatalf("should be sealed")
|
|
}
|
|
|
|
if prog := c.SecretProgress(); prog != 0 {
|
|
t.Fatalf("bad progress: %d", prog)
|
|
}
|
|
|
|
unseal, err := c.Unseal(res.SecretShares[0])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if !unseal {
|
|
t.Fatalf("should be unsealed")
|
|
}
|
|
if prog := c.SecretProgress(); prog != 0 {
|
|
t.Fatalf("bad progress: %d", prog)
|
|
}
|
|
|
|
sealed, err = c.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if sealed {
|
|
t.Fatalf("should not be sealed")
|
|
}
|
|
}
|
|
|
|
func TestCore_Route_Sealed(t *testing.T) {
|
|
c := TestCore(t)
|
|
sealConf := &SealConfig{
|
|
SecretShares: 1,
|
|
SecretThreshold: 1,
|
|
}
|
|
|
|
// Should not route anything
|
|
req := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "sys/mounts",
|
|
}
|
|
_, err := c.HandleRequest(req)
|
|
if err != ErrSealed {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
res, err := c.Initialize(sealConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
unseal, err := c.Unseal(res.SecretShares[0])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !unseal {
|
|
t.Fatalf("should be unsealed")
|
|
}
|
|
|
|
// Should not error after unseal
|
|
_, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
// Attempt to unseal after doing a first seal
|
|
func TestCore_SealUnseal(t *testing.T) {
|
|
c, key := TestCoreUnsealed(t)
|
|
if err := c.Seal(); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if unseal, err := c.Unseal(key); err != nil || !unseal {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
// Ensure we get a VaultID
|
|
func TestCore_HandleRequest_Lease(t *testing.T) {
|
|
c, _ := TestCoreUnsealed(t)
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
}
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
|
|
// Read the key
|
|
req.Operation = logical.ReadOperation
|
|
req.Data = nil
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp == nil || resp.Secret == nil || resp.Data == nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
if resp.Secret.Lease != time.Hour {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Secret.VaultID == "" {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Data["foo"] != "bar" {
|
|
t.Fatalf("bad: %#v", resp.Data)
|
|
}
|
|
}
|