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 Secret Engine for LDAP #119

Merged
merged 10 commits into from
Jun 6, 2023
1 change: 1 addition & 0 deletions benchmarktests/target_auth_ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type LDAPAuthConfig struct {
TokenNumUses int `hcl:"token_num_uses,optional"`
TokenPeriod string `hcl:"token_period,optional"`
TokenType string `hcl:"token_type,optional"`
MaxPageSize string `hcl:"max_page_size,optional"`
ltcarbonell marked this conversation as resolved.
Show resolved Hide resolved
}

type LDAPTestUserConfig struct {
Expand Down
4 changes: 2 additions & 2 deletions benchmarktests/target_secret_kvv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ func (k *KVV2Test) Setup(client *api.Client, randomMountName bool, mountName str
config := k.config.Config
switch k.action {
case "write":
k.logger = targetLogger.Named(KVV1WriteTestType)
k.logger = targetLogger.Named(KVV2WriteTestType)
default:
k.logger = targetLogger.Named(KVV1ReadTestType)
k.logger = targetLogger.Named(KVV2ReadTestType)
}

if randomMountName {
Expand Down
186 changes: 186 additions & 0 deletions benchmarktests/target_secret_ldap_dynamic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// 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 (
LDAPDynamicSecretTestType = "ldap_dynamic_secret"
LDAPDynamicSecretTestMethod = "GET"
LDAPSecretBindPassEnvVar = VaultBenchmarkEnvVarPrefix + "LDAP_BIND_PASS"
)

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

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

// Main Config Struct
type LDAPDynamicSecretTestConfig struct {
LDAPDynamicConfig *LDAPDynamicConfig `hcl:"secret,block"`
LDAPDynamicRoleConfig *LDAPDynamicRoleConfig `hcl:"role,block"`
}

type LDAPDynamicConfig struct {
BindDN string `hcl:"binddn"`
BindPass string `hcl:"bindpass,optional"`
URL string `hcl:"url,optional"`
PasswordPolicy string `hcl:"password_policy,optional"`
Schema string `hcl:"schema,optional"`
UserDN string `hcl:"userdn,optional"`
UserAttr string `hcl:"userattr,optional"`
UPNDomain string `hcl:"upndomain,optional"`
ConnectionTimeout int `hcl:"connection_timeout,optional"`
RequestTimeout int `hcl:"request_timeout,optional"`
StartTLS bool `hcl:"starttls,optional"`
InsecureTLS bool `hcl:"insecure_tls,optional"`
Certificate string `hcl:"certificate,optional"`
ClientTLSCert string `hcl:"client_tls_cert,optional"`
ClientTLSKey string `hcl:"client_tls_key,optional"`
}

type LDAPDynamicRoleConfig struct {
RoleName string `hcl:"role_name,optional"`
CreationLDIF string `hcl:"creation_ldif"`
DeletionLDIF string `hcl:"deletion_ldif"`
RollbackLDIF string `hcl:"rollback_ldif,optional"`
UsernameTemplate string `hcl:"username_template,optional"`
DefaultTTL int `hcl:"default_ttl,optional"`
MaxTTL int `hcl:"max_ttl,optional"`
}

func (r *LDAPDynamicSecretTest) ParseConfig(body hcl.Body) error {
testConfig := &struct {
Config *LDAPDynamicSecretTestConfig `hcl:"config,block"`
}{
Config: &LDAPDynamicSecretTestConfig{
LDAPDynamicConfig: &LDAPDynamicConfig{
BindPass: os.Getenv(LDAPAuthBindPassEnvVar),
},
LDAPDynamicRoleConfig: &LDAPDynamicRoleConfig{
RoleName: "benchmark-role",
},
},
}

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

r.config = testConfig.Config

if r.config.LDAPDynamicConfig.BindPass == "" {
return fmt.Errorf("no ldap bindpass provided but required")
}

return nil
}

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

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

func (r *LDAPDynamicSecretTest) GetTargetInfo() TargetInfo {
return TargetInfo{
method: LDAPDynamicSecretTestMethod,
pathPrefix: r.pathPrefix,
}
}

func (r *LDAPDynamicSecretTest) Setup(client *api.Client, randomMountName bool, mountName string) (BenchmarkBuilder, error) {
var err error
secretPath := mountName
r.logger = targetLogger.Named(LDAPDynamicSecretTestType)

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

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

setupLogger := r.logger.Named(secretPath)

// Decode LDAP Connection Config
setupLogger.Trace(parsingConfigLogMessage("ldap secret"))
connectionConfigData, err := structToMap(r.config.LDAPDynamicConfig)
if err != nil {
return nil, fmt.Errorf("error parsing ldap secret config from struct: %v", err)
}

// Write connection config
setupLogger.Trace(writingLogMessage("ldap secret config"))
_, err = client.Logical().Write(secretPath+"/config", connectionConfigData)
if err != nil {
return nil, fmt.Errorf("error writing ldap secret config: %v", err)
}

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

// Create Role
setupLogger.Trace(writingLogMessage("ldap secret role"), "name", r.config.LDAPDynamicRoleConfig.RoleName)
_, err = client.Logical().Write(secretPath+"/role/"+r.config.LDAPDynamicRoleConfig.RoleName, roleConfigData)
if err != nil {
return nil, fmt.Errorf("error writing ldap secret role: %v", err)
}

return &LDAPDynamicSecretTest{
pathPrefix: "/v1/" + secretPath,
header: generateHeader(client),
roleName: r.config.LDAPDynamicRoleConfig.RoleName,
logger: r.logger,
}, nil
}

func (m *LDAPDynamicSecretTest) Flags(fs *flag.FlagSet) {}
181 changes: 181 additions & 0 deletions benchmarktests/target_secret_ldap_static.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// 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 (
LDAPStaticSecretTestType = "ldap_static_secret_rotate"
LDAPStaticSecretTestMethod = "POST"
LDAPStaticSecretBindPassEnvVar = VaultBenchmarkEnvVarPrefix + "LDAP_BIND_PASS"
)

func init() {
// "Register" this test to the main test registry
TestList[LDAPStaticSecretTestType] = func() BenchmarkBuilder { return &LDAPStaticSecretTest{action: "rotate"} }
}

type LDAPStaticSecretTest struct {
pathPrefix string
header http.Header
roleName string
config *LDAPStaticSecretTestConfig
logger hclog.Logger
action string
}

// Main Config Struct
type LDAPStaticSecretTestConfig struct {
LDAPStaticConfig *LDAPStaticConfig `hcl:"secret,block"`
LDAPStaticRoleConfig *LDAPStaticRoleConfig `hcl:"role,block"`
}

type LDAPStaticConfig struct {
BindDN string `hcl:"binddn"`
BindPass string `hcl:"bindpass,optional"`
URL string `hcl:"url,optional"`
PasswordPolicy string `hcl:"password_policy,optional"`
Schema string `hcl:"schema,optional"`
UserDN string `hcl:"userdn,optional"`
UserAttr string `hcl:"userattr,optional"`
UPNDomain string `hcl:"upndomain,optional"`
ConnectionTimeout int `hcl:"connection_timeout,optional"`
RequestTimeout int `hcl:"request_timeout,optional"`
StartTLS bool `hcl:"starttls,optional"`
InsecureTLS bool `hcl:"insecure_tls,optional"`
Certificate string `hcl:"certificate,optional"`
ClientTLSCert string `hcl:"client_tls_cert,optional"`
ClientTLSKey string `hcl:"client_tls_key,optional"`
}

type LDAPStaticRoleConfig struct {
Username string `hcl:"username"`
DN string `hcl:"dn,optional"`
RotationPeriod string `hcl:"rotation_period"`
}

func (r *LDAPStaticSecretTest) ParseConfig(body hcl.Body) error {
testConfig := &struct {
Config *LDAPStaticSecretTestConfig `hcl:"config,block"`
}{
Config: &LDAPStaticSecretTestConfig{
LDAPStaticConfig: &LDAPStaticConfig{
BindPass: os.Getenv(LDAPAuthBindPassEnvVar),
},
LDAPStaticRoleConfig: &LDAPStaticRoleConfig{},
},
}

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

r.config = testConfig.Config

if r.config.LDAPStaticConfig.BindPass == "" {
return fmt.Errorf("no ldap bindpass provided but required")
}

return nil
}

func (r *LDAPStaticSecretTest) Target(client *api.Client) vegeta.Target {
return vegeta.Target{
Method: LDAPStaticSecretTestMethod,
URL: client.Address() + r.pathPrefix + "/rotate-role/" + r.roleName,
Header: r.header,
}
}

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

func (r *LDAPStaticSecretTest) GetTargetInfo() TargetInfo {
return TargetInfo{
method: LDAPStaticSecretTestMethod,
pathPrefix: r.pathPrefix,
}
}

func (r *LDAPStaticSecretTest) Setup(client *api.Client, randomMountName bool, mountName string) (BenchmarkBuilder, error) {
var err error
secretPath := mountName
r.logger = targetLogger.Named(LDAPDynamicSecretTestType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be static?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let me add this


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

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

setupLogger := r.logger.Named(secretPath)

// Decode LDAP Connection Config
setupLogger.Trace(parsingConfigLogMessage("ldap secret"))
connectionConfigData, err := structToMap(r.config.LDAPStaticConfig)
if err != nil {
return nil, fmt.Errorf("error parsing ldap secret config from struct: %v", err)
}

// Write connection config
setupLogger.Trace(writingLogMessage("ldap secret config"))
_, err = client.Logical().Write(secretPath+"/config", connectionConfigData)
if err != nil {
return nil, fmt.Errorf("error writing ldap secret config: %v", err)
}

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

// Create Role
setupLogger.Trace(writingLogMessage("ldap secret role"), "name", r.config.LDAPStaticRoleConfig.Username)
_, err = client.Logical().Write(secretPath+"/static-role/"+r.config.LDAPStaticRoleConfig.Username, roleConfigData)
if err != nil {
return nil, fmt.Errorf("error writing ldap secret static role: %v", err)
}

return &LDAPStaticSecretTest{
pathPrefix: "/v1/" + secretPath,
header: generateHeader(client),
roleName: r.config.LDAPStaticRoleConfig.Username,
logger: r.logger,
}, nil
}

func (m *LDAPStaticSecretTest) Flags(fs *flag.FlagSet) {}
Loading
Loading