From 490b79f22c7e7742b39131d95af75918654e4d21 Mon Sep 17 00:00:00 2001 From: "xiuting.xu" Date: Tue, 14 Jul 2026 14:44:40 +0800 Subject: [PATCH] add scripts --- README.client.md | 10 +- README.md | 28 +++ certs/ssh/_probe_key | 8 - certs/ssh/_probe_key.pub | 1 - certs/ssh/bird-rtr-client-rsa.pem | 39 ---- certs/ssh/bird-rtr-client-rsa.pem.pub | Bin 1110 -> 0 bytes certs/ssh/bird-rtr-client.key | 39 ---- certs/ssh/bird-rtr-client.key.pub | 1 - certs/ssh/bird-rtr-client.pem | 40 ---- certs/ssh/bird-rtr-client.pem.pub | Bin 1110 -> 0 bytes certs/ssh/rtr-authorized_keys | 5 - certs/ssh/rtr-client.key | 38 ---- certs/ssh/rtr-client.key.pub | 1 - certs/ssh/rtr-client.pem | 38 ---- certs/ssh/ssh_host_rsa_key | 38 ---- certs/ssh/ssh_host_rsa_key.pub | 1 - certs/ssh/ssh_known_hosts | 2 - certs/ssh/testkey | 8 - certs/ssh/testkey.pub | 1 - certs/ssh/testkey3 | 8 - certs/ssh/testkey3.pub | 1 - certs/tls/client-ca.crt | 19 -- certs/tls/client-good.crt | 20 -- certs/tls/client-good.key | 28 --- certs/tls/server-dns.crt | 19 -- certs/tls/server-dns.key | 28 --- deploy/bird/README.md | 2 +- deploy/bird/README.zh.md | 2 +- deploy/bird/_debug_ssh.conf | 2 +- deploy/bird/bird.conf.ssh.example | 2 +- deploy/bird/bird.conf.ssh.template | 2 +- deploy/client/.env | 3 + deploy/server/.env | 5 +- scripts/00_0_install.sh | 203 +++++++++++++++++ scripts/00_1_pre_check.sh | 155 +++++++++++++ scripts/00_2_generate_ccr.sh | 54 +++++ scripts/00_2_pre_check_prepare.sh | 96 ++++++++ scripts/00_3_pre_check_cleanup.sh | 56 +++++ scripts/01_start.sh | 159 ++++++++++++++ scripts/02_self_check.sh | 184 ++++++++++++++++ scripts/03_shutdown.sh | 92 ++++++++ scripts/10_generate_certs.sh | 303 ++++++++++++++++++++++++++ scripts/11_start_client.sh | 136 ++++++++++++ scripts/12_shutdown_client.sh | 82 +++++++ scripts/start-rtr-server-tcp.sh | 34 --- scripts/start-rtr-server-tls.sh | 45 ---- scripts/start-rtr-server.sh | 45 ---- 47 files changed, 1569 insertions(+), 514 deletions(-) delete mode 100644 certs/ssh/_probe_key delete mode 100644 certs/ssh/_probe_key.pub delete mode 100644 certs/ssh/bird-rtr-client-rsa.pem delete mode 100644 certs/ssh/bird-rtr-client-rsa.pem.pub delete mode 100644 certs/ssh/bird-rtr-client.key delete mode 100644 certs/ssh/bird-rtr-client.key.pub delete mode 100644 certs/ssh/bird-rtr-client.pem delete mode 100644 certs/ssh/bird-rtr-client.pem.pub delete mode 100644 certs/ssh/rtr-authorized_keys delete mode 100644 certs/ssh/rtr-client.key delete mode 100644 certs/ssh/rtr-client.key.pub delete mode 100644 certs/ssh/rtr-client.pem delete mode 100644 certs/ssh/ssh_host_rsa_key delete mode 100644 certs/ssh/ssh_host_rsa_key.pub delete mode 100644 certs/ssh/ssh_known_hosts delete mode 100644 certs/ssh/testkey delete mode 100644 certs/ssh/testkey.pub delete mode 100644 certs/ssh/testkey3 delete mode 100644 certs/ssh/testkey3.pub delete mode 100644 certs/tls/client-ca.crt delete mode 100644 certs/tls/client-good.crt delete mode 100644 certs/tls/client-good.key delete mode 100644 certs/tls/server-dns.crt delete mode 100644 certs/tls/server-dns.key create mode 100644 scripts/00_0_install.sh create mode 100644 scripts/00_1_pre_check.sh create mode 100644 scripts/00_2_generate_ccr.sh create mode 100644 scripts/00_2_pre_check_prepare.sh create mode 100644 scripts/00_3_pre_check_cleanup.sh create mode 100644 scripts/01_start.sh create mode 100644 scripts/02_self_check.sh create mode 100644 scripts/03_shutdown.sh create mode 100644 scripts/10_generate_certs.sh create mode 100644 scripts/11_start_client.sh create mode 100644 scripts/12_shutdown_client.sh delete mode 100644 scripts/start-rtr-server-tcp.sh delete mode 100644 scripts/start-rtr-server-tls.sh delete mode 100644 scripts/start-rtr-server.sh diff --git a/README.client.md b/README.client.md index 2abf00b..214d835 100644 --- a/README.client.md +++ b/README.client.md @@ -37,6 +37,14 @@ ./deploy/server/build-run.sh up --mode ssh ``` +TLS/SSH 所需的本地开发证书和密钥可以通过脚本生成: + +```bash +bash scripts/generate-certs.sh +``` + +已有 `certs/` 时使用 `--force` 覆盖。脚本生成的 `certs/tls` 和 `certs/ssh` 文件名与本仓库 compose 默认配置一致。 + Client 连接地址不要求固定在某个 Docker 网络里。`deploy/client` 的 TCP/TLS/SSH 模式分别通过 `RPKI_RTR_TCP_SERVER_ADDR`、`RPKI_RTR_TLS_SERVER_ADDR`、`RPKI_RTR_SSH_SERVER_ADDR` 指定 server 地址。`deploy/bird` 通过 `RPKI_BIRD_RPKI_HOST`/`RPKI_BIRD_RPKI_PORT` 指定 TCP server 地址,通过 `RPKI_BIRD_SSH_RPKI_HOST`/`RPKI_RTR_SSH_PORT` 指定 SSH server 地址。示例 compose 里的默认值只是为了和本仓库 server compose 直接联调。 `deploy/frr` 示例默认连接 `127.0.0.1:323`,可以通过 `RPKI_FRR_RPKI_HOST`/`RPKI_FRR_RPKI_PORT` 覆盖。 @@ -213,7 +221,7 @@ SSH 模式默认读取 `certs/ssh` 目录下的 key,并使用 `bird.conf.ssh.t | --- | --- | --- | | `RPKI_BIRD_SSH_RPKI_HOST` | `rpki-rtr` | SSH RTR server 主机名 | | `RPKI_RTR_SSH_PORT` | `22` | SSH RTR server 端口 | -| `RPKI_BIRD_SSH_CERTS_HOST_DIR` | `../../certs/ssh` | 宿主机 SSH 证书和 key 目录 | +| `RPKI_BIRD_SSH_CERTS_HOST_DIR` | `../../certs/ssh` | 宿主机 SSH 证书和 key 目录,复用 `rtr-client.key` | ## 如何判断 Client 正常 diff --git a/README.md b/README.md index b0e457a..0b40f1f 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ - [构建与测试](#构建与测试) - [数据输入](#数据输入) - [快速启动](#快速启动) + - [前置数据生成](#前置数据生成) - [Docker 运行](#docker-运行) - [跨架构构建](#跨架构构建) - [传输模式](#传输模式) @@ -77,6 +78,33 @@ cargo run --bin rpki_rtr ## 快速启动 +### 前置数据生成 + +TLS/mTLS 或 SSH transport 需要本地证书和密钥。开发和 Docker 联调可用脚本生成 compose 默认引用的最小文件集: + +```bash +bash scripts/10_generate-certs.sh +``` + +如需覆盖已有 `certs/` 内容: + +```bash +bash scripts/10_generate-certs.sh --force +``` + +`10_generate-certs.sh` 常用参数: + +| 参数 | 说明 | +| --- | --- | +| `--out-dir DIR` | 指定输出目录,默认生成到 `./certs`。 | +| `--server-dns NAME` | 指定 TLS server 证书的 dNSName SAN,默认 `localhost`。 | +| `--tls-only` | 只生成 `certs/tls` 下的 TLS 证书和私钥。 | +| `--ssh-only` | 只生成 `certs/ssh` 下的 SSH host key、client key 和 `authorized_keys`。 | +| `--force` | 覆盖已有文件。未指定时,如果目标文件已存在会直接报错退出。 | +| `-h`, `--help` | 查看脚本帮助。 | + +TLS server name 默认是 `localhost`。如果需要其它 DNS 名称,需要同时设置 `scripts/10_generate-certs.sh --server-dns ` 和 client 的 `RPKI_RTR_TLS_SERVER_NAME=`。 + ### Docker 运行 推荐使用 `deploy/server/build-run.sh` 和 `deploy/client/build-run.sh` 分别管理 server 与 debug client: diff --git a/certs/ssh/_probe_key b/certs/ssh/_probe_key deleted file mode 100644 index 61297f0..0000000 --- a/certs/ssh/_probe_key +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABChIc/mh2 -2ZlTnDw27U26Q9AAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHh6RLKV3/Dd8Ku0 -PB+lLBZo6E+EXJ2WYLYjIzVU/5t9AAAAoLzKj5jOlcoxcNfmUjP4KnfUEQ0wB2dhwpmzym -p7kqtBnTd7q1VZ+tszUyOEEAwvWChWPsQ5qcFkrXlI12NJVl0bv4XHXXXCoUzTuzasQhWg -ajws+FR9McWjTwYbuxPK8nZlRMGKVv3KTEb7IKzPiB1/+XVKRQCVyzhfJCFJTSIwL66KbP -knSamwB5g59wp8xcjPmdecqba6a7wTeHy21cg= ------END OPENSSH PRIVATE KEY----- diff --git a/certs/ssh/_probe_key.pub b/certs/ssh/_probe_key.pub deleted file mode 100644 index 2267478..0000000 --- a/certs/ssh/_probe_key.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHh6RLKV3/Dd8Ku0PB+lLBZo6E+EXJ2WYLYjIzVU/5t9 xuxiuting@LAPTOP-A2IH6TNR diff --git a/certs/ssh/bird-rtr-client-rsa.pem b/certs/ssh/bird-rtr-client-rsa.pem deleted file mode 100644 index c190cd3..0000000 --- a/certs/ssh/bird-rtr-client-rsa.pem +++ /dev/null @@ -1,39 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/certs/ssh/bird-rtr-client-rsa.pem.pub b/certs/ssh/bird-rtr-client-rsa.pem.pub deleted file mode 100644 index cff53f4fb24018586954b5f245a604b20bda336c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1110 zcmezWub82jA%j7ep@^ZFA(26W!4V3b7>pVG7^)Z&8Jrmm87di)8H^ZQp>mE4E)0$g zfeelePEZjihIEEN24@Bf1{VfnhH!>V21AAt22+MA1}BDWhHwT027d-Gh8%_ph6sj4 zh9t1M3I;S9+Pkqll8RSfA2z6|;dNel%HUJULGc?{YN z=?p#$Aq-&*&J5WMW(?sBP7DDIhG3Z_h9m}ehCqf2hHQpJ1~-OWhAajnh71M|hC~K^ zhB5{d1}6r0hDe48215pChB5|shH!=e1_Opv27d+<20w;GhA4(?hI9rqhE%Y>eHo${ zQW>HciW#yPf*HygTp7$5su)5UEEp;o3>eH9v>BopLcrk(3ZnvsFoqC@R0dau0ERS% zG=?08JcdAqJO)FCDu!$ZBL-83QU+57CvZ&rFlaMqGej^1F(fnOFgP(}GB`05Gk7vo zGI%f;Go&-PGlVe|GXyfYGvqR4GvqPkGn6s-F!(W)Go&+=FqAL^GB`0Fk*;gFk|q7rYUWPRE83UDuyTq zQ-)lIa0XunBZf+bN``!f0){AXyc#o@GvqSpGZ->tFcdKqF_gFZtjgAX`_Eg5{lIj54LfWeKy0-Rb)!7<>;5Xw-%;KvZaV8P(UP{82G z;KAU+5XRukP|je^pwD2+;KrcMP{B~fP{k0)UQISlR$J`9!&1q>MsQ4AIgB@E^aDGZei!3+fqz6`Dm z#SEnkZVX-wkqjja+6-O{Sqz2@K43D0!IHs}!GJ-ZA&0@6A%vllA%Y>DA(x??!G*zx s!IdF|!H6N9A&sGc!JQ$KA&?=PA(NqmA&Lp&0x#G%fQ6|0D=g$r~m)} diff --git a/certs/ssh/bird-rtr-client.key b/certs/ssh/bird-rtr-client.key deleted file mode 100644 index df4fbac..0000000 --- a/certs/ssh/bird-rtr-client.key +++ /dev/null @@ -1,39 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -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 -tO97CdjmrFG+AR8pwNqjvJnH9fw= ------END OPENSSH PRIVATE KEY----- diff --git a/certs/ssh/bird-rtr-client.key.pub b/certs/ssh/bird-rtr-client.key.pub deleted file mode 100644 index 868327c..0000000 --- a/certs/ssh/bird-rtr-client.key.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQGmbM6oQ3HYuaghpNTbIfOQQqC7m0vX5dUqcDlhlMyntyHAQOCzV8M5jbr2REeyTw60XXEezvb05mRm4NKQXJGsBR+/n5ZgvxpSFfso9ZBTTflEwBgBSQdoz/abWXe2inJ1MATu2An1vIAERQO/GvV9HYcLgF3sZ9T6X6yvVUt5TKWaQ2wNKQb3KNTpnBzrK74gkUaDj1HUt/DeVk42L80boycKZq2/jkHb4U6ThRXof7NJzgpHMgtMbCwAsN68fY4iIIN1046TIGHwy0sJaYrRDVzpWFLRS+hc68g5MWbhtiUFGGVjjMkUbSwegorB6hRxEyMYjn1bhLuUhiMVJkF552Y4Dwu7D1zRd4qk6ZL3nlEOCNsLTo+HIFCP0XUodaWdx2WJKZlpUpjzN7JF3MXqY0/xEgLNlL/SLSB1tod1PWfKfF7WDYluZJNCPv3V4296vcpG/VNBVew6ODRFT1WTQhVPenJLHuxZrOTg3sbGMXrsY8LhJjQtVTY4ijvRk= bird-rtr-client diff --git a/certs/ssh/bird-rtr-client.pem b/certs/ssh/bird-rtr-client.pem deleted file mode 100644 index 0218f4c..0000000 --- a/certs/ssh/bird-rtr-client.pem +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -qD4S5Fez6uDBzHtzJjKizWw= ------END PRIVATE KEY----- diff --git a/certs/ssh/bird-rtr-client.pem.pub b/certs/ssh/bird-rtr-client.pem.pub deleted file mode 100644 index def71f694bc4f1378bbb2193b9cb1c1e4252b439..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1110 zcmezWub82jA%j7ep@^ZFA(26W!4V3b7>pVG7^)Z&8Jrmm87di)8H^ZQp>mE4E)0$g zfeelePEZjihIEEN24@CKhIEDs1`h^(23H1e1~Uc=h9rgxhHwT8h7<;GhDZi|hGd2i zhA;+G1``HXhHM5ChA@UO1|x99(3>YF93>X3#q8PFmj2SW+To_Uqav4e(f*ArC zG8yt2Tp8RMk{KcyoEWMY${5@jQW-$ul*LfMV8IZ?pwHk1_92KJ!r;OX#Sq5e%23MS z$q>ch&JfHH%wWop%3#h=!H~-k#Sq9~!eGka%uvP<&fo+N5f=ta23Lk+h7yKE27iVK z24jYN21f=C;{Weh{@E({(F z1q^-+c?_itSzuR`FqAO_FeEYLFr+YeGL$ofG6XRgGPpCOF=R7jF}O46GdMC-FnBX$ zF?cYPfYWOwLllE0gCT<{Lj*$_gBwF8gFZtcLn4DOLo$OILn=cCLoP!AgExaegEm76 zI1QyTxH4ohBs2Ihm@$Mim@pJGI5HG5R527YSb$SoBttQS5rYAP2}24)B108}AwwF2 zB{*b!z~SM=5XRudkik&Q5X#`kkj;?6kPlXs!w|%f#E{33$WX`-#Zbgx!QjJC%23Xb z#gGH`A1FRc7*ZJw7(y9x7)rqLTgqV0;KE?QV8D>h;LQ-qP|gs<5XIoZP|je%;LhO6 zV91coP|V=T;Ll*h5XunDP{t6&kjN0ikj{|KP|T3b;KWeLkj>!5P{mNlV9t=w;Lc#i z;KoqKP{iQJP|2Xr;KN|Skik&G;K2~i;L1?N;KGo@;KdNd;LYH|5DLyYK@4ULQ4AFf wi43I-X$;8>K@8;#-VDYJK@4sT?hF+S1`Ngw1`Os5e&D&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" diff --git a/scripts/00_1_pre_check.sh b/scripts/00_1_pre_check.sh new file mode 100644 index 0000000..a3a6f9c --- /dev/null +++ b/scripts/00_1_pre_check.sh @@ -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" diff --git a/scripts/00_2_generate_ccr.sh b/scripts/00_2_generate_ccr.sh new file mode 100644 index 0000000..d115d9c --- /dev/null +++ b/scripts/00_2_generate_ccr.sh @@ -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 diff --git a/scripts/00_2_pre_check_prepare.sh b/scripts/00_2_pre_check_prepare.sh new file mode 100644 index 0000000..dd54e0f --- /dev/null +++ b/scripts/00_2_pre_check_prepare.sh @@ -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" </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 diff --git a/scripts/01_start.sh b/scripts/01_start.sh new file mode 100644 index 0000000..308db65 --- /dev/null +++ b/scripts/01_start.sh @@ -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" diff --git a/scripts/02_self_check.sh b/scripts/02_self_check.sh new file mode 100644 index 0000000..94b529f --- /dev/null +++ b/scripts/02_self_check.sh @@ -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 ' 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" diff --git a/scripts/03_shutdown.sh b/scripts/03_shutdown.sh new file mode 100644 index 0000000..cac1cf3 --- /dev/null +++ b/scripts/03_shutdown.sh @@ -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" diff --git a/scripts/10_generate_certs.sh b/scripts/10_generate_certs.sh new file mode 100644 index 0000000..e66e113 --- /dev/null +++ b/scripts/10_generate_certs.sh @@ -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" <"${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" <"${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 <&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" diff --git a/scripts/12_shutdown_client.sh b/scripts/12_shutdown_client.sh new file mode 100644 index 0000000..47f67ce --- /dev/null +++ b/scripts/12_shutdown_client.sh @@ -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" diff --git a/scripts/start-rtr-server-tcp.sh b/scripts/start-rtr-server-tcp.sh deleted file mode 100644 index e24fa01..0000000 --- a/scripts/start-rtr-server-tcp.sh +++ /dev/null @@ -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 diff --git a/scripts/start-rtr-server-tls.sh b/scripts/start-rtr-server-tls.sh deleted file mode 100644 index 1946de9..0000000 --- a/scripts/start-rtr-server-tls.sh +++ /dev/null @@ -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 diff --git a/scripts/start-rtr-server.sh b/scripts/start-rtr-server.sh deleted file mode 100644 index 1946de9..0000000 --- a/scripts/start-rtr-server.sh +++ /dev/null @@ -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