add scripts

This commit is contained in:
xiuting.xu
2026-07-14 14:44:40 +08:00
parent e2bdcd3820
commit 490b79f22c
47 changed files with 1569 additions and 514 deletions

203
scripts/00_0_install.sh Normal file
View File

@@ -0,0 +1,203 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
IMAGE_DIR="${ROOT_DIR}/images"
SERVER_ENV_FILE="${ROOT_DIR}/deploy/server/.env"
CONSOLE_ENV_FILE="${ROOT_DIR}/deploy/console/.env"
SERVER_TAR="${RPKI_RTR_SERVER_IMAGE_TAR:-}"
CLIENT_TAR="${RPKI_RTR_CLIENT_IMAGE_TAR:-}"
CONSOLE_TAR="${RPKI_RTR_CONSOLE_IMAGE_TAR:-}"
usage() {
cat <<'EOF'
Usage: scripts/00_0_install.sh [options]
Load offline Docker image tarballs from ./images, then run scripts/00_1_pre_check.sh.
Options:
--server-tar FILE Server image tarball. Env: RPKI_RTR_SERVER_IMAGE_TAR
--client-tar FILE Debug client image tarball. Env: RPKI_RTR_CLIENT_IMAGE_TAR
--console-tar FILE Console image tarball. Env: RPKI_RTR_CONSOLE_IMAGE_TAR
-h, --help Show this help.
Default tarballs under ./images:
server: rpki-rtr-*.tar, excluding rpki-rtr-debug-client-*.tar
client: rpki-rtr-debug-client-*.tar
console: rpki-console-ui-*.tar
EOF
}
log() {
printf '[00_install] %s\n' "$*" >&2
}
stage() {
printf '\n====== %s ======\n' "$*" >&2
}
die() {
printf '[00_install] ERROR: %s\n' "$*" >&2
exit 1
}
set_env_var() {
local env_file="$1"
local key="$2"
local value="$3"
local tmp_file
[ -f "${env_file}" ] || die "env file not found: ${env_file}"
tmp_file="${env_file}.tmp.$$"
awk -v key="${key}" -v value="${value}" '
BEGIN { done = 0 }
$0 ~ "^[[:space:]]*#?[[:space:]]*" key "=" {
if (!done) {
print key "=" value
done = 1
}
next
}
{ print }
END {
if (!done) {
print key "=" value
}
}
' "${env_file}" >"${tmp_file}"
mv "${tmp_file}" "${env_file}"
}
while [ "$#" -gt 0 ]; do
case "$1" in
--server-tar)
[ "$#" -ge 2 ] || die "--server-tar requires a value"
SERVER_TAR="$2"
shift 2
;;
--client-tar)
[ "$#" -ge 2 ] || die "--client-tar requires a value"
CLIENT_TAR="$2"
shift 2
;;
--console-tar)
[ "$#" -ge 2 ] || die "--console-tar requires a value"
CONSOLE_TAR="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
resolve_tar() {
local path="$1"
if [ -f "${path}" ]; then
cd "$(dirname "${path}")" && printf '%s/%s\n' "$(pwd -P)" "$(basename "${path}")"
return 0
fi
if [ -f "${ROOT_DIR}/${path}" ]; then
cd "$(dirname "${ROOT_DIR}/${path}")" && printf '%s/%s\n' "$(pwd -P)" "$(basename "${path}")"
return 0
fi
return 1
}
load_image() {
local label="$1"
local tar_path="$2"
local output image
[ -f "${tar_path}" ] || die "${label} image tarball not found: ${tar_path}"
output="$(docker load -i "${tar_path}" 2>&1)" || {
printf '%s\n' "${output}" >&2
die "failed to load ${label} image from ${tar_path}"
}
printf '%s\n' "${output}" >&2
image="$(printf '%s\n' "${output}" | sed -n 's/^Loaded image: //p' | tail -n 1)"
[ -n "${image}" ] || die "cannot determine ${label} image name from docker load output"
printf '%s\n' "${image}"
}
find_single_tar() {
local label="$1"
local include_pattern="$2"
local exclude_pattern="${3:-}"
local matches=()
local file
while IFS= read -r -d '' file; do
matches+=("${file}")
done < <(
if [ -n "${exclude_pattern}" ]; then
find "${IMAGE_DIR}" -maxdepth 1 -type f -name "${include_pattern}" ! -name "${exclude_pattern}" -print0 | sort -z
else
find "${IMAGE_DIR}" -maxdepth 1 -type f -name "${include_pattern}" -print0 | sort -z
fi
)
case "${#matches[@]}" in
0)
die "no ${label} image tarball found under ${IMAGE_DIR} matching ${include_pattern}"
;;
1)
printf '%s\n' "${matches[0]}"
;;
*)
printf '[00_install] ERROR: multiple %s image tarballs found:\n' "${label}" >&2
printf ' %s\n' "${matches[@]}" >&2
die "please specify --${label}-tar or RPKI_RTR_${label^^}_IMAGE_TAR"
;;
esac
}
stage "Checking Docker"
command -v docker >/dev/null 2>&1 || die "docker command not found"
[ -d "${IMAGE_DIR}" ] || die "image directory not found: ${IMAGE_DIR}"
[ -f "${SERVER_ENV_FILE}" ] || die "server env file not found: ${SERVER_ENV_FILE}"
if [ -n "${SERVER_TAR}" ]; then
SERVER_TAR="$(resolve_tar "${SERVER_TAR}")" || die "server image tarball not found: ${SERVER_TAR}"
else
SERVER_TAR="$(find_single_tar server 'rpki-rtr-*.tar' 'rpki-rtr-debug-client-*.tar')"
fi
if [ -n "${CLIENT_TAR}" ]; then
CLIENT_TAR="$(resolve_tar "${CLIENT_TAR}")" || die "client image tarball not found: ${CLIENT_TAR}"
else
CLIENT_TAR="$(find_single_tar client 'rpki-rtr-debug-client-*.tar')"
fi
if [ -n "${CONSOLE_TAR}" ]; then
CONSOLE_TAR="$(resolve_tar "${CONSOLE_TAR}")" || die "console image tarball not found: ${CONSOLE_TAR}"
else
CONSOLE_TAR="$(find_single_tar console 'rpki-console-ui-*.tar')"
fi
stage "Loading Docker Images"
SERVER_IMAGE="$(load_image server "${SERVER_TAR}")"
CLIENT_IMAGE="$(load_image client "${CLIENT_TAR}")"
CONSOLE_IMAGE="$(load_image console "${CONSOLE_TAR}")"
stage "Updating Deploy Env"
set_env_var "${SERVER_ENV_FILE}" RPKI_RTR_SERVER_IMAGE "${SERVER_IMAGE}"
set_env_var "${CONSOLE_ENV_FILE}" RPKI_CONSOLE_UI_IMAGE "${CONSOLE_IMAGE}"
stage "Running Pre-Check"
RPKI_RTR_SERVER_IMAGE="${SERVER_IMAGE}" \
RPKI_RTR_CLIENT_IMAGE="${CLIENT_IMAGE}" \
bash "${SCRIPT_DIR}/00_1_pre_check.sh" || die "pre-check failed"
stage "Install Finished"

155
scripts/00_1_pre_check.sh Normal file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
PRECHECK_WORK_DIR="${PRECHECK_WORK_DIR:-${ROOT_DIR}/.precheck}"
PRECHECK_ENV_FILE="${PRECHECK_ENV_FILE:-${PRECHECK_WORK_DIR}/precheck.env}"
PRECHECK_WAIT_SECS="${PRECHECK_WAIT_SECS:-60}"
KEEP_PRECHECK="${KEEP_PRECHECK:-0}"
log() {
printf '[00_1_pre_check] %s\n' "$*"
}
stage() {
printf '\n====== %s ======\n' "$*"
}
die() {
printf '[00_1_pre_check] ERROR: %s\n' "$*" >&2
exit 1
}
command -v docker >/dev/null 2>&1 || die "docker command not found"
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
die "docker compose is not available"
fi
stage "Preparing Pre-Check Data"
bash "${SCRIPT_DIR}/00_2_pre_check_prepare.sh" || die "prepare step failed"
set -a
# shellcheck disable=SC1090
. "${PRECHECK_ENV_FILE}"
set +a
ADMIN_PORT="${RPKI_RTR_ADMIN_ADDR##*:}"
ADMIN_TOKEN="${RPKI_RTR_ADMIN_TOKEN}"
server_compose() {
"${COMPOSE_BIN[@]}" --env-file "${PRECHECK_ENV_FILE}" -p "${PRECHECK_PROJECT}" -f "${PRECHECK_SERVER_COMPOSE_FILE}" "$@"
}
client_compose() {
"${COMPOSE_BIN[@]}" --env-file "${PRECHECK_ENV_FILE}" -p "${PRECHECK_PROJECT}" -f "${PRECHECK_CLIENT_COMPOSE_FILE}" "$@"
}
cleanup() {
KEEP_PRECHECK="${KEEP_PRECHECK}" PRECHECK_WORK_DIR="${PRECHECK_WORK_DIR}" PRECHECK_ENV_FILE="${PRECHECK_ENV_FILE}" \
bash "${SCRIPT_DIR}/00_3_pre_check_cleanup.sh"
}
trap cleanup EXIT
http_get() {
local host="$1"
local port="$2"
local path="$3"
local token="$4"
exec 3<>"/dev/tcp/${host}/${port}" || return 1
printf 'GET %s HTTP/1.1\r\nHost: %s:%s\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n' \
"${path}" "${host}" "${port}" "${token}" >&3
cat <&3
exec 3>&-
}
wait_for_admin_health() {
local deadline=$((SECONDS + PRECHECK_WAIT_SECS))
local response
log "waiting for server health"
while [ "${SECONDS}" -lt "${deadline}" ]; do
response="$(http_get 127.0.0.1 "${ADMIN_PORT}" /admin/rtr/health "${ADMIN_TOKEN}" 2>/dev/null || true)"
if printf '%s' "${response}" | grep -q '"status"[[:space:]]*:[[:space:]]*"ok"'; then
log "admin health check passed"
return 0
fi
if [ "$(server_compose ps -q rpki-rtr | wc -l | tr -d ' ')" = "0" ]; then
server_compose logs rpki-rtr || true
die "server container is not running"
fi
sleep 1
done
server_compose logs rpki-rtr || true
die "admin health check did not pass within ${PRECHECK_WAIT_SECS}s"
}
wait_for_client_eod() {
local deadline=$((SECONDS + PRECHECK_WAIT_SECS))
local log_file
log "waiting for client response"
while [ "${SECONDS}" -lt "${deadline}" ]; do
for log_file in "${PRECHECK_WORK_DIR}"/logs/client/*.stdout.log; do
[ -f "${log_file}" ] || continue
if grep -q 'EndOfData:' "${log_file}" && grep -q 'summary[[:space:]]*:' "${log_file}"; then
log "client received EndOfData and payload summary"
return 0
fi
if grep -qi 'Error Report' "${log_file}"; then
cat "${log_file}" >&2
die "client received RTR Error Report"
fi
done
sleep 1
done
find "${PRECHECK_WORK_DIR}/logs/client" -type f -maxdepth 1 -print -exec cat {} \; >&2 || true
die "client did not receive EndOfData and summary within ${PRECHECK_WAIT_SECS}s"
}
assert_no_error_logs() {
local bad
log "checking logs"
bad="$(find "${PRECHECK_WORK_DIR}/logs" -type f -name '*.log' -print0 \
| xargs -0 grep -Ein 'panic|failed|RTR admin config server exited|Error Report' 2>/dev/null || true)"
if [ -n "${bad}" ]; then
printf '%s\n' "${bad}" >&2
die "error pattern found in pre-check logs"
fi
}
assert_report_written() {
local deadline=$((SECONDS + PRECHECK_WAIT_SECS))
log "waiting for source report"
while [ "${SECONDS}" -lt "${deadline}" ]; do
if find "${PRECHECK_WORK_DIR}/report" -type f -name 'rtr-source-*.json' | grep -q .; then
log "source report written"
return 0
fi
sleep 1
done
die "server did not write rtr-source report within ${PRECHECK_WAIT_SECS}s"
}
stage "Starting RTR Server"
server_compose up -d --no-build rpki-rtr
wait_for_admin_health
assert_report_written
stage "Starting Debug Client"
client_compose up -d --no-build rtr-debug-client
wait_for_client_eod
assert_no_error_logs
stage "Pre-Check Passed"

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
OUT_PATH="${1:-}"
usage() {
cat <<'EOF'
Usage: scripts/00_2_generate_ccr.sh OUT.ccr
Generate a small deterministic CCR snapshot for pre-check.
This script writes a fixed base64-encoded CCR fixture and does not require Python.
The generated snapshot contains:
- VRP: 10.0.0.0/24 maxLen 24 AS65001
- VRP: 10.0.1.0/24 maxLen 24 AS65003
- VRP: 2001:db8:1::/48 maxLen 48 AS65002
- ASPA: customer AS65010 providers AS65011, AS65012, AS65013
- ASPA: customer AS65020 provider AS65021
EOF
}
die() {
printf '[00_2_generate_ccr] ERROR: %s\n' "$*" >&2
exit 1
}
if [ "${OUT_PATH}" = "-h" ] || [ "${OUT_PATH}" = "--help" ]; then
usage
exit 0
fi
[ -n "${OUT_PATH}" ] || die "missing OUT.ccr"
mkdir -p "$(dirname "${OUT_PATH}")"
decode_base64() {
if command -v base64 >/dev/null 2>&1; then
base64 -d
return
fi
if command -v openssl >/dev/null 2>&1; then
openssl base64 -d
return
fi
die "base64 or openssl command is required to write CCR fixture"
}
cat <<'EOF' | decode_base64 >"${OUT_PATH}"
MIIBAwYLKoZIhvcNAQkQATaggfMwgfACAQAwDQYJYIZIAWUDBAIBBQAYDzIwMjYwNDAzMDAwMjAx
WqJ9MHswVzAaAgMA/ekwEzARBAIAATALMAkDBAAKAAACARgwGgIDAP3rMBMwEQQCAAEwCzAJAwQA
CgABAgEYMB0CAwD96jAWMBQEAgACMA4wDAMHACABDbgAAQIBMAQgsUgCHfy5AJcFp5RymOD0HuGO
wfhoWpll90ugYECGQPSjTDBKMCYwFgIDAP3yMA8CAwD98wIDAP30AgMA/fUwDAIDAP38MAUCAwD9
/QQgRPxVPOOUsUuuV4tZ8BG5PHNtKiaEr23Ird+yNxlWJ+w=
EOF

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
PRECHECK_WORK_DIR="${PRECHECK_WORK_DIR:-${ROOT_DIR}/.precheck}"
PRECHECK_PROJECT="${PRECHECK_PROJECT:-rpki-precheck}"
PRECHECK_ADMIN_TOKEN="${PRECHECK_ADMIN_TOKEN:-rpki-precheck-token}"
PRECHECK_PROTOCOL_VERSION="${PRECHECK_PROTOCOL_VERSION:-2}"
PRECHECK_SERVER_IMAGE="${PRECHECK_SERVER_IMAGE:-${RPKI_RTR_SERVER_IMAGE:-rpki-rtr-arm64:v1.2}}"
PRECHECK_CLIENT_IMAGE="${PRECHECK_CLIENT_IMAGE:-${RPKI_RTR_CLIENT_IMAGE:-rpki-rtr-debug-client-arm64:v1.2}}"
log() {
printf '[00_1_pre_check_prepare] %s\n' "$*"
}
die() {
printf '[00_1_pre_check_prepare] ERROR: %s\n' "$*" >&2
exit 1
}
abs_dir() {
local path="$1"
mkdir -p "${path}"
cd "${path}" && pwd -P
}
[ -f "${SCRIPT_DIR}/00_2_generate_ccr.sh" ] || die "missing ${SCRIPT_DIR}/00_2_generate_ccr.sh"
rm -rf "${PRECHECK_WORK_DIR}"
mkdir -p \
"${PRECHECK_WORK_DIR}/ccr" \
"${PRECHECK_WORK_DIR}/slurm" \
"${PRECHECK_WORK_DIR}/db" \
"${PRECHECK_WORK_DIR}/logs/server" \
"${PRECHECK_WORK_DIR}/logs/client" \
"${PRECHECK_WORK_DIR}/report"
bash "${SCRIPT_DIR}/00_2_generate_ccr.sh" \
"${PRECHECK_WORK_DIR}/ccr/20260403T000201Z-precheck.ccr"
cat >"${PRECHECK_WORK_DIR}/slurm/precheck.slurm" <<'EOF'
{
"slurmVersion": 2,
"validationOutputFilters": {
"prefixFilters": [],
"bgpsecFilters": [],
"aspaFilters": []
},
"locallyAddedAssertions": {
"prefixAssertions": [
{
"prefix": "203.0.113.0/24",
"maxPrefixLength": 24,
"asn": 64496,
"comment": "pre-check local assertion"
}
],
"bgpsecAssertions": [],
"aspaAssertions": []
}
}
EOF
PRECHECK_WORK_DIR_ABS="$(abs_dir "${PRECHECK_WORK_DIR}")"
cat >"${PRECHECK_WORK_DIR}/precheck.env" <<EOF
PRECHECK_WORK_DIR=${PRECHECK_WORK_DIR_ABS}
PRECHECK_PROJECT=${PRECHECK_PROJECT}
RPKI_RTR_SERVER_IMAGE=${PRECHECK_SERVER_IMAGE}
RPKI_RTR_CLIENT_IMAGE=${PRECHECK_CLIENT_IMAGE}
PRECHECK_SERVER_COMPOSE_FILE=${ROOT_DIR}/deploy/server/docker-compose.tcp.yml
PRECHECK_CLIENT_COMPOSE_FILE=${ROOT_DIR}/deploy/client/docker-compose.tcp.yml
RPKI_RTR_CCR_HOST_DIR=${PRECHECK_WORK_DIR_ABS}/ccr
RPKI_RTR_SLURM_HOST_DIR=${PRECHECK_WORK_DIR_ABS}/slurm
RPKI_RTR_DB_HOST_DIR=${PRECHECK_WORK_DIR_ABS}/db
RPKI_RTR_LOG_HOST_DIR=${PRECHECK_WORK_DIR_ABS}/logs/server
RPKI_RTR_CLIENT_LOG_HOST_DIR=${PRECHECK_WORK_DIR_ABS}/logs/client
RPKI_RTR_REPORT_HOST_DIR=${PRECHECK_WORK_DIR_ABS}/report
RPKI_RTR_CCR_DIR=/app/data
RPKI_RTR_SLURM_DIR=/app/slurm
RPKI_RTR_DB_PATH=/app/rtr-db
RPKI_RTR_REPORT_DIR=/app/report
RPKI_RTR_ADMIN_ADDR=0.0.0.0:8323
RPKI_RTR_ADMIN_TOKEN=${PRECHECK_ADMIN_TOKEN}
RPKI_RTR_RUNTIME_REPORT_INTERVAL_SECS=5
RPKI_RTR_REPORT_HISTORY_LIMIT=3
RPKI_RTR_TIMEZONE=Asia/Shanghai
RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS=30
RPKI_RTR_MAX_DELTA=10
RPKI_RTR_MAX_CONNECTIONS=32
RPKI_RTR_MAX_CONCURRENT_HANDSHAKES=8
RPKI_RTR_TCP_SERVER_ADDR=rpki-rtr:323
RPKI_RTR_PROTOCOL_VERSION=${PRECHECK_PROTOCOL_VERSION}
RUST_LOG=info
EOF

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
PRECHECK_WORK_DIR="${PRECHECK_WORK_DIR:-${ROOT_DIR}/.precheck}"
ENV_FILE="${PRECHECK_ENV_FILE:-${PRECHECK_WORK_DIR}/precheck.env}"
KEEP_PRECHECK="${KEEP_PRECHECK:-0}"
if [ -f "${ENV_FILE}" ]; then
set -a
# shellcheck disable=SC1090
. "${ENV_FILE}"
set +a
fi
PRECHECK_PROJECT="${PRECHECK_PROJECT:-rpki-precheck}"
PRECHECK_SERVER_COMPOSE_FILE="${PRECHECK_SERVER_COMPOSE_FILE:-${ROOT_DIR}/deploy/server/docker-compose.tcp.yml}"
PRECHECK_CLIENT_COMPOSE_FILE="${PRECHECK_CLIENT_COMPOSE_FILE:-${ROOT_DIR}/deploy/client/docker-compose.tcp.yml}"
COMPOSE_BIN=()
log() {
printf '[00_1_pre_check_cleanup] %s\n' "$*"
}
compose_run() {
local compose_file="$1"
shift
if [ "${#COMPOSE_BIN[@]}" -eq 0 ]; then
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
return 0
fi
fi
"${COMPOSE_BIN[@]}" --env-file "${ENV_FILE}" -p "${PRECHECK_PROJECT}" -f "${compose_file}" "$@"
}
if command -v docker >/dev/null 2>&1; then
log "stopping pre-check client compose stack"
[ -f "${PRECHECK_CLIENT_COMPOSE_FILE}" ] && compose_run "${PRECHECK_CLIENT_COMPOSE_FILE}" down --remove-orphans >/dev/null 2>&1 || true
log "stopping pre-check server compose stack"
[ -f "${PRECHECK_SERVER_COMPOSE_FILE}" ] && compose_run "${PRECHECK_SERVER_COMPOSE_FILE}" down --remove-orphans >/dev/null 2>&1 || true
fi
if [ "${KEEP_PRECHECK}" = "1" ]; then
log "KEEP_PRECHECK=1, preserving ${PRECHECK_WORK_DIR}"
else
rm -rf "${PRECHECK_WORK_DIR}"
log "removed ${PRECHECK_WORK_DIR}"
fi

159
scripts/01_start.sh Normal file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
COMMON_ENV_FILE="${ROOT_DIR}/environment"
SERVER_ENV_FILE="${ROOT_DIR}/deploy/server/.env"
CONSOLE_ENV_FILE="${ROOT_DIR}/deploy/console/.env"
CONSOLE_COMPOSE_FILE="${ROOT_DIR}/deploy/console/docker-compose.yml"
MODE="${RPKI_RTR_SERVER_MODE:-tcp}"
COMPOSE_PROJECT="${RPKI_RTR_COMPOSE_PROJECT:-rpki-rtr}"
CONSOLE_COMPOSE_PROJECT="${RPKI_CONSOLE_COMPOSE_PROJECT:-rpki-console}"
usage() {
cat <<'EOF'
Usage: scripts/01_start.sh [options]
Read root ./environment, update deploy/server/.env, then start the RTR server and console.
Options:
--env FILE Common env file. Default: ./environment
--mode MODE Server mode: tcp|tls|ssh|base. Default: tcp
-h, --help Show this help.
EOF
}
log() {
printf '[01_start] %s\n' "$*" >&2
}
stage() {
printf '\n====== %s ======\n' "$*" >&2
}
die() {
printf '[01_start] ERROR: %s\n' "$*" >&2
exit 1
}
set_env_var() {
local env_file="$1"
local key="$2"
local value="$3"
local tmp_file
tmp_file="${env_file}.tmp.$$"
awk -v key="${key}" -v value="${value}" '
BEGIN { done = 0 }
$0 ~ "^[[:space:]]*#?[[:space:]]*" key "=" {
if (!done) {
print key "=" value
done = 1
}
next
}
{ print }
END {
if (!done) {
print key "=" value
}
}
' "${env_file}" >"${tmp_file}"
mv "${tmp_file}" "${env_file}"
}
compose_file_for_mode() {
case "$1" in
tcp) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.tcp.yml" ;;
tls) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.tls.yml" ;;
ssh) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.ssh.yml" ;;
base) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.yml" ;;
*) die "unsupported mode '$1', expected tcp|tls|ssh|base" ;;
esac
}
update_env_from_common() {
local target_env_file="$1"
local updated_count=0
local line key value
while IFS= read -r line || [ -n "${line}" ]; do
case "${line}" in
''|\#*) continue ;;
*=*)
key="${line%%=*}"
value="${line#*=}"
set_env_var "${target_env_file}" "${key}" "${value}"
updated_count=$((updated_count + 1))
;;
esac
done <"${COMMON_ENV_FILE}"
printf '%s\n' "${updated_count}"
}
while [ "$#" -gt 0 ]; do
case "$1" in
--env)
[ "$#" -ge 2 ] || die "--env requires a value"
COMMON_ENV_FILE="$2"
shift 2
;;
--mode)
[ "$#" -ge 2 ] || die "--mode requires a value"
MODE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
SERVER_COMPOSE_FILE="$(compose_file_for_mode "${MODE}")"
stage "Checking Docker"
command -v docker >/dev/null 2>&1 || die "docker command not found"
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
die "docker compose is not available"
fi
[ -f "${SERVER_ENV_FILE}" ] || die "server env file not found: ${SERVER_ENV_FILE}"
[ -f "${SERVER_COMPOSE_FILE}" ] || die "server compose file not found: ${SERVER_COMPOSE_FILE}"
[ -f "${CONSOLE_ENV_FILE}" ] || die "console env file not found: ${CONSOLE_ENV_FILE}"
[ -f "${CONSOLE_COMPOSE_FILE}" ] || die "console compose file not found: ${CONSOLE_COMPOSE_FILE}"
if [ -f "${COMMON_ENV_FILE}" ]; then
stage "Reading Environment"
server_updated_count="$(update_env_from_common "${SERVER_ENV_FILE}")"
log "updated ${server_updated_count} server env variable(s)"
else
stage "Reading Environment"
log "environment file not found, skip env update"
fi
stage "Starting RTR Server"
log "starting docker compose service: rpki-rtr"
"${COMPOSE_BIN[@]}" --env-file "${SERVER_ENV_FILE}" -p "${COMPOSE_PROJECT}" -f "${SERVER_COMPOSE_FILE}" up -d --no-build rpki-rtr
log "checking server compose status"
"${COMPOSE_BIN[@]}" --env-file "${SERVER_ENV_FILE}" -p "${COMPOSE_PROJECT}" -f "${SERVER_COMPOSE_FILE}" ps rpki-rtr
stage "Starting Console"
log "starting docker compose service: rpki-console-ui"
"${COMPOSE_BIN[@]}" --env-file "${CONSOLE_ENV_FILE}" -p "${CONSOLE_COMPOSE_PROJECT}" -f "${CONSOLE_COMPOSE_FILE}" up -d --no-build rpki-console-ui
log "checking console compose status"
"${COMPOSE_BIN[@]}" --env-file "${CONSOLE_ENV_FILE}" -p "${CONSOLE_COMPOSE_PROJECT}" -f "${CONSOLE_COMPOSE_FILE}" ps rpki-console-ui
stage "Start Finished"

184
scripts/02_self_check.sh Normal file
View File

@@ -0,0 +1,184 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
SERVER_ENV_FILE="${ROOT_DIR}/deploy/server/.env"
CONSOLE_ENV_FILE="${ROOT_DIR}/deploy/console/.env"
SERVER_COMPOSE_FILE="${ROOT_DIR}/deploy/server/docker-compose.tcp.yml"
CONSOLE_COMPOSE_FILE="${ROOT_DIR}/deploy/console/docker-compose.yml"
SERVER_PROJECT="${RPKI_RTR_COMPOSE_PROJECT:-rpki-rtr}"
CONSOLE_PROJECT="${RPKI_CONSOLE_COMPOSE_PROJECT:-rpki-console}"
WAIT_SECS="${RPKI_SELF_CHECK_WAIT_SECS:-60}"
usage() {
cat <<'EOF'
Usage: scripts/02_self_check.sh [options]
Check whether the RTR server and console started by scripts/01_start.sh are healthy.
Options:
--wait SECS Wait timeout for each check. Default: 60
-h, --help Show this help.
EOF
}
log() {
printf '[02_self_check] %s\n' "$*" >&2
}
stage() {
printf '\n====== %s ======\n' "$*" >&2
}
die() {
printf '[02_self_check] ERROR: %s\n' "$*" >&2
exit 1
}
while [ "$#" -gt 0 ]; do
case "$1" in
--wait)
[ "$#" -ge 2 ] || die "--wait requires a value"
WAIT_SECS="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
command -v docker >/dev/null 2>&1 || die "docker command not found"
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
die "docker compose is not available"
fi
[ -f "${SERVER_ENV_FILE}" ] || die "server env file not found: ${SERVER_ENV_FILE}"
[ -f "${SERVER_COMPOSE_FILE}" ] || die "server compose file not found: ${SERVER_COMPOSE_FILE}"
[ -f "${CONSOLE_ENV_FILE}" ] || die "console env file not found: ${CONSOLE_ENV_FILE}"
[ -f "${CONSOLE_COMPOSE_FILE}" ] || die "console compose file not found: ${CONSOLE_COMPOSE_FILE}"
set -a
# shellcheck disable=SC1090
. "${SERVER_ENV_FILE}"
# shellcheck disable=SC1090
. "${CONSOLE_ENV_FILE}"
set +a
RTR_PORT="${RPKI_RTR_TCP_HOST_PORT:-323}"
ADMIN_PORT="${RPKI_RTR_ADMIN_ADDR##*:}"
ADMIN_PORT="${ADMIN_PORT:-8323}"
ADMIN_TOKEN="${RPKI_RTR_ADMIN_TOKEN:-}"
CONSOLE_PORT="${RPKI_CONSOLE_UI_PORT:-5173}"
server_compose() {
"${COMPOSE_BIN[@]}" --env-file "${SERVER_ENV_FILE}" -p "${SERVER_PROJECT}" -f "${SERVER_COMPOSE_FILE}" "$@"
}
console_compose() {
"${COMPOSE_BIN[@]}" --env-file "${CONSOLE_ENV_FILE}" -p "${CONSOLE_PROJECT}" -f "${CONSOLE_COMPOSE_FILE}" "$@"
}
container_running() {
local compose_kind="$1"
local service="$2"
local cid state
if [ "${compose_kind}" = "server" ]; then
cid="$(server_compose ps -q "${service}" 2>/dev/null || true)"
else
cid="$(console_compose ps -q "${service}" 2>/dev/null || true)"
fi
[ -n "${cid}" ] || return 1
state="$(docker inspect -f '{{.State.Running}}' "${cid}" 2>/dev/null || true)"
[ "${state}" = "true" ]
}
tcp_open() {
local host="$1"
local port="$2"
if command -v nc >/dev/null 2>&1; then
nc -z "${host}" "${port}" >/dev/null 2>&1
return
fi
(exec 3<>"/dev/tcp/${host}/${port}") >/dev/null 2>&1
}
http_get() {
local url="$1"
local token="${2:-}"
if command -v curl >/dev/null 2>&1; then
if [ -n "${token}" ]; then
curl -fsS -H "Authorization: Bearer ${token}" "${url}"
else
curl -fsS "${url}"
fi
return
fi
die "curl command is required for HTTP health checks"
}
wait_until() {
local label="$1"
shift
local deadline=$((SECONDS + WAIT_SECS))
while [ "${SECONDS}" -lt "${deadline}" ]; do
if "$@"; then
log "${label}: ok"
return 0
fi
sleep 1
done
die "${label}: failed after ${WAIT_SECS}s"
}
show_command() {
printf '[02_self_check] check command: %s\n' "$*" >&2
}
check_admin_health() {
local body
body="$(http_get "http://127.0.0.1:${ADMIN_PORT}/admin/rtr/health" "${ADMIN_TOKEN}" 2>/dev/null || true)"
printf '%s' "${body}" | grep -q '"status"[[:space:]]*:[[:space:]]*"ok"'
}
check_console_http() {
http_get "http://127.0.0.1:${CONSOLE_PORT}/" >/dev/null 2>&1
}
stage "Checking Containers"
show_command "${COMPOSE_BIN[*]} --env-file ${SERVER_ENV_FILE} -p ${SERVER_PROJECT} -f ${SERVER_COMPOSE_FILE} ps -q rpki-rtr && docker inspect"
wait_until "server container" container_running server rpki-rtr
show_command "${COMPOSE_BIN[*]} --env-file ${CONSOLE_ENV_FILE} -p ${CONSOLE_PROJECT} -f ${CONSOLE_COMPOSE_FILE} ps -q rpki-console-ui && docker inspect"
wait_until "console container" container_running console rpki-console-ui
stage "Checking Server"
show_command "tcp connect 127.0.0.1:${RTR_PORT}"
wait_until "RTR port ${RTR_PORT}" tcp_open 127.0.0.1 "${RTR_PORT}"
show_command "curl -fsS -H 'Authorization: Bearer <token>' http://127.0.0.1:${ADMIN_PORT}/admin/rtr/health"
wait_until "Admin health" check_admin_health
stage "Checking Console"
show_command "tcp connect 127.0.0.1:${CONSOLE_PORT}"
wait_until "Console port ${CONSOLE_PORT}" tcp_open 127.0.0.1 "${CONSOLE_PORT}"
show_command "curl -fsS http://127.0.0.1:${CONSOLE_PORT}/"
wait_until "Console HTTP" check_console_http
stage "Self-Check Passed"

92
scripts/03_shutdown.sh Normal file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
SERVER_ENV_FILE="${ROOT_DIR}/deploy/server/.env"
CONSOLE_ENV_FILE="${ROOT_DIR}/deploy/console/.env"
MODE="${RPKI_RTR_SERVER_MODE:-tcp}"
SERVER_PROJECT="${RPKI_RTR_COMPOSE_PROJECT:-rpki-rtr}"
CONSOLE_PROJECT="${RPKI_CONSOLE_COMPOSE_PROJECT:-rpki-console}"
usage() {
cat <<'EOF'
Usage: scripts/03_shutdown.sh [options]
Stop services started by scripts/01_start.sh.
Options:
--mode MODE Server mode: tcp|tls|ssh|base. Default: tcp
-h, --help Show this help.
EOF
}
log() {
printf '[03_shutdown] %s\n' "$*" >&2
}
stage() {
printf '\n====== %s ======\n' "$*" >&2
}
die() {
printf '[03_shutdown] ERROR: %s\n' "$*" >&2
exit 1
}
compose_file_for_mode() {
case "$1" in
tcp) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.tcp.yml" ;;
tls) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.tls.yml" ;;
ssh) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.ssh.yml" ;;
base) printf '%s\n' "${ROOT_DIR}/deploy/server/docker-compose.yml" ;;
*) die "unsupported mode '$1', expected tcp|tls|ssh|base" ;;
esac
}
while [ "$#" -gt 0 ]; do
case "$1" in
--mode)
[ "$#" -ge 2 ] || die "--mode requires a value"
MODE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
SERVER_COMPOSE_FILE="$(compose_file_for_mode "${MODE}")"
CONSOLE_COMPOSE_FILE="${ROOT_DIR}/deploy/console/docker-compose.yml"
stage "Checking Docker"
command -v docker >/dev/null 2>&1 || die "docker command not found"
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
die "docker compose is not available"
fi
stage "Stopping Console"
if [ -f "${CONSOLE_ENV_FILE}" ] && [ -f "${CONSOLE_COMPOSE_FILE}" ]; then
"${COMPOSE_BIN[@]}" --env-file "${CONSOLE_ENV_FILE}" -p "${CONSOLE_PROJECT}" -f "${CONSOLE_COMPOSE_FILE}" down --remove-orphans
else
log "console compose/env not found, skip"
fi
stage "Stopping RTR Server"
if [ -f "${SERVER_ENV_FILE}" ] && [ -f "${SERVER_COMPOSE_FILE}" ]; then
"${COMPOSE_BIN[@]}" --env-file "${SERVER_ENV_FILE}" -p "${SERVER_PROJECT}" -f "${SERVER_COMPOSE_FILE}" down --remove-orphans
else
log "server compose/env not found, skip"
fi
stage "Shutdown Finished"

View File

@@ -0,0 +1,303 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUT_DIR="${ROOT_DIR}/certs"
TLS_DIR="${OUT_DIR}/tls"
SSH_DIR="${OUT_DIR}/ssh"
SERVER_DNS="localhost"
FORCE=0
DO_TLS=1
DO_SSH=1
usage() {
cat <<'USAGE'
Generate local development TLS certificates and SSH keys for RTR Docker deploys.
Usage:
scripts/10_generate-certs.sh [options]
Options:
--out-dir DIR Output directory, default: ./certs
--server-dns NAME TLS server dNSName SAN, default: localhost
--tls-only Generate only TLS files
--ssh-only Generate only SSH files
--force Overwrite existing files
-h, --help Show this help
Generated TLS files:
certs/tls/client-ca.crt
certs/tls/client-ca.key
certs/tls/server-dns.crt
certs/tls/server-dns.key
certs/tls/client-good.crt
certs/tls/client-good.key
Generated SSH files:
certs/ssh/ssh_host_rsa_key
certs/ssh/ssh_host_rsa_key.pub
certs/ssh/rtr-client.key
certs/ssh/rtr-client.key.pub
certs/ssh/rtr-authorized_keys
USAGE
}
die() {
echo "error: $*" >&2
exit 1
}
info() {
echo "[generate-certs] $*"
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}
run_quiet() {
local log_file
log_file="$(mktemp)"
if "$@" >"${log_file}" 2>&1; then
rm -f "${log_file}"
return 0
fi
cat "${log_file}" >&2
rm -f "${log_file}"
return 1
}
ensure_abs_out_dir() {
case "${OUT_DIR}" in
/*) ;;
[A-Za-z]:*) ;;
*) OUT_DIR="${ROOT_DIR}/${OUT_DIR}" ;;
esac
TLS_DIR="${OUT_DIR}/tls"
SSH_DIR="${OUT_DIR}/ssh"
}
check_overwrite() {
local path="$1"
if [[ -e "${path}" && "${FORCE}" != "1" ]]; then
die "${path} already exists; rerun with --force to overwrite"
fi
}
write_tls_config_files() {
local tmp_dir="$1"
cat >"${tmp_dir}/ca.cnf" <<'EOF'
[ req ]
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
CN = rpki-rtr-client-ca
EOF
cat >"${tmp_dir}/server.cnf" <<EOF
[ req ]
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
CN = ${SERVER_DNS}
EOF
cat >"${tmp_dir}/client.cnf" <<'EOF'
[ req ]
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
CN = rpki-rtr-client
EOF
cat >"${tmp_dir}/server.ext" <<EOF
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:${SERVER_DNS}
EOF
cat >"${tmp_dir}/client.ext" <<'EOF'
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=clientAuth
EOF
}
generate_tls() {
need_cmd openssl
mkdir -p "${TLS_DIR}"
info "generating TLS certificates in ${TLS_DIR}"
info "TLS server dNSName SAN: ${SERVER_DNS}"
local files=(
"${TLS_DIR}/client-ca.crt"
"${TLS_DIR}/client-ca.key"
"${TLS_DIR}/server-dns.crt"
"${TLS_DIR}/server-dns.key"
"${TLS_DIR}/client-good.crt"
"${TLS_DIR}/client-good.key"
)
for file in "${files[@]}"; do
check_overwrite "${file}"
done
local tmp_dir
tmp_dir="$(mktemp -d)"
write_tls_config_files "${tmp_dir}"
run_quiet openssl genrsa -out "${TLS_DIR}/client-ca.key" 2048
info "created TLS CA key: ${TLS_DIR}/client-ca.key"
run_quiet openssl req -x509 -new -nodes \
-config "${tmp_dir}/ca.cnf" \
-key "${TLS_DIR}/client-ca.key" \
-sha256 -days 3650 \
-out "${TLS_DIR}/client-ca.crt"
info "created TLS CA cert: ${TLS_DIR}/client-ca.crt"
run_quiet openssl genrsa -out "${TLS_DIR}/server-dns.key" 2048
info "created TLS server key: ${TLS_DIR}/server-dns.key"
run_quiet openssl req -new \
-config "${tmp_dir}/server.cnf" \
-key "${TLS_DIR}/server-dns.key" \
-out "${tmp_dir}/server-dns.csr"
run_quiet openssl x509 -req \
-in "${tmp_dir}/server-dns.csr" \
-CA "${TLS_DIR}/client-ca.crt" \
-CAkey "${TLS_DIR}/client-ca.key" \
-CAcreateserial \
-out "${TLS_DIR}/server-dns.crt" \
-days 3650 -sha256 \
-extfile "${tmp_dir}/server.ext"
info "created TLS server cert: ${TLS_DIR}/server-dns.crt"
run_quiet openssl genrsa -out "${TLS_DIR}/client-good.key" 2048
info "created TLS client key: ${TLS_DIR}/client-good.key"
run_quiet openssl req -new \
-config "${tmp_dir}/client.cnf" \
-key "${TLS_DIR}/client-good.key" \
-out "${tmp_dir}/client-good.csr"
run_quiet openssl x509 -req \
-in "${tmp_dir}/client-good.csr" \
-CA "${TLS_DIR}/client-ca.crt" \
-CAkey "${TLS_DIR}/client-ca.key" \
-CAcreateserial \
-out "${TLS_DIR}/client-good.crt" \
-days 3650 -sha256 \
-extfile "${tmp_dir}/client.ext"
info "created TLS client cert: ${TLS_DIR}/client-good.crt"
rm -f "${TLS_DIR}/client-ca.srl"
rm -rf "${tmp_dir}"
chmod 600 "${TLS_DIR}"/*.key
chmod 644 "${TLS_DIR}"/*.crt
}
generate_ssh_key() {
local path="$1"
local comment="$2"
check_overwrite "${path}"
check_overwrite "${path}.pub"
rm -f "${path}" "${path}.pub"
run_quiet ssh-keygen -q -t rsa -b 3072 -m PEM -N "" -C "${comment}" -f "${path}"
}
generate_ssh() {
need_cmd ssh-keygen
mkdir -p "${SSH_DIR}"
info "generating SSH keys in ${SSH_DIR}"
generate_ssh_key "${SSH_DIR}/ssh_host_rsa_key" "rpki-rtr-host"
info "created SSH host key: ${SSH_DIR}/ssh_host_rsa_key"
generate_ssh_key "${SSH_DIR}/rtr-client.key" "rpki-rtr-debug-client"
info "created SSH client key: ${SSH_DIR}/rtr-client.key"
check_overwrite "${SSH_DIR}/rtr-authorized_keys"
cp "${SSH_DIR}/rtr-client.key.pub" "${SSH_DIR}/rtr-authorized_keys"
info "created SSH authorized_keys: ${SSH_DIR}/rtr-authorized_keys"
chmod 600 \
"${SSH_DIR}/ssh_host_rsa_key" \
"${SSH_DIR}/rtr-client.key"
chmod 644 "${SSH_DIR}"/*.pub "${SSH_DIR}/rtr-authorized_keys"
}
while [[ $# -gt 0 ]]; do
case "$1" in
--out-dir)
[[ $# -ge 2 ]] || die "--out-dir requires a value"
OUT_DIR="$2"
shift 2
;;
--server-dns)
[[ $# -ge 2 ]] || die "--server-dns requires a value"
SERVER_DNS="$2"
shift 2
;;
--tls-only)
DO_TLS=1
DO_SSH=0
shift
;;
--ssh-only)
DO_TLS=0
DO_SSH=1
shift
;;
--force)
FORCE=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
ensure_abs_out_dir
if [[ "${DO_TLS}" == "1" ]]; then
generate_tls
fi
if [[ "${DO_SSH}" == "1" ]]; then
generate_ssh
fi
info "done"
info "generated files under ${OUT_DIR}"
if [[ "${DO_TLS}" == "1" ]]; then
cat <<EOF
TLS files:
${TLS_DIR}/client-ca.crt
${TLS_DIR}/server-dns.crt
${TLS_DIR}/server-dns.key
${TLS_DIR}/client-good.crt
${TLS_DIR}/client-good.key
${TLS_DIR}/client-ca.key (local signing key; not mounted by compose)
EOF
fi
if [[ "${DO_SSH}" == "1" ]]; then
cat <<EOF
SSH files:
${SSH_DIR}/ssh_host_rsa_key
${SSH_DIR}/ssh_host_rsa_key.pub
${SSH_DIR}/rtr-client.key
${SSH_DIR}/rtr-client.key.pub
${SSH_DIR}/rtr-authorized_keys
EOF
fi

136
scripts/11_start_client.sh Normal file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
COMMON_ENV_FILE="${ROOT_DIR}/environment.client"
CLIENT_ENV_FILE="${ROOT_DIR}/deploy/client/.env"
MODE="${RPKI_RTR_CLIENT_MODE:-tcp}"
COMPOSE_PROJECT="${RPKI_RTR_CLIENT_COMPOSE_PROJECT:-rpki-rtr-client}"
usage() {
cat <<'EOF'
Usage: scripts/11_start_client.sh [options]
Read root ./environment.client, update deploy/client/.env, then start the RTR debug client.
Options:
--mode MODE Client mode: tcp|tls|ssh. Default: tcp
--env FILE Client common env file. Default: ./environment.client
-h, --help Show this help.
EOF
}
log() {
printf '[11_start_client] %s\n' "$*" >&2
}
stage() {
printf '\n====== %s ======\n' "$*" >&2
}
die() {
printf '[11_start_client] ERROR: %s\n' "$*" >&2
exit 1
}
set_env_var() {
local env_file="$1"
local key="$2"
local value="$3"
local tmp_file
tmp_file="${env_file}.tmp.$$"
awk -v key="${key}" -v value="${value}" '
BEGIN { done = 0 }
$0 ~ "^[[:space:]]*#?[[:space:]]*" key "=" {
if (!done) {
print key "=" value
done = 1
}
next
}
{ print }
END {
if (!done) {
print key "=" value
}
}
' "${env_file}" >"${tmp_file}"
mv "${tmp_file}" "${env_file}"
}
compose_file_for_mode() {
case "$1" in
tcp) printf '%s\n' "${ROOT_DIR}/deploy/client/docker-compose.tcp.yml" ;;
tls) printf '%s\n' "${ROOT_DIR}/deploy/client/docker-compose.tls.yml" ;;
ssh) printf '%s\n' "${ROOT_DIR}/deploy/client/docker-compose.ssh.yml" ;;
*) die "unsupported mode '$1', expected tcp|tls|ssh" ;;
esac
}
while [ "$#" -gt 0 ]; do
case "$1" in
--mode)
[ "$#" -ge 2 ] || die "--mode requires a value"
MODE="$2"
shift 2
;;
--env)
[ "$#" -ge 2 ] || die "--env requires a value"
COMMON_ENV_FILE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
CLIENT_COMPOSE_FILE="$(compose_file_for_mode "${MODE}")"
stage "Checking Docker"
command -v docker >/dev/null 2>&1 || die "docker command not found"
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
die "docker compose is not available"
fi
[ -f "${CLIENT_ENV_FILE}" ] || die "deploy client env file not found: ${CLIENT_ENV_FILE}"
[ -f "${CLIENT_COMPOSE_FILE}" ] || die "client compose file not found: ${CLIENT_COMPOSE_FILE}"
stage "Reading Client Environment"
if [ -f "${COMMON_ENV_FILE}" ]; then
updated_count=0
while IFS= read -r line || [ -n "${line}" ]; do
case "${line}" in
''|\#*) continue ;;
*=*)
key="${line%%=*}"
value="${line#*=}"
set_env_var "${CLIENT_ENV_FILE}" "${key}" "${value}"
updated_count=$((updated_count + 1))
;;
esac
done <"${COMMON_ENV_FILE}"
log "updated ${updated_count} client env variable(s)"
else
log "environment.client file not found, skip env update"
fi
stage "Starting RTR Debug Client"
log "mode: ${MODE}"
"${COMPOSE_BIN[@]}" --env-file "${CLIENT_ENV_FILE}" -p "${COMPOSE_PROJECT}" -f "${CLIENT_COMPOSE_FILE}" up -d --no-build rtr-debug-client
log "checking client compose status"
"${COMPOSE_BIN[@]}" --env-file "${CLIENT_ENV_FILE}" -p "${COMPOSE_PROJECT}" -f "${CLIENT_COMPOSE_FILE}" ps rtr-debug-client
stage "Client Started"

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
CLIENT_ENV_FILE="${ROOT_DIR}/deploy/client/.env"
MODE="${RPKI_RTR_CLIENT_MODE:-tcp}"
COMPOSE_PROJECT="${RPKI_RTR_CLIENT_COMPOSE_PROJECT:-rpki-rtr-client}"
usage() {
cat <<'EOF'
Usage: scripts/12_shutdown_client.sh [options]
Stop the RTR debug client started by scripts/11_start_client.sh.
Options:
--mode MODE Client mode: tcp|tls|ssh. Default: tcp
-h, --help Show this help.
EOF
}
log() {
printf '[12_shutdown_client] %s\n' "$*" >&2
}
stage() {
printf '\n====== %s ======\n' "$*" >&2
}
die() {
printf '[12_shutdown_client] ERROR: %s\n' "$*" >&2
exit 1
}
compose_file_for_mode() {
case "$1" in
tcp) printf '%s\n' "${ROOT_DIR}/deploy/client/docker-compose.tcp.yml" ;;
tls) printf '%s\n' "${ROOT_DIR}/deploy/client/docker-compose.tls.yml" ;;
ssh) printf '%s\n' "${ROOT_DIR}/deploy/client/docker-compose.ssh.yml" ;;
*) die "unsupported mode '$1', expected tcp|tls|ssh" ;;
esac
}
while [ "$#" -gt 0 ]; do
case "$1" in
--mode)
[ "$#" -ge 2 ] || die "--mode requires a value"
MODE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown option: $1"
;;
esac
done
CLIENT_COMPOSE_FILE="$(compose_file_for_mode "${MODE}")"
stage "Checking Docker"
command -v docker >/dev/null 2>&1 || die "docker command not found"
if docker compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker compose)
elif command -v docker-compose >/dev/null 2>&1 && docker-compose version >/dev/null 2>&1; then
COMPOSE_BIN=(docker-compose)
else
die "docker compose is not available"
fi
stage "Stopping RTR Debug Client"
log "mode: ${MODE}"
if [ -f "${CLIENT_ENV_FILE}" ] && [ -f "${CLIENT_COMPOSE_FILE}" ]; then
"${COMPOSE_BIN[@]}" --env-file "${CLIENT_ENV_FILE}" -p "${COMPOSE_PROJECT}" -f "${CLIENT_COMPOSE_FILE}" down --remove-orphans
else
log "client compose/env not found, skip"
fi
stage "Client Shutdown Finished"

View File

@@ -1,34 +0,0 @@
#!/usr/bin/env sh
set -eu
: "${RPKI_RTR_ENABLE_TLS:=false}"
: "${RPKI_RTR_TCP_ADDR:=0.0.0.0:323}"
export RPKI_RTR_ENABLE_TLS
export RPKI_RTR_TCP_ADDR
: "${RPKI_RTR_DB_PATH:=./rtr-db}"
: "${RPKI_RTR_CCR_DIR:=./data}"
export RPKI_RTR_DB_PATH
export RPKI_RTR_CCR_DIR
: "${RPKI_RTR_MAX_DELTA:=100}"
: "${RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS:=300}"
: "${RPKI_RTR_TIMING_REFRESH_SECS:=3600}"
: "${RPKI_RTR_TIMING_RETRY_SECS:=600}"
: "${RPKI_RTR_TIMING_EXPIRE_SECS:=7200}"
: "${RPKI_RTR_MAX_CONNECTIONS:=512}"
: "${RPKI_RTR_NOTIFY_QUEUE_SIZE:=1024}"
export RPKI_RTR_MAX_DELTA
export RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS
export RPKI_RTR_TIMING_REFRESH_SECS
export RPKI_RTR_TIMING_RETRY_SECS
export RPKI_RTR_TIMING_EXPIRE_SECS
export RPKI_RTR_MAX_CONNECTIONS
export RPKI_RTR_NOTIFY_QUEUE_SIZE
: "${RPKI_RTR_TCP_KEEPALIVE_SECS:=60}"
: "${RPKI_RTR_WARN_INSECURE_TCP:=true}"
export RPKI_RTR_TCP_KEEPALIVE_SECS
export RPKI_RTR_WARN_INSECURE_TCP
cargo run

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env sh
set -eu
: "${RPKI_RTR_ENABLE_TLS:=true}"
: "${RPKI_RTR_TCP_ADDR:=0.0.0.0:323}"
: "${RPKI_RTR_TLS_ADDR:=0.0.0.0:324}"
export RPKI_RTR_ENABLE_TLS
export RPKI_RTR_TCP_ADDR
export RPKI_RTR_TLS_ADDR
: "${RPKI_RTR_DB_PATH:=./rtr-db}"
: "${RPKI_RTR_CCR_DIR:=./data}"
export RPKI_RTR_DB_PATH
export RPKI_RTR_CCR_DIR
: "${RPKI_RTR_TLS_CERT_PATH:=./certs/server-dns.crt}"
: "${RPKI_RTR_TLS_KEY_PATH:=./certs/server-dns.key}"
: "${RPKI_RTR_TLS_CLIENT_CA_PATH:=./certs/client-ca.crt}"
export RPKI_RTR_TLS_CERT_PATH
export RPKI_RTR_TLS_KEY_PATH
export RPKI_RTR_TLS_CLIENT_CA_PATH
: "${RPKI_RTR_MAX_DELTA:=100}"
: "${RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS:=300}"
: "${RPKI_RTR_TIMING_REFRESH_SECS:=3600}"
: "${RPKI_RTR_TIMING_RETRY_SECS:=600}"
: "${RPKI_RTR_TIMING_EXPIRE_SECS:=7200}"
: "${RPKI_RTR_MAX_CONNECTIONS:=512}"
: "${RPKI_RTR_NOTIFY_QUEUE_SIZE:=1024}"
export RPKI_RTR_MAX_DELTA
export RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS
export RPKI_RTR_TIMING_REFRESH_SECS
export RPKI_RTR_TIMING_RETRY_SECS
export RPKI_RTR_TIMING_EXPIRE_SECS
export RPKI_RTR_MAX_CONNECTIONS
export RPKI_RTR_NOTIFY_QUEUE_SIZE
: "${RPKI_RTR_TCP_KEEPALIVE_SECS:=60}"
: "${RPKI_RTR_WARN_INSECURE_TCP:=true}"
: "${RPKI_RTR_REQUIRE_TLS_SERVER_DNS_NAME_SAN:=true}"
export RPKI_RTR_TCP_KEEPALIVE_SECS
export RPKI_RTR_WARN_INSECURE_TCP
export RPKI_RTR_REQUIRE_TLS_SERVER_DNS_NAME_SAN
cargo run

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env sh
set -eu
: "${RPKI_RTR_ENABLE_TLS:=true}"
: "${RPKI_RTR_TCP_ADDR:=0.0.0.0:323}"
: "${RPKI_RTR_TLS_ADDR:=0.0.0.0:324}"
export RPKI_RTR_ENABLE_TLS
export RPKI_RTR_TCP_ADDR
export RPKI_RTR_TLS_ADDR
: "${RPKI_RTR_DB_PATH:=./rtr-db}"
: "${RPKI_RTR_CCR_DIR:=./data}"
export RPKI_RTR_DB_PATH
export RPKI_RTR_CCR_DIR
: "${RPKI_RTR_TLS_CERT_PATH:=./certs/server-dns.crt}"
: "${RPKI_RTR_TLS_KEY_PATH:=./certs/server-dns.key}"
: "${RPKI_RTR_TLS_CLIENT_CA_PATH:=./certs/client-ca.crt}"
export RPKI_RTR_TLS_CERT_PATH
export RPKI_RTR_TLS_KEY_PATH
export RPKI_RTR_TLS_CLIENT_CA_PATH
: "${RPKI_RTR_MAX_DELTA:=100}"
: "${RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS:=300}"
: "${RPKI_RTR_TIMING_REFRESH_SECS:=3600}"
: "${RPKI_RTR_TIMING_RETRY_SECS:=600}"
: "${RPKI_RTR_TIMING_EXPIRE_SECS:=7200}"
: "${RPKI_RTR_MAX_CONNECTIONS:=512}"
: "${RPKI_RTR_NOTIFY_QUEUE_SIZE:=1024}"
export RPKI_RTR_MAX_DELTA
export RPKI_RTR_SOURCE_REFRESH_INTERVAL_SECS
export RPKI_RTR_TIMING_REFRESH_SECS
export RPKI_RTR_TIMING_RETRY_SECS
export RPKI_RTR_TIMING_EXPIRE_SECS
export RPKI_RTR_MAX_CONNECTIONS
export RPKI_RTR_NOTIFY_QUEUE_SIZE
: "${RPKI_RTR_TCP_KEEPALIVE_SECS:=60}"
: "${RPKI_RTR_WARN_INSECURE_TCP:=true}"
: "${RPKI_RTR_REQUIRE_TLS_SERVER_DNS_NAME_SAN:=true}"
export RPKI_RTR_TCP_KEEPALIVE_SECS
export RPKI_RTR_WARN_INSECURE_TCP
export RPKI_RTR_REQUIRE_TLS_SERVER_DNS_NAME_SAN
cargo run