diff --git a/common/src/address.rs b/common/src/address.rs index 5926ae4138a..b5c7f4e912f 100644 --- a/common/src/address.rs +++ b/common/src/address.rs @@ -8,13 +8,10 @@ //! and Nexus, who need to agree upon addressing schemes. use crate::api::external::{self, Error, Ipv4Net, Ipv6Net}; -use ipnetwork::{IpNetworkError, Ipv4Network, Ipv6Network}; +use ipnetwork::{Ipv4Network, Ipv6Network}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::{ - net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV6}, - str::FromStr, -}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV6}; pub const AZ_PREFIX: u8 = 48; pub const RACK_PREFIX: u8 = 56; @@ -169,27 +166,6 @@ impl Ipv6Subnet { } } -impl FromStr for Ipv6Subnet { - type Err = Ipv6SubnetParseError; - - fn from_str(s: &str) -> Result { - let net = Ipv6Net(s.parse()?); - if net.prefix() == N { - Ok(Self { net }) - } else { - Err(Ipv6SubnetParseError::InvalidPrefix { expected: N, net }) - } - } -} - -#[derive(Debug, thiserror::Error)] -pub enum Ipv6SubnetParseError { - #[error(transparent)] - IpNetworkError(#[from] IpNetworkError), - #[error("expected prefix {expected} but found {}: {net}", net.prefix())] - InvalidPrefix { expected: u8, net: Ipv6Net }, -} - // We need a custom Deserialize to ensure that the subnet is what we expect. impl<'de, const N: u8> Deserialize<'de> for Ipv6Subnet { fn deserialize(deserializer: D) -> Result diff --git a/sled-agent/src/services.rs b/sled-agent/src/services.rs index e0b53ececcf..f91b5091e6a 100644 --- a/sled-agent/src/services.rs +++ b/sled-agent/src/services.rs @@ -1698,9 +1698,14 @@ impl ServiceManager { "config/mgs-address", &format!("[::1]:{MGS_PORT}"), )?; + + // We intentionally bind `nexus-proxy-address` to `::` so + // wicketd will serve this on all interfaces, particularly + // the tech port interfaces, allowing external clients to + // connect to this Nexus proxy. smfh.setprop( "config/nexus-proxy-address", - &format!("[::1]:{WICKETD_NEXUS_PROXY_PORT}"), + &format!("[::]:{WICKETD_NEXUS_PROXY_PORT}"), )?; if let Some(underlay_address) = self .inner @@ -2725,9 +2730,8 @@ impl ServiceManager { ); *request = new_request; - let address = request - .addresses - .get(0) + let first_address = request.addresses.get(0); + let address = first_address .map(|addr| addr.to_string()) .unwrap_or_else(|| "".to_string()); @@ -2833,6 +2837,29 @@ impl ServiceManager { } smfh.refresh()?; } + ServiceType::Wicketd { .. } => { + if let Some(&address) = first_address { + let rack_subnet = + Ipv6Subnet::::new(address); + + info!( + self.inner.log, "configuring wicketd"; + "rack_subnet" => %rack_subnet.net().ip(), + ); + + smfh.setprop( + "config/rack-subnet", + &rack_subnet.net().ip().to_string(), + )?; + + smfh.refresh()?; + } else { + error!( + self.inner.log, + "underlay address unexpectedly missing", + ); + } + } ServiceType::Tfport { .. } => { // Since tfport and dpd communicate using localhost, // the tfport service shouldn't need to be restarted. diff --git a/smf/wicketd/manifest.xml b/smf/wicketd/manifest.xml index e1c2a4d6750..e15c20d0131 100644 --- a/smf/wicketd/manifest.xml +++ b/smf/wicketd/manifest.xml @@ -28,7 +28,7 @@ listens on a `::1` (IPv6 localhost) address without TLS. --> diff --git a/wicketd/src/bin/wicketd.rs b/wicketd/src/bin/wicketd.rs index fdd08242e22..a619ba4d8b8 100644 --- a/wicketd/src/bin/wicketd.rs +++ b/wicketd/src/bin/wicketd.rs @@ -6,11 +6,11 @@ use clap::Parser; use omicron_common::{ - address::{Ipv6Subnet, AZ_PREFIX}, + address::Ipv6Subnet, cmd::{fatal, CmdError}, }; use sled_hardware::Baseboard; -use std::net::SocketAddrV6; +use std::net::{Ipv6Addr, SocketAddrV6}; use std::path::PathBuf; use wicketd::{self, run_openapi, Config, Server, SmfConfigValues}; @@ -54,7 +54,7 @@ enum Args { /// The subnet for the rack; typically read directly from our SMF config /// via `--read-smf-config` or an SMF refresh #[clap(long, action, conflicts_with("read_smf_config"))] - rack_subnet: Option>, + rack_subnet: Option, }, } @@ -109,7 +109,7 @@ async fn do_run() -> Result<(), CmdError> { })?; let rack_subnet = match rack_subnet { - Some(addr) => Some(addr), + Some(addr) => Some(Ipv6Subnet::new(addr)), None if read_smf_config => { let smf_values = SmfConfigValues::read_current() .map_err(|e| CmdError::Failure(e.to_string()))? diff --git a/wicketd/src/lib.rs b/wicketd/src/lib.rs index cab61e8e352..b4c1e5e3caf 100644 --- a/wicketd/src/lib.rs +++ b/wicketd/src/lib.rs @@ -88,10 +88,10 @@ impl SmfConfigValues { let addr = rack_subnet.parse().with_context(|| { format!( "failed to parse {CONFIG_PG}/{PROP_RACK_SUBNET} \ - value {rack_subnet:?} as a rack subnet" + value {rack_subnet:?} as an IP address" ) })?; - Some(addr) + Some(Ipv6Subnet::new(addr)) }; Ok(Some(Self { rack_subnet })) diff --git a/wicketd/src/nexus_proxy.rs b/wicketd/src/nexus_proxy.rs index 138a18cc49e..33ff02a9457 100644 --- a/wicketd/src/nexus_proxy.rs +++ b/wicketd/src/nexus_proxy.rs @@ -104,7 +104,7 @@ impl Inner { info!( log, "closing connection; no internal DNS resolver available \ - (rack subnet unknown?" + (rack subnet unknown?)" ); return; };