1959 lines
42 KiB
Go
1959 lines
42 KiB
Go
package vault
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/audit"
|
|
"github.com/hashicorp/vault/logical"
|
|
"github.com/hashicorp/vault/physical"
|
|
)
|
|
|
|
var (
|
|
// invalidKey is used to test Unseal
|
|
invalidKey = []byte("abcdefghijklmnopqrstuvwxyz")[:17]
|
|
)
|
|
|
|
func TestNewCore_badAdvertiseAddr(t *testing.T) {
|
|
conf := &CoreConfig{
|
|
AdvertiseAddr: "127.0.0.1:8200",
|
|
Physical: physical.NewInmem(),
|
|
DisableMlock: true,
|
|
}
|
|
_, err := NewCore(conf)
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestSealConfig_Invalid(t *testing.T) {
|
|
s := &SealConfig{
|
|
SecretShares: 2,
|
|
SecretThreshold: 1,
|
|
}
|
|
err := s.Validate()
|
|
if err == nil {
|
|
t.Fatalf("expected err")
|
|
}
|
|
}
|
|
|
|
func TestCore_Init(t *testing.T) {
|
|
inm := physical.NewInmem()
|
|
conf := &CoreConfig{
|
|
Physical: inm,
|
|
DisableMlock: true,
|
|
}
|
|
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)
|
|
}
|
|
if res.RootToken == "" {
|
|
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)
|
|
}
|
|
if res.RootToken == "" {
|
|
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(res.RootToken)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ignore redundant
|
|
err = c.Seal(res.RootToken)
|
|
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
|
|
req.ClientToken = res.RootToken
|
|
_, 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, root := TestCoreUnsealed(t)
|
|
if err := c.Seal(root); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if unseal, err := c.Unseal(key); err != nil || !unseal {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
if err := c.Seal("foo"); err == nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if sealed, err := c.Sealed(); err != nil || sealed {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
// Ensure we get a LeaseID
|
|
func TestCore_HandleRequest_Lease(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
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)
|
|
}
|
|
if resp == nil || resp.Secret == nil || resp.Data == nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
if resp.Secret.TTL != time.Hour {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Secret.LeaseID == "" {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Data["foo"] != "bar" {
|
|
t.Fatalf("bad: %#v", resp.Data)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_Lease_MaxLength(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1000h",
|
|
},
|
|
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)
|
|
}
|
|
if resp == nil || resp.Secret == nil || resp.Data == nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
if resp.Secret.TTL != c.maxLeaseTTL {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Secret.LeaseID == "" {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Data["foo"] != "bar" {
|
|
t.Fatalf("bad: %#v", resp.Data)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "0h",
|
|
},
|
|
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)
|
|
}
|
|
if resp == nil || resp.Secret == nil || resp.Data == nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
if resp.Secret.TTL != c.defaultLeaseTTL {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Secret.LeaseID == "" {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
if resp.Data["foo"] != "bar" {
|
|
t.Fatalf("bad: %#v", resp.Data)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_MissingToken(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 != 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, _, _ := TestCoreUnsealed(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.ErrPermissionDenied {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp.Data["error"] != "permission denied" {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
}
|
|
|
|
// Check that standard permissions work
|
|
func TestCore_HandleRequest_NoSlash(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.HelpOperation,
|
|
Path: "secret",
|
|
ClientToken: root,
|
|
}
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v, resp: %v", err, resp)
|
|
}
|
|
if _, ok := resp.Data["help"]; !ok {
|
|
t.Fatalf("resp: %v", resp)
|
|
}
|
|
}
|
|
|
|
// Test a root path is denied if non-root
|
|
func TestCore_HandleRequest_RootPath(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(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, resp: %v", err, resp)
|
|
}
|
|
}
|
|
|
|
// Test a root path is allowed if non-root but with sudo
|
|
func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(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)
|
|
}
|
|
}
|
|
|
|
// Check that standard permissions work
|
|
func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
testCoreMakeToken(t, c, root, "child", []string{"test"})
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
ClientToken: "child",
|
|
}
|
|
resp, err := c.HandleRequest(req)
|
|
if err != logical.ErrPermissionDenied {
|
|
t.Fatalf("err: %v, resp: %v", err, resp)
|
|
}
|
|
}
|
|
|
|
// Check that standard permissions work
|
|
func TestCore_HandleRequest_PermissionAllowed(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
testCoreMakeToken(t, c, root, "child", []string{"test"})
|
|
|
|
// Set the 'test' policy object to permit access to secret/
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "sys/policy/test",
|
|
Data: map[string]interface{}{
|
|
"rules": `path "secret/*" { policy = "write" }`,
|
|
},
|
|
ClientToken: root,
|
|
}
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
|
|
// Write should work now
|
|
req = &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
ClientToken: "child",
|
|
}
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_NoConnection(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Response: &logical.Response{},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.logicalBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the logical backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/mounts/foo")
|
|
req.Data["type"] = "noop"
|
|
req.Data["description"] = "foo"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to request with connection data
|
|
req = &logical.Request{
|
|
Path: "foo/login",
|
|
Connection: &logical.Connection{},
|
|
}
|
|
req.ClientToken = root
|
|
if _, err := c.HandleRequest(req); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if noop.Requests[0].Connection != nil {
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_NoClientToken(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Response: &logical.Response{},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.logicalBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the logical backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/mounts/foo")
|
|
req.Data["type"] = "noop"
|
|
req.Data["description"] = "foo"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to request with connection data
|
|
req = &logical.Request{
|
|
Path: "foo/login",
|
|
}
|
|
req.ClientToken = root
|
|
if _, err := c.HandleRequest(req); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
ct := noop.Requests[0].ClientToken
|
|
if ct == "" || ct == root {
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_ConnOnLogin(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Login: []string{"login"},
|
|
Response: &logical.Response{},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.credentialBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to request with connection data
|
|
req = &logical.Request{
|
|
Path: "auth/foo/login",
|
|
Connection: &logical.Connection{},
|
|
}
|
|
if _, err := c.HandleRequest(req); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if noop.Requests[0].Connection == nil {
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
}
|
|
}
|
|
|
|
// Ensure we get a client token
|
|
func TestCore_HandleLogin_Token(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Login: []string{"login"},
|
|
Response: &logical.Response{
|
|
Auth: &logical.Auth{
|
|
Policies: []string{"foo", "bar"},
|
|
Metadata: map[string]string{
|
|
"user": "armon",
|
|
},
|
|
DisplayName: "armon",
|
|
},
|
|
},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.credentialBackends["noop"] = func(conf *logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to login
|
|
lreq := &logical.Request{
|
|
Path: "auth/foo/login",
|
|
}
|
|
lresp, err := c.HandleRequest(lreq)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ensure we got a client token back
|
|
clientToken := lresp.Auth.ClientToken
|
|
if 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]string{
|
|
"user": "armon",
|
|
},
|
|
DisplayName: "foo-armon",
|
|
TTL: time.Hour * 24,
|
|
CreationTime: te.CreationTime,
|
|
}
|
|
|
|
if !reflect.DeepEqual(te, expect) {
|
|
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
|
}
|
|
|
|
// Check that we have a lease with default duration
|
|
if lresp.Auth.TTL != noop.System().DefaultLeaseTTL() {
|
|
t.Fatalf("bad: %#v, defaultLeaseTTL: %#v", lresp.Auth, c.defaultLeaseTTL)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_AuditTrail(t *testing.T) {
|
|
// Create a noop audit backend
|
|
noop := &NoopAudit{}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.auditBackends["noop"] = func(config *audit.BackendConfig) (audit.Backend, error) {
|
|
noop = &NoopAudit{
|
|
Config: config,
|
|
}
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the audit backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/audit/noop")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Make a request
|
|
req = &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
ClientToken: root,
|
|
}
|
|
req.ClientToken = root
|
|
if _, err := c.HandleRequest(req); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Check the audit trail on request and response
|
|
if len(noop.ReqAuth) != 1 {
|
|
t.Fatalf("bad: %#v", noop)
|
|
}
|
|
auth := noop.ReqAuth[0]
|
|
if auth.ClientToken != root {
|
|
t.Fatalf("bad client token: %#v", auth)
|
|
}
|
|
if len(auth.Policies) != 1 || auth.Policies[0] != "root" {
|
|
t.Fatalf("bad: %#v", auth)
|
|
}
|
|
if len(noop.Req) != 1 || !reflect.DeepEqual(noop.Req[0], req) {
|
|
t.Fatalf("Bad: %#v", noop.Req[0])
|
|
}
|
|
|
|
if len(noop.RespAuth) != 2 {
|
|
t.Fatalf("bad: %#v", noop)
|
|
}
|
|
if !reflect.DeepEqual(noop.RespAuth[1], auth) {
|
|
t.Fatalf("bad: %#v", auth)
|
|
}
|
|
if len(noop.RespReq) != 2 || !reflect.DeepEqual(noop.RespReq[1], req) {
|
|
t.Fatalf("Bad: %#v", noop.RespReq[1])
|
|
}
|
|
if len(noop.Resp) != 2 || !reflect.DeepEqual(noop.Resp[1], resp) {
|
|
t.Fatalf("Bad: %#v", noop.Resp[1])
|
|
}
|
|
}
|
|
|
|
// Ensure we get a client token
|
|
func TestCore_HandleLogin_AuditTrail(t *testing.T) {
|
|
// Create a badass credential backend that always logs in as armon
|
|
noop := &NoopAudit{}
|
|
noopBack := &NoopBackend{
|
|
Login: []string{"login"},
|
|
Response: &logical.Response{
|
|
Auth: &logical.Auth{
|
|
LeaseOptions: logical.LeaseOptions{
|
|
TTL: time.Hour,
|
|
},
|
|
Policies: []string{"foo", "bar"},
|
|
Metadata: map[string]string{
|
|
"user": "armon",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.credentialBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noopBack, nil
|
|
}
|
|
c.auditBackends["noop"] = func(config *audit.BackendConfig) (audit.Backend, error) {
|
|
noop = &NoopAudit{
|
|
Config: config,
|
|
}
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Enable the audit backend
|
|
req = logical.TestRequest(t, logical.WriteOperation, "sys/audit/noop")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to login
|
|
lreq := &logical.Request{
|
|
Path: "auth/foo/login",
|
|
}
|
|
lresp, err := c.HandleRequest(lreq)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ensure we got a client token back
|
|
clientToken := lresp.Auth.ClientToken
|
|
if clientToken == "" {
|
|
t.Fatalf("bad: %#v", lresp)
|
|
}
|
|
|
|
// Check the audit trail on request and response
|
|
if len(noop.ReqAuth) != 1 {
|
|
t.Fatalf("bad: %#v", noop)
|
|
}
|
|
if len(noop.Req) != 1 || !reflect.DeepEqual(noop.Req[0], lreq) {
|
|
t.Fatalf("Bad: %#v %#v", noop.Req[0], lreq)
|
|
}
|
|
|
|
if len(noop.RespAuth) != 2 {
|
|
t.Fatalf("bad: %#v", noop)
|
|
}
|
|
auth := noop.RespAuth[1]
|
|
if auth.ClientToken != clientToken {
|
|
t.Fatalf("bad client token: %#v", auth)
|
|
}
|
|
if len(auth.Policies) != 2 || auth.Policies[0] != "foo" || auth.Policies[1] != "bar" {
|
|
t.Fatalf("bad: %#v", auth)
|
|
}
|
|
if len(noop.RespReq) != 2 || !reflect.DeepEqual(noop.RespReq[1], lreq) {
|
|
t.Fatalf("Bad: %#v", noop.RespReq[1])
|
|
}
|
|
if len(noop.Resp) != 2 || !reflect.DeepEqual(noop.Resp[1], lresp) {
|
|
t.Fatalf("Bad: %#v %#v", noop.Resp[1], lresp)
|
|
}
|
|
}
|
|
|
|
// Check that we register a lease for new tokens
|
|
func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
// Create a new credential
|
|
req := logical.TestRequest(t, logical.WriteOperation, "auth/token/create")
|
|
req.ClientToken = root
|
|
req.Data["policies"] = []string{"foo"}
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ensure we got a new client token back
|
|
clientToken := resp.Auth.ClientToken
|
|
if clientToken == "" {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
|
|
// Check the policy and metadata
|
|
te, err := c.tokenStore.Lookup(clientToken)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
expect := &TokenEntry{
|
|
ID: clientToken,
|
|
Parent: root,
|
|
Policies: []string{"foo"},
|
|
Path: "auth/token/create",
|
|
DisplayName: "token",
|
|
CreationTime: te.CreationTime,
|
|
TTL: time.Hour * 24 * 30,
|
|
}
|
|
if !reflect.DeepEqual(te, expect) {
|
|
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
|
}
|
|
|
|
// Check that we have a lease with default duration
|
|
if resp.Auth.TTL != c.defaultLeaseTTL {
|
|
t.Fatalf("bad: %#v", resp.Auth)
|
|
}
|
|
}
|
|
|
|
func TestCore_LimitedUseToken(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
// Create a new credential
|
|
req := logical.TestRequest(t, logical.WriteOperation, "auth/token/create")
|
|
req.ClientToken = root
|
|
req.Data["num_uses"] = "1"
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Put a secret
|
|
req = &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/foo",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
ClientToken: resp.Auth.ClientToken,
|
|
}
|
|
_, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Second operation should fail
|
|
_, err = c.HandleRequest(req)
|
|
if err != logical.ErrPermissionDenied {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCore_Standby(t *testing.T) {
|
|
// Create the first core and initialize it
|
|
inm := physical.NewInmemHA()
|
|
advertiseOriginal := "http://127.0.0.1:8200"
|
|
core, err := NewCore(&CoreConfig{
|
|
Physical: inm,
|
|
AdvertiseAddr: advertiseOriginal,
|
|
DisableMlock: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
key, root := TestCoreInit(t, core)
|
|
if _, err := core.Unseal(TestKeyCopy(key)); err != nil {
|
|
t.Fatalf("unseal err: %s", err)
|
|
}
|
|
|
|
// Verify unsealed
|
|
sealed, err := core.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err checking seal status: %s", err)
|
|
}
|
|
if sealed {
|
|
t.Fatal("should not be sealed")
|
|
}
|
|
|
|
// Wait for core to become active
|
|
testWaitActive(t, core)
|
|
|
|
// Put a secret
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/foo",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
ClientToken: root,
|
|
}
|
|
_, err = core.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Check the leader is local
|
|
isLeader, advertise, err := core.Leader()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !isLeader {
|
|
t.Fatalf("should be leader")
|
|
}
|
|
if advertise != advertiseOriginal {
|
|
t.Fatalf("Bad advertise: %v", advertise)
|
|
}
|
|
|
|
// Create a second core, attached to same in-memory store
|
|
advertiseOriginal2 := "http://127.0.0.1:8500"
|
|
core2, err := NewCore(&CoreConfig{
|
|
Physical: inm,
|
|
AdvertiseAddr: advertiseOriginal2,
|
|
DisableMlock: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if _, err := core2.Unseal(TestKeyCopy(key)); err != nil {
|
|
t.Fatalf("unseal err: %s", err)
|
|
}
|
|
|
|
// Verify unsealed
|
|
sealed, err = core2.Sealed()
|
|
if err != nil {
|
|
t.Fatalf("err checking seal status: %s", err)
|
|
}
|
|
if sealed {
|
|
t.Fatal("should not be sealed")
|
|
}
|
|
|
|
// Core2 should be in standby
|
|
standby, err := core2.Standby()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !standby {
|
|
t.Fatalf("should be standby")
|
|
}
|
|
|
|
// Request should fail in standby mode
|
|
_, err = core2.HandleRequest(req)
|
|
if err != ErrStandby {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Check the leader is not local
|
|
isLeader, advertise, err = core2.Leader()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if isLeader {
|
|
t.Fatalf("should not be leader")
|
|
}
|
|
if advertise != advertiseOriginal {
|
|
t.Fatalf("Bad advertise: %v", advertise)
|
|
}
|
|
|
|
// Seal the first core, should step down
|
|
err = core.Seal(root)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Core should be in standby
|
|
standby, err = core.Standby()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !standby {
|
|
t.Fatalf("should be standby")
|
|
}
|
|
|
|
// Wait for core2 to become active
|
|
testWaitActive(t, core2)
|
|
|
|
// Read the secret
|
|
req = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "secret/foo",
|
|
ClientToken: root,
|
|
}
|
|
resp, err := core2.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify the response
|
|
if resp.Data["foo"] != "bar" {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
|
|
// Check the leader is local
|
|
isLeader, advertise, err = core2.Leader()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !isLeader {
|
|
t.Fatalf("should be leader")
|
|
}
|
|
if advertise != advertiseOriginal2 {
|
|
t.Fatalf("Bad advertise: %v", advertise)
|
|
}
|
|
}
|
|
|
|
// Ensure that InternalData is never returned
|
|
func TestCore_HandleRequest_Login_InternalData(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Login: []string{"login"},
|
|
Response: &logical.Response{
|
|
Auth: &logical.Auth{
|
|
Policies: []string{"foo", "bar"},
|
|
InternalData: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.credentialBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to login
|
|
lreq := &logical.Request{
|
|
Path: "auth/foo/login",
|
|
}
|
|
lresp, err := c.HandleRequest(lreq)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ensure we do not get the internal data
|
|
if lresp.Auth.InternalData != nil {
|
|
t.Fatalf("bad: %#v", lresp)
|
|
}
|
|
}
|
|
|
|
// Ensure that InternalData is never returned
|
|
func TestCore_HandleRequest_InternalData(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Response: &logical.Response{
|
|
Secret: &logical.Secret{
|
|
InternalData: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
}
|
|
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.logicalBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/mounts/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to read
|
|
lreq := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "foo/test",
|
|
ClientToken: root,
|
|
}
|
|
lresp, err := c.HandleRequest(lreq)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Ensure we do not get the internal data
|
|
if lresp.Secret.InternalData != nil {
|
|
t.Fatalf("bad: %#v", lresp)
|
|
}
|
|
}
|
|
|
|
// Ensure login does not return a secret
|
|
func TestCore_HandleLogin_ReturnSecret(t *testing.T) {
|
|
// Create a badass credential backend that always logs in as armon
|
|
noopBack := &NoopBackend{
|
|
Login: []string{"login"},
|
|
Response: &logical.Response{
|
|
Secret: &logical.Secret{},
|
|
Auth: &logical.Auth{
|
|
Policies: []string{"foo", "bar"},
|
|
},
|
|
},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.credentialBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noopBack, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to login
|
|
lreq := &logical.Request{
|
|
Path: "auth/foo/login",
|
|
}
|
|
_, err = c.HandleRequest(lreq)
|
|
if err != ErrInternalError {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
// Renew should return the same lease back
|
|
func TestCore_RenewSameLease(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
// Create a leasable secret
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
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)
|
|
}
|
|
if resp == nil || resp.Secret == nil || resp.Secret.LeaseID == "" {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
original := resp.Secret.LeaseID
|
|
|
|
// Renew the lease
|
|
req = logical.TestRequest(t, logical.WriteOperation, "sys/renew/"+resp.Secret.LeaseID)
|
|
req.ClientToken = root
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify the lease did not change
|
|
if resp.Secret.LeaseID != original {
|
|
t.Fatalf("lease id changed: %s %s", original, resp.Secret.LeaseID)
|
|
}
|
|
}
|
|
|
|
// Renew of a token should not create a new lease
|
|
func TestCore_RenewToken_SingleRegister(t *testing.T) {
|
|
c, _, root := TestCoreUnsealed(t)
|
|
|
|
// Create a new token
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "auth/token/create",
|
|
Data: map[string]interface{}{
|
|
"lease": "1h",
|
|
},
|
|
ClientToken: root,
|
|
}
|
|
resp, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
newClient := resp.Auth.ClientToken
|
|
|
|
// Renew the token
|
|
req = logical.TestRequest(t, logical.WriteOperation, "auth/token/renew/"+newClient)
|
|
req.ClientToken = newClient
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Revoke using the renew prefix
|
|
req = logical.TestRequest(t, logical.WriteOperation, "sys/revoke-prefix/auth/token/renew/")
|
|
req.ClientToken = root
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify our token is still valid (e.g. we did not get invalided by the revoke)
|
|
req = logical.TestRequest(t, logical.ReadOperation, "auth/token/lookup/"+newClient)
|
|
req.ClientToken = newClient
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify the token exists
|
|
if resp.Data["id"] != newClient {
|
|
t.Fatalf("bad: %#v", resp.Data)
|
|
}
|
|
}
|
|
|
|
// Based on bug GH-203, attempt to disable a credential backend with leased secrets
|
|
func TestCore_EnableDisableCred_WithLease(t *testing.T) {
|
|
// Create a badass credential backend that always logs in as armon
|
|
noopBack := &NoopBackend{
|
|
Login: []string{"login"},
|
|
Response: &logical.Response{
|
|
Auth: &logical.Auth{
|
|
Policies: []string{"root"},
|
|
},
|
|
},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.credentialBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noopBack, nil
|
|
}
|
|
|
|
// Enable the credential backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/auth/foo")
|
|
req.Data["type"] = "noop"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to login
|
|
lreq := &logical.Request{
|
|
Path: "auth/foo/login",
|
|
}
|
|
lresp, err := c.HandleRequest(lreq)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Create a leasable secret
|
|
req = &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "secret/test",
|
|
Data: map[string]interface{}{
|
|
"foo": "bar",
|
|
"lease": "1h",
|
|
},
|
|
ClientToken: lresp.Auth.ClientToken,
|
|
}
|
|
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.Secret.LeaseID == "" {
|
|
t.Fatalf("bad: %#v", resp.Secret)
|
|
}
|
|
|
|
// Renew the lease
|
|
req = logical.TestRequest(t, logical.WriteOperation, "sys/renew/"+resp.Secret.LeaseID)
|
|
req.ClientToken = lresp.Auth.ClientToken
|
|
_, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Disable the credential backend
|
|
req = logical.TestRequest(t, logical.DeleteOperation, "sys/auth/foo")
|
|
req.ClientToken = lresp.Auth.ClientToken
|
|
resp, err = c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v %#v", err, resp)
|
|
}
|
|
}
|
|
|
|
func TestCore_HandleRequest_MountPoint(t *testing.T) {
|
|
noop := &NoopBackend{
|
|
Response: &logical.Response{},
|
|
}
|
|
c, _, root := TestCoreUnsealed(t)
|
|
c.logicalBackends["noop"] = func(*logical.BackendConfig) (logical.Backend, error) {
|
|
return noop, nil
|
|
}
|
|
|
|
// Enable the logical backend
|
|
req := logical.TestRequest(t, logical.WriteOperation, "sys/mounts/foo")
|
|
req.Data["type"] = "noop"
|
|
req.Data["description"] = "foo"
|
|
req.ClientToken = root
|
|
_, err := c.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Attempt to request
|
|
req = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "foo/test",
|
|
Connection: &logical.Connection{},
|
|
}
|
|
req.ClientToken = root
|
|
if _, err := c.HandleRequest(req); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify Path and MountPoint
|
|
if noop.Requests[0].Path != "test" {
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
}
|
|
if noop.Requests[0].MountPoint != "foo/" {
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
}
|
|
}
|
|
|
|
func TestCore_Rekey_Lifecycle(t *testing.T) {
|
|
c, master, _ := TestCoreUnsealed(t)
|
|
|
|
// Verify update not allowed
|
|
if _, err := c.RekeyUpdate(master); err == nil {
|
|
t.Fatalf("no rekey in progress")
|
|
}
|
|
|
|
// Should be no progress
|
|
num, err := c.RekeyProgress()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if num != 0 {
|
|
t.Fatalf("bad: %d", num)
|
|
}
|
|
|
|
// Should be no config
|
|
conf, err := c.RekeyConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if conf != nil {
|
|
t.Fatalf("bad: %v", conf)
|
|
}
|
|
|
|
// Cancel should be idempotent
|
|
err = c.RekeyCancel()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Start a rekey
|
|
newConf := &SealConfig{
|
|
SecretThreshold: 3,
|
|
SecretShares: 5,
|
|
}
|
|
err = c.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Should get config
|
|
conf, err = c.RekeyConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(conf, newConf) {
|
|
t.Fatalf("bad: %v", conf)
|
|
}
|
|
|
|
// Cancel should be clear
|
|
err = c.RekeyCancel()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Should be no config
|
|
conf, err = c.RekeyConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if conf != nil {
|
|
t.Fatalf("bad: %v", conf)
|
|
}
|
|
}
|
|
|
|
func TestCore_Rekey_Init(t *testing.T) {
|
|
c, _, _ := TestCoreUnsealed(t)
|
|
|
|
// Try an invalid config
|
|
badConf := &SealConfig{
|
|
SecretThreshold: 5,
|
|
SecretShares: 1,
|
|
}
|
|
err := c.RekeyInit(badConf)
|
|
if err == nil {
|
|
t.Fatalf("should fail")
|
|
}
|
|
|
|
// Start a rekey
|
|
newConf := &SealConfig{
|
|
SecretThreshold: 3,
|
|
SecretShares: 5,
|
|
}
|
|
err = c.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Second should fail
|
|
err = c.RekeyInit(newConf)
|
|
if err == nil {
|
|
t.Fatalf("should fail")
|
|
}
|
|
}
|
|
|
|
func TestCore_Rekey_Update(t *testing.T) {
|
|
c, master, root := TestCoreUnsealed(t)
|
|
|
|
// Start a rekey
|
|
newConf := &SealConfig{
|
|
SecretThreshold: 3,
|
|
SecretShares: 5,
|
|
}
|
|
err := c.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Provide the master
|
|
result, err := c.RekeyUpdate(master)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if result == nil || len(result.SecretShares) != 5 {
|
|
t.Fatalf("Bad: %#v", result)
|
|
}
|
|
|
|
// Should be no progress
|
|
num, err := c.RekeyProgress()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if num != 0 {
|
|
t.Fatalf("bad: %d", num)
|
|
}
|
|
|
|
// Should be no config
|
|
conf, err := c.RekeyConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if conf != nil {
|
|
t.Fatalf("bad: %v", conf)
|
|
}
|
|
|
|
// SealConfig should update
|
|
conf, err = c.SealConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(conf, newConf) {
|
|
t.Fatalf("bad: %#v", conf)
|
|
}
|
|
|
|
// Attempt unseal
|
|
err = c.Seal(root)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
_, err = c.Unseal(result.SecretShares[i])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
if sealed, _ := c.Sealed(); sealed {
|
|
t.Fatalf("should be unsealed")
|
|
}
|
|
|
|
// Start another rekey, this time we require a quorum!
|
|
newConf = &SealConfig{
|
|
SecretThreshold: 1,
|
|
SecretShares: 1,
|
|
}
|
|
err = c.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Provide the parts master
|
|
oldResult := result
|
|
for i := 0; i < 3; i++ {
|
|
result, err = c.RekeyUpdate(oldResult.SecretShares[i])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Should be progress
|
|
num, err := c.RekeyProgress()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if (i == 2 && num != 0) || (i != 2 && num != i+1) {
|
|
t.Fatalf("bad: %d", num)
|
|
}
|
|
}
|
|
if result == nil || len(result.SecretShares) != 1 {
|
|
t.Fatalf("Bad: %#v", result)
|
|
}
|
|
|
|
// Attempt unseal
|
|
err = c.Seal(root)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
unseal, err := c.Unseal(result.SecretShares[0])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !unseal {
|
|
t.Fatalf("should be unsealed")
|
|
}
|
|
|
|
// SealConfig should update
|
|
conf, err = c.SealConfig()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(conf, newConf) {
|
|
t.Fatalf("bad: %#v", conf)
|
|
}
|
|
}
|
|
|
|
func TestCore_Rekey_InvalidMaster(t *testing.T) {
|
|
c, master, _ := TestCoreUnsealed(t)
|
|
|
|
// Start a rekey
|
|
newConf := &SealConfig{
|
|
SecretThreshold: 3,
|
|
SecretShares: 5,
|
|
}
|
|
err := c.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Provide the master (invalid)
|
|
master[0]++
|
|
_, err = c.RekeyUpdate(master)
|
|
if err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
}
|
|
|
|
func testWaitActive(t *testing.T, core *Core) {
|
|
start := time.Now()
|
|
var standby bool
|
|
var err error
|
|
for time.Now().Sub(start) < time.Second {
|
|
standby, err = core.Standby()
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if !standby {
|
|
break
|
|
}
|
|
}
|
|
if standby {
|
|
t.Fatalf("should not be in standby mode")
|
|
}
|
|
}
|
|
|
|
func TestCore_Standby_Rotate(t *testing.T) {
|
|
// Create the first core and initialize it
|
|
inm := physical.NewInmemHA()
|
|
advertiseOriginal := "http://127.0.0.1:8200"
|
|
core, err := NewCore(&CoreConfig{
|
|
Physical: inm,
|
|
AdvertiseAddr: advertiseOriginal,
|
|
DisableMlock: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
key, root := TestCoreInit(t, core)
|
|
if _, err := core.Unseal(TestKeyCopy(key)); err != nil {
|
|
t.Fatalf("unseal err: %s", err)
|
|
}
|
|
|
|
// Wait for core to become active
|
|
testWaitActive(t, core)
|
|
|
|
// Create a second core, attached to same in-memory store
|
|
advertiseOriginal2 := "http://127.0.0.1:8500"
|
|
core2, err := NewCore(&CoreConfig{
|
|
Physical: inm,
|
|
AdvertiseAddr: advertiseOriginal2,
|
|
DisableMlock: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if _, err := core2.Unseal(TestKeyCopy(key)); err != nil {
|
|
t.Fatalf("unseal err: %s", err)
|
|
}
|
|
|
|
// Rotate the encryption key
|
|
req := &logical.Request{
|
|
Operation: logical.WriteOperation,
|
|
Path: "sys/rotate",
|
|
ClientToken: root,
|
|
}
|
|
_, err = core.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Seal the first core, should step down
|
|
err = core.Seal(root)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Wait for core2 to become active
|
|
testWaitActive(t, core2)
|
|
|
|
// Read the key status
|
|
req = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "sys/key-status",
|
|
ClientToken: root,
|
|
}
|
|
resp, err := core2.HandleRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify the response
|
|
if resp.Data["term"] != 2 {
|
|
t.Fatalf("bad: %#v", resp)
|
|
}
|
|
}
|
|
|
|
func TestCore_Standby_Rekey(t *testing.T) {
|
|
// Create the first core and initialize it
|
|
inm := physical.NewInmemHA()
|
|
advertiseOriginal := "http://127.0.0.1:8200"
|
|
core, err := NewCore(&CoreConfig{
|
|
Physical: inm,
|
|
AdvertiseAddr: advertiseOriginal,
|
|
DisableMlock: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
key, root := TestCoreInit(t, core)
|
|
if _, err := core.Unseal(TestKeyCopy(key)); err != nil {
|
|
t.Fatalf("unseal err: %s", err)
|
|
}
|
|
|
|
// Wait for core to become active
|
|
testWaitActive(t, core)
|
|
|
|
// Create a second core, attached to same in-memory store
|
|
advertiseOriginal2 := "http://127.0.0.1:8500"
|
|
core2, err := NewCore(&CoreConfig{
|
|
Physical: inm,
|
|
AdvertiseAddr: advertiseOriginal2,
|
|
DisableMlock: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if _, err := core2.Unseal(TestKeyCopy(key)); err != nil {
|
|
t.Fatalf("unseal err: %s", err)
|
|
}
|
|
|
|
// Rekey the master key
|
|
newConf := &SealConfig{
|
|
SecretShares: 1,
|
|
SecretThreshold: 1,
|
|
}
|
|
err = core.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
result, err := core.RekeyUpdate(key)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatalf("rekey failed")
|
|
}
|
|
|
|
// Seal the first core, should step down
|
|
err = core.Seal(root)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Wait for core2 to become active
|
|
testWaitActive(t, core2)
|
|
|
|
// Rekey the master key again
|
|
err = core2.RekeyInit(newConf)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
result, err = core2.RekeyUpdate(result.SecretShares[0])
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatalf("rekey failed")
|
|
}
|
|
}
|