-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Nomad Secret Engine Tests (#125)
* Add secret engine for Nomad * add docs * cleanup docs
- Loading branch information
1 parent
35b87fa
commit c916879
Showing
2 changed files
with
227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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% | ||
``` |