From 404ba25a8b96b2b053c99d92f60276b7f3d3d915 Mon Sep 17 00:00:00 2001 From: Hang Yin Date: Thu, 25 Dec 2025 22:27:29 +0000 Subject: [PATCH] ct_monitor: Use HTTP endpoint instead of pRPC for acme-info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gateway exposes /acme-info as an HTTP endpoint, but ct_monitor was using pRPC which expects Tproxy.* method names due to the gateway's `trim: "Tproxy."` configuration. This caused 405 Method Not Allowed errors. Switch to using the HTTP /acme-info endpoint directly, which returns JSON with hex-encoded public keys. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Cargo.lock | 3 +-- ct_monitor/Cargo.toml | 4 +--- ct_monitor/src/main.rs | 47 +++++++++++++++++++++++++++++++++++------- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 348a9493..9b4babe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1731,9 +1731,8 @@ version = "0.5.5" dependencies = [ "anyhow", "clap", - "dstack-gateway-rpc", + "hex", "hex_fmt", - "ra-rpc", "regex", "reqwest", "serde", diff --git a/ct_monitor/Cargo.toml b/ct_monitor/Cargo.toml index 3828526e..5e7dcc96 100644 --- a/ct_monitor/Cargo.toml +++ b/ct_monitor/Cargo.toml @@ -12,6 +12,7 @@ license.workspace = true [dependencies] anyhow.workspace = true clap = { workspace = true, features = ["derive"] } +hex = { workspace = true, features = ["alloc", "std"] } hex_fmt.workspace = true regex.workspace = true reqwest = { workspace = true, default-features = false, features = ["json", "rustls-tls", "charset", "hickory-dns"] } @@ -21,6 +22,3 @@ tokio = { workspace = true, features = ["full"] } tracing.workspace = true tracing-subscriber.workspace = true x509-parser.workspace = true - -dstack-gateway-rpc.workspace = true -ra-rpc = { workspace = true, default-features = false, features = ["client"] } diff --git a/ct_monitor/src/main.rs b/ct_monitor/src/main.rs index 6a168cce..8f0f268d 100644 --- a/ct_monitor/src/main.rs +++ b/ct_monitor/src/main.rs @@ -4,8 +4,6 @@ use anyhow::{bail, Context, Result}; use clap::Parser; -use dstack_gateway_rpc::gateway_client::GatewayClient; -use ra_rpc::client::RaClient; use regex::Regex; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -15,6 +13,13 @@ use x509_parser::prelude::*; const BASE_URL: &str = "https://crt.sh"; +#[derive(Debug, Deserialize)] +struct AcmeInfoResponse { + #[allow(dead_code)] + account_uri: String, + hist_keys: Vec, +} + struct Monitor { gateway_uri: String, domain: String, @@ -48,12 +53,38 @@ impl Monitor { } async fn refresh_known_keys(&mut self) -> Result<()> { - info!("fetching known public keys from {}", self.gateway_uri); - // TODO: Use RA-TLS - let tls_no_check = true; - let rpc = GatewayClient::new(RaClient::new(self.gateway_uri.clone(), tls_no_check)?); - let info = rpc.acme_info().await?; - self.known_keys = info.hist_keys.into_iter().collect(); + let acme_info_url = format!("{}/acme-info", self.gateway_uri.trim_end_matches('/')); + info!("fetching known public keys from {}", acme_info_url); + + let client = reqwest::Client::builder() + .danger_accept_invalid_certs(true) // TODO: Use RA-TLS verification + .build() + .context("failed to build http client")?; + + let response = client + .get(&acme_info_url) + .send() + .await + .context("failed to fetch acme-info")?; + + if !response.status().is_success() { + bail!( + "failed to fetch acme-info: HTTP {}", + response.status().as_u16() + ); + } + + let info: AcmeInfoResponse = response + .json() + .await + .context("failed to parse acme-info response")?; + + self.known_keys = info + .hist_keys + .into_iter() + .map(|hex_key| hex::decode(&hex_key).context("invalid hex in hist_keys")) + .collect::>>()?; + info!("got {} known public keys", self.known_keys.len()); for key in self.known_keys.iter() { debug!(" {}", hex_fmt::HexFmt(key));