220 lines
11 KiB
Bash
Executable File
220 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# AWOOOI post-reboot next-gate dispatch checklist.
|
|
# Read-only by design. It does not send requests, restart services, modify
|
|
# hosts, query secrets, or enable runtime actions.
|
|
|
|
set -uo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
SUMMARY_FILE=""
|
|
RUN_SUMMARY=1
|
|
NO_COLOR_FLAG=0
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: post-reboot-next-gate-dispatch.sh [options]
|
|
|
|
Turns post-reboot readiness summary blockers into a deterministic owner/action
|
|
checklist. This is a dispatch checklist only; it does not dispatch requests.
|
|
|
|
Options:
|
|
--summary-file PATH Use an existing post-reboot readiness summary file.
|
|
--no-run-summary Do not run post-reboot-readiness-summary.sh.
|
|
--no-color Disable color in delegated summary.
|
|
-h, --help Show this help.
|
|
|
|
Exit codes:
|
|
0 = checklist emitted. Runtime action remains unauthorized.
|
|
2 = summary unavailable or service blocker observed.
|
|
USAGE
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--summary-file)
|
|
shift
|
|
SUMMARY_FILE="${1:-}"
|
|
;;
|
|
--no-run-summary)
|
|
RUN_SUMMARY=0
|
|
;;
|
|
--no-color)
|
|
NO_COLOR_FLAG=1
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf 'Unknown argument: %s\n' "$1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ -z "$SUMMARY_FILE" && "$RUN_SUMMARY" -eq 1 ]]; then
|
|
SUMMARY_FILE="$(mktemp -t awoooi-post-reboot-summary.XXXXXX)"
|
|
summary_args=()
|
|
[[ "$NO_COLOR_FLAG" -eq 1 ]] && summary_args+=(--no-color)
|
|
bash "$ROOT_DIR/scripts/reboot-recovery/post-reboot-readiness-summary.sh" "${summary_args[@]}" >"$SUMMARY_FILE" 2>&1
|
|
summary_rc=$?
|
|
else
|
|
summary_rc=0
|
|
fi
|
|
|
|
if [[ -z "$SUMMARY_FILE" || ! -s "$SUMMARY_FILE" ]]; then
|
|
echo "BLOCKED_SUMMARY_UNAVAILABLE=1"
|
|
echo "RUNTIME_ACTION_AUTHORIZED=0"
|
|
exit 2
|
|
fi
|
|
|
|
value_for() {
|
|
local key="$1"
|
|
awk -F= -v key="$key" '$1 == key {value=$2; found=1} END {if (found) print value; else print ""}' "$SUMMARY_FILE"
|
|
}
|
|
|
|
service_green="$(value_for SERVICE_GREEN)"
|
|
product_data_green="$(value_for PRODUCT_DATA_GREEN)"
|
|
overall_declaration="$(value_for OVERALL_DECLARATION)"
|
|
next_required_gates="$(value_for NEXT_REQUIRED_GATES)"
|
|
stock_freshness_status="$(value_for STOCK_FRESHNESS_STATUS)"
|
|
stock_latest_trading_date="$(value_for STOCK_LATEST_TRADING_DATE)"
|
|
stock_blockers="$(value_for STOCK_BLOCKERS)"
|
|
stock_eod_window_pending="$(value_for STOCK_EOD_WINDOW_PENDING)"
|
|
stock_eod_classification="$(value_for STOCK_EOD_CLASSIFICATION)"
|
|
stock_eod_next_action="$(value_for STOCK_EOD_NEXT_ACTION)"
|
|
stock_eod_first_full_window_end="$(value_for STOCK_EOD_FIRST_FULL_WINDOW_END_LOCAL)"
|
|
stock_eod_final_retry_window_end="$(value_for STOCK_EOD_FINAL_RETRY_WINDOW_END_LOCAL)"
|
|
escrow_missing_count="$(value_for ESCROW_MISSING_COUNT)"
|
|
host_188_hygiene_blocked="$(value_for HOST_188_HYGIENE_BLOCKED)"
|
|
wazuh_registry_accepted="$(value_for WAZUH_MANAGER_REGISTRY_ACCEPTED)"
|
|
wazuh_coverage_scope="$(value_for WAZUH_COVERAGE_SCOPE)"
|
|
wazuh_direct_active="$(value_for WAZUH_DIRECT_ACTIVE)"
|
|
wazuh_no_transport="$(value_for WAZUH_NO_TRANSPORT)"
|
|
wazuh_ssh_blocked="$(value_for WAZUH_SSH_BLOCKED)"
|
|
wazuh_route_code="$(value_for WAZUH_ROUTE_CODE)"
|
|
wazuh_transport_count="$(value_for WAZUH_TRANSPORT_COUNT)"
|
|
wazuh_dashboard_api_connection="$(value_for WAZUH_DASHBOARD_API_CONNECTION)"
|
|
wazuh_dashboard_index_ok="$(value_for WAZUH_DASHBOARD_INDEX_OK)"
|
|
runtime_action_authorized="$(value_for RUNTIME_ACTION_AUTHORIZED)"
|
|
summary_artifact_dir="$(value_for ARTIFACT_DIR)"
|
|
|
|
contains_gate() {
|
|
local gate="$1"
|
|
[[ ",${next_required_gates}," == *",${gate},"* ]]
|
|
}
|
|
|
|
print_gate_header() {
|
|
local id="$1"
|
|
local title="$2"
|
|
echo
|
|
echo "GATE_ID=$id"
|
|
echo "GATE_TITLE=$title"
|
|
}
|
|
|
|
echo "AWOOOI_POST_REBOOT_NEXT_GATE_DISPATCH=1"
|
|
echo "SUMMARY_FILE=$SUMMARY_FILE"
|
|
echo "SUMMARY_ARTIFACT_DIR=${summary_artifact_dir:-unknown}"
|
|
echo "SUMMARY_RC=$summary_rc"
|
|
echo "SERVICE_GREEN=${service_green:-unknown}"
|
|
echo "PRODUCT_DATA_GREEN=${product_data_green:-unknown}"
|
|
echo "OVERALL_DECLARATION=${overall_declaration:-unknown}"
|
|
echo "NEXT_REQUIRED_GATES=${next_required_gates:-unknown}"
|
|
echo "RUNTIME_ACTION_AUTHORIZED=0"
|
|
echo "DISPATCH_AUTHORIZED=0"
|
|
echo "REQUEST_SENT_COUNT=0"
|
|
echo "HOST_WRITE_AUTHORIZED=0"
|
|
echo "SECRET_VALUE_COLLECTION_ALLOWED=0"
|
|
|
|
if [[ "$service_green" != "1" ]] \
|
|
&& ! contains_gate "product_data_freshness_recovery" \
|
|
&& ! contains_gate "stockplatform_eod_window_completion"; then
|
|
echo
|
|
echo "BLOCKED_SERVICE_GREEN=0"
|
|
echo "NEXT_STEP=restore_service_before_boundary_dispatch"
|
|
exit 2
|
|
fi
|
|
|
|
gate_count=0
|
|
|
|
if contains_gate "stockplatform_eod_window_completion"; then
|
|
gate_count=$((gate_count + 1))
|
|
print_gate_header "stockplatform_eod_window_completion" "StockPlatform scheduled EOD freshness completion"
|
|
echo "GATE_PRIORITY=P0"
|
|
echo "GATE_STATUS=scheduled_eod_window_pending"
|
|
echo "CURRENT_EVIDENCE=status:${stock_freshness_status:-unknown};latest_trading_date:${stock_latest_trading_date:-unknown};blockers:${stock_blockers:-unknown};pending:${stock_eod_window_pending:-unknown};classification:${stock_eod_classification:-unknown};next:${stock_eod_next_action:-unknown};first_full_window_end:${stock_eod_first_full_window_end:-unknown};final_retry_window_end:${stock_eod_final_retry_window_end:-unknown}"
|
|
echo "OWNER_GROUP=stockplatform_data_owner,market_data_owner,oncall_operator"
|
|
echo "REQUIRED_EVIDENCE=freshness_endpoint_after_19_15,cron_log_refs,daily_ingestion_ref,price_ref,chips_ref,margin_ref,ai_recommendation_ref"
|
|
echo "FORBIDDEN_PAYLOADS=raw_db_dump,secret_value,api_token,manual_fake_row,handwritten_freshness_override"
|
|
echo "FORBIDDEN_ACTIONS=manual_db_update,truncate_restore,fake_freshness_marker,skip_data_gate,declare_product_data_green_before_status_ok"
|
|
echo "ALLOWED_ACTION=wait_for_scheduled_cron_and_recheck_summary"
|
|
echo "DONE_CRITERIA=stock_freshness_status:ok,product_data_green:1,post_reboot_summary_recheck_after_eod_window"
|
|
fi
|
|
|
|
if contains_gate "product_data_freshness_recovery"; then
|
|
gate_count=$((gate_count + 1))
|
|
print_gate_header "product_data_freshness_recovery" "Product data freshness recovery"
|
|
echo "GATE_PRIORITY=P0"
|
|
echo "GATE_STATUS=data_freshness_recovery_required"
|
|
echo "CURRENT_EVIDENCE=status:${stock_freshness_status:-unknown};latest_trading_date:${stock_latest_trading_date:-unknown};blockers:${stock_blockers:-unknown};classification:${stock_eod_classification:-unknown};next:${stock_eod_next_action:-unknown}"
|
|
echo "OWNER_GROUP=stockplatform_data_owner,market_data_owner,rollback_owner"
|
|
echo "REQUIRED_EVIDENCE=source_of_truth_commit,cron_entrypoint_refs,cron_log_refs,official_source_status,db_count_readback,freshness_endpoint_after_recovery,rollback_plan"
|
|
echo "FORBIDDEN_PAYLOADS=raw_db_dump,secret_value,api_token,manual_fake_row,handwritten_freshness_override"
|
|
echo "FORBIDDEN_ACTIONS=manual_db_update,truncate_restore,fake_freshness_marker,skip_data_gate,declare_product_data_green_before_status_ok"
|
|
echo "ALLOWED_ACTION=diagnose_source_or_cron_failure_before_controlled_recovery"
|
|
echo "DONE_CRITERIA=stock_freshness_status:ok,product_data_green:1,post_reboot_summary_status:not_service_blocked"
|
|
fi
|
|
|
|
if contains_gate "credential_escrow_evidence"; then
|
|
gate_count=$((gate_count + 1))
|
|
print_gate_header "credential_escrow_evidence" "DR credential escrow non-secret evidence"
|
|
echo "GATE_PRIORITY=P0"
|
|
echo "GATE_STATUS=owner_evidence_required"
|
|
echo "CURRENT_EVIDENCE=escrow_missing_count:${escrow_missing_count:-unknown}"
|
|
echo "OWNER_GROUP=backup_dr_owner,security_owner,business_owner"
|
|
echo "REQUIRED_ITEMS=restic_repository_password,offsite_provider_credentials,break_glass_admin_credentials,dns_registrar_recovery,oauth_ai_provider_recovery"
|
|
echo "REQUIRED_EVIDENCE=non_secret_evidence_id,owner_role,owner_team,evidence_location,review_date,reviewer"
|
|
echo "FORBIDDEN_PAYLOADS=password,token,secret_value,hash,prefix,suffix,raw_credential,screenshot_with_secret"
|
|
echo "ALLOWED_ACTION=collect_non_secret_marker_evidence_only"
|
|
echo "FORBIDDEN_ACTION=mark_placeholder,write_fake_marker,store_secret,disable_alert"
|
|
echo "DONE_CRITERIA=escrow_missing_count:0,offsite_report_full_marker:1,backup_status_core_blockers:0"
|
|
fi
|
|
|
|
if contains_gate "host_188_hygiene_maintenance_window"; then
|
|
gate_count=$((gate_count + 1))
|
|
print_gate_header "host_188_hygiene_maintenance_window" "188 host PostgreSQL / certbot hygiene maintenance window"
|
|
echo "GATE_PRIORITY=P0"
|
|
echo "GATE_STATUS=maintenance_window_required"
|
|
echo "CURRENT_EVIDENCE=host_188_hygiene_blocked:${host_188_hygiene_blocked:-unknown}"
|
|
echo "OWNER_GROUP=188_host_owner,db_owner,dns_tls_owner,rollback_owner"
|
|
echo "REQUIRED_DECISIONS=postgresql_14_main_retire_or_restore_or_break_glass,certbot_dns_acme_owner_path,startup_unit_source_of_truth,rollback_owner,postcheck_owner"
|
|
echo "REQUIRED_EVIDENCE=maintenance_window,rollback_plan,precheck_artifact,postcheck_artifact,service_impact,stop_condition"
|
|
echo "FORBIDDEN_ACTIONS=pg_resetwal,certbot_renew,nginx_reload,systemctl_reset_failed,db_restore,docker_restart,host_file_write"
|
|
echo "ALLOWED_ACTION=prepare_maintenance_packet_and_read_only_preflight"
|
|
echo "DONE_CRITERIA=service_green:1,host_188_hygiene_blocked:0,systemd_failed_units:0,certbot_owner_evidence_accepted:1"
|
|
fi
|
|
|
|
if contains_gate "wazuh_manager_registry_export"; then
|
|
gate_count=$((gate_count + 1))
|
|
print_gate_header "wazuh_manager_registry_export" "Wazuh manager registry redacted export"
|
|
echo "GATE_PRIORITY=P0"
|
|
echo "GATE_STATUS=readonly_registry_export_required"
|
|
echo "CURRENT_EVIDENCE=wazuh_manager_registry_accepted:${wazuh_registry_accepted:-unknown};coverage_scope:${wazuh_coverage_scope:-unknown};direct_active:${wazuh_direct_active:-unknown};no_transport:${wazuh_no_transport:-unknown};ssh_blocked:${wazuh_ssh_blocked:-unknown};route:${wazuh_route_code:-unknown};transport:${wazuh_transport_count:-unknown};dashboard_api:${wazuh_dashboard_api_connection:-unknown};index_ok:${wazuh_dashboard_index_ok:-unknown}"
|
|
echo "OWNER_GROUP=iwooos_soc_owner,wazuh_owner,host_owner"
|
|
echo "REQUIRED_EXPORT=redacted_manager_registry_counts,per_host_alias_status,dashboard_api_connection_status,dashboard_api_version_status,collection_time_window,reviewer"
|
|
echo "FORBIDDEN_PAYLOADS=agent_real_name,internal_ip,client_keys,raw_wazuh_payload,token,password,authorization_header"
|
|
echo "FORBIDDEN_ACTIONS=active_response,agent_reenroll,wazuh_restart,secret_patch,host_write,kali_active_scan"
|
|
echo "ALLOWED_ACTION=collect_redacted_registry_export_or_no_data_attestation"
|
|
echo "DONE_CRITERIA=manager_registry_accepted:1,owner_evidence_accepted:1,runtime_gate:0"
|
|
fi
|
|
|
|
echo
|
|
echo "NEXT_GATE_COUNT=$gate_count"
|
|
echo "NEXT_STEP=dispatch_owner_packets_manually_after_review"
|
|
echo "POSTCHECK_COMMAND=scripts/reboot-recovery/post-reboot-readiness-summary.sh --no-color"
|
|
echo "NO_FALSE_GREEN_RULE=service_green_does_not_equal_dr_complete_or_wazuh_registry_recovered"
|
|
|
|
exit 0
|