From 64f50d3ae8713cd8e56af057dbfcf1e1f3bdb0d2 Mon Sep 17 00:00:00 2001 From: Le Zhang Date: Mon, 11 Sep 2023 15:11:14 -0400 Subject: [PATCH] Issue open-horizon#3718 - NodeSecret: hzn deploycheck checks node secret Signed-off-by: Le Zhang --- agreementbot/agreementworker.go | 1 + cli/deploycheck/secretbinding.go | 2 +- cli/exchange/business.go | 2 +- cli/exchange/pattern.go | 2 +- cli/hzn.go | 3 +- compcheck/secretbinding_check.go | 65 +++++++++++++++++++++++--------- 6 files changed, 53 insertions(+), 22 deletions(-) diff --git a/agreementbot/agreementworker.go b/agreementbot/agreementworker.go index 9218d2ebe..10bb64c6e 100644 --- a/agreementbot/agreementworker.go +++ b/agreementbot/agreementworker.go @@ -873,6 +873,7 @@ func (b *BaseAgreementWorker) ValidateAndExtractSecrets(consumerPolicy *policy.P nil, "", exchange.GetOrg(deviceId), + exchange.GetId(deviceId), msgPrinter) if err != nil { diff --git a/cli/deploycheck/secretbinding.go b/cli/deploycheck/secretbinding.go index 676d8c9a2..49320c245 100644 --- a/cli/deploycheck/secretbinding.go +++ b/cli/deploycheck/secretbinding.go @@ -14,7 +14,7 @@ import ( // check if the user inputs for services are compatible func SecretBindingCompatible(org string, userPw string, nodeId string, nodeArch string, nodeType string, nodeOrg string, businessPolId string, businessPolFile string, patternId string, patternFile string, - svcDefFiles []string, checkAllSvcs bool, showDetail bool) { + svcDefFiles []string, checkAllSvcs bool, showDetail bool, checkNodeLevelSec bool) { msgPrinter := i18n.GetMessagePrinter() diff --git a/cli/exchange/business.go b/cli/exchange/business.go index 048b701b2..0e268d12c 100644 --- a/cli/exchange/business.go +++ b/cli/exchange/business.go @@ -282,7 +282,7 @@ func verifySecretBindingForPolicy(policy *businesspolicy.BusinessPolicy, polOrg // make sure the vault secret exists agbotUrl := cliutils.GetAgbotSecureAPIUrlBase() vaultSecretExists := exchange.GetHTTPVaultSecretExistsHandler(ec) - msgMap, err := compcheck.VerifyVaultSecrets(neededSB, polOrg, agbotUrl, vaultSecretExists, msgPrinter) + msgMap, err := compcheck.VerifyVaultSecrets(neededSB, polOrg, "", agbotUrl, vaultSecretExists, msgPrinter) if err != nil { cliutils.Fatal(cliutils.CLI_INPUT_ERROR, msgPrinter.Sprintf("Failed to verify the binding secret in the secret manager. %v", err)) } else if msgMap != nil && len(msgMap) > 0 { diff --git a/cli/exchange/pattern.go b/cli/exchange/pattern.go index 91a92f3b1..156ddcb8f 100644 --- a/cli/exchange/pattern.go +++ b/cli/exchange/pattern.go @@ -551,7 +551,7 @@ func verifySecretBindingForPattern(secretBinding []exchangecommon.SecretBinding, // make sure the vault secret exists. agbotUrl := cliutils.GetAgbotSecureAPIUrlBase() vaultSecretExists := exchange.GetHTTPVaultSecretExistsHandler(ec) - msgMap, err := compcheck.VerifyVaultSecrets(neededSB, patOrg, agbotUrl, vaultSecretExists, msgPrinter) + msgMap, err := compcheck.VerifyVaultSecrets(neededSB, patOrg, "", agbotUrl, vaultSecretExists, msgPrinter) if err != nil { cliutils.Fatal(cliutils.CLI_INPUT_ERROR, msgPrinter.Sprintf("Failed to verify the binding secret in the secret manager. %v", err)) } else if msgMap != nil && len(msgMap) > 0 { diff --git a/cli/hzn.go b/cli/hzn.go index 160698872..c89239b81 100644 --- a/cli/hzn.go +++ b/cli/hzn.go @@ -228,6 +228,7 @@ Environment Variables: secretCompSvcFile := secretCompCmd.Flag("service", msgPrinter.Sprintf("(optional) The JSON input file name containing the service definition. If omitted, the service defined in the deployment policy or pattern will be retrieved from the Exchange. This flag can be repeated to specify different versions of the service.")).Strings() secretCompPatternId := secretCompCmd.Flag("pattern-id", msgPrinter.Sprintf("The Horizon exchange pattern ID. Mutually exclusive with -P, -b and -B. If you don't prepend it with the organization id, it will automatically be prepended with the node's organization id.")).Short('p').String() secretCompPatternFile := secretCompCmd.Flag("pattern", msgPrinter.Sprintf("The JSON input file name containing the pattern. Mutually exclusive with -p, -b and -B.")).Short('P').String() + secretCompCheckNodeLevel := secretCompCmd.Flag("check-node-secret", msgPrinter.Sprintf("check secretbinding against node level secret")).Bool() userinputCompCmd := deploycheckCmd.Command("userinput | u", msgPrinter.Sprintf("Check user input compatibility.")).Alias("u").Alias("userinput") userinputCompNodeArch := userinputCompCmd.Flag("arch", msgPrinter.Sprintf("The architecture of the node. It is required when -n is not specified. If omitted, the service of all the architectures referenced in the deployment policy or pattern will be checked for compatibility.")).Short('a').String() userinputCompNodeType := userinputCompCmd.Flag("node-type", msgPrinter.Sprintf("The node type. The valid values are 'device' and 'cluster'. The default value is the type of the node provided by -n or current registered device, if omitted.")).Short('t').String() @@ -1363,7 +1364,7 @@ Environment Variables: case userinputCompCmd.FullCommand(): deploycheck.UserInputCompatible(*deploycheckOrg, *deploycheckUserPw, *userinputCompNodeId, *userinputCompNodeArch, *userinputCompNodeType, *userinputCompNodeUIFile, *userinputCompBPolId, *userinputCompBPolFile, *userinputCompPatternId, *userinputCompPatternFile, *userinputCompSvcFile, *deploycheckCheckAll, *deploycheckLong) case secretCompCmd.FullCommand(): - deploycheck.SecretBindingCompatible(*deploycheckOrg, *deploycheckUserPw, *secretCompNodeId, *secretCompNodeArch, *secretCompNodeType, *secretCompNodeOrg, *secretCompDepPolId, *secretCompDepPolFile, *secretCompPatternId, *secretCompPatternFile, *secretCompSvcFile, *deploycheckCheckAll, *deploycheckLong) + deploycheck.SecretBindingCompatible(*deploycheckOrg, *deploycheckUserPw, *secretCompNodeId, *secretCompNodeArch, *secretCompNodeType, *secretCompNodeOrg, *secretCompDepPolId, *secretCompDepPolFile, *secretCompPatternId, *secretCompPatternFile, *secretCompSvcFile, *deploycheckCheckAll, *deploycheckLong, *secretCompCheckNodeLevel) case allCompCmd.FullCommand(): deploycheck.AllCompatible(*deploycheckOrg, *deploycheckUserPw, *allCompNodeId, *allCompHAGroup, *allCompNodeArch, *allCompNodeType, *allCompNodeNs, *allCompNodIsNamespaceScoped, *allCompNodeOrg, *allCompNodePolFile, *allCompNodeUIFile, *allCompBPolId, *allCompBPolFile, *allCompPatternId, *allCompPatternFile, *allCompSPolFile, *allCompSvcFile, *deploycheckCheckAll, *deploycheckLong) case agreementListCmd.FullCommand(): diff --git a/compcheck/secretbinding_check.go b/compcheck/secretbinding_check.go index a0567746d..180e3e3ff 100644 --- a/compcheck/secretbinding_check.go +++ b/compcheck/secretbinding_check.go @@ -109,6 +109,8 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, checkAllSvcs bool, msgPrinter *message.Printer) (*CompCheckOutput, error) { + // Lily: TODO - remove "checkNodeLevelSec bool" and hzn flag if not needed + // get default message printer if nil if msgPrinter == nil { msgPrinter = i18n.GetMessagePrinter() @@ -236,7 +238,7 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, continue } sSpec := NewServiceSpec(serviceRef.ServiceURL, serviceRef.ServiceOrg, workload.Version, serviceRef.ServiceArch) - if compatible, reason, imap, topSvcDef, _, depSvcDefs, err := VerifySecretBindingForService(sSpec, serviceDefResolverHandler, vaultSecretExists, agbotUrl, secretBinding, resources.NodeOrg, msgPrinter); err != nil { + if compatible, reason, imap, topSvcDef, _, depSvcDefs, err := VerifySecretBindingForService(sSpec, serviceDefResolverHandler, vaultSecretExists, agbotUrl, secretBinding, resources.NodeOrg, resources.NodeId, msgPrinter); err != nil { return nil, err } else { // for performance, save the services that gotten from the exchange for use later @@ -258,6 +260,9 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, service_compatible = true service_comp[sId] = topSvcDef messages[sId] = msg_compatible + if reason != "" { + messages[sId] = msgPrinter.Sprintf("%v, Warning: %v", msg_compatible, reason) + } if !checkAllSvcs { break } @@ -277,7 +282,7 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, if !needHandleService(sId, input.ServiceToCheck) { continue } - if compatible, reason, imap, depSvcDefs, err := VerifySecretBindingForServiceDef(&svc, resources.DepServices, serviceDefResolverHandler, vaultSecretExists, agbotUrl, secretBinding, resources.NodeOrg, msgPrinter); err != nil { + if compatible, reason, imap, depSvcDefs, err := VerifySecretBindingForServiceDef(&svc, resources.DepServices, serviceDefResolverHandler, vaultSecretExists, agbotUrl, secretBinding, resources.NodeOrg, resources.NodeId, msgPrinter); err != nil { return nil, err } else { // for performance, save the services that gotten from the exchange for use later @@ -299,6 +304,9 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, service_compatible = true service_comp[sId] = &svc messages[sId] = msg_compatible + if reason != "" { + messages[sId] = msgPrinter.Sprintf("%v, Warning: %v", msg_compatible, reason) + } if !checkAllSvcs { break } @@ -329,7 +337,7 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, if useSDef.GetOrg() == "" { useSDef.(*common.ServiceFile).Org = serviceRef.ServiceOrg } - if compatible, reason, imap, depSvcDefs, err := VerifySecretBindingForServiceDef(useSDef, resources.DepServices, serviceDefResolverHandler, vaultSecretExists, agbotUrl, secretBinding, resources.NodeOrg, msgPrinter); err != nil { + if compatible, reason, imap, depSvcDefs, err := VerifySecretBindingForServiceDef(useSDef, resources.DepServices, serviceDefResolverHandler, vaultSecretExists, agbotUrl, secretBinding, resources.NodeOrg, resources.NodeId, msgPrinter); err != nil { return nil, err } else { // for performance, save the services that gotten from the exchange for use later @@ -351,6 +359,9 @@ func secretBindingCompatible(getDeviceHandler exchange.DeviceHandler, service_compatible = true service_comp[sId] = useSDef messages[sId] = msg_compatible + if reason != "" { + messages[sId] = msgPrinter.Sprintf("%v, Warning: %v", msg_compatible, reason) + } if !checkAllSvcs { break } @@ -435,7 +446,7 @@ func VerifySecretBindingForServiceCache(sTopDef common.AbstractServiceFile, dependentServices map[string]exchange.ServiceDefinition, secretBinding []exchangecommon.SecretBinding, vaultSecretExists exchange.VaultSecretExistsHandler, - agbotUrl string, nodeOrg string, + agbotUrl string, nodeOrg string, nodeId string, msgPrinter *message.Printer) (bool, string, map[int]map[string]bool, error) { // get default message printer if nil @@ -474,7 +485,7 @@ func VerifySecretBindingForServiceCache(sTopDef common.AbstractServiceFile, // verify secrets exist in the secret manager if agbotUrl != "" && vaultSecretExists != nil { neededSB, _ := GroupSecretBindings(secretBinding, index_map) - if verified, reason, err := VerifyVaultSecrets_strict(neededSB, nodeOrg, agbotUrl, vaultSecretExists, msgPrinter); err != nil { + if verified, reason, err := VerifyVaultSecrets_strict(neededSB, nodeOrg, nodeId, agbotUrl, vaultSecretExists, msgPrinter); err != nil { return false, "", index_map, fmt.Errorf(msgPrinter.Sprintf("Error verifying secret in the secret manager. %v", err)) } else { return verified, reason, index_map, nil @@ -497,7 +508,7 @@ func VerifySecretBindingForServiceCache(sTopDef common.AbstractServiceFile, func VerifySecretBindingForService(svcSpec *ServiceSpec, serviceDefResolverHandler exchange.ServiceDefResolverHandler, vaultSecretExists exchange.VaultSecretExistsHandler, agbotUrl string, - secretBinding []exchangecommon.SecretBinding, nodeOrg string, + secretBinding []exchangecommon.SecretBinding, nodeOrg string, nodeId string, msgPrinter *message.Printer) (bool, string, map[int]map[string]bool, common.AbstractServiceFile, string, map[string]exchange.ServiceDefinition, error) { // get default message printer if nil @@ -517,7 +528,7 @@ func VerifySecretBindingForService(svcSpec *ServiceSpec, compSDef := ServiceDefinition{svcSpec.ServiceOrgid, *sDef} - compatible, reason, inxex_map, err := VerifySecretBindingForServiceCache(&compSDef, svc_map, secretBinding, vaultSecretExists, agbotUrl, nodeOrg, msgPrinter) + compatible, reason, inxex_map, err := VerifySecretBindingForServiceCache(&compSDef, svc_map, secretBinding, vaultSecretExists, agbotUrl, nodeOrg, nodeId, msgPrinter) return compatible, reason, inxex_map, &compSDef, sId, svc_map, err } @@ -535,7 +546,7 @@ func VerifySecretBindingForServiceDef(sDef common.AbstractServiceFile, dependentServices map[string]exchange.ServiceDefinition, // can be nil serviceDefResolverHandler exchange.ServiceDefResolverHandler, vaultSecretExists exchange.VaultSecretExistsHandler, agbotUrl string, - secretBinding []exchangecommon.SecretBinding, nodeOrg string, + secretBinding []exchangecommon.SecretBinding, nodeOrg string, nodeId string, msgPrinter *message.Printer) (bool, string, map[int]map[string]bool, map[string]exchange.ServiceDefinition, error) { // get default message printer if nil @@ -554,7 +565,7 @@ func VerifySecretBindingForServiceDef(sDef common.AbstractServiceFile, return false, "", nil, nil, NewCompCheckError(fmt.Errorf(msgPrinter.Sprintf("Failed to find the dependent services for %v/%v %v %v. %v", sDef.GetOrg(), sDef.GetURL(), sDef.GetArch(), sDef.GetVersion(), err)), COMPCHECK_GENERAL_ERROR) } - compatible, reason, inxex_map, err := VerifySecretBindingForServiceCache(sDef, service_map, secretBinding, vaultSecretExists, agbotUrl, nodeOrg, msgPrinter) + compatible, reason, inxex_map, err := VerifySecretBindingForServiceCache(sDef, service_map, secretBinding, vaultSecretExists, agbotUrl, nodeOrg, nodeId, msgPrinter) return compatible, reason, inxex_map, service_map, err } @@ -706,7 +717,7 @@ func GetSecretBindingForService(secretBinding []exchangecommon.SecretBinding, sv // It does not return when the vault secret does not exist or there is an error accessing // the vault api. Instead it will return a messages for each vault secret name that could // not be verified. -func VerifyVaultSecrets(secretBinding []exchangecommon.SecretBinding, nodeOrg string, agbotURL string, +func VerifyVaultSecrets(secretBinding []exchangecommon.SecretBinding, nodeOrg string, nodeId string, agbotURL string, vaultSecretExists exchange.VaultSecretExistsHandler, msgPrinter *message.Printer) (map[string]string, error) { if secretBinding == nil || len(secretBinding) == 0 { @@ -730,6 +741,7 @@ func VerifyVaultSecrets(secretBinding []exchangecommon.SecretBinding, nodeOrg st ret := map[string]string{} vs_checked := map[string]bool{} for _, sn := range secretBinding { + enableNodeSecret := sn.EnableNodeLevelSecrets for _, vbind := range sn.Secrets { // make sure each vault get checked only once @@ -740,7 +752,7 @@ func VerifyVaultSecrets(secretBinding []exchangecommon.SecretBinding, nodeOrg st vs_checked[vaultSecretName] = true } - if exists, err := VerifySingleVaultSecret(vaultSecretName, nodeOrg, agbotURL, vaultSecretExists, msgPrinter); err != nil { + if exists, err := VerifySingleVaultSecret(vaultSecretName, nodeOrg, nodeId, agbotURL, vaultSecretExists, enableNodeSecret, msgPrinter); err != nil { ret[vaultSecretName] = err.Error() } else if !exists { msg := msgPrinter.Sprintf("Secret %v does not exist in the secret manager.", vaultSecretName) @@ -758,7 +770,7 @@ func VerifyVaultSecrets(secretBinding []exchangecommon.SecretBinding, nodeOrg st // Call the agbot API to verify the vault secrets exists. // It returns immediately when a vault secret does not exist or there is an error accessing // the vault api. -func VerifyVaultSecrets_strict(secretBinding []exchangecommon.SecretBinding, nodeOrg string, agbotURL string, +func VerifyVaultSecrets_strict(secretBinding []exchangecommon.SecretBinding, nodeOrg string, nodeId string, agbotURL string, vaultSecretExists exchange.VaultSecretExistsHandler, msgPrinter *message.Printer) (bool, string, error) { if secretBinding == nil || len(secretBinding) == 0 { return true, "", nil @@ -780,6 +792,7 @@ func VerifyVaultSecrets_strict(secretBinding []exchangecommon.SecretBinding, nod // go through each secret binding making sure the vault secret exist in vault vs_checked := map[string]bool{} for _, sn := range secretBinding { + enableNodeSecret := sn.EnableNodeLevelSecrets for _, vbind := range sn.Secrets { _, vaultSecretName := vbind.GetBinding() @@ -790,8 +803,10 @@ func VerifyVaultSecrets_strict(secretBinding []exchangecommon.SecretBinding, nod vs_checked[vaultSecretName] = true } - if exists, err := VerifySingleVaultSecret(vaultSecretName, nodeOrg, agbotURL, vaultSecretExists, msgPrinter); err != nil { + if exists, err := VerifySingleVaultSecret(vaultSecretName, nodeOrg, nodeId, agbotURL, vaultSecretExists, enableNodeSecret, msgPrinter); !exists && err != nil { return false, "", err + } else if exists && err != nil { + return true, msgPrinter.Sprintf("%v, use non node-level secret", err), nil } else if !exists { return false, msgPrinter.Sprintf("Secret %v does not exist in the secret manager.", vaultSecretName), nil } @@ -802,8 +817,8 @@ func VerifyVaultSecrets_strict(secretBinding []exchangecommon.SecretBinding, nod } // It calls the agbot API to verify whether the given secret name exist in vault or not. -func VerifySingleVaultSecret(vaultSecretName string, nodeOrg string, agbotURL string, - vaultSecretExists exchange.VaultSecretExistsHandler, msgPrinter *message.Printer) (bool, error) { +func VerifySingleVaultSecret(vaultSecretName string, nodeOrg string, nodeId string, agbotURL string, + vaultSecretExists exchange.VaultSecretExistsHandler, enableNodeSecret bool, msgPrinter *message.Printer) (bool, error) { // get default message printer if nil if msgPrinter == nil { @@ -811,14 +826,28 @@ func VerifySingleVaultSecret(vaultSecretName string, nodeOrg string, agbotURL st } // parse the name - userName, nodeName, sName, err_parse := ParseVaultSecretName(vaultSecretName, msgPrinter) + userName, nName, sName, err_parse := ParseVaultSecretName(vaultSecretName, msgPrinter) if err_parse != nil { return false, fmt.Errorf(msgPrinter.Sprintf("Error parsing secret name in the secret binding. %v", err_parse)) } + // nName == "" from ParseVaultSecretName() function if this is deploycheck CLI + if nodeId != "" && nName == "" { + nodeName := exchange.GetId(nodeId) + nName = nodeName + } + // check the existance - if exists, err := vaultSecretExists(agbotURL, nodeOrg, userName, nodeName, sName); err != nil { - return false, fmt.Errorf(msgPrinter.Sprintf("Error checking secret %v in the secret manager. %v", vaultSecretName, err)) + if exists, err := vaultSecretExists(agbotURL, nodeOrg, userName, nName, sName); err != nil { + return false, fmt.Errorf(msgPrinter.Sprintf("Error checking secret %v for node %v in the secret manager. %v", vaultSecretName, nName, err)) + } else if !exists && nName != "" { + // then check set nName == "" to check non-node level + if exists, err = vaultSecretExists(agbotURL, nodeOrg, userName, "", sName); err != nil { + return false, fmt.Errorf(msgPrinter.Sprintf("Error checking secret %v in the secret manager. %v", vaultSecretName, err)) + } + + // return exists and + return exists, fmt.Errorf(msgPrinter.Sprintf("Node level secret %v doesn't exist for node %v.", vaultSecretName, nName)) } else { return exists, nil }