@@ -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
404460def 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+
471531def 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"\n The 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 + "\n The 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 + "\n The system is ready to use." )
0 commit comments