L.4 joiner-side helper: connect to `seed`'s bootstrap listener with `token`, receive `(ca_cert, node_cert, node_key, cluster_secret)`, write the files to `tls_dir/`, and return the loaded credentials. Blocks the calling thread on a short-lived tokio runtime so the helper composes with the synchronous resolve path.
(
settings: &ClusterSettings,
tls_dir: &Path,
token_hex: &str,
seed: std::net::SocketAddr,
)
| 264 | /// Blocks the calling thread on a short-lived tokio runtime so the |
| 265 | /// helper composes with the synchronous resolve path. |
| 266 | fn fetch_creds_via_bootstrap( |
| 267 | settings: &ClusterSettings, |
| 268 | tls_dir: &Path, |
| 269 | token_hex: &str, |
| 270 | seed: std::net::SocketAddr, |
| 271 | ) -> crate::Result<TlsCredentials> { |
| 272 | fs::create_dir_all(tls_dir).map_err(|e| crate::Error::Config { |
| 273 | detail: format!("create tls dir {}: {e}", tls_dir.display()), |
| 274 | })?; |
| 275 | |
| 276 | let rt = tokio::runtime::Builder::new_current_thread() |
| 277 | .enable_all() |
| 278 | .build() |
| 279 | .map_err(|e| crate::Error::Config { |
| 280 | detail: format!("build bootstrap runtime: {e}"), |
| 281 | })?; |
| 282 | let resp = rt |
| 283 | .block_on(nodedb_cluster::bootstrap_listener::fetch_creds( |
| 284 | seed, |
| 285 | token_hex, |
| 286 | settings.node_id, |
| 287 | std::time::Duration::from_secs(30), |
| 288 | )) |
| 289 | .map_err(|e| crate::Error::Config { |
| 290 | detail: format!("bootstrap fetch_creds: {e}"), |
| 291 | })?; |
| 292 | |
| 293 | // Persist to disk so a restart takes the normal data-dir path |
| 294 | // without needing the token a second time. |
| 295 | write_pem_cert(&tls_dir.join(CA_CERT_FILE), &resp.ca_cert_der)?; |
| 296 | write_pem_cert(&tls_dir.join(NODE_CERT_FILE), &resp.node_cert_der)?; |
| 297 | write_pem_private_key(&tls_dir.join(NODE_KEY_FILE), &resp.node_key_der)?; |
| 298 | if resp.cluster_secret.len() != CLUSTER_SECRET_LEN { |
| 299 | return Err(crate::Error::Config { |
| 300 | detail: format!( |
| 301 | "bootstrap response cluster_secret has {} bytes, expected {CLUSTER_SECRET_LEN}", |
| 302 | resp.cluster_secret.len() |
| 303 | ), |
| 304 | }); |
| 305 | } |
| 306 | let mut secret = [0u8; CLUSTER_SECRET_LEN]; |
| 307 | secret.copy_from_slice(&resp.cluster_secret); |
| 308 | write_cluster_secret(&tls_dir.join(CLUSTER_SECRET_FILE), &secret)?; |
| 309 | |
| 310 | load_from_data_dir(tls_dir) |
| 311 | } |
| 312 | |
| 313 | fn bootstrap_credentials( |
| 314 | settings: &ClusterSettings, |
no test coverage detected