policy: Cleanup policy response labels (#6722)

Policy controller API responses include a set of labels. These labels
are to be used in proxy m$etrics to indicate why traffic is permitted to
a pod. This permits metrics to be associated with `Server` and
ServerAuthorization` resources (i.e. for `stat`).

This change updates the response API to include a `name` label
referencing the server's name. When the policy is derived from a default
configuration (and not a `Server` instance), the name takes the form
'default:<policy>'.

This change also updates authorization labels. Defaults are encoded as
servers are, otherwise the authorization's name is set as a label. The
`tls` and `authn` labels have been removed, as they're redundant with
other labels that are already present.
This commit is contained in:
Oliver Gould 2021-08-23 14:56:19 -07:00 committed by GitHub
parent 154ad9a228
commit 49f4af6e6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 98 additions and 135 deletions

View File

@ -23,6 +23,7 @@ pub type InboundServerStream = Pin<Box<dyn Stream<Item = InboundServer> + Send +
/// Inbound server configuration. /// Inbound server configuration.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct InboundServer { pub struct InboundServer {
pub name: String,
pub protocol: ProxyProtocol, pub protocol: ProxyProtocol,
pub authorizations: HashMap<String, ClientAuthorization>, pub authorizations: HashMap<String, ClientAuthorization>,
} }

View File

@ -10,7 +10,7 @@ use linkerd_policy_controller_core::{
ClientAuthentication, ClientAuthorization, DiscoverInboundServer, IdentityMatch, InboundServer, ClientAuthentication, ClientAuthorization, DiscoverInboundServer, IdentityMatch, InboundServer,
InboundServerStream, IpNet, NetworkMatch, ProxyProtocol, InboundServerStream, IpNet, NetworkMatch, ProxyProtocol,
}; };
use std::{collections::HashMap, sync::Arc}; use std::sync::Arc;
use tracing::trace; use tracing::trace;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -20,12 +20,6 @@ pub struct Server<T> {
cluster_networks: Arc<[IpNet]>, cluster_networks: Arc<[IpNet]>,
} }
struct Labels {
authn: bool,
tls: bool,
name: String,
}
// === impl Server === // === impl Server ===
impl<T> Server<T> impl<T> Server<T>
@ -200,9 +194,15 @@ fn to_server(srv: &InboundServer, cluster_networks: &[IpNet]) -> proto::Server {
.collect(); .collect();
trace!(?authorizations); trace!(?authorizations);
let labels = vec![("name".to_string(), srv.name.to_string())]
.into_iter()
.collect();
trace!(?labels);
proto::Server { proto::Server {
protocol: Some(protocol), protocol: Some(protocol),
authorizations, authorizations,
labels,
..Default::default() ..Default::default()
} }
} }
@ -233,59 +233,32 @@ fn to_authz(
.collect() .collect()
}; };
match authentication { let labels = vec![("name".to_string(), name.to_string())]
ClientAuthentication::Unauthenticated => { .into_iter()
let labels = Labels { .collect();
authn: false,
tls: false, let authn = match authentication {
name: name.to_string(), ClientAuthentication::Unauthenticated => proto::Authn {
};
proto::Authz {
networks,
labels: labels.into(),
authentication: Some(proto::Authn {
permit: Some(proto::authn::Permit::Unauthenticated( permit: Some(proto::authn::Permit::Unauthenticated(
proto::authn::PermitUnauthenticated {}, proto::authn::PermitUnauthenticated {},
)), )),
}), },
}
}
ClientAuthentication::TlsUnauthenticated => { ClientAuthentication::TlsUnauthenticated => proto::Authn {
let labels = Labels {
authn: false,
tls: true,
name: name.to_string(),
};
proto::Authz {
networks,
labels: labels.into(),
authentication: Some(proto::Authn {
permit: Some(proto::authn::Permit::MeshTls(proto::authn::PermitMeshTls { permit: Some(proto::authn::Permit::MeshTls(proto::authn::PermitMeshTls {
clients: Some(proto::authn::permit_mesh_tls::Clients::Unauthenticated( clients: Some(proto::authn::permit_mesh_tls::Clients::Unauthenticated(
proto::authn::PermitUnauthenticated {}, proto::authn::PermitUnauthenticated {},
)), )),
})), })),
}), },
}
}
// Authenticated connections must have TLS and apply to all // Authenticated connections must have TLS and apply to all
// networks. // networks.
ClientAuthentication::TlsAuthenticated(identities) => { ClientAuthentication::TlsAuthenticated(identities) => {
let labels = Labels {
authn: true,
tls: true,
name: name.to_string(),
};
let authn = {
let suffixes = identities let suffixes = identities
.iter() .iter()
.filter_map(|i| match i { .filter_map(|i| match i {
IdentityMatch::Suffix(s) => { IdentityMatch::Suffix(s) => Some(proto::IdentitySuffix { parts: s.to_vec() }),
Some(proto::IdentitySuffix { parts: s.to_vec() })
}
_ => None, _ => None,
}) })
.collect(); .collect();
@ -310,27 +283,12 @@ fn to_authz(
)), )),
})), })),
} }
}
}; };
proto::Authz { proto::Authz {
networks, networks,
labels: labels.into(), labels,
authentication: Some(authn), authentication: Some(authn),
} }
} }
}
}
// === impl Labels ===
impl From<Labels> for HashMap<String, String> {
fn from(labels: Labels) -> HashMap<String, String> {
vec![
("authn".to_string(), labels.authn.to_string()),
("tls".to_string(), labels.tls.to_string()),
("name".to_string(), labels.name),
]
.into_iter()
.collect()
}
}

View File

@ -171,6 +171,7 @@ impl DefaultPolicyWatches {
} }
DefaultPolicy::Deny => InboundServer { DefaultPolicy::Deny => InboundServer {
name: "default:deny".to_string(),
protocol, protocol,
authorizations: Default::default(), authorizations: Default::default(),
}, },
@ -218,6 +219,7 @@ impl DefaultPolicyWatches {
}; };
InboundServer { InboundServer {
name: name.clone(),
protocol, protocol,
authorizations: Some((name, authz)).into_iter().collect(), authorizations: Some((name, authz)).into_iter().collect(),
} }

View File

@ -191,6 +191,7 @@ impl SrvIndex {
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
debug!(authzs = ?authzs.keys()); debug!(authzs = ?authzs.keys());
let (tx, rx) = watch::channel(InboundServer { let (tx, rx) = watch::channel(InboundServer {
name: entry.key().clone(),
protocol: protocol.clone(), protocol: protocol.clone(),
authorizations: authzs.clone(), authorizations: authzs.clone(),
}); });

View File

@ -40,15 +40,13 @@ async fn incrementally_configure_server() {
); );
idx.apply_pod(pod.clone()).unwrap(); idx.apply_pod(pod.clone()).unwrap();
let default_config = InboundServer { let default = DefaultPolicy::Allow {
authorizations: mk_default_policy(
DefaultPolicy::Allow {
authenticated_only: false, authenticated_only: false,
cluster_only: true, cluster_only: true,
}, };
cluster_net, let default_config = InboundServer {
kubelet_ip, name: format!("default:{}", default),
), authorizations: mk_default_policy(default, cluster_net, kubelet_ip),
protocol: ProxyProtocol::Detect { protocol: ProxyProtocol::Detect {
timeout: detect_timeout, timeout: detect_timeout,
}, },
@ -77,6 +75,7 @@ async fn incrementally_configure_server() {
// Check that the watch has been updated to reflect the above change and that this change _only_ // Check that the watch has been updated to reflect the above change and that this change _only_
// applies to the correct port. // applies to the correct port.
let basic_config = InboundServer { let basic_config = InboundServer {
name: "srv-0".into(),
protocol: ProxyProtocol::Http1, protocol: ProxyProtocol::Http1,
authorizations: vec![healthcheck_authz(kubelet_ip)].into_iter().collect(), authorizations: vec![healthcheck_authz(kubelet_ip)].into_iter().collect(),
}; };
@ -103,6 +102,7 @@ async fn incrementally_configure_server() {
assert_eq!( assert_eq!(
time::timeout(time::Duration::from_secs(1), rx.next()).await, time::timeout(time::Duration::from_secs(1), rx.next()).await,
Ok(Some(InboundServer { Ok(Some(InboundServer {
name: "srv-0".into(),
protocol: ProxyProtocol::Http1, protocol: ProxyProtocol::Http1,
authorizations: vec![ authorizations: vec![
( (
@ -150,13 +150,14 @@ fn server_update_deselects_pod() {
(ips.next().unwrap(), ips.next().unwrap()) (ips.next().unwrap(), ips.next().unwrap())
}; };
let detect_timeout = time::Duration::from_secs(1); let detect_timeout = time::Duration::from_secs(1);
let default = DefaultPolicy::Allow {
authenticated_only: false,
cluster_only: true,
};
let (lookup_rx, mut idx) = Index::new( let (lookup_rx, mut idx) = Index::new(
vec![cluster_net], vec![cluster_net],
"cluster.example.com".into(), "cluster.example.com".into(),
DefaultPolicy::Allow { default,
authenticated_only: false,
cluster_only: true,
},
detect_timeout, detect_timeout,
); );
@ -182,6 +183,7 @@ fn server_update_deselects_pod() {
assert_eq!( assert_eq!(
port2222.get(), port2222.get(),
InboundServer { InboundServer {
name: "srv-0".into(),
protocol: ProxyProtocol::Http2, protocol: ProxyProtocol::Http2,
authorizations: vec![healthcheck_authz(kubelet_ip)].into_iter().collect(), authorizations: vec![healthcheck_authz(kubelet_ip)].into_iter().collect(),
} }
@ -195,14 +197,8 @@ fn server_update_deselects_pod() {
assert_eq!( assert_eq!(
port2222.get(), port2222.get(),
InboundServer { InboundServer {
authorizations: mk_default_policy( name: format!("default:{}", default),
DefaultPolicy::Allow { authorizations: mk_default_policy(default, cluster_net, kubelet_ip),
authenticated_only: false,
cluster_only: true,
},
cluster_net,
kubelet_ip
),
protocol: ProxyProtocol::Detect { protocol: ProxyProtocol::Detect {
timeout: detect_timeout, timeout: detect_timeout,
}, },
@ -243,6 +239,7 @@ fn default_policy_global() {
idx.reset_pods(vec![p]).unwrap(); idx.reset_pods(vec![p]).unwrap();
let config = InboundServer { let config = InboundServer {
name: format!("default:{}", default),
authorizations: mk_default_policy(*default, cluster_net, kubelet_ip), authorizations: mk_default_policy(*default, cluster_net, kubelet_ip),
protocol: ProxyProtocol::Detect { protocol: ProxyProtocol::Detect {
timeout: detect_timeout, timeout: detect_timeout,
@ -300,6 +297,7 @@ fn default_policy_annotated() {
idx.reset_pods(vec![p]).unwrap(); idx.reset_pods(vec![p]).unwrap();
let config = InboundServer { let config = InboundServer {
name: format!("default:{}", default),
authorizations: mk_default_policy(*default, cluster_net, kubelet_ip), authorizations: mk_default_policy(*default, cluster_net, kubelet_ip),
protocol: ProxyProtocol::Detect { protocol: ProxyProtocol::Detect {
timeout: detect_timeout, timeout: detect_timeout,
@ -323,13 +321,14 @@ fn default_policy_annotated_invalid() {
}; };
let detect_timeout = time::Duration::from_secs(1); let detect_timeout = time::Duration::from_secs(1);
let default = DefaultPolicy::Allow {
authenticated_only: false,
cluster_only: false,
};
let (lookup_rx, mut idx) = Index::new( let (lookup_rx, mut idx) = Index::new(
vec![cluster_net], vec![cluster_net],
"cluster.example.com".into(), "cluster.example.com".into(),
DefaultPolicy::Allow { default,
authenticated_only: false,
cluster_only: false,
},
detect_timeout, detect_timeout,
); );
@ -353,6 +352,7 @@ fn default_policy_annotated_invalid() {
assert_eq!( assert_eq!(
port2222.get(), port2222.get(),
InboundServer { InboundServer {
name: format!("default:{}", default),
authorizations: mk_default_policy( authorizations: mk_default_policy(
DefaultPolicy::Allow { DefaultPolicy::Allow {
authenticated_only: false, authenticated_only: false,
@ -400,6 +400,7 @@ fn opaque_annotated() {
idx.reset_pods(vec![p]).unwrap(); idx.reset_pods(vec![p]).unwrap();
let config = InboundServer { let config = InboundServer {
name: format!("default:{}", default),
authorizations: mk_default_policy(*default, cluster_net, kubelet_ip), authorizations: mk_default_policy(*default, cluster_net, kubelet_ip),
protocol: ProxyProtocol::Opaque, protocol: ProxyProtocol::Opaque,
}; };
@ -444,21 +445,21 @@ fn authenticated_annotated() {
); );
idx.reset_pods(vec![p]).unwrap(); idx.reset_pods(vec![p]).unwrap();
let config = InboundServer { let config = {
authorizations: mk_default_policy( let policy = match *default {
match *default {
DefaultPolicy::Allow { cluster_only, .. } => DefaultPolicy::Allow { DefaultPolicy::Allow { cluster_only, .. } => DefaultPolicy::Allow {
cluster_only, cluster_only,
authenticated_only: true, authenticated_only: true,
}, },
DefaultPolicy::Deny => DefaultPolicy::Deny, DefaultPolicy::Deny => DefaultPolicy::Deny,
}, };
cluster_net, InboundServer {
kubelet_ip, name: format!("default:{}", policy),
), authorizations: mk_default_policy(policy, cluster_net, kubelet_ip),
protocol: ProxyProtocol::Detect { protocol: ProxyProtocol::Detect {
timeout: detect_timeout, timeout: detect_timeout,
}, },
}
}; };
let port2222 = lookup_rx let port2222 = lookup_rx