Skip to content

Commit a032bfd

Browse files
Config validation for OpenBao and simplify init operation
2 parents 5c62581 + fccc158 commit a032bfd

12 files changed

Lines changed: 502 additions & 27 deletions

File tree

docker-compose.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ x-default-healthcheck: &default-healthcheck
1111
services:
1212
# CB-Tumblebug
1313
cb-tumblebug:
14-
image: cloudbaristaorg/cb-tumblebug:0.12.24
14+
image: cloudbaristaorg/cb-tumblebug:0.12.25
1515
container_name: cb-tumblebug
1616
build:
1717
context: .
@@ -148,7 +148,7 @@ services:
148148

149149
# CB-Spider
150150
cb-spider:
151-
image: cloudbaristaorg/cb-spider:0.12.33
151+
image: cloudbaristaorg/cb-spider:0.12.35
152152
container_name: cb-spider
153153
# build:
154154
# context: ../cb-spider
@@ -179,7 +179,7 @@ services:
179179
# CB-MapUI
180180
# This is the Map-based client for CB-Tumblebug.
181181
cb-mapui:
182-
image: cloudbaristaorg/cb-mapui:0.12.48
182+
image: cloudbaristaorg/cb-mapui:0.12.50
183183
container_name: cb-mapui
184184
# build:
185185
# context: ../cb-mapui

init/init.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,62 @@ def get_decryption_key():
400400
sys.exit(1)
401401

402402

403+
# Preflight: verify the OpenBao credential store is usable by CB-Tumblebug.
404+
# Credentials are also stored in OpenBao for direct CSP API calls; if OpenBao is
405+
# misconfigured (missing VAULT_TOKEN/VAULT_ADDR, sealed, unreachable), credential
406+
# registration would silently skip that step — surface it to the user up front.
407+
def check_openbao_status():
408+
try:
409+
resp = requests.get(f"http://{TUMBLEBUG_SERVER}/tumblebug/credential/openbaoStatus", headers=HEADERS, timeout=10)
410+
if resp.status_code == 404:
411+
# Older CB-Tumblebug without this endpoint — skip the preflight quietly.
412+
return {"available": None}
413+
resp.raise_for_status()
414+
return resp.json()
415+
except requests.RequestException as e:
416+
return {"available": False, "message": f"could not query OpenBao status from CB-Tumblebug: {e}"}
417+
418+
419+
def print_openbao_warning(status):
420+
print(Fore.RED + "\n⚠ OpenBao credential store is NOT available to CB-Tumblebug")
421+
print(Fore.YELLOW + f" Reason: {status.get('message', 'unknown')}")
422+
print(Fore.YELLOW + f" VAULT_ADDR: {status.get('vaultAddr', '(unknown)')}")
423+
424+
# Show VAULT_TOKEN validity only when it was actually verifiable:
425+
# "set" alone would read as "valid" and mislead (e.g., a wrong token is set).
426+
reachable = status.get("reachable", False)
427+
initialized = status.get("initialized", False)
428+
sealed = status.get("sealed", True)
429+
token_checkable = reachable and initialized and not sealed
430+
if not status.get("vaultTokenSet", False):
431+
print(Fore.YELLOW + " VAULT_TOKEN: not set")
432+
elif token_checkable and not status.get("tokenValid", False):
433+
print(Fore.YELLOW + " VAULT_TOKEN: set, but INVALID (rejected by OpenBao)")
434+
435+
print(Fore.YELLOW + " Impact: CB-Tumblebug features will not fully work.")
436+
if not reachable:
437+
print(Fore.YELLOW + " Fix: start OpenBao and services: make up")
438+
elif not initialized:
439+
print(Fore.YELLOW + " Fix: initialize OpenBao: make init-openbao, then restart services: make up")
440+
elif sealed:
441+
print(Fore.YELLOW + " Fix: unseal OpenBao: make unseal")
442+
else:
443+
# Token missing or invalid — make up restores it from init/openbao/secrets/openbao-init.json.
444+
print(Fore.YELLOW + " Fix: set VAULT_TOKEN= (empty) in .env, then run: make up")
445+
446+
447+
if run_credentials:
448+
openbao_status = check_openbao_status()
449+
if openbao_status.get("available") is True:
450+
print(Fore.GREEN + "OpenBao credential store is available.\n")
451+
elif openbao_status.get("available") is False:
452+
print_openbao_warning(openbao_status)
453+
print(Fore.RED + "Initialization aborted. Fix the OpenBao configuration and re-run: make init")
454+
sys.exit(1)
455+
else:
456+
print(Fore.YELLOW + "OpenBao status check not supported by this CB-Tumblebug version; skipping preflight.\n")
457+
458+
403459
# Function to encrypt credentials using AES and RSA public key
404460
def encrypt_credential_value_with_publickey(public_key_pem, credentials):
405461
public_key = RSA.import_key(public_key_pem)
@@ -468,11 +524,22 @@ def register_credential(holder_name, provider, credentials):
468524

469525

470526
# Function to print formatted credential information
527+
# Collects per-credential OpenBao registration failures for the final summary.
528+
openbao_issues = []
529+
530+
471531
def print_credential_info(response):
472532
if "credentialName" in response and "credentialHolder" in response:
473533
# Print credential name and holder in bold
474534
print(Fore.YELLOW + f"\n{response['credentialName'].upper()} (holder: {response['credentialHolder']})" + Style.RESET_ALL)
475535

536+
# Collect OpenBao registration failures for the final summary only.
537+
# OpenBao problems are global (sealed, unreachable, bad token), so a per-CSP
538+
# line would just repeat the same root cause once per provider.
539+
openbao_status = response.get("openBaoStatus", "")
540+
if openbao_status and not openbao_status.startswith("registered"):
541+
openbao_issues.append(openbao_status)
542+
476543
if "allConnections" in response and "connectionconfig" in response["allConnections"]:
477544
# Print the explanation line with icons
478545
print(
@@ -1009,4 +1076,21 @@ def load_resources():
10091076
except Exception as e:
10101077
print(Fore.YELLOW + f"\n[Warning] Could not notify initialization completion: {e}")
10111078

1012-
print(Fore.YELLOW + f"\nThe system is ready to use.")
1079+
# Re-check OpenBao at the end so the final message reflects the actual state:
1080+
# declaring "ready to use" while the credential store is broken is misleading.
1081+
final_openbao = check_openbao_status() if run_credentials else {"available": None}
1082+
if final_openbao.get("available") is False or openbao_issues:
1083+
print(Fore.YELLOW + "\nThe system is ready to use, EXCEPT the OpenBao credential store:")
1084+
if final_openbao.get("available") is False:
1085+
print(Fore.RED + f" - {final_openbao.get('message', 'unavailable')}")
1086+
if openbao_issues:
1087+
# OpenBao problems are global — show the count and deduplicated
1088+
# root cause(s), not one line per CSP.
1089+
print(Fore.RED + f" - {len(openbao_issues)} credential(s) were NOT stored in OpenBao:")
1090+
unique_reasons = sorted(set(" ".join(issue.split()) for issue in openbao_issues))
1091+
for reason in unique_reasons[:3]:
1092+
print(Fore.RED + f" {reason[:200]}")
1093+
print(Fore.YELLOW + " Direct CSP API features of CB-Tumblebug will not work until this is resolved.")
1094+
print(Fore.YELLOW + " Fix the configuration (see the OpenBao warning above) and re-run: make init")
1095+
else:
1096+
print(Fore.YELLOW + "\nThe system is ready to use.")

init/multi-init.sh

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,24 @@ read -s -p "Enter the password for credentials.yaml.enc: " MULTI_INIT_PWD
1111
echo ""
1212
export MULTI_INIT_PWD
1313

14-
# 1. OpenBao
15-
if [ -f "$SCRIPT_DIR/openbao/openbao-register-creds.sh" ]; then
16-
OPENBAO_SH="$SCRIPT_DIR/openbao/openbao-register-creds.sh"
17-
elif [ -f "$SCRIPT_DIR/../../openbao/openbao-register-creds.sh" ]; then
18-
# When executed within cm-beetle
19-
OPENBAO_SH="$SCRIPT_DIR/../../openbao/openbao-register-creds.sh"
20-
else
21-
echo "Error: Cannot find openbao-register-creds.sh"
22-
exit 1
23-
fi
24-
25-
echo ""
26-
echo "Step 1. Registering credentials to OpenBao..."
27-
chmod +x "$OPENBAO_SH" 2>/dev/null || true
28-
bash "$OPENBAO_SH"
29-
if [ $? -ne 0 ]; then exit 1; fi
14+
# 1. Step 1 script execution code is deprecated (to be removed) for operational simplicity:
15+
# CB-Tumblebug server registers credentials to OpenBao automatically during Step 2.
16+
#
17+
# if [ -f "$SCRIPT_DIR/openbao/openbao-register-creds.sh" ]; then
18+
# OPENBAO_SH="$SCRIPT_DIR/openbao/openbao-register-creds.sh"
19+
# elif [ -f "$SCRIPT_DIR/../../openbao/openbao-register-creds.sh" ]; then
20+
# # When executed within cm-beetle
21+
# OPENBAO_SH="$SCRIPT_DIR/../../openbao/openbao-register-creds.sh"
22+
# else
23+
# echo "Error: Cannot find openbao-register-creds.sh"
24+
# exit 1
25+
# fi
26+
#
27+
# echo ""
28+
# echo "Step 1. Registering credentials to OpenBao..."
29+
# chmod +x "$OPENBAO_SH" 2>/dev/null || true
30+
# bash "$OPENBAO_SH"
31+
# if [ $? -ne 0 ]; then exit 1; fi
3032

3133
# 2. Tumblebug
3234
if [ -f "$SCRIPT_DIR/init.sh" ]; then

init/openbao/openbao-init.sh

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,23 @@ done
109109
INIT_STATUS=$(curl -sf "${VAULT_ADDR}/v1/sys/seal-status" | grep -o '"initialized":[a-z]*' | cut -d: -f2)
110110
if [ "$INIT_STATUS" = "true" ]; then
111111
echo -e "${YELLOW}[openbao-init]${NC} OpenBao is already initialized."
112-
echo " If you need to re-initialize, destroy the volume first:"
113-
echo " docker compose down -v"
112+
# Restore VAULT_TOKEN into .env from the saved init output when possible —
113+
# covers the case where .env was recreated or its VAULT_TOKEN was emptied.
114+
if [ -f "$INIT_OUTPUT" ]; then
115+
ROOT_TOKEN=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["root_token"])' "$INIT_OUTPUT" 2>/dev/null || true)
116+
if [ -n "${ROOT_TOKEN:-}" ]; then
117+
if grep -q "^VAULT_TOKEN=" "${ENV_FILE}"; then
118+
sed -i "s|^VAULT_TOKEN=.*|VAULT_TOKEN=${ROOT_TOKEN}|" "${ENV_FILE}"
119+
else
120+
echo "VAULT_TOKEN=${ROOT_TOKEN}" >> "${ENV_FILE}"
121+
fi
122+
echo -e "${GREEN}[openbao-init]${NC} Restored VAULT_TOKEN in ${ENV_FILE} from ${INIT_OUTPUT}"
123+
fi
124+
else
125+
echo -e "${RED}[openbao-init]${NC} Cannot restore VAULT_TOKEN: ${INIT_OUTPUT} not found."
126+
echo " If the token is lost, reset OpenBao (destroys stored secrets):"
127+
echo " docker compose down -v"
128+
fi
114129
exit 0
115130
fi
116131

src/core/common/utility.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,15 +1018,32 @@ func RegisterCredential(req model.CredentialReq) (model.CredentialInfo, error) {
10181018
}
10191019
//PrintJsonPretty(callResult)
10201020

1021-
// Register credentials in OpenBao for runtime CSP access (non-fatal: warn and continue if unavailable)
1022-
if model.VaultToken != "" {
1021+
// Register credentials in OpenBao for runtime CSP access (non-fatal: warn and
1022+
// continue if unavailable). The outcome is reported in the response via
1023+
// OpenBaoStatus so init tooling can surface silent failures to the user —
1024+
// without OpenBao, direct CSP API features cannot access this credential.
1025+
if model.VaultToken == "" {
1026+
callResult.OpenBaoStatus = "skipped: VAULT_TOKEN is not set in the cb-tumblebug environment; credential NOT stored in OpenBao"
1027+
log.Warn().Msgf("OpenBao registration skipped (VAULT_TOKEN not set): provider=%s holder=%s", req.ProviderName, req.CredentialHolder)
1028+
} else {
1029+
// Bound the OpenBao calls so a slow/unreachable OpenBao cannot stall
1030+
// credential registration for long (write + placeholder sweep).
1031+
openBaoCtx, openBaoCancel := context.WithTimeout(context.Background(), 15*time.Second)
1032+
defer openBaoCancel()
1033+
10231034
secretPath := csp.BuildSecretPathForHolder(req.CredentialHolder, req.ProviderName)
10241035
secretData := csp.ApplyCredentialKeyMap(req.ProviderName, decryptedKeyValueList)
1025-
if err := csp.WriteOpenBaoSecret(context.Background(), secretPath, secretData); err != nil {
1036+
if err := csp.WriteOpenBaoSecret(openBaoCtx, secretPath, secretData); err != nil {
1037+
callResult.OpenBaoStatus = fmt.Sprintf("failed: %v; credential NOT stored in OpenBao", err)
10261038
log.Warn().Err(err).Msgf("Failed to register credential in OpenBao (non-fatal): provider=%s holder=%s", req.ProviderName, req.CredentialHolder)
10271039
} else {
1040+
callResult.OpenBaoStatus = "registered at " + secretPath
10281041
log.Info().Msgf("Registered credential in OpenBao: path=%s", secretPath)
10291042
}
1043+
// Ensure every known CSP has at least a placeholder secret so consumers
1044+
// that read all CSP paths (e.g. mc-terrarium's tofu plan) don't hard-fail
1045+
// on providers without credentials. CAS-protected: never overwrites.
1046+
csp.EnsurePlaceholderCredentialSecrets(openBaoCtx)
10301047
}
10311048

10321049
callResult.CredentialHolder = req.CredentialHolder

0 commit comments

Comments
 (0)