749 lines
27 KiB
Bash
Executable File
749 lines
27 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
CLOUD_NEUTRAL_DIR="$(cd "${ROOT_DIR}/.." && pwd)"
|
|
ACCOUNTS_REPO="${ACCOUNTS_REPO:-${CLOUD_NEUTRAL_DIR}/accounts.svc.plus}"
|
|
ACCOUNTS_SERVICE_URL="${ACCOUNTS_SERVICE_URL:-https://accounts.svc.plus}"
|
|
OVERLAY_NODE_ID="${OVERLAY_NODE_ID:-xworkmate-bridge}"
|
|
OVERLAY_GROUP_VARS="${OVERLAY_GROUP_VARS:-${ROOT_DIR}/group_vars/xworkmate_bridge_distributed.yml}"
|
|
OVERLAY_ATTACH_TO="${OVERLAY_ATTACH_TO:-jp-xhttp-contabo.svc.plus,cn-xworkmate-bridge.svc.plus}"
|
|
OVERLAY_REGISTER_ARGS="${OVERLAY_REGISTER_ARGS:-}"
|
|
OVERLAY_USE_SUDO="${OVERLAY_USE_SUDO:-1}"
|
|
OVERLAY_SKIP_DEPLOY="${OVERLAY_SKIP_DEPLOY:-0}"
|
|
OVERLAY_SKIP_UP="${OVERLAY_SKIP_UP:-0}"
|
|
OVERLAY_TEARDOWN="${OVERLAY_TEARDOWN:-0}"
|
|
OVERLAY_TEARDOWN_ON_ERROR="${OVERLAY_TEARDOWN_ON_ERROR:-0}"
|
|
OVERLAY_STATE_FILE="${OVERLAY_STATE_FILE:-${HOME}/.xoverlay/session.json}"
|
|
OVERLAY_SKIP_LOCAL_TOOL_CHECK="${OVERLAY_SKIP_LOCAL_TOOL_CHECK:-0}"
|
|
OVERLAY_ANSIBLE_SYNTAX_ARGS="${OVERLAY_ANSIBLE_SYNTAX_ARGS:-}"
|
|
OVERLAY_ANSIBLE_DEPLOY_ARGS="${OVERLAY_ANSIBLE_DEPLOY_ARGS:--f 1}"
|
|
OVERLAY_CONFIG_FILE="${OVERLAY_CONFIG_FILE:-${HOME}/.xoverlay/overlay-config.json}"
|
|
OVERLAY_EVIDENCE_DIR="${OVERLAY_EVIDENCE_DIR:-/tmp/wireguard-over-vless-closure-$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
OVERLAY_CAPTURE_LOG="${OVERLAY_CAPTURE_LOG:-1}"
|
|
OVERLAY_CHECK_ONLY="${OVERLAY_CHECK_ONLY:-0}"
|
|
OVERLAY_BUILD_BIN="${OVERLAY_BUILD_BIN:-0}"
|
|
OVERLAY_BUILD_BIN_PATH="${OVERLAY_BUILD_BIN_PATH:-${OVERLAY_EVIDENCE_DIR}/overlayctl}"
|
|
OVERLAY_ENV_FILES="${OVERLAY_ENV_FILES:-}"
|
|
overlay_runtime_started=0
|
|
missing_required_envs=()
|
|
missing_required_tools=()
|
|
missing_required_paths=()
|
|
loaded_env_keys=()
|
|
loaded_env_files=()
|
|
active_step=""
|
|
if [[ "${OVERLAY_CAPTURE_LOG}" == "1" ]]; then
|
|
mkdir -p "${OVERLAY_EVIDENCE_DIR}"
|
|
run_log_fifo="${OVERLAY_EVIDENCE_DIR}/.run-log.fifo"
|
|
rm -f "${run_log_fifo}"
|
|
mkfifo "${run_log_fifo}"
|
|
tee -a "${OVERLAY_EVIDENCE_DIR}/run.log" < "${run_log_fifo}" &
|
|
run_log_tee_pid=$!
|
|
exec 3>&1 4>&2
|
|
exec > "${run_log_fifo}" 2>&1
|
|
rm -f "${run_log_fifo}"
|
|
fi
|
|
|
|
sha256_file() {
|
|
local path="$1"
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
shasum -a 256 "${path}" | awk '{print $1}'
|
|
return
|
|
fi
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "${path}" | awk '{print $1}'
|
|
return
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
git_head() {
|
|
local repo="$1"
|
|
git -C "${repo}" rev-parse HEAD 2>/dev/null || true
|
|
}
|
|
|
|
git_dirty() {
|
|
local repo="$1"
|
|
if ! git -C "${repo}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
echo "unknown"
|
|
return
|
|
fi
|
|
if [[ -n "$(git -C "${repo}" status --short 2>/dev/null)" ]]; then
|
|
echo "1"
|
|
return
|
|
fi
|
|
echo "0"
|
|
}
|
|
|
|
is_overlay_env_key() {
|
|
case "$1" in
|
|
ACCOUNT_EMAIL|ACCOUNT_PASSWORD|BRIDGE_AUTH_TOKEN|VAULT_SERVER_ROOT_ACCESS_TOKEN|VAULT_TOKEN|INTERNAL_SERVICE_TOKEN)
|
|
return 0
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
trim_quotes() {
|
|
local value="$1"
|
|
value="${value#"${value%%[![:space:]]*}"}"
|
|
value="${value%"${value##*[![:space:]]}"}"
|
|
if [[ "${value}" == \"*\" && "${value}" == *\" ]]; then
|
|
value="${value:1:${#value}-2}"
|
|
elif [[ "${value}" == \'*\' && "${value}" == *\' ]]; then
|
|
value="${value:1:${#value}-2}"
|
|
fi
|
|
printf '%s' "${value}"
|
|
}
|
|
|
|
shell_quote() {
|
|
printf "%q" "$1"
|
|
}
|
|
|
|
mark_step() {
|
|
local step="$1"
|
|
local status="$2"
|
|
mkdir -p "${OVERLAY_EVIDENCE_DIR}"
|
|
printf '%s\t%s\t%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${step}" "${status}" >> "${OVERLAY_EVIDENCE_DIR}/steps.log"
|
|
}
|
|
|
|
begin_step() {
|
|
active_step="$1"
|
|
}
|
|
|
|
complete_step() {
|
|
local step="$1"
|
|
local status="$2"
|
|
mark_step "${step}" "${status}"
|
|
if [[ "${active_step}" == "${step}" ]]; then
|
|
active_step=""
|
|
fi
|
|
}
|
|
|
|
step_status() {
|
|
local step="$1"
|
|
if [[ ! -f "${OVERLAY_EVIDENCE_DIR}/steps.log" ]]; then
|
|
echo "not_run"
|
|
return
|
|
fi
|
|
awk -F '\t' -v step="${step}" '$2 == step { status = $3 } END { if (status == "") { print "not_run" } else { print status } }' "${OVERLAY_EVIDENCE_DIR}/steps.log"
|
|
}
|
|
|
|
write_closure_requirements() {
|
|
local output="$1"
|
|
local account_login_status="$2"
|
|
local device_register_status="$3"
|
|
local config_initial_status="$4"
|
|
local playbooks_projection_status="$5"
|
|
local gateway_deploy_status="$6"
|
|
local config_refresh_status="$7"
|
|
local local_runtime_status="$8"
|
|
local connectivity_status="$9"
|
|
local ack_status="${10}"
|
|
{
|
|
printf 'requirement\tstep\tstatus\trequired_for_completion\n'
|
|
printf 'account_login\t01.login\t%s\t1\n' "${account_login_status}"
|
|
printf 'device_registration\t02.register_device\t%s\t1\n' "${device_register_status}"
|
|
printf 'initial_config_sync_render_preflight\t03.initial_sync_render_preflight\t%s\t1\n' "${config_initial_status}"
|
|
printf 'playbooks_client_projection\t04.apply_playbooks_client\t%s\t1\n' "${playbooks_projection_status}"
|
|
printf 'gateway_deploy_and_heartbeat\t05.deploy_gateway\t%s\t1\n' "${gateway_deploy_status}"
|
|
printf 'post_heartbeat_config_refresh\t06.refresh_sync_render_preflight\t%s\t1\n' "${config_refresh_status}"
|
|
printf 'local_runtime_up\t07.local_runtime_up\t%s\t1\n' "${local_runtime_status}"
|
|
printf 'private_connectivity\t08.connectivity\t%s\t1\n' "${connectivity_status}"
|
|
printf 'config_ack\t09.ack_config\t%s\t1\n' "${ack_status}"
|
|
printf 'optional_teardown\t10.teardown\t%s\t0\n' "$(step_status "10.teardown")"
|
|
} > "${output}"
|
|
}
|
|
|
|
closure_complete_value() {
|
|
local account_login_status="$1"
|
|
local device_register_status="$2"
|
|
local config_initial_status="$3"
|
|
local playbooks_projection_status="$4"
|
|
local gateway_deploy_status="$5"
|
|
local config_refresh_status="$6"
|
|
local local_runtime_status="$7"
|
|
local connectivity_status="$8"
|
|
local ack_status="$9"
|
|
if [[ "${account_login_status}" == "ok" \
|
|
&& "${device_register_status}" == "ok" \
|
|
&& "${config_initial_status}" == "ok" \
|
|
&& "${playbooks_projection_status}" == "ok" \
|
|
&& "${gateway_deploy_status}" == "ok" \
|
|
&& "${config_refresh_status}" == "ok" \
|
|
&& "${local_runtime_status}" == "ok" \
|
|
&& "${connectivity_status}" == "ok" \
|
|
&& "${ack_status}" == "ok" ]]; then
|
|
echo "1"
|
|
return
|
|
fi
|
|
echo "0"
|
|
}
|
|
|
|
write_closure_verdict() {
|
|
local output="$1"
|
|
local complete="$2"
|
|
local failed_items
|
|
failed_items="$(awk -F '\t' 'NR > 1 && $4 == "1" && $3 != "ok" { items = items ? items "," $1 ":" $3 : $1 ":" $3 } END { print items }' "${OVERLAY_EVIDENCE_DIR}/closure-requirements.tsv")"
|
|
{
|
|
echo "closure_ready=${complete}"
|
|
echo "required_items_failed=${failed_items}"
|
|
echo "requirements_file=${OVERLAY_EVIDENCE_DIR}/closure-requirements.tsv"
|
|
} > "${output}"
|
|
}
|
|
|
|
load_env_files() {
|
|
if [[ -z "${OVERLAY_ENV_FILES}" ]]; then
|
|
return
|
|
fi
|
|
local env_file line key value
|
|
IFS=',' read -r -a env_files <<< "${OVERLAY_ENV_FILES}"
|
|
for env_file in "${env_files[@]}"; do
|
|
env_file="${env_file#"${env_file%%[![:space:]]*}"}"
|
|
env_file="${env_file%"${env_file##*[![:space:]]}"}"
|
|
if [[ -z "${env_file}" ]]; then
|
|
continue
|
|
fi
|
|
if [[ ! -f "${env_file}" ]]; then
|
|
missing_required_paths+=("overlay_env_file:${env_file}")
|
|
continue
|
|
fi
|
|
loaded_env_files+=("${env_file}")
|
|
while IFS= read -r line || [[ -n "${line}" ]]; do
|
|
line="${line#"${line%%[![:space:]]*}"}"
|
|
[[ -z "${line}" || "${line}" == \#* ]] && continue
|
|
[[ "${line}" == export\ * ]] && line="${line#export }"
|
|
[[ "${line}" != *=* ]] && continue
|
|
key="${line%%=*}"
|
|
value="${line#*=}"
|
|
key="${key#"${key%%[![:space:]]*}"}"
|
|
key="${key%"${key##*[![:space:]]}"}"
|
|
if [[ "${key}" == "${line}" || -z "${key}" ]]; then
|
|
continue
|
|
fi
|
|
if ! is_overlay_env_key "${key}"; then
|
|
continue
|
|
fi
|
|
if [[ -n "${!key:-}" ]]; then
|
|
continue
|
|
fi
|
|
value="$(trim_quotes "${value}")"
|
|
if [[ -z "${value}" ]]; then
|
|
continue
|
|
fi
|
|
export "${key}=${value}"
|
|
loaded_env_keys+=("${key}@${env_file}")
|
|
done < "${env_file}"
|
|
done
|
|
}
|
|
|
|
write_evidence() {
|
|
local status="$1"
|
|
mkdir -p "${OVERLAY_EVIDENCE_DIR}"
|
|
{
|
|
echo "status=${status}"
|
|
echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
echo "accounts_repo=${ACCOUNTS_REPO}"
|
|
echo "accounts_git_head=$(git_head "${ACCOUNTS_REPO}")"
|
|
echo "accounts_git_dirty=$(git_dirty "${ACCOUNTS_REPO}")"
|
|
echo "playbooks_git_head=$(git_head "${ROOT_DIR}")"
|
|
echo "playbooks_git_dirty=$(git_dirty "${ROOT_DIR}")"
|
|
echo "accounts_service_url=${ACCOUNTS_SERVICE_URL}"
|
|
echo "overlay_node_id=${OVERLAY_NODE_ID}"
|
|
echo "overlay_group_vars=${OVERLAY_GROUP_VARS}"
|
|
echo "overlay_attach_to=${OVERLAY_ATTACH_TO}"
|
|
echo "overlay_skip_deploy=${OVERLAY_SKIP_DEPLOY}"
|
|
echo "overlay_skip_up=${OVERLAY_SKIP_UP}"
|
|
echo "overlay_use_sudo=${OVERLAY_USE_SUDO}"
|
|
echo "overlay_teardown=${OVERLAY_TEARDOWN}"
|
|
echo "overlay_teardown_on_error=${OVERLAY_TEARDOWN_ON_ERROR}"
|
|
echo "overlay_state_file=${OVERLAY_STATE_FILE}"
|
|
echo "overlay_config_file=${OVERLAY_CONFIG_FILE}"
|
|
echo "overlay_check_only=${OVERLAY_CHECK_ONLY}"
|
|
echo "overlay_build_bin=${OVERLAY_BUILD_BIN}"
|
|
echo "overlay_build_bin_path=${OVERLAY_BUILD_BIN_PATH}"
|
|
echo "overlay_env_files=${OVERLAY_ENV_FILES}"
|
|
echo "overlayctl_bin=${OVERLAYCTL_BIN:-}"
|
|
local account_login_status device_register_status config_initial_status playbooks_projection_status
|
|
local gateway_deploy_status config_refresh_status local_runtime_status connectivity_status ack_status
|
|
account_login_status="$(step_status "01.login")"
|
|
device_register_status="$(step_status "02.register_device")"
|
|
config_initial_status="$(step_status "03.initial_sync_render_preflight")"
|
|
playbooks_projection_status="$(step_status "04.apply_playbooks_client")"
|
|
gateway_deploy_status="$(step_status "05.deploy_gateway")"
|
|
config_refresh_status="$(step_status "06.refresh_sync_render_preflight")"
|
|
local_runtime_status="$(step_status "07.local_runtime_up")"
|
|
connectivity_status="$(step_status "08.connectivity")"
|
|
ack_status="$(step_status "09.ack_config")"
|
|
local closure_complete
|
|
closure_complete="$(closure_complete_value \
|
|
"${account_login_status}" \
|
|
"${device_register_status}" \
|
|
"${config_initial_status}" \
|
|
"${playbooks_projection_status}" \
|
|
"${gateway_deploy_status}" \
|
|
"${config_refresh_status}" \
|
|
"${local_runtime_status}" \
|
|
"${connectivity_status}" \
|
|
"${ack_status}")"
|
|
write_closure_requirements \
|
|
"${OVERLAY_EVIDENCE_DIR}/closure-requirements.tsv" \
|
|
"${account_login_status}" \
|
|
"${device_register_status}" \
|
|
"${config_initial_status}" \
|
|
"${playbooks_projection_status}" \
|
|
"${gateway_deploy_status}" \
|
|
"${config_refresh_status}" \
|
|
"${local_runtime_status}" \
|
|
"${connectivity_status}" \
|
|
"${ack_status}"
|
|
write_closure_verdict "${OVERLAY_EVIDENCE_DIR}/closure-verdict.env" "${closure_complete}"
|
|
echo "closure_account_login_status=${account_login_status}"
|
|
echo "closure_device_register_status=${device_register_status}"
|
|
echo "closure_config_initial_status=${config_initial_status}"
|
|
echo "closure_playbooks_projection_status=${playbooks_projection_status}"
|
|
echo "closure_gateway_deploy_status=${gateway_deploy_status}"
|
|
echo "closure_config_refresh_status=${config_refresh_status}"
|
|
echo "closure_local_runtime_status=${local_runtime_status}"
|
|
echo "closure_connectivity_status=${connectivity_status}"
|
|
echo "closure_ack_status=${ack_status}"
|
|
echo "closure_complete=${closure_complete}"
|
|
if [[ -s "${OVERLAY_EVIDENCE_DIR}/steps.log" ]]; then
|
|
local last_step_timestamp last_step last_step_status
|
|
IFS=$'\t' read -r last_step_timestamp last_step last_step_status < <(tail -n 1 "${OVERLAY_EVIDENCE_DIR}/steps.log")
|
|
echo "last_step_timestamp=${last_step_timestamp}"
|
|
echo "last_step=${last_step}"
|
|
echo "last_step_status=${last_step_status}"
|
|
fi
|
|
if [[ ${#loaded_env_files[@]} -gt 0 ]]; then
|
|
local joined_loaded_env_files
|
|
joined_loaded_env_files="$(IFS=','; echo "${loaded_env_files[*]}")"
|
|
echo "loaded_env_files=${joined_loaded_env_files}"
|
|
fi
|
|
if [[ ${#loaded_env_keys[@]} -gt 0 ]]; then
|
|
local joined_loaded_env_keys
|
|
joined_loaded_env_keys="$(IFS=','; echo "${loaded_env_keys[*]}")"
|
|
echo "loaded_env_keys=${joined_loaded_env_keys}"
|
|
fi
|
|
if [[ -n "${OVERLAYCTL_BIN:-}" && -x "${OVERLAYCTL_BIN}" ]]; then
|
|
overlayctl_sha256="$(sha256_file "${OVERLAYCTL_BIN}" 2>/dev/null || true)"
|
|
if [[ -n "${overlayctl_sha256}" ]]; then
|
|
echo "overlayctl_sha256=${overlayctl_sha256}"
|
|
fi
|
|
fi
|
|
if [[ ${#missing_required_envs[@]} -gt 0 ]]; then
|
|
local joined_missing_envs
|
|
joined_missing_envs="$(IFS=','; echo "${missing_required_envs[*]}")"
|
|
echo "missing_required_envs=${joined_missing_envs}"
|
|
fi
|
|
if [[ ${#missing_required_tools[@]} -gt 0 ]]; then
|
|
local joined_missing_tools
|
|
joined_missing_tools="$(IFS=','; echo "${missing_required_tools[*]}")"
|
|
echo "missing_required_tools=${joined_missing_tools}"
|
|
fi
|
|
if [[ ${#missing_required_paths[@]} -gt 0 ]]; then
|
|
local joined_missing_paths
|
|
joined_missing_paths="$(IFS=','; echo "${missing_required_paths[*]}")"
|
|
echo "missing_required_paths=${joined_missing_paths}"
|
|
fi
|
|
} > "${OVERLAY_EVIDENCE_DIR}/summary.env"
|
|
{
|
|
echo "# Non-secret settings for rerunning the closure script."
|
|
echo "# Source or copy these exports, then provide the missing secret values separately."
|
|
echo "export ACCOUNTS_REPO=$(shell_quote "${ACCOUNTS_REPO}")"
|
|
echo "export ACCOUNTS_SERVICE_URL=$(shell_quote "${ACCOUNTS_SERVICE_URL}")"
|
|
echo "export OVERLAY_NODE_ID=$(shell_quote "${OVERLAY_NODE_ID}")"
|
|
echo "export OVERLAY_GROUP_VARS=$(shell_quote "${OVERLAY_GROUP_VARS}")"
|
|
echo "export OVERLAY_ATTACH_TO=$(shell_quote "${OVERLAY_ATTACH_TO}")"
|
|
echo "export OVERLAY_USE_SUDO=$(shell_quote "${OVERLAY_USE_SUDO}")"
|
|
echo "export OVERLAY_SKIP_DEPLOY=$(shell_quote "${OVERLAY_SKIP_DEPLOY}")"
|
|
echo "export OVERLAY_SKIP_UP=$(shell_quote "${OVERLAY_SKIP_UP}")"
|
|
echo "export OVERLAY_TEARDOWN=$(shell_quote "${OVERLAY_TEARDOWN}")"
|
|
echo "export OVERLAY_TEARDOWN_ON_ERROR=$(shell_quote "${OVERLAY_TEARDOWN_ON_ERROR}")"
|
|
echo "export OVERLAY_STATE_FILE=$(shell_quote "${OVERLAY_STATE_FILE}")"
|
|
echo "export OVERLAY_CONFIG_FILE=$(shell_quote "${OVERLAY_CONFIG_FILE}")"
|
|
echo "export OVERLAY_ENV_FILES=$(shell_quote "${OVERLAY_ENV_FILES}")"
|
|
echo "export OVERLAY_BUILD_BIN=$(shell_quote "${OVERLAY_BUILD_BIN}")"
|
|
echo "# OVERLAY_BUILD_BIN_PATH was $(shell_quote "${OVERLAY_BUILD_BIN_PATH}")"
|
|
echo "# Leave it unset to build into the next run's evidence directory."
|
|
if [[ ${#missing_required_envs[@]} -gt 0 ]]; then
|
|
local joined_missing_envs
|
|
joined_missing_envs="$(IFS=','; echo "${missing_required_envs[*]}")"
|
|
echo "# missing_required_envs=${joined_missing_envs}"
|
|
fi
|
|
echo "# Recommended preflight:"
|
|
echo "# OVERLAY_CHECK_ONLY=1 scripts/verify-wireguard-over-vless-closure.sh"
|
|
echo "# Recommended full run:"
|
|
echo "# scripts/verify-wireguard-over-vless-closure.sh"
|
|
} > "${OVERLAY_EVIDENCE_DIR}/rerun.env"
|
|
{
|
|
command -v go >/dev/null 2>&1 && go version 2>/dev/null || true
|
|
command -v ansible-playbook >/dev/null 2>&1 && ansible-playbook --version 2>/dev/null | head -n 1 || true
|
|
command -v wg >/dev/null 2>&1 && wg --version 2>/dev/null || true
|
|
command -v wg-quick >/dev/null 2>&1 && echo "wg-quick $(command -v wg-quick)" || true
|
|
command -v xray >/dev/null 2>&1 && xray version 2>/dev/null | head -n 1 || true
|
|
if [[ -n "${OVERLAYCTL_BIN:-}" ]]; then
|
|
echo "overlayctl ${OVERLAYCTL_BIN}"
|
|
overlayctl_sha256="$(sha256_file "${OVERLAYCTL_BIN}" 2>/dev/null || true)"
|
|
if [[ -n "${overlayctl_sha256}" ]]; then
|
|
echo "overlayctl-sha256 ${overlayctl_sha256}"
|
|
fi
|
|
"${OVERLAYCTL_BIN}" --help 2>/dev/null | head -n 1 || true
|
|
else
|
|
echo "overlayctl go-run ${ACCOUNTS_REPO}/cmd/overlayctl"
|
|
fi
|
|
} > "${OVERLAY_EVIDENCE_DIR}/tool-versions.txt"
|
|
git -C "${ROOT_DIR}" status --short --branch > "${OVERLAY_EVIDENCE_DIR}/playbooks-git-status.txt" 2>/dev/null || true
|
|
git -C "${ACCOUNTS_REPO}" status --short --branch > "${OVERLAY_EVIDENCE_DIR}/accounts-git-status.txt" 2>/dev/null || true
|
|
if [[ -f "${OVERLAY_STATE_FILE}" ]]; then
|
|
python3 - "${OVERLAY_STATE_FILE}" "${OVERLAY_EVIDENCE_DIR}/state-redacted.json" <<'PY'
|
|
import json, sys
|
|
data = json.load(open(sys.argv[1]))
|
|
for key in ("token", "wireguard_private_key"):
|
|
if key in data:
|
|
data[key] = "<redacted>"
|
|
print(json.dumps(data, indent=2, sort_keys=True), file=open(sys.argv[2], "w"))
|
|
PY
|
|
fi
|
|
if [[ -f "${OVERLAY_CONFIG_FILE}" ]]; then
|
|
python3 - "${OVERLAY_CONFIG_FILE}" "${OVERLAY_EVIDENCE_DIR}/config-redacted.json" <<'PY'
|
|
import json, sys
|
|
data = json.load(open(sys.argv[1]))
|
|
transport = data.get("transport")
|
|
if isinstance(transport, dict) and "uuid" in transport:
|
|
transport["uuid"] = "<redacted>"
|
|
print(json.dumps(data, indent=2, sort_keys=True), file=open(sys.argv[2], "w"))
|
|
PY
|
|
fi
|
|
}
|
|
|
|
check_local_tools() {
|
|
missing_required_tools=()
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
missing_required_tools+=("python3")
|
|
fi
|
|
if [[ -n "${OVERLAYCTL_BIN:-}" ]]; then
|
|
if [[ ! -x "${OVERLAYCTL_BIN}" ]]; then
|
|
missing_required_tools+=("OVERLAYCTL_BIN executable:${OVERLAYCTL_BIN}")
|
|
fi
|
|
else
|
|
if ! command -v go >/dev/null 2>&1; then
|
|
missing_required_tools+=("go")
|
|
fi
|
|
fi
|
|
if [[ "${OVERLAY_SKIP_DEPLOY}" != "1" ]]; then
|
|
if ! command -v ansible-playbook >/dev/null 2>&1; then
|
|
missing_required_tools+=("ansible-playbook")
|
|
fi
|
|
fi
|
|
if [[ "${OVERLAY_SKIP_UP}" != "1" ]]; then
|
|
if ! command -v wg >/dev/null 2>&1; then
|
|
missing_required_tools+=("wg")
|
|
fi
|
|
if ! command -v wg-quick >/dev/null 2>&1; then
|
|
missing_required_tools+=("wg-quick")
|
|
fi
|
|
if ! command -v xray >/dev/null 2>&1; then
|
|
missing_required_tools+=("xray")
|
|
fi
|
|
if [[ "${OVERLAY_USE_SUDO}" == "1" ]]; then
|
|
if ! command -v sudo >/dev/null 2>&1; then
|
|
missing_required_tools+=("sudo")
|
|
fi
|
|
fi
|
|
fi
|
|
if [[ ${#missing_required_tools[@]} -gt 0 ]]; then
|
|
local name
|
|
for name in "${missing_required_tools[@]}"; do
|
|
echo "missing required tool: ${name}" >&2
|
|
done
|
|
mark_step "preflight.tools" "failed"
|
|
exit 2
|
|
fi
|
|
mark_step "preflight.tools" "ok"
|
|
}
|
|
|
|
check_required_paths() {
|
|
missing_required_paths=()
|
|
if [[ ! -d "${ACCOUNTS_REPO}" ]]; then
|
|
missing_required_paths+=("accounts_repo:${ACCOUNTS_REPO}")
|
|
fi
|
|
if [[ ! -f "${OVERLAY_GROUP_VARS}" ]]; then
|
|
missing_required_paths+=("overlay_group_vars:${OVERLAY_GROUP_VARS}")
|
|
fi
|
|
}
|
|
|
|
report_missing_required_paths() {
|
|
if [[ ${#missing_required_paths[@]} -gt 0 ]]; then
|
|
local path
|
|
for path in "${missing_required_paths[@]}"; do
|
|
echo "missing required path: ${path}" >&2
|
|
done
|
|
mark_step "preflight.paths" "failed"
|
|
exit 2
|
|
fi
|
|
mark_step "preflight.paths" "ok"
|
|
}
|
|
|
|
build_overlayctl_bin() {
|
|
if [[ "${OVERLAY_BUILD_BIN}" != "1" ]]; then
|
|
return
|
|
fi
|
|
if [[ -n "${OVERLAYCTL_BIN:-}" ]]; then
|
|
echo "using explicit OVERLAYCTL_BIN=${OVERLAYCTL_BIN}; skipping overlayctl build"
|
|
return
|
|
fi
|
|
if ! command -v go >/dev/null 2>&1; then
|
|
missing_required_tools+=("go")
|
|
echo "missing required tool: go" >&2
|
|
mark_step "overlayctl.build" "failed"
|
|
exit 2
|
|
fi
|
|
mkdir -p "$(dirname "${OVERLAY_BUILD_BIN_PATH}")"
|
|
if [[ ! -d "${ACCOUNTS_REPO}/cmd/overlayctl" ]]; then
|
|
missing_required_paths+=("overlayctl_package:${ACCOUNTS_REPO}/cmd/overlayctl")
|
|
echo "missing required path: overlayctl_package:${ACCOUNTS_REPO}/cmd/overlayctl" >&2
|
|
mark_step "overlayctl.build" "failed"
|
|
exit 2
|
|
fi
|
|
echo "building overlayctl from ${ACCOUNTS_REPO} to ${OVERLAY_BUILD_BIN_PATH}"
|
|
(
|
|
cd "${ACCOUNTS_REPO}"
|
|
CGO_ENABLED=0 go build -trimpath -o "${OVERLAY_BUILD_BIN_PATH}" ./cmd/overlayctl
|
|
)
|
|
OVERLAYCTL_BIN="${OVERLAY_BUILD_BIN_PATH}"
|
|
export OVERLAYCTL_BIN
|
|
mark_step "overlayctl.build" "ok"
|
|
}
|
|
|
|
check_required_environment() {
|
|
missing_required_envs=()
|
|
local name
|
|
for name in ACCOUNT_EMAIL ACCOUNT_PASSWORD BRIDGE_AUTH_TOKEN; do
|
|
if [[ -z "${!name:-}" ]]; then
|
|
missing_required_envs+=("${name}")
|
|
fi
|
|
done
|
|
if [[ "${OVERLAY_SKIP_DEPLOY}" != "1" ]]; then
|
|
if [[ -z "${VAULT_SERVER_ROOT_ACCESS_TOKEN:-}" && -z "${VAULT_TOKEN:-}" ]]; then
|
|
missing_required_envs+=("VAULT_SERVER_ROOT_ACCESS_TOKEN or VAULT_TOKEN")
|
|
fi
|
|
if [[ -z "${INTERNAL_SERVICE_TOKEN:-}" ]]; then
|
|
missing_required_envs+=("INTERNAL_SERVICE_TOKEN")
|
|
fi
|
|
fi
|
|
if [[ ${#missing_required_envs[@]} -gt 0 ]]; then
|
|
for name in "${missing_required_envs[@]}"; do
|
|
echo "missing required environment: ${name}" >&2
|
|
done
|
|
mark_step "preflight.environment" "failed"
|
|
exit 2
|
|
fi
|
|
mark_step "preflight.environment" "ok"
|
|
}
|
|
|
|
run_overlayctl() {
|
|
if [[ -n "${OVERLAYCTL_BIN:-}" ]]; then
|
|
"${OVERLAYCTL_BIN}" "$@"
|
|
return
|
|
fi
|
|
(cd "${ACCOUNTS_REPO}" && go run ./cmd/overlayctl "$@")
|
|
}
|
|
|
|
run_overlayctl_root() {
|
|
if [[ "${OVERLAY_USE_SUDO}" == "1" ]]; then
|
|
if [[ -n "${OVERLAYCTL_BIN:-}" ]]; then
|
|
sudo -E env HOME="${HOME}" "${OVERLAYCTL_BIN}" "$@"
|
|
return
|
|
fi
|
|
sudo -E env HOME="${HOME}" bash -c 'cd "$1" && shift && "$@"' bash "${ACCOUNTS_REPO}" go run ./cmd/overlayctl "$@"
|
|
return
|
|
fi
|
|
run_overlayctl "$@"
|
|
}
|
|
|
|
cleanup_on_exit() {
|
|
local status=$?
|
|
if [[ ${status} -ne 0 && -n "${active_step}" ]]; then
|
|
mark_step "${active_step}" "failed"
|
|
active_step=""
|
|
fi
|
|
write_evidence "${status}"
|
|
if [[ ${status} -eq 0 && "${OVERLAY_CHECK_ONLY}" != "1" ]]; then
|
|
local evidence_checker="${ROOT_DIR}/scripts/check-wireguard-over-vless-closure-evidence.sh"
|
|
if [[ -x "${evidence_checker}" ]]; then
|
|
echo "closure evidence check pending" > "${OVERLAY_EVIDENCE_DIR}/closure-check.log"
|
|
if ! "${evidence_checker}" "${OVERLAY_EVIDENCE_DIR}" > "${OVERLAY_EVIDENCE_DIR}/closure-check.log" 2>&1; then
|
|
cat "${OVERLAY_EVIDENCE_DIR}/closure-check.log" >&2
|
|
status=1
|
|
fi
|
|
else
|
|
echo "missing evidence checker: ${evidence_checker}" | tee "${OVERLAY_EVIDENCE_DIR}/closure-check.log" >&2
|
|
status=1
|
|
fi
|
|
fi
|
|
local evidence_message="closure evidence: ${OVERLAY_EVIDENCE_DIR}"
|
|
echo "${evidence_message}" >&2
|
|
if [[ ${status} -ne 0 && "${OVERLAY_TEARDOWN_ON_ERROR}" == "1" && "${overlay_runtime_started}" == "1" ]]; then
|
|
echo "closure failed after local runtime start; running overlayctl down because OVERLAY_TEARDOWN_ON_ERROR=1" >&2
|
|
set +e
|
|
run_overlayctl_root down
|
|
fi
|
|
if [[ -n "${run_log_tee_pid:-}" ]]; then
|
|
exec 1>&3 2>&4
|
|
exec 3>&- 4>&-
|
|
wait "${run_log_tee_pid}" 2>/dev/null || true
|
|
fi
|
|
exit "${status}"
|
|
}
|
|
|
|
trap cleanup_on_exit EXIT
|
|
|
|
state_value() {
|
|
local key="$1"
|
|
if [[ ! -f "${OVERLAY_STATE_FILE}" ]]; then
|
|
return 0
|
|
fi
|
|
python3 -c 'import json, sys; print(str(json.load(open(sys.argv[1])).get(sys.argv[2], "")).strip())' "${OVERLAY_STATE_FILE}" "${key}"
|
|
}
|
|
|
|
attach_args=()
|
|
IFS=',' read -r -a attach_hosts <<< "${OVERLAY_ATTACH_TO}"
|
|
for host in "${attach_hosts[@]}"; do
|
|
host="${host#"${host%%[![:space:]]*}"}"
|
|
host="${host%"${host##*[![:space:]]}"}"
|
|
if [[ -n "${host}" ]]; then
|
|
attach_args+=(--attach-to "${host}")
|
|
fi
|
|
done
|
|
|
|
register_args=()
|
|
if [[ -n "${OVERLAY_REGISTER_ARGS}" ]]; then
|
|
read -r -a register_args <<< "${OVERLAY_REGISTER_ARGS}"
|
|
fi
|
|
ansible_syntax_args=()
|
|
if [[ -n "${OVERLAY_ANSIBLE_SYNTAX_ARGS}" ]]; then
|
|
read -r -a ansible_syntax_args <<< "${OVERLAY_ANSIBLE_SYNTAX_ARGS}"
|
|
fi
|
|
ansible_deploy_args=()
|
|
if [[ -n "${OVERLAY_ANSIBLE_DEPLOY_ARGS}" ]]; then
|
|
read -r -a ansible_deploy_args <<< "${OVERLAY_ANSIBLE_DEPLOY_ARGS}"
|
|
fi
|
|
|
|
check_required_paths
|
|
load_env_files
|
|
report_missing_required_paths
|
|
build_overlayctl_bin
|
|
if [[ "${OVERLAY_SKIP_LOCAL_TOOL_CHECK}" != "1" ]]; then
|
|
check_local_tools
|
|
else
|
|
mark_step "preflight.tools" "skipped"
|
|
fi
|
|
|
|
check_required_environment
|
|
if [[ "${OVERLAY_CHECK_ONLY}" == "1" ]]; then
|
|
mark_step "check_only" "ok"
|
|
echo "closure prerequisites satisfied; exiting because OVERLAY_CHECK_ONLY=1"
|
|
exit 0
|
|
fi
|
|
|
|
echo "[1/10] login to ${ACCOUNTS_SERVICE_URL}"
|
|
begin_step "01.login"
|
|
run_overlayctl login \
|
|
--server "${ACCOUNTS_SERVICE_URL}" \
|
|
--email "${ACCOUNT_EMAIL}" \
|
|
--password "${ACCOUNT_PASSWORD}"
|
|
complete_step "01.login" "ok"
|
|
|
|
echo "[2/10] register local WireGuard device"
|
|
begin_step "02.register_device"
|
|
if [[ ${#register_args[@]} -eq 0 ]]; then
|
|
existing_public_key="$(state_value wireguard_public_key)"
|
|
existing_private_key="$(state_value wireguard_private_key)"
|
|
if [[ -n "${existing_public_key}" && -n "${existing_private_key}" ]]; then
|
|
echo "Reusing WireGuard keypair from ${OVERLAY_STATE_FILE}"
|
|
register_args+=(--public-key "${existing_public_key}" --private-key "${existing_private_key}")
|
|
else
|
|
echo "No existing WireGuard keypair found in ${OVERLAY_STATE_FILE}; generating a new one"
|
|
register_args+=(--generate-key)
|
|
fi
|
|
fi
|
|
run_overlayctl register-device "${register_args[@]}"
|
|
complete_step "02.register_device" "ok"
|
|
|
|
echo "[3/10] sync and render initial overlay config"
|
|
begin_step "03.initial_sync_render_preflight"
|
|
run_overlayctl sync-config --node-id "${OVERLAY_NODE_ID}"
|
|
run_overlayctl render
|
|
run_overlayctl preflight
|
|
complete_step "03.initial_sync_render_preflight" "ok"
|
|
|
|
echo "[4/10] project local device into playbooks client peers"
|
|
begin_step "04.apply_playbooks_client"
|
|
run_overlayctl apply-playbooks-client \
|
|
--group-vars "${OVERLAY_GROUP_VARS}" \
|
|
"${attach_args[@]}"
|
|
complete_step "04.apply_playbooks_client" "ok"
|
|
|
|
if [[ "${OVERLAY_SKIP_DEPLOY}" != "1" ]]; then
|
|
echo "[5/10] deploy WireGuard-over-VLESS gateway path"
|
|
begin_step "05.deploy_gateway"
|
|
(
|
|
cd "${ROOT_DIR}"
|
|
ANSIBLE_CONFIG=ansible.cfg ansible-playbook -i inventory.ini vpn-wireguard-over-vless.yml --syntax-check "${ansible_syntax_args[@]}"
|
|
ANSIBLE_CONFIG=ansible.cfg ansible-playbook -i inventory.ini vpn-wireguard-over-vless.yml "${ansible_deploy_args[@]}"
|
|
)
|
|
complete_step "05.deploy_gateway" "ok"
|
|
else
|
|
echo "[5/10] skipped deploy because OVERLAY_SKIP_DEPLOY=1"
|
|
mark_step "05.deploy_gateway" "skipped"
|
|
fi
|
|
|
|
echo "[6/10] refresh config after gateway heartbeat"
|
|
begin_step "06.refresh_sync_render_preflight"
|
|
run_overlayctl sync-config --node-id "${OVERLAY_NODE_ID}"
|
|
run_overlayctl render
|
|
run_overlayctl preflight
|
|
complete_step "06.refresh_sync_render_preflight" "ok"
|
|
|
|
if [[ "${OVERLAY_SKIP_UP}" != "1" ]]; then
|
|
echo "[7/10] start local Xray and WireGuard"
|
|
begin_step "07.local_runtime_up"
|
|
run_overlayctl_root up
|
|
overlay_runtime_started=1
|
|
run_overlayctl_root status
|
|
complete_step "07.local_runtime_up" "ok"
|
|
|
|
echo "[8/10] verify private bridge connectivity"
|
|
begin_step "08.connectivity"
|
|
run_overlayctl check-connectivity --bearer "${BRIDGE_AUTH_TOKEN}"
|
|
complete_step "08.connectivity" "ok"
|
|
|
|
echo "[9/10] acknowledge applied overlay config"
|
|
begin_step "09.ack_config"
|
|
run_overlayctl ack-config
|
|
complete_step "09.ack_config" "ok"
|
|
|
|
if [[ "${OVERLAY_TEARDOWN}" == "1" ]]; then
|
|
echo "[10/10] tear down local overlay runtime"
|
|
begin_step "10.teardown"
|
|
run_overlayctl_root down
|
|
overlay_runtime_started=0
|
|
complete_step "10.teardown" "ok"
|
|
else
|
|
echo "[10/10] leaving local overlay runtime up; set OVERLAY_TEARDOWN=1 to tear it down"
|
|
mark_step "10.teardown" "skipped_runtime_left_up"
|
|
fi
|
|
else
|
|
echo "[7/10] skipped local runtime start because OVERLAY_SKIP_UP=1"
|
|
echo "[8/10] skipped connectivity check because OVERLAY_SKIP_UP=1"
|
|
echo "[9/10] skipped config ack because OVERLAY_SKIP_UP=1"
|
|
echo "[10/10] skipped teardown because OVERLAY_SKIP_UP=1"
|
|
mark_step "07.local_runtime_up" "skipped"
|
|
mark_step "08.connectivity" "skipped"
|
|
mark_step "09.ack_config" "skipped"
|
|
mark_step "10.teardown" "skipped"
|
|
fi
|