a7ada08b3b
* govet cleanup in token store * adding general ttl handling to login requests * consolidating TTL calculation to system view * deprecate LeaseExtend * deprecate LeaseExtend * set the increment to the correct value * move calculateTTL out of SystemView * remove unused value * add back clearing of lease id * implement core ttl in some backends * removing increment and issue time from lease options * adding ttl tests, fixing some compile issue * adding ttl tests * fixing some explicit max TTL logic * fixing up some tests * removing unneeded test * off by one errors... * adding back some logic for bc * adding period to return on renewal * tweaking max ttl capping slightly * use the appropriate precision for ttl calculation * deprecate proto fields instead of delete * addressing feedback * moving TTL handling for backends to core * mongo is a secret backend not auth * adding estimated ttl for backends that also manage the expiration time * set the estimate values before calling the renew request * moving calculate TTL to framework, revert removal of increment and issue time from logical * minor edits * addressing feedback * address more feedback
303 lines
7.5 KiB
Go
303 lines
7.5 KiB
Go
package nomad
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
nomadapi "github.com/hashicorp/nomad/api"
|
|
"github.com/hashicorp/vault/logical"
|
|
"github.com/mitchellh/mapstructure"
|
|
dockertest "gopkg.in/ory-am/dockertest.v3"
|
|
)
|
|
|
|
func prepareTestContainer(t *testing.T) (cleanup func(), retAddress string, nomadToken string) {
|
|
nomadToken = os.Getenv("NOMAD_TOKEN")
|
|
|
|
retAddress = os.Getenv("NOMAD_ADDR")
|
|
|
|
if retAddress != "" {
|
|
return func() {}, retAddress, nomadToken
|
|
}
|
|
|
|
pool, err := dockertest.NewPool("")
|
|
if err != nil {
|
|
t.Fatalf("Failed to connect to docker: %s", err)
|
|
}
|
|
|
|
dockerOptions := &dockertest.RunOptions{
|
|
Repository: "djenriquez/nomad",
|
|
Tag: "latest",
|
|
Cmd: []string{"agent", "-dev"},
|
|
Env: []string{`NOMAD_LOCAL_CONFIG=bind_addr = "0.0.0.0" acl { enabled = true }`},
|
|
}
|
|
resource, err := pool.RunWithOptions(dockerOptions)
|
|
if err != nil {
|
|
t.Fatalf("Could not start local Nomad docker container: %s", err)
|
|
}
|
|
|
|
cleanup = func() {
|
|
err := pool.Purge(resource)
|
|
if err != nil {
|
|
t.Fatalf("Failed to cleanup local container: %s", err)
|
|
}
|
|
}
|
|
|
|
retAddress = fmt.Sprintf("http://localhost:%s/", resource.GetPort("4646/tcp"))
|
|
// Give Nomad time to initialize
|
|
|
|
time.Sleep(5000 * time.Millisecond)
|
|
// exponential backoff-retry
|
|
if err = pool.Retry(func() error {
|
|
var err error
|
|
nomadapiConfig := nomadapi.DefaultConfig()
|
|
nomadapiConfig.Address = retAddress
|
|
nomad, err := nomadapi.NewClient(nomadapiConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
aclbootstrap, _, err := nomad.ACLTokens().Bootstrap(nil)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
nomadToken = aclbootstrap.SecretID
|
|
t.Logf("[WARN] Generated Master token: %s", nomadToken)
|
|
policy := &nomadapi.ACLPolicy{
|
|
Name: "test",
|
|
Description: "test",
|
|
Rules: `namespace "default" {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
}
|
|
anonPolicy := &nomadapi.ACLPolicy{
|
|
Name: "anonymous",
|
|
Description: "Deny all access for anonymous requests",
|
|
Rules: `namespace "default" {
|
|
policy = "deny"
|
|
}
|
|
agent {
|
|
policy = "deny"
|
|
}
|
|
node {
|
|
policy = "deny"
|
|
}
|
|
`,
|
|
}
|
|
nomadAuthConfig := nomadapi.DefaultConfig()
|
|
nomadAuthConfig.Address = retAddress
|
|
nomadAuthConfig.SecretID = nomadToken
|
|
nomadAuth, err := nomadapi.NewClient(nomadAuthConfig)
|
|
_, err = nomadAuth.ACLPolicies().Upsert(policy, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = nomadAuth.ACLPolicies().Upsert(anonPolicy, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return err
|
|
}); err != nil {
|
|
cleanup()
|
|
t.Fatalf("Could not connect to docker: %s", err)
|
|
}
|
|
return cleanup, retAddress, nomadToken
|
|
}
|
|
|
|
func TestBackend_config_access(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
config.StorageView = &logical.InmemStorage{}
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cleanup, connURL, connToken := prepareTestContainer(t)
|
|
defer cleanup()
|
|
|
|
connData := map[string]interface{}{
|
|
"address": connURL,
|
|
"token": connToken,
|
|
}
|
|
|
|
confReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "config/access",
|
|
Storage: config.StorageView,
|
|
Data: connData,
|
|
}
|
|
|
|
resp, err := b.HandleRequest(context.Background(), confReq)
|
|
if err != nil || (resp != nil && resp.IsError()) || resp != nil {
|
|
t.Fatalf("failed to write configuration: resp:%#v err:%s", resp, err)
|
|
}
|
|
|
|
confReq.Operation = logical.ReadOperation
|
|
resp, err = b.HandleRequest(context.Background(), confReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("failed to write configuration: resp:%#v err:%s", resp, err)
|
|
}
|
|
|
|
expected := map[string]interface{}{
|
|
"address": connData["address"].(string),
|
|
}
|
|
if !reflect.DeepEqual(expected, resp.Data) {
|
|
t.Fatalf("bad: expected:%#v\nactual:%#v\n", expected, resp.Data)
|
|
}
|
|
if resp.Data["token"] != nil {
|
|
t.Fatalf("token should not be set in the response")
|
|
}
|
|
}
|
|
|
|
func TestBackend_renew_revoke(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
config.StorageView = &logical.InmemStorage{}
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cleanup, connURL, connToken := prepareTestContainer(t)
|
|
defer cleanup()
|
|
connData := map[string]interface{}{
|
|
"address": connURL,
|
|
"token": connToken,
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Storage: config.StorageView,
|
|
Operation: logical.UpdateOperation,
|
|
Path: "config/access",
|
|
Data: connData,
|
|
}
|
|
resp, err := b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
req.Path = "role/test"
|
|
req.Data = map[string]interface{}{
|
|
"policies": []string{"policy"},
|
|
"lease": "6h",
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
req.Operation = logical.ReadOperation
|
|
req.Path = "creds/test"
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("resp nil")
|
|
}
|
|
if resp.IsError() {
|
|
t.Fatalf("resp is error: %v", resp.Error())
|
|
}
|
|
|
|
generatedSecret := resp.Secret
|
|
generatedSecret.TTL = 6 * time.Hour
|
|
|
|
var d struct {
|
|
Token string `mapstructure:"secret_id"`
|
|
Accessor string `mapstructure:"accessor_id"`
|
|
}
|
|
if err := mapstructure.Decode(resp.Data, &d); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Logf("[WARN] Generated token: %s with accessor %s", d.Token, d.Accessor)
|
|
|
|
// Build a client and verify that the credentials work
|
|
nomadapiConfig := nomadapi.DefaultConfig()
|
|
nomadapiConfig.Address = connData["address"].(string)
|
|
nomadapiConfig.SecretID = d.Token
|
|
client, err := nomadapi.NewClient(nomadapiConfig)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Log("[WARN] Verifying that the generated token works...")
|
|
_, err = client.Agent().Members, nil
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
req.Operation = logical.RenewOperation
|
|
req.Secret = generatedSecret
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("got nil response from renew")
|
|
}
|
|
|
|
req.Operation = logical.RevokeOperation
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Build a management client and verify that the token does not exist anymore
|
|
nomadmgmtConfig := nomadapi.DefaultConfig()
|
|
nomadmgmtConfig.Address = connData["address"].(string)
|
|
nomadmgmtConfig.SecretID = connData["token"].(string)
|
|
mgmtclient, err := nomadapi.NewClient(nomadmgmtConfig)
|
|
|
|
q := &nomadapi.QueryOptions{
|
|
Namespace: "default",
|
|
}
|
|
|
|
t.Log("[WARN] Verifying that the generated token does not exist...")
|
|
_, _, err = mgmtclient.ACLTokens().Info(d.Accessor, q)
|
|
if err == nil {
|
|
t.Fatal("err: expected error")
|
|
}
|
|
}
|
|
|
|
func TestBackend_CredsCreateEnvVar(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
config.StorageView = &logical.InmemStorage{}
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cleanup, connURL, connToken := prepareTestContainer(t)
|
|
defer cleanup()
|
|
|
|
req := logical.TestRequest(t, logical.UpdateOperation, "role/test")
|
|
req.Data = map[string]interface{}{
|
|
"policies": []string{"policy"},
|
|
"lease": "6h",
|
|
}
|
|
resp, err := b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
os.Setenv("NOMAD_TOKEN", connToken)
|
|
defer os.Unsetenv("NOMAD_TOKEN")
|
|
os.Setenv("NOMAD_ADDR", connURL)
|
|
defer os.Unsetenv("NOMAD_ADDR")
|
|
|
|
req.Operation = logical.ReadOperation
|
|
req.Path = "creds/test"
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("resp nil")
|
|
}
|
|
if resp.IsError() {
|
|
t.Fatalf("resp is error: %v", resp.Error())
|
|
}
|
|
}
|