Skip to content
Merged
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
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ pub struct Config {
pub ztunnel_identity: Option<identity::Identity>,

pub ztunnel_workload: Option<state::WorkloadInfo>,

pub ipv6_enabled: bool,
}

#[derive(serde::Serialize, Clone, Copy, Debug)]
Expand Down Expand Up @@ -862,6 +864,7 @@ pub fn construct_config(pc: ProxyConfig) -> Result<Config, Error> {
localhost_app_tunnel: parse_default(LOCALHOST_APP_TUNNEL, true)?,
ztunnel_identity,
ztunnel_workload,
ipv6_enabled,
})
}

Expand Down
37 changes: 30 additions & 7 deletions src/dns/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl Server {
socket_factory: &(dyn SocketFactory + Send + Sync),
local_workload_information: Arc<LocalWorkloadFetcher>,
prefered_service_namespace: Option<String>,
ipv6_enabled: bool,
) -> Result<Self, Error> {
// if the address we got from config is supposed to be v6-enabled,
// actually check if the local pod context our socketfactory operates in supports V6.
Expand All @@ -104,6 +105,7 @@ impl Server {
metrics,
local_workload_information,
prefered_service_namespace,
ipv6_enabled,
);
let store = Arc::new(store);
let handler = dns::handler::Handler::new(store.clone());
Expand Down Expand Up @@ -194,6 +196,7 @@ struct Store {
metrics: Arc<Metrics>,
local_workload: Arc<LocalWorkloadFetcher>,
prefered_service_namespace: Option<String>,
ipv6_enabled: bool,
}

impl Store {
Expand All @@ -204,6 +207,7 @@ impl Store {
metrics: Arc<Metrics>,
local_workload_information: Arc<LocalWorkloadFetcher>,
prefered_service_namespace: Option<String>,
ipv6_enabled: bool,
) -> Self {
let domain = as_name(domain);
let svc_domain = append_name(as_name("svc"), &domain);
Expand All @@ -216,6 +220,7 @@ impl Store {
metrics,
local_workload: local_workload_information,
prefered_service_namespace,
ipv6_enabled,
}
}

Expand Down Expand Up @@ -422,6 +427,13 @@ impl Store {
None
}

fn record_type_enabled(&self, addr: &IpAddr) -> bool {
match addr {
IpAddr::V4(_) => true, // IPv4 always
IpAddr::V6(_) => self.ipv6_enabled, // IPv6 must be not be disabled in config
}
}

/// Gets the list of addresses of the requested record type from the server.
fn get_addresses(
&self,
Expand All @@ -434,7 +446,7 @@ impl Store {
.workload_ips
.iter()
.filter_map(|addr| {
if is_record_type(addr, record_type) {
if is_record_type(addr, record_type) && self.record_type_enabled(addr) {
Some(*addr)
} else {
None
Expand All @@ -453,10 +465,9 @@ impl Store {
debug!("failed to fetch workload for {}", ep.workload_uid);
return None;
};
wl.workload_ips
.iter()
.copied()
.find(|addr| is_record_type(addr, record_type))
wl.workload_ips.iter().copied().find(|addr| {
is_record_type(addr, record_type) && self.record_type_enabled(addr)
})
})
.collect()
} else {
Expand All @@ -468,6 +479,7 @@ impl Store {
.filter_map(|vip| {
if is_record_type(&vip.address, record_type)
&& client.network == vip.network
&& self.record_type_enabled(&vip.address)
{
Some(vip.address)
} else {
Expand Down Expand Up @@ -637,7 +649,7 @@ impl Resolver for Store {
// From this point on, we are the authority for the response.
let is_authoritative = true;

if !service_family_allowed(&service_match.server, record_type) {
if !service_family_allowed(&service_match.server, record_type, self.ipv6_enabled) {
access_log(
request,
Some(&client),
Expand Down Expand Up @@ -706,7 +718,13 @@ impl Resolver for Store {
/// anyway, so would naturally work.
/// Headless services, however, do not have VIPs, and the Pods behind them can have dual stack IPs even with
/// the Service being single-stack. In this case, we are NOT supposed to return both IPs.
fn service_family_allowed(server: &Address, record_type: RecordType) -> bool {
/// If IPv6 is globally disabled, AAAA records are not allowed.
fn service_family_allowed(server: &Address, record_type: RecordType, ipv6_enabled: bool) -> bool {
// If IPv6 is globally disabled, don't allow AAAA records
if !ipv6_enabled && record_type == RecordType::AAAA {
return false;
}

match server {
Address::Service(service) => match service.ip_families {
Some(IpFamily::IPv4) if record_type == RecordType::AAAA => false,
Expand Down Expand Up @@ -1087,6 +1105,7 @@ mod tests {
metrics: test_metrics(),
local_workload,
prefered_service_namespace: None,
ipv6_enabled: true,
};

let namespaced_domain = n(format!("{}.svc.cluster.local", c.client_namespace));
Expand Down Expand Up @@ -1432,6 +1451,7 @@ mod tests {
&factory,
local_workload,
Some(PREFERRED.to_string()),
true, // ipv6_enabled for tests
)
.await
.unwrap();
Expand Down Expand Up @@ -1519,6 +1539,7 @@ mod tests {
&factory,
local_workload,
None,
true, // ipv6_enabled for tests
)
.await
.unwrap();
Expand Down Expand Up @@ -1569,6 +1590,7 @@ mod tests {
state.clone(),
),
prefered_service_namespace: None,
ipv6_enabled: true,
};

let ip4n6_client_ip = ip("::ffff:202:202");
Expand Down Expand Up @@ -1603,6 +1625,7 @@ mod tests {
&factory,
local_workload,
None,
true, // ipv6_enabled for tests
)
.await
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/proxyfactory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl ProxyFactory {
socket_factory.as_ref(),
local_workload_information.as_fetcher(),
self.config.prefered_service_namespace.clone(),
self.config.ipv6_enabled,
)
.await?;
resolver = Some(server.resolver());
Expand Down
1 change: 1 addition & 0 deletions src/test_helpers/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ pub async fn run_dns(responses: HashMap<Name, Vec<IpAddr>>) -> anyhow::Result<Te
state.clone(),
),
Some("prefered-namespace".to_string()),
true, // ipv6_enabled for tests
)
.await?;

Expand Down