Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Nomad Secret Engine Tests #125

Merged
merged 3 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
173 changes: 173 additions & 0 deletions benchmarktests/target_secret_nomad.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package benchmarktests

import (
"flag"
"fmt"
"log"
"net/http"
"os"
"strings"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/vault/api"
vegeta "github.com/tsenart/vegeta/v12/lib"
)

// Constants for test
const (
NomadSecretTestType = "nomad_secret"
NomadSecretTestMethod = "GET"
NomadTokenEnvVar = VaultBenchmarkEnvVarPrefix + "NOMAD_TOKEN"
)

func init() {
// "Register" this test to the main test registry
TestList[NomadSecretTestType] = func() BenchmarkBuilder { return &NomadTest{} }
}

type NomadTest struct {
pathPrefix string
header http.Header
roleName string
config *NomadSecretTestConfig
logger hclog.Logger
}

type NomadSecretTestConfig struct {
NomadConfig *NomadConfig `hcl:"nomad,block"`
NomadRoleConfig *NomadRoleConfig `hcl:"role,block"`
}

type NomadConfig struct {
Address string `hcl:"address"`
Token string `hcl:"token,optional"`
MaxTokenNameLength int `hcl:"max_token_name_length,optional"`
CaCert string `hcl:"ca_cert,optional"`
ClientCert string `hcl:"client_cert,optional"`
ClientKey string `hcl:"client_key,optional"`
}

type NomadRoleConfig struct {
Name string `hcl:"name,optional"`
Policies []string `hcl:"policies,optional"`
Global bool `hcl:"global,optional"`
Type string `hcl:"type,optional"`
}

func (c *NomadTest) ParseConfig(body hcl.Body) error {
testConfig := &struct {
Config *NomadSecretTestConfig `hcl:"config,block"`
}{
Config: &NomadSecretTestConfig{
NomadConfig: &NomadConfig{
Token: os.Getenv(NomadTokenEnvVar),
},
NomadRoleConfig: &NomadRoleConfig{
Name: "benchmark-role",
},
},
}

diags := gohcl.DecodeBody(body, nil, testConfig)
if diags.HasErrors() {
return fmt.Errorf("error decoding to struct: %v", diags)
}
c.config = testConfig.Config

// Ensure that the token has been set by either the environment variable or the config
if c.config.NomadConfig.Token == "" {
return fmt.Errorf("nomad token must be set")
}
return nil
}

func (c *NomadTest) Target(client *api.Client) vegeta.Target {
return vegeta.Target{
Method: "GET",
URL: client.Address() + c.pathPrefix + "/creds/" + c.roleName,
Header: c.header,
}
}

func (c *NomadTest) Cleanup(client *api.Client) error {
c.logger.Trace(cleanupLogMessage(c.pathPrefix))
_, err := client.Logical().Delete(strings.Replace(c.pathPrefix, "/v1/", "/sys/mounts/", 1))
if err != nil {
return fmt.Errorf("error cleaning up mount: %v", err)
}
return nil
}

func (c *NomadTest) GetTargetInfo() TargetInfo {
return TargetInfo{
method: NomadSecretTestMethod,
pathPrefix: c.pathPrefix,
}
}

func (c *NomadTest) Setup(client *api.Client, randomMountName bool, mountName string) (BenchmarkBuilder, error) {
var err error
secretPath := mountName
config := c.config
c.logger = targetLogger.Named(NomadSecretTestType)

if randomMountName {
secretPath, err = uuid.GenerateUUID()
if err != nil {
log.Fatalf("can't create UUID")
}
}

c.logger.Trace(mountLogMessage("secrets", "nomad", secretPath))
err = client.Sys().Mount(secretPath, &api.MountInput{
Type: "nomad",
})
if err != nil {
return nil, fmt.Errorf("error mounting nomad: %v", err)
}

setupLogger := c.logger.Named(secretPath)

// Decode Nomad Config
setupLogger.Trace(parsingConfigLogMessage("nomad"))
nomadConfigData, err := structToMap(config.NomadConfig)
if err != nil {
return nil, fmt.Errorf("error parsing nomad config from struct: %v", err)
}

// Write Nomad config
setupLogger.Trace(writingLogMessage("nomad config"))
_, err = client.Logical().Write(secretPath+"/config/access", nomadConfigData)
if err != nil {
return nil, fmt.Errorf("error writing nomad config: %v", err)
}

// Decode Role Config
setupLogger.Trace(parsingConfigLogMessage("role"))
nomadRoleConfigData, err := structToMap(config.NomadRoleConfig)
if err != nil {
return nil, fmt.Errorf("error parsing role config from struct: %v", err)
}

// Create Role
setupLogger.Trace(writingLogMessage("nomad role"), "name", config.NomadRoleConfig.Name)
_, err = client.Logical().Write(secretPath+"/role/"+config.NomadRoleConfig.Name, nomadRoleConfigData)
if err != nil {
return nil, fmt.Errorf("error writing nomad role: %v", err)
}

return &NomadTest{
pathPrefix: "/v1/" + secretPath,
header: generateHeader(client),
roleName: config.NomadRoleConfig.Name,
logger: c.logger,
}, nil
}

func (c *NomadTest) Flags(fs *flag.FlagSet) {}
54 changes: 54 additions & 0 deletions docs/tests/secret-nomad.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Nomad Secrets Engine Benchmark

This benchmark will test the dynamic generation of Nomad credentials.

## Test Parameters

### Nomad Database Configuration `nomad`

#### NOTE: Ensure that the Nomad system has a limit high enough to support the number of roles you are creating. More information can be found in the [Nomad Documentation](https://developer.hashicorp.com/nomad/docs/configuration#limits)

- `address` `(string: "")` – Specifies the address of the Nomad instance, provided as `"protocol://host:port"` like `"http://127.0.0.1:4646"`.
- `token` `(string: "")` – Specifies the Nomad Management token to use. This value can also be provided on individual calls with the NOMAD_TOKEN environment variable. This can also be provided via the `VAULT_BENCHMARK_NOMAD_TOKEN` environment variable.
- `max_token_name_length` `(int: <optional>)` – Specifies the maximum length to use for the name of the Nomad token generated with [Generate Credential](https://developer.hashicorp.com/vault/api-docs/secret/nomad#generate-credential). If omitted, `0` is used and ignored, defaulting to the max value allowed by the Nomad version. For Nomad versions 0.8.3 and earlier, the default is `64`. For Nomad version 0.8.4 and later, the default is `256`.
- `ca_cert` `(string: "")` - CA certificate to use when verifying Nomad server certificate, must be x509 PEM encoded.
- `client_cert` `(string: "")` - Client certificate used for Nomad's TLS communication, must be x509 PEM encoded and if this is set you need to also set client_key.
- `client_key` `(string: "")` - Client key used for Nomad's TLS communication, must be x509 PEM encoded and if this is set you need to also set client_cert.

### Role Config `role`

- `name` `(string: "benchmark-role")` – Specifies the name of an existing role against which to create this Nomad tokens. This is part of the request URL.
- `policies` `(string: "")` – Comma separated list of Nomad policies the token is going to be created against. These need to be created beforehand in Nomad.
- `global` `(bool: "false")` – Specifies if the token should be global, as defined in the [Nomad Documentation](https://developer.hashicorp.com/nomad/tutorials/access-control#acl-tokens).
- `type` `(string: "client")` - Specifies the type of token to create when using this role. Valid values are `"client"` or `"management"`.

## Example Configuration

```hcl
test "nomad_secret" "nomad_test_1" {
weight = 100
config {
nomad {
address = "http://127.0.0.1:4646"
token = "NOMAD_TOKEN"
}
role {
global = true
type = "management"
}
}
}

```

### Example Usage

```bash
$ vault-benchmark run -config=config.hcl
2023-06-01T09:41:27.096-0500 [INFO] vault-benchmark: setting up targets
2023-06-01T09:41:27.102-0500 [INFO] vault-benchmark: starting benchmarks: duration=5s
2023-06-01T09:41:32.311-0500 [INFO] vault-benchmark: benchmark complete
Target: http://127.0.0.1:8200
op count rate throughput mean 95th% 99th% successRatio
nomad_test_1 177 35.057995 33.990891 290.850018ms 375.292712ms 451.573602ms 100.00%
```