Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions clients/ddm-admin-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ pub use ddm_admin_client::types;

use ddm_admin_client::Client as InnerClient;
use either::Either;
use omicron_common::address::BOOTSTRAP_PREFIX;
use omicron_common::address::BOOTSTRAP_SLED_SUBNET_PREFIX_LENGTH;
use omicron_common::address::DDMD_PORT;
use oxnet::Ipv6Net;
use sled_hardware_types::underlay::BOOTSTRAP_MASK;
use sled_hardware_types::underlay::BOOTSTRAP_PREFIX;
use sled_hardware_types::underlay::BootstrapInterface;
use slog::Logger;
use slog_error_chain::SlogInlineError;
Expand Down Expand Up @@ -114,7 +114,8 @@ impl Client {
Ok(prefixes.into_values().flat_map(|prefixes| {
prefixes.into_iter().flat_map(|prefix| {
let mut segments = prefix.destination.addr().segments();
if prefix.destination.width() == BOOTSTRAP_MASK
if prefix.destination.width()
== BOOTSTRAP_SLED_SUBNET_PREFIX_LENGTH
&& segments[0] == BOOTSTRAP_PREFIX
{
Either::Left(interfaces.iter().map(move |interface| {
Expand Down
51 changes: 51 additions & 0 deletions common/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ use std::{
sync::LazyLock,
};

/// The routing prefix length of the bootstrap network /40.
///
/// This is distinct from [`BOOTSTRAP_SLED_SUBNET_PREFIX_LENGTH`], which is the
/// prefix length of the /64 subnet for an individual sled's bootstrap
/// addresses.
pub const BOOTSTRAP_SUBNET_PREFIX_LENGTH: u8 = 40;
pub const AZ_PREFIX_LENGTH: u8 = 48;
pub const RACK_PREFIX_LENGTH: u8 = 56;
Expand Down Expand Up @@ -449,6 +454,33 @@ pub const CP_SERVICES_RESERVED_ADDRESSES: u16 = 0xFFFF;
/// with each sled in the Reconfigurator blueprint.
pub const SLED_RESERVED_ADDRESSES: u16 = 2;

/// Initial octets of IPv6 for bootstrap addresses.
pub const BOOTSTRAP_PREFIX: u16 = 0xfdb0;

/// IPv6 subnet for the *entire* bootstrap network.
///
/// This is a /16 subnet with the prefix [`BOOTSTRAP_PREFIX`]. Individual sled
/// bootstrap networks are /64 subnets within this range, with the rest of the
/// prefix constructed from the sled's MAC address. This constant is intended to
/// be used for checking whether an address is a bootstrap address on *any*
/// sled's bootstrap network.
pub const BOOTSTRAP_NETWORK_SUBNET: Ipv6Net = Ipv6Net::new_unchecked(
Ipv6Addr::new(BOOTSTRAP_PREFIX, 0, 0, 0, 0, 0, 0, 0),
16,
);

/// IPv6 prefix length for bootstrap network sled subnets.
///
/// This is the length of the prefix for the bootstrap network subnet *of an
/// individual sled*. The prefix begins with [`BOOTSTRAP_PREFIX`], and the rest
/// of the /64 prefix is constructed from the sled's MAC address.
///
/// This is distinct from the prefix length of [`BOOTSTRAP_NETWORK_SUBNET`],
/// which is the /16 subnet that contains *all* sled bootstrap networks, and
/// from [`BOOTSTRAP_SUBNET_PREFIX_LENGTH`], which is the prefix length of the
/// announced bootstrap route.
pub const BOOTSTRAP_SLED_SUBNET_PREFIX_LENGTH: u8 = 64;

/// Wraps an [`Ipv6Net`] with a compile-time prefix length.
#[derive(
Debug,
Expand Down Expand Up @@ -529,6 +561,25 @@ impl<'de, const N: u8> Deserialize<'de> for Ipv6Subnet<N> {
}
}

/// A rack's underlay subnet, along with the AZ-wide underlay subnet
/// containing it.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct UnderlaySubnets {
pub rack_subnet: Ipv6Subnet<RACK_PREFIX_LENGTH>,
pub az_subnet: Ipv6Subnet<AZ_PREFIX_LENGTH>,
}

impl UnderlaySubnets {
/// Constructs a new `UnderlaySubnets` from the rack subnet, widening it to
/// determine the AZ subnet.
pub fn new(rack_subnet: Ipv6Subnet<RACK_PREFIX_LENGTH>) -> Self {
Self {
rack_subnet,
az_subnet: Ipv6Subnet::new(rack_subnet.net().addr()),
}
}
}

/// Represents a subnet which may be used for contacting DNS services.
#[derive(
Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord,
Expand Down
38 changes: 38 additions & 0 deletions nexus-config/src/nexus_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,33 @@ struct UnvalidatedTunables {
load_timeout: Option<std::time::Duration>,
}

/// Whether HTTP clients for external services permit requests to loopback
/// addresses.
///
/// This is an enum rather than a `bool`, so that if you want to turn on the
/// test-only config, you have to type the string "yes_for_test_purposes_only"
/// in the config file so you know what you're doing.
#[derive(
Clone,
Copy,
Debug,
Default,
Deserialize,
Eq,
JsonSchema,
PartialEq,
Serialize,
)]
#[serde(rename_all = "snake_case")]
pub enum TreatLoopbackAsExternal {
/// Loopback addresses are considered "external". This must only be used
/// in test environments.
YesForTestPurposesOnly,
/// Loopback addresses are rejected (the default).
#[default]
No,
}

/// Configuration for HTTP clients to external services.
#[derive(
Clone, Debug, Default, Deserialize, PartialEq, Serialize, JsonSchema,
Expand All @@ -285,6 +312,15 @@ pub struct ExternalHttpClientConfig {
/// specified interface name.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub interface: Option<String>,
/// Whether external HTTP clients are permitted to make requests to
/// loopback addresses (and names in the special-use "localhost." zone).
///
/// External HTTP clients normally refuse to make requests to any address
/// that isn't external to the rack, including loopback addresses. Test
/// environments, however, run their "external" servers on localhost, so
/// the test suite needs a way to turn that off.
#[serde(default)]
pub treat_loopback_as_external: TreatLoopbackAsExternal,
}

/// Tunable configuration parameters, intended for use in test environments or
Expand Down Expand Up @@ -1257,6 +1293,7 @@ mod test {
external_dns_servers = [ "1.1.1.1", "9.9.9.9" ]
[deployment.external_http_clients]
interface = "opte0"
treat_loopback_as_external = "yes_for_test_purposes_only"
[deployment.dropshot_external]
bind_address = "10.1.2.3:4567"
default_request_body_max_bytes = 1024
Expand Down Expand Up @@ -1404,6 +1441,7 @@ mod test {
],
external_http_clients: ExternalHttpClientConfig {
interface: Some("opte0".to_string()),
treat_loopback_as_external: TreatLoopbackAsExternal::YesForTestPurposesOnly,
},
},
pkg: PackageConfig {
Expand Down
2 changes: 2 additions & 0 deletions nexus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ tokio-util = { workspace = true, features = ["codec", "rt"] }
tough.workspace = true
trust-quorum-types.workspace = true
tufaceous-artifact.workspace = true
url.workspace = true
usdt.workspace = true
uuid.workspace = true

Expand Down Expand Up @@ -210,6 +211,7 @@ sp-sim.workspace = true
strum.workspace = true
subprocess.workspace = true
term.workspace = true
transient-dns-server.workspace = true
tufaceous.workspace = true
tufaceous-artifact.workspace = true
tufaceous-lib.workspace = true
Expand Down
7 changes: 7 additions & 0 deletions nexus/examples/config-second.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ techport_external_server_port = 0
# These are the DNS servers it should use.
external_dns_servers = ["1.1.1.1", "9.9.9.9"]

# Simulated/development deployments run "external" servers (e.g. webhook
# receivers) on localhost, so external HTTP clients must be permitted to
# connect to loopback addresses. This must never be enabled in production
# configs.
[deployment.external_http_clients]
treat_loopback_as_external = "yes_for_test_purposes_only"

[deployment.dropshot_external]
# IP Address and TCP port on which to listen for the external API
# This config file uses 12222 to avoid colliding with the usual 12220 that's
Expand Down
7 changes: 7 additions & 0 deletions nexus/examples/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ rack_id = "c19a698f-c6f9-4a17-ae30-20d711b8f7dc"
# These are the DNS servers it should use.
external_dns_servers = ["1.1.1.1", "9.9.9.9"]

# Simulated/development deployments run "external" servers (e.g. webhook
# receivers) on localhost, so external HTTP clients must be permitted to
# connect to loopback addresses. This must never be enabled in production
# configs.
[deployment.external_http_clients]
treat_loopback_as_external = "yes_for_test_purposes_only"

[deployment.dropshot_external]
# IP Address and TCP port on which to listen for the external API
bind_address = "127.0.0.1:12220"
Expand Down
5 changes: 3 additions & 2 deletions nexus/src/app/background/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ use super::tasks::vpc_routes;
use super::tasks::webhook_deliverator;
use crate::Nexus;
use crate::app::background::tasks::populate_switch_ports;
use crate::app::external_client::ExternalHttpClient;
use crate::app::oximeter::PRODUCER_LEASE_DURATION;
use crate::app::quiesce::NexusQuiesceHandle;
use crate::app::saga::StartSaga;
Expand Down Expand Up @@ -1334,11 +1335,11 @@ pub struct BackgroundTasksData {
pub tuf_artifact_replication_rx: mpsc::Receiver<ArtifactsWithPlan>,
/// Channel for exposing the latest loaded blueprint
pub blueprint_load_tx: watch::Sender<Option<LoadedTargetBlueprint>>,
/// `reqwest::Client` for webhook delivery requests.
/// [`ExternalHttpClient`] for webhook delivery requests.
///
/// This is shared with the external API as it's also used when sending
/// webhook liveness probe requests from the API.
pub webhook_delivery_client: reqwest::Client,
pub webhook_delivery_client: ExternalHttpClient,
/// Channel for configuring pending MGS updates
pub mgs_updates_tx: watch::Sender<PendingMgsUpdates>,
/// handle for controlling Nexus quiesce
Expand Down
5 changes: 3 additions & 2 deletions nexus/src/app/background/tasks/webhook_deliverator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
//! [`app::webhook`]: crate::app::webhook

use crate::app::background::BackgroundTask;
use crate::app::external_client::ExternalHttpClient;
use crate::app::webhook::ReceiverClient;
use futures::future::BoxFuture;
use nexus_db_queries::context::OpContext;
Expand Down Expand Up @@ -102,7 +103,7 @@ use std::sync::Arc;
pub struct WebhookDeliverator {
datastore: Arc<DataStore>,
nexus_id: OmicronZoneUuid,
client: reqwest::Client,
client: ExternalHttpClient,
cfg: DeliveryConfig,
}

Expand Down Expand Up @@ -135,7 +136,7 @@ impl WebhookDeliverator {
datastore: Arc<DataStore>,
cfg: DeliveryConfig,
nexus_id: OmicronZoneUuid,
client: reqwest::Client,
client: ExternalHttpClient,
) -> Self {
Self { datastore, nexus_id, cfg, client }
}
Expand Down
Loading
Loading