diff --git a/plugin/pki/backend_test.go b/plugin/pki/backend_test.go index 4bcbd7e5..79164171 100644 --- a/plugin/pki/backend_test.go +++ b/plugin/pki/backend_test.go @@ -170,3 +170,14 @@ func TestTokenIntegration(t *testing.T) { t.Run("TPP Token sign certificate and ttl attribute", integrationTestEnv.TokenIntegrationSignWithTTLCertificate) } + +func TestZoneOverride(t *testing.T) { + + integrationTestEnv, err := newIntegrationTestEnv() + if err != nil { + t.Fatal(err) + } + + t.Run("Token enroll with role zone", integrationTestEnv.TokenEnrollWithRoleZone) + t.Run("Token enroll with Venafi secret zone", integrationTestEnv.TokenEnrollWithVenafiSecretZone) +} diff --git a/plugin/pki/env_test.go b/plugin/pki/env_test.go index bf320d48..4a5fb31d 100644 --- a/plugin/pki/env_test.go +++ b/plugin/pki/env_test.go @@ -71,9 +71,10 @@ const ( venafiConfigMixedTppAndCloud venafiConfigString = "MixedTppCloud" venafiConfigMixedTppAndToken venafiConfigString = "MixedTppToken" venafiConfigMixedTokenAndCloud venafiConfigString = "MixedTokenCloud" - - venafiVenafiConfigFake venafiConfigString = "VenafiFake" - venafiRoleConfig venafiConfigString = "Role" + venafiVenafiConfigFake venafiConfigString = "VenafiFake" + venafiRoleConfig venafiConfigString = "Role" + venafiRoleWithZoneConfig venafiConfigString = "venafiRoleWithZone" + venafiRoleWithVenafiSecretConfig venafiConfigString = "venafiVenafiSecretZone" ) var venafiTestRoleConfig = map[string]interface{}{ @@ -128,6 +129,12 @@ var venafiTestTokenConfig = map[string]interface{}{ "trust_bundle_file": os.Getenv("TRUST_BUNDLE"), } +var venafiTestTokenConfigForRoleZone = map[string]interface{}{ + "zone": os.Getenv("TPP_ZONE2"), +} + +var venafiTestTokenConfigForVenafiSecretZone = map[string]interface{}{} + var venafiTestTokenConfigPredefined = map[string]interface{}{ "url": "https://tpp.example.com", "access_token": "admin", @@ -212,6 +219,39 @@ func (e *testEnv) writeRoleToBackend(t *testing.T, configString venafiConfigStri if err != nil { t.Fatal(err) } + roleData = copyMap(roleData) + + //Adding Venafi secret reference to Role + roleData["venafi_secret"] = e.VenafiSecretName + //Removing the zone from the data, as the Venafi secret zone must be used + roleData["zone"] = "" + + ttl := strconv.Itoa(role_ttl_test_property) + "h" + roleData["ttl"] = ttl + roleData["issuer_hint"] = util.IssuerHintMicrosoft + + resp, err := e.Backend.HandleRequest(e.Context, &logical.Request{ + Operation: logical.UpdateOperation, + Path: "roles/" + e.RoleName, + Storage: e.Storage, + Data: roleData, + }) + + if err != nil { + t.Fatal(err) + } + + if resp != nil && resp.IsError() { + t.Fatalf("failed to create role, %#v", resp) + } +} + +func (e *testEnv) writeRoleWithZoneToBackend(t *testing.T, configString venafiConfigString) { + roleData, err := makeConfig(configString) + if err != nil { + t.Fatal(err) + } + roleData = copyMap(roleData) //Adding Venafi secret reference to Role roleData["venafi_secret"] = e.VenafiSecretName @@ -342,6 +382,7 @@ func (e *testEnv) writeVenafiToBackend(t *testing.T, configString venafiConfigSt if err != nil { t.Fatal(err) } + roleData = copyMap(roleData) resp, err := e.Backend.HandleRequest(e.Context, &logical.Request{ Operation: logical.UpdateOperation, @@ -956,6 +997,10 @@ func makeConfig(configString venafiConfigString) (roleData map[string]interface{ roleData = venafiVenafiTestFakeConfig case venafiRoleConfig: roleData = venafiTestRoleConfig + case venafiRoleWithZoneConfig: + roleData = venafiTestTokenConfigForRoleZone + case venafiRoleWithVenafiSecretConfig: + roleData = venafiTestTokenConfigForVenafiSecretZone default: return roleData, fmt.Errorf("do not have config data for config %s", configString) } @@ -1642,6 +1687,38 @@ func (e *testEnv) TokenIntegrationIssueCertificateWithTTLOnIssueData(t *testing. e.IssueCertificateAndValidateTTL(t, data) } +func (e *testEnv) TokenEnrollWithRoleZone(t *testing.T) { + data := testData{} + randString := e.TestRandString + domain := "venafi.example.com" + data.cn = randString + "." + domain + data.dnsNS = "alt-" + data.cn + data.dnsIP = "192.168.1.1" + data.dnsEmail = "venafi@example.com" + + var config = venafiConfigToken + var roleConfig = venafiRoleWithZoneConfig + e.writeVenafiToBackend(t, config) + e.writeRoleWithZoneToBackend(t, roleConfig) + e.IssueCertificateAndSaveSerial(t, data, config) +} + +func (e *testEnv) TokenEnrollWithVenafiSecretZone(t *testing.T) { + data := testData{} + randString := e.TestRandString + domain := "venafi.example.com" + data.cn = randString + "." + domain + data.dnsNS = "alt-" + data.cn + data.dnsIP = "192.168.1.1" + data.dnsEmail = "venafi@example.com" + + var config = venafiConfigToken + var roleConfig = venafiRoleWithVenafiSecretConfig + e.writeVenafiToBackend(t, config) + e.writeRoleWithZoneToBackend(t, roleConfig) + e.IssueCertificateAndSaveSerial(t, data, config) +} + func checkStandardCert(t *testing.T, data testData) { var err error log.Println("Testing certificate:", data.cert) diff --git a/plugin/pki/path_roles.go b/plugin/pki/path_roles.go index a07bd31e..50932427 100644 --- a/plugin/pki/path_roles.go +++ b/plugin/pki/path_roles.go @@ -31,6 +31,14 @@ func pathRoles(b *backend) *framework.Path { Description: "Name of the role", Required: true, }, + "zone": { + Type: framework.TypeString, + Description: `Name of Venafi Platform policy or Venafi Cloud project zone. +This field overrides the zone field declared in the Venafi secret. +Example for Platform: testpolicy\\vault +Example for Venafi Cloud: e33f3e40-4e7e-11ea-8da3-b3c196ebeb0b`, + Required: false, + }, "store_by_cn": { Type: framework.TypeBool, Description: `Set it to true to store certificates by CN in certs/ path`, @@ -86,7 +94,7 @@ the key_type. Default: 2048`, Description: `Key curve for EC key type. Valid values are: "P256","P384","P521"`, }, "ttl": { - Type: framework.TypeDurationSecond, + Type: framework.TypeDurationSecond, Description: `The certificate validity if no specific certificate validity is requested.`, }, "issuer_hint": { @@ -219,69 +227,69 @@ func (b *backend) pathRoleUpdate(ctx context.Context, req *logical.Request, data } _, isSet := data.GetOk("chain_option") - chain_option := data.Get("chain_option").(string) - if isSet && (entry.ChainOption != chain_option) { - entry.ChainOption = chain_option + chainOption := data.Get("chain_option").(string) + if isSet && (entry.ChainOption != chainOption) { + entry.ChainOption = chainOption } _, isSet = data.GetOk("store_by_cn") - store_by_cn := data.Get("store_by_cn").(bool) - if isSet && store_by_cn { + storeByCn := data.Get("store_by_cn").(bool) + if isSet && storeByCn { entry.StoreBy = "cn" } _, isSet = data.GetOk("store_by_serial") - store_by_serial := data.Get("store_by_serial").(bool) - if isSet && store_by_serial { + storeBySerial := data.Get("store_by_serial").(bool) + if isSet && storeBySerial { entry.StoreBy = "serial" } _, isSet = data.GetOk("store_by") - store_by := data.Get("store_by").(string) - if isSet && (entry.StoreBy != store_by) { - entry.StoreBy = store_by + storeBy := data.Get("store_by").(string) + if isSet && (entry.StoreBy != storeBy) { + entry.StoreBy = storeBy } _, isSet = data.GetOk("no_store") - no_store := data.Get("no_store").(bool) - if isSet && (entry.NoStore != no_store) { - entry.NoStore = no_store + noStore := data.Get("no_store").(bool) + if isSet && (entry.NoStore != noStore) { + entry.NoStore = noStore } _, isSet = data.GetOk("service_generated_cert") - service_generated_cert := data.Get("service_generated_cert").(bool) - if isSet && (entry.ServiceGenerated != service_generated_cert) { - entry.ServiceGenerated = service_generated_cert + serviceGeneratedCert := data.Get("service_generated_cert").(bool) + if isSet && (entry.ServiceGenerated != serviceGeneratedCert) { + entry.ServiceGenerated = serviceGeneratedCert } _, isSet = data.GetOk("store_pkey") - store_pkey := data.Get("store_pkey").(bool) - if isSet && (entry.StorePrivateKey != store_pkey) { - entry.StorePrivateKey = store_pkey + storePkey := data.Get("store_pkey").(bool) + if isSet && (entry.StorePrivateKey != storePkey) { + entry.StorePrivateKey = storePkey } _, isSet = data.GetOk("key_type") - key_type := data.Get("key_type").(string) - if isSet && (entry.KeyType != key_type) { - entry.KeyType = key_type + keyType := data.Get("key_type").(string) + if isSet && (entry.KeyType != keyType) { + entry.KeyType = keyType } _, isSet = data.GetOk("key_bits") - key_bits := data.Get("key_bits").(int) - if isSet && (entry.KeyBits != key_bits) { - entry.KeyBits = key_bits + keyBits := data.Get("key_bits").(int) + if isSet && (entry.KeyBits != keyBits) { + entry.KeyBits = keyBits } _, isSet = data.GetOk("key_curve") - key_curve := data.Get("key_curve").(string) - if isSet && (entry.KeyCurve != key_curve) { - entry.KeyCurve = key_curve + keyCurve := data.Get("key_curve").(string) + if isSet && (entry.KeyCurve != keyCurve) { + entry.KeyCurve = keyCurve } _, isSet = data.GetOk("max_ttl") - max_ttl := time.Duration(data.Get("max_ttl").(int)) * time.Second - if isSet && (entry.MaxTTL != max_ttl) { - entry.MaxTTL = max_ttl + maxTtl := time.Duration(data.Get("max_ttl").(int)) * time.Second + if isSet && (entry.MaxTTL != maxTtl) { + entry.MaxTTL = maxTtl } _, isSet = data.GetOk("ttl") @@ -291,15 +299,15 @@ func (b *backend) pathRoleUpdate(ctx context.Context, req *logical.Request, data } _, isSet = data.GetOk("generate_lease") - generate_lease := data.Get("generate_lease").(bool) - if isSet && (entry.GenerateLease != generate_lease) { - entry.GenerateLease = generate_lease + generateLease := data.Get("generate_lease").(bool) + if isSet && (entry.GenerateLease != generateLease) { + entry.GenerateLease = generateLease } _, isSet = data.GetOk("server_timeout") - server_timeout := time.Duration(data.Get("server_timeout").(int)) * time.Second - if isSet && (entry.ServerTimeout != server_timeout) { - entry.ServerTimeout = server_timeout + serverTimeout := time.Duration(data.Get("server_timeout").(int)) * time.Second + if isSet && (entry.ServerTimeout != serverTimeout) { + entry.ServerTimeout = serverTimeout } _, isSet = data.GetOk("venafi_secret") @@ -308,6 +316,12 @@ func (b *backend) pathRoleUpdate(ctx context.Context, req *logical.Request, data entry.VenafiSecret = venafiSecret } + _, isSet = data.GetOk("zone") + zone := data.Get("zone").(string) + if isSet && (entry.Zone != zone) { + entry.Zone = zone + } + err = validateEntry(entry) if err != nil { return nil, err @@ -348,6 +362,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data GenerateLease: data.Get("generate_lease").(bool), ServerTimeout: time.Duration(data.Get("server_timeout").(int)) * time.Second, VenafiSecret: data.Get("venafi_secret").(string), + Zone: data.Get("zone").(string), } } @@ -464,11 +479,13 @@ type roleEntry struct { DeprecatedTTL string `json:"ttl"` ServerTimeout time.Duration `json:"server_timeout"` VenafiSecret string `json:"venafi_secret"` + Zone string `json:"zone"` } func (r *roleEntry) ToResponseData() map[string]interface{} { responseData := map[string]interface{}{ "venafi_secret": r.VenafiSecret, + "role_zone": r.Zone, "store_by": r.StoreBy, "no_store": r.NoStore, "service_generated_cert": r.ServiceGenerated, diff --git a/plugin/pki/path_venafi_secrets.go b/plugin/pki/path_venafi_secrets.go index 84fd32e8..ac7160b3 100644 --- a/plugin/pki/path_venafi_secrets.go +++ b/plugin/pki/path_venafi_secrets.go @@ -310,38 +310,29 @@ type venafiSecretEntry struct { } func (p *venafiSecretEntry) ToResponseData() map[string]interface{} { - var tppPass, accessToken, refreshToken, apiKey string - if p.TppPassword != "" { - tppPass = "********" - } - if p.AccessToken != "" { - accessToken = "********" - } - if p.RefreshToken != "" { - refreshToken = "********" - } - if p.Apikey != "" { - apiKey = "********" - } - responseData := map[string]interface{}{ - //Sensible data will not be returned. + //Sensible data will not be disclosed. //tpp_password, api_key, access_token, refresh_token "url": p.URL, "zone": p.Zone, "tpp_user": p.TppUser, - "tpp_password": tppPass, - "access_token": accessToken, - "refresh_token": refreshToken, - "apikey": apiKey, + "tpp_password": p.getStringMask(), + "access_token": p.getStringMask(), + "refresh_token": p.getStringMask(), + "apikey": p.getStringMask(), "trust_bundle_file": p.TrustBundleFile, "fakemode": p.Fakemode, } return responseData } +func (p *venafiSecretEntry) getStringMask() string { + return stringMask +} + const ( + stringMask = "********" pathListVenafiSecretsHelpSyn = `List the existing Venafi Secrets in this backend` // #nosec pathListVenafiSecretsHelpDesc = `Venafi Secrets will be listed by the secret name.` // #nosec pathVenafiSecretsHelpSyn = `Manage the Venafi Secrets that can be created with this backend.` // #nosec diff --git a/plugin/pki/util.go b/plugin/pki/util.go index 3a3bde02..8469f2fe 100644 --- a/plugin/pki/util.go +++ b/plugin/pki/util.go @@ -323,3 +323,17 @@ func parseTrustBundlePEM(trustBundlePem string) (*x509.CertPool, error) { return connectionTrustBundle, nil } + +func copyMap(m map[string]interface{}) map[string]interface{} { + cp := make(map[string]interface{}) + for k, v := range m { + vm, ok := v.(map[string]interface{}) + if ok { + cp[k] = copyMap(vm) + } else { + cp[k] = v + } + } + + return cp +} diff --git a/plugin/pki/vcert.go b/plugin/pki/vcert.go index fb453f10..5f2670df 100644 --- a/plugin/pki/vcert.go +++ b/plugin/pki/vcert.go @@ -73,9 +73,19 @@ func (b *backend) getConfig(ctx context.Context, req *logical.Request, roleName trustBundlePEM = string(trustBundle) } + //If the role has a Zone declared, it takes priority over the Zone in the Venafi secret + var zone string + if role.Zone != "" { + b.Logger().Debug(fmt.Sprintf("Using role zone: [%s]. Overrides venafi Secret zone: [%s]", role.Zone, venafiSecret.Zone)) + zone = role.Zone + } else { + b.Logger().Debug(fmt.Sprintf("Using venafi secret zone: [%s]. Role zone not found. ", venafiSecret.Zone)) + zone = venafiSecret.Zone + } + cfg = &vcert.Config{} cfg.BaseUrl = venafiSecret.URL - cfg.Zone = venafiSecret.Zone + cfg.Zone = zone cfg.LogVerbose = true if trustBundlePEM != "" { cfg.ConnectionTrust = trustBundlePEM @@ -124,5 +134,4 @@ func (b *backend) getConfig(ctx context.Context, req *logical.Request, roleName } return cfg, nil - }