diff --git a/deploy/docker-installer/.env.example b/deploy/docker-installer/.env.example index 8b3493d..f03e0b4 100644 --- a/deploy/docker-installer/.env.example +++ b/deploy/docker-installer/.env.example @@ -39,12 +39,31 @@ RETAIN_RUNS=100 # TAL/TA input mode: # file-with-ta: use packaged fixture TAL + TA only. # file-live-ta: use packaged fixture TAL; snapshot waits for live TA refresh, delta refreshes TA in background. +# custom-file-with-ta: use the custom TAL/TA below (requires exactly RIRS=custom); +# for the local test repo compose component or a third-party test TA. # url: pass TAL URL to child process. TAL_INPUT_MODE=file-live-ta LIVE_TA_REFRESH_BEFORE_SNAPSHOT=1 LIVE_TA_REFRESH_CONNECT_TIMEOUT_SECS=15 LIVE_TA_REFRESH_MAX_TIME_SECS=120 +# Custom TAL/TA mode (only active with RIRS=custom + TAL_INPUT_MODE=custom-file-with-ta). +# CUSTOM_FIXTURE_HOST_DIR is the host directory bind-mounted to /opt/ours-rp/custom-fixtures. +# CUSTOM_TAL_URI is recorded in CIR as the TAL origin URI. +# HTTP_ROOT_CERT_PATHS lists HTTPS TLS root certificates (space-separated, container +# paths) for self-signed RRDP/TAL servers; it is independent of the RPKI TA. +#RIRS=custom +#TAL_INPUT_MODE=custom-file-with-ta +#CUSTOM_FIXTURE_HOST_DIR=../custom-fixtures +#CUSTOM_TAL_PATH=/opt/ours-rp/custom-fixtures/tal/custom.tal +#CUSTOM_TA_PATH=/opt/ours-rp/custom-fixtures/ta/custom-ta.cer +#CUSTOM_TAL_URI=https://rpki-local-rrdp:8443/tal/custom.tal +#HTTP_ROOT_CERT_PATHS=/opt/ours-rp/custom-fixtures/certs/rrdp-ca.pem +# Optional shared docker network between the soak container and the stack local +# test repository component. When set, scripts/start.sh attaches the soak +# container to this external network before starting it. +#CUSTOM_REPO_NETWORK=rpki-local-repo-net + # Sync and runtime behavior. RSYNC_SCOPE=module-root # HTTP User-Agent for all outgoing HTTP requests (RRDP / TAL / TA / dead-repo probes). diff --git a/deploy/docker-installer/compose/docker-compose.yml b/deploy/docker-installer/compose/docker-compose.yml index 43c1110..2c0a047 100644 --- a/deploy/docker-installer/compose/docker-compose.yml +++ b/deploy/docker-installer/compose/docker-compose.yml @@ -11,8 +11,10 @@ services: RUN_ROOT: /var/lib/ours-rp BIN_DIR: /opt/ours-rp/bin FIXTURE_DIR: /opt/ours-rp/fixtures + CUSTOM_FIXTURE_DIR: /opt/ours-rp/custom-fixtures volumes: - ../.env:/opt/ours-rp/.env:ro + - ${CUSTOM_FIXTURE_HOST_DIR:-../custom-fixtures}:/opt/ours-rp/custom-fixtures:ro - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/state:/var/lib/ours-rp/state - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/runs:/var/lib/ours-rp/runs - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/logs:/var/lib/ours-rp/logs diff --git a/deploy/docker-installer/custom-fixtures/README.md b/deploy/docker-installer/custom-fixtures/README.md new file mode 100644 index 0000000..e40760e --- /dev/null +++ b/deploy/docker-installer/custom-fixtures/README.md @@ -0,0 +1,19 @@ +# custom-fixtures 占位目录 + +默认空目录,仅作为 `compose/docker-compose.yml` 中 +`${CUSTOM_FIXTURE_HOST_DIR:-../custom-fixtures}` 挂载的缺省目标,保证 +RIR 默认模式下 compose 不因缺目录而失败。 + +启用 custom TAL 模式(`RIRS=custom` + `TAL_INPUT_MODE=custom-file-with-ta`)时, +把 `CUSTOM_FIXTURE_HOST_DIR` 指向包含以下结构的主机目录: + +```text +/ +├── tal/custom.tal # 自定义 TAL +├── ta/custom-ta.cer # 自定义 TA DER +└── certs/rrdp-ca.pem # 自签 HTTPS 服务的 TLS root cert(如需要) +``` + +本仓库的 `rpki_local_repo_installer` 组件会在其 `$HOST_DATA_DIR/fixtures` +下生成完全兼容的目录结构;stack installer 的 `STACK_TA_MODE=local` 会自动 +完成该指向。 diff --git a/deploy/docker-installer/docs/README.en.md b/deploy/docker-installer/docs/README.en.md index 21d72a8..ab4968c 100644 --- a/deploy/docker-installer/docs/README.en.md +++ b/deploy/docker-installer/docs/README.en.md @@ -54,6 +54,12 @@ Defaults: - `ALLOW_CROSS_ARCH=0` - `RTR_REPORT_DIR=__HOST_DATA_DIR__/empty-rtr-report`, which mounts an empty fallback directory by default; after deploying a separate RTR service, register its same-host report directory through `scripts/register_rtr_monitor.sh` +To integrate a local or third-party test TA, use the custom TAL mode +(`RIRS=custom` + `TAL_INPUT_MODE=custom-file-with-ta` together with +`CUSTOM_TAL_PATH` / `CUSTOM_TA_PATH` / `CUSTOM_TAL_URI` / +`HTTP_ROOT_CERT_PATHS` and the `CUSTOM_FIXTURE_HOST_DIR` mount); see the +commented section in `.env.example` for details. + ## First Start Semantics If there is no successful run under `HOST_DATA_DIR/runs`, `start.sh` starts the core `ours-rp-soak` service first and waits for the first snapshot to succeed before starting metrics, Prometheus and Grafana. diff --git a/deploy/docker-installer/docs/README.zh-CN.md b/deploy/docker-installer/docs/README.zh-CN.md index 78d05e4..8acf650 100644 --- a/deploy/docker-installer/docs/README.zh-CN.md +++ b/deploy/docker-installer/docs/README.zh-CN.md @@ -54,6 +54,11 @@ vim .env - `ALLOW_CROSS_ARCH=0` - `RTR_REPORT_DIR=__HOST_DATA_DIR__/empty-rtr-report`,默认挂载空目录;独立 RTR 服务部署完成后,使用 `scripts/register_rtr_monitor.sh` 注册同机 report 目录 +如需对接本地或第三方测试 TA,可使用 custom TAL 模式(`RIRS=custom` + +`TAL_INPUT_MODE=custom-file-with-ta`,配合 `CUSTOM_TAL_PATH` / `CUSTOM_TA_PATH` / +`CUSTOM_TAL_URI` / `HTTP_ROOT_CERT_PATHS` 与 `CUSTOM_FIXTURE_HOST_DIR` 挂载), +配置项说明见 `.env.example` 中的注释段。 + ## 首次启动语义 如果 `HOST_DATA_DIR/runs` 下没有成功 run,`start.sh` 会先启动核心 `ours-rp-soak`,等待第一轮 snapshot 成功后再启动 metrics、Prometheus 和 Grafana。 diff --git a/deploy/docker-installer/scripts/start.sh b/deploy/docker-installer/scripts/start.sh index 28b00b6..dd462f4 100755 --- a/deploy/docker-installer/scripts/start.sh +++ b/deploy/docker-installer/scripts/start.sh @@ -47,7 +47,21 @@ if has_success_run; then fi log "starting core soak service" -compose_cmd --profile core up -d ours-rp-soak +if [[ -n "${CUSTOM_REPO_NETWORK:-}" ]]; then + # Custom TAL mode against the stack local test repository: create the soak + # container without starting it, attach the shared external network first, + # then start it. This avoids the first snapshot racing the network connect. + compose_cmd --profile core up -d --no-start ours-rp-soak 2>/dev/null \ + || compose_cmd --profile core create ours-rp-soak + soak_container="${COMPOSE_PROJECT_NAME:-ours-rp-package-installer}-soak" + if ! docker inspect -f '{{json .NetworkSettings.Networks}}' "$soak_container" | grep -q "\"$CUSTOM_REPO_NETWORK\""; then + log "attaching soak container to custom repo network: $CUSTOM_REPO_NETWORK" + docker network connect "$CUSTOM_REPO_NETWORK" "$soak_container" + fi + compose_cmd --profile core start ours-rp-soak +else + compose_cmd --profile core up -d ours-rp-soak +fi if [[ "$had_success" == "0" && "$WAIT_FIRST_RUN" == "1" ]]; then log "no previous successful run found; waiting for first run timeout=${timeout_secs}s" diff --git a/scripts/soak/run_soak.sh b/scripts/soak/run_soak.sh index 0d061bf..b95d661 100755 --- a/scripts/soak/run_soak.sh +++ b/scripts/soak/run_soak.sh @@ -36,6 +36,10 @@ DISABLE_COMPETING_RPS="${DISABLE_COMPETING_RPS:-1}" ENABLE_CHILD_CERTIFICATE_VALIDATION_CACHE="${ENABLE_CHILD_CERTIFICATE_VALIDATION_CACHE:-0}" RPKI_EXTRA_ARGS="${RPKI_EXTRA_ARGS:-}" RPKI_ANALYZE="${RPKI_ANALYZE:-0}" +CUSTOM_TAL_PATH="${CUSTOM_TAL_PATH:-$PACKAGE_ROOT/custom-fixtures/tal/custom.tal}" +CUSTOM_TA_PATH="${CUSTOM_TA_PATH:-$PACKAGE_ROOT/custom-fixtures/ta/custom-ta.cer}" +CUSTOM_TAL_URI="${CUSTOM_TAL_URI:-}" +HTTP_ROOT_CERT_PATHS="${HTTP_ROOT_CERT_PATHS:-}" BIN_DIR="${BIN_DIR:-$PACKAGE_ROOT/bin}" FIXTURE_DIR="${FIXTURE_DIR:-$PACKAGE_ROOT/fixtures}" @@ -119,10 +123,10 @@ validate_rsync_scope() { validate_tal_input_mode() { case "$TAL_INPUT_MODE" in - file-with-ta|file-live-ta|url) + file-with-ta|file-live-ta|custom-file-with-ta|url) ;; *) - die "TAL_INPUT_MODE must be file-with-ta, file-live-ta or url: $TAL_INPUT_MODE" + die "TAL_INPUT_MODE must be file-with-ta, file-live-ta, custom-file-with-ta or url: $TAL_INPUT_MODE" ;; esac } @@ -156,14 +160,46 @@ parse_rirs() { afrinic|apnic|arin|lacnic|ripe) RIR_LIST+=("$normalized") ;; + custom) + [[ "$TAL_INPUT_MODE" == "custom-file-with-ta" ]] \ + || die "RIRS=custom requires TAL_INPUT_MODE=custom-file-with-ta" + RIR_LIST+=("$normalized") + ;; *) - die "invalid RIRS entry: $raw_token; allowed: afrinic,apnic,arin,lacnic,ripe" + die "invalid RIRS entry: $raw_token; allowed: afrinic,apnic,arin,lacnic,ripe,custom (custom mode only)" ;; esac done [[ "${#RIR_LIST[@]}" -gt 0 ]] || die "RIRS must contain at least one RIR" } +validate_custom_inputs() { + local root_cert_path + local -a root_cert_paths=() + + if [[ "$TAL_INPUT_MODE" == "custom-file-with-ta" ]]; then + [[ "${#RIR_LIST[@]}" -eq 1 && "${RIR_LIST[0]}" == "custom" ]] \ + || die "custom-file-with-ta requires exactly RIRS=custom" + [[ -s "$CUSTOM_TAL_PATH" ]] \ + || die "missing custom TAL: $CUSTOM_TAL_PATH" + [[ -s "$CUSTOM_TA_PATH" ]] \ + || die "missing custom TA DER: $CUSTOM_TA_PATH" + [[ -n "$CUSTOM_TAL_URI" ]] \ + || die "CUSTOM_TAL_URI is required in custom-file-with-ta mode" + elif [[ "$RIRS" == *custom* ]]; then + die "RIRS=custom requires TAL_INPUT_MODE=custom-file-with-ta" + fi + + if [[ -n "$HTTP_ROOT_CERT_PATHS" ]]; then + # shellcheck disable=SC2206 + root_cert_paths=( $HTTP_ROOT_CERT_PATHS ) + for root_cert_path in "${root_cert_paths[@]}"; do + [[ -s "$root_cert_path" ]] \ + || die "missing HTTP root certificate: $root_cert_path" + done + fi +} + tal_file_for_rir() { case "$1" in afrinic) printf '%s' "$FIXTURE_DIR/tal/afrinic.tal" ;; @@ -171,6 +207,7 @@ tal_file_for_rir() { arin) printf '%s' "$FIXTURE_DIR/tal/arin.tal" ;; lacnic) printf '%s' "$FIXTURE_DIR/tal/lacnic.tal" ;; ripe) printf '%s' "$FIXTURE_DIR/tal/ripe-ncc.tal" ;; + custom) printf '%s' "$CUSTOM_TAL_PATH" ;; *) die "unknown RIR: $1" ;; esac } @@ -182,6 +219,7 @@ ta_file_for_rir() { arin) printf '%s' "$FIXTURE_DIR/ta/arin-ta.cer" ;; lacnic) printf '%s' "$FIXTURE_DIR/ta/lacnic-ta.cer" ;; ripe) printf '%s' "$FIXTURE_DIR/ta/ripe-ncc-ta.cer" ;; + custom) printf '%s' "$CUSTOM_TA_PATH" ;; *) die "unknown RIR: $1" ;; esac } @@ -193,6 +231,7 @@ tal_url_for_rir() { arin) printf '%s' "https://www.arin.net/resources/manage/rpki/arin.tal" ;; lacnic) printf '%s' "https://www.lacnic.net/innovaportal/file/4983/1/lacnic.tal" ;; ripe) printf '%s' "https://tal.rpki.ripe.net/ripe-ncc.tal" ;; + custom) printf '%s' "$CUSTOM_TAL_URI" ;; *) die "unknown RIR: $1" ;; esac } @@ -1058,6 +1097,17 @@ build_child_args() { CHILD_ARGS+=(--rsync-mirror-root "$TMP_DIR/rsync-mirror-{run_id}") fi + local root_cert_path + local -a root_cert_paths=() + if [[ -n "$HTTP_ROOT_CERT_PATHS" ]]; then + # HTTPS trust roots for RRDP/TAL fetches (independent of the RPKI TA). + # shellcheck disable=SC2206 + root_cert_paths=( $HTTP_ROOT_CERT_PATHS ) + for root_cert_path in "${root_cert_paths[@]}"; do + CHILD_ARGS+=(--http-root-cert "$root_cert_path") + done + fi + CHILD_ARGS+=( --parallel-phase2-ready-batch-size 256 --parallel-phase2-ready-batch-wall-time-budget-ms 100 @@ -1417,6 +1467,7 @@ main() { validate_positive_int "DB_STATS_EXACT_EVERY" "$DB_STATS_EXACT_EVERY" fi parse_rirs + validate_custom_inputs [[ -x "$RPKI_BIN" ]] || die "missing executable: $RPKI_BIN" [[ -x "$RPKI_DAEMON_BIN" ]] || die "missing executable: $RPKI_DAEMON_BIN"