open-vault/vault/core_test.go

555 lines
11 KiB
Go
Raw Normal View History

2015-03-11 18:52:01 +00:00
package vault
import (
2015-03-11 18:57:05 +00:00
"reflect"
2015-03-11 18:52:01 +00:00
"testing"
"time"
2015-03-11 18:52:01 +00:00
2015-03-23 20:56:43 +00:00
"github.com/hashicorp/vault/credential"
"github.com/hashicorp/vault/logical"
2015-03-11 18:52:01 +00:00
"github.com/hashicorp/vault/physical"
)
2015-03-12 18:20:27 +00:00
var (
// invalidKey is used to test Unseal
invalidKey = []byte("abcdefghijklmnopqrstuvwxyz")[:17]
)
2015-03-11 18:52:01 +00:00
func TestCore_Init(t *testing.T) {
inm := physical.NewInmem()
2015-03-12 17:22:12 +00:00
conf := &CoreConfig{Physical: inm}
2015-03-11 18:52:01 +00:00
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")
}
2015-03-11 18:57:05 +00:00
// Check the seal configuration
outConf, err := c.SealConfig()
if err != nil {
t.Fatalf("err: %v", err)
}
if outConf != nil {
t.Fatalf("bad: %v", outConf)
}
2015-03-11 18:52:01 +00:00
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)
}
if res.RootToken == "" {
t.Fatalf("Bad: %v", res)
}
2015-03-11 18:52:01 +00:00
2015-03-11 18:57:05 +00:00
_, err = c.Initialize(sealConf)
if err != ErrAlreadyInit {
t.Fatalf("err: %v", err)
}
2015-03-11 18:52:01 +00:00
init, err = c.Initialized()
if err != nil {
t.Fatalf("err: %v", err)
}
if !init {
t.Fatalf("should be init")
}
2015-03-11 18:57:05 +00:00
// 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)
}
2015-03-11 18:52:01 +00:00
// New Core, same backend
c2, err := NewCore(conf)
if err != nil {
t.Fatalf("err: %v", err)
}
2015-03-11 18:57:05 +00:00
_, err = c2.Initialize(sealConf)
if err != ErrAlreadyInit {
t.Fatalf("err: %v", err)
}
2015-03-11 18:52:01 +00:00
init, err = c2.Initialized()
if err != nil {
t.Fatalf("err: %v", err)
}
if !init {
t.Fatalf("should be init")
}
2015-03-11 18:57:05 +00:00
// 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)
2015-03-11 18:57:05 +00:00
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)
}
if res.RootToken == "" {
t.Fatalf("Bad: %v", res)
2015-03-11 18:57:05 +00:00
}
// 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)
}
2015-03-11 18:52:01 +00:00
}
2015-03-11 21:25:16 +00:00
func TestCore_Unseal_MultiShare(t *testing.T) {
c := TestCore(t)
2015-03-11 21:25:16 +00:00
2015-03-12 18:20:27 +00:00
_, err := c.Unseal(invalidKey)
2015-03-11 21:25:16 +00:00
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)
2015-03-11 21:25:16 +00:00
2015-03-12 18:20:27 +00:00
_, err := c.Unseal(invalidKey)
2015-03-11 21:25:16 +00:00
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")
}
}
2015-03-11 21:31:55 +00:00
func TestCore_Route_Sealed(t *testing.T) {
c := TestCore(t)
2015-03-11 21:31:55 +00:00
sealConf := &SealConfig{
SecretShares: 1,
SecretThreshold: 1,
}
// Should not route anything
req := &logical.Request{
Operation: logical.ReadOperation,
2015-03-11 22:19:41 +00:00
Path: "sys/mounts",
2015-03-11 21:31:55 +00:00
}
_, 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
2015-03-24 18:37:07 +00:00
req.ClientToken = res.RootToken
2015-03-11 21:31:55 +00:00
_, err = c.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
2015-03-13 18:16:24 +00:00
// Attempt to unseal after doing a first seal
func TestCore_SealUnseal(t *testing.T) {
c, key := TestCoreUnsealed(t)
2015-03-13 18:16:24 +00:00
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) {
2015-03-24 18:37:07 +00:00
c, _, root := TestCoreUnsealedToken(t)
req := &logical.Request{
Operation: logical.WriteOperation,
Path: "secret/test",
Data: map[string]interface{}{
"foo": "bar",
"lease": "1h",
},
2015-03-24 18:37:07 +00:00
ClientToken: root,
}
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)
}
vault: clean up VaultID duplications, make secret responses clearer /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.
2015-03-19 22:11:42 +00:00
if resp == nil || resp.Secret == nil || resp.Data == nil {
t.Fatalf("bad: %#v", resp)
}
vault: clean up VaultID duplications, make secret responses clearer /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.
2015-03-19 22:11:42 +00:00
if resp.Secret.Lease != time.Hour {
t.Fatalf("bad: %#v", resp.Secret)
}
vault: clean up VaultID duplications, make secret responses clearer /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.
2015-03-19 22:11:42 +00:00
if resp.Secret.VaultID == "" {
t.Fatalf("bad: %#v", resp.Secret)
}
if resp.Data["foo"] != "bar" {
t.Fatalf("bad: %#v", resp.Data)
}
}
2015-03-23 20:56:43 +00:00
2015-03-24 18:57:08 +00:00
func TestCore_HandleRequest_MissingToken(t *testing.T) {
c, _, _ := TestCoreUnsealedToken(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 != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp.Data["error"] != "missing client token" {
t.Fatalf("bad: %#v", resp)
}
}
func TestCore_HandleRequest_InvalidToken(t *testing.T) {
c, _, _ := TestCoreUnsealedToken(t)
req := &logical.Request{
Operation: logical.WriteOperation,
Path: "secret/test",
Data: map[string]interface{}{
"foo": "bar",
"lease": "1h",
},
ClientToken: "foobarbaz",
}
resp, err := c.HandleRequest(req)
if err != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp.Data["error"] != "invalid client token" {
t.Fatalf("bad: %#v", resp)
}
}
// Test a root path is denied if non-root
func TestCore_HandleRequest_RootPath(t *testing.T) {
c, _, root := TestCoreUnsealedToken(t)
testCoreMakeToken(t, c, root, "child", []string{"test"})
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "sys/policy", // root protected!
ClientToken: "child",
}
resp, err := c.HandleRequest(req)
if err != logical.ErrPermissionDenied {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
}
// Test a root path is allowed if non-root but with sudo
func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) {
c, _, root := TestCoreUnsealedToken(t)
// Set the 'test' policy object to permit access to sys/policy
req := &logical.Request{
Operation: logical.WriteOperation,
Path: "sys/policy/test", // root protected!
Data: map[string]interface{}{
"rules": `path "sys/policy" { policy = "sudo" }`,
},
ClientToken: root,
}
resp, err := c.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
// Child token (non-root) but with 'test' policy should have access
testCoreMakeToken(t, c, root, "child", []string{"test"})
req = &logical.Request{
Operation: logical.ReadOperation,
Path: "sys/policy", // root protected!
ClientToken: "child",
}
resp, err = c.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp == nil {
t.Fatalf("bad: %#v", resp)
}
}
2015-03-24 18:57:08 +00:00
// TODO: Check that standard permissions work
//func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
// c, _, _ := TestCoreUnsealedToken(t)
// req := &logical.Request{
// Operation: logical.WriteOperation,
// Path: "secret/test",
// Data: map[string]interface{}{
// "foo": "bar",
// "lease": "1h",
// },
// ClientToken: "foobarbaz",
// }
// resp, err := c.HandleRequest(req)
// if err != logical.ErrInvalidRequest {
// t.Fatalf("err: %v", err)
// }
// if resp.Data["error"] != "invalid client token" {
// t.Fatalf("bad: %#v", resp)
// }
//}
2015-03-23 20:56:43 +00:00
// Ensure we get a client token
func TestCore_HandleLogin_Token(t *testing.T) {
// Create a badass credential backend that always logs in as armon
noop := &NoopCred{
Login: []string{"login"},
LoginResponse: &credential.Response{
Secret: &logical.Secret{
InternalData: map[string]interface{}{
credential.PolicyKey: []string{"foo", "bar"},
credential.MetadataKey + "user": "armon",
},
Lease: time.Hour,
},
},
}
2015-03-24 18:37:07 +00:00
c, _, root := TestCoreUnsealedToken(t)
2015-03-23 20:56:43 +00:00
c.credentialBackends["noop"] = func(map[string]string) (credential.Backend, error) {
return noop, nil
}
// Enable the credential backend
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
req.Data["type"] = "noop"
2015-03-24 18:37:07 +00:00
req.ClientToken = root
2015-03-23 20:56:43 +00:00
_, err := c.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
// Attempt to login
lreq := &credential.Request{
Path: "auth/foo/login",
}
lresp, err := c.HandleLogin(lreq)
if err != nil {
t.Fatalf("err: %v", err)
}
// Ensure we got a client token back
clientToken, ok := lresp.Data[clientTokenKey].(string)
if !ok || clientToken == "" {
t.Fatalf("bad: %#v", lresp)
}
// Check the policy and metadata
te, err := c.tokenStore.Lookup(clientToken)
if err != nil {
t.Fatalf("err: %v", err)
}
expect := &TokenEntry{
ID: clientToken,
Parent: "",
Policies: []string{"foo", "bar"},
Path: "auth/foo/login",
Meta: map[string]interface{}{
"user": "armon",
},
}
if !reflect.DeepEqual(te, expect) {
t.Fatalf("Bad: %#v expect: %#v", te, expect)
}
// Check that we have a lease with a VaultID
if lresp.Secret.Lease != time.Hour {
t.Fatalf("bad: %#v", lresp.Secret)
}
if lresp.Secret.VaultID == "" {
t.Fatalf("bad: %#v", lresp.Secret)
}
}