diff --git a/policy-controller/k8s/api/src/policy/server.rs b/policy-controller/k8s/api/src/policy/server.rs index ff42ad8937a12..bfa04fefb0dce 100644 --- a/policy-controller/k8s/api/src/policy/server.rs +++ b/policy-controller/k8s/api/src/policy/server.rs @@ -18,6 +18,7 @@ pub struct ServerSpec { pub selector: Selector, pub port: Port, pub proxy_protocol: Option, + pub access_policy: Option, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema)] diff --git a/policy-controller/k8s/index/src/defaults.rs b/policy-controller/k8s/index/src/defaults.rs index f17c1a22a595b..65d87502164cf 100644 --- a/policy-controller/k8s/index/src/defaults.rs +++ b/policy-controller/k8s/index/src/defaults.rs @@ -21,6 +21,9 @@ pub enum DefaultPolicy { /// Indicates that all traffic is denied unless explicitly permitted by an authorization policy. Deny, + + /// Indicates that all traffic is let through, but gets audited + Audit, } // === impl DefaultPolicy === @@ -47,6 +50,7 @@ impl std::str::FromStr for DefaultPolicy { cluster_only: true, }), "deny" => Ok(Self::Deny), + "audit" => Ok(Self::Audit), s => Err(anyhow!("invalid mode: {:?}", s)), } } @@ -72,6 +76,7 @@ impl DefaultPolicy { cluster_only: true, } => "cluster-unauthenticated", Self::Deny => "deny", + Self::Audit => "audit", } } @@ -80,32 +85,35 @@ impl DefaultPolicy { config: &ClusterInfo, ) -> HashMap { let mut authzs = HashMap::default(); - if let DefaultPolicy::Allow { - authenticated_only, - cluster_only, - } = self - { - let authentication = if authenticated_only { - ClientAuthentication::TlsAuthenticated(vec![IdentityMatch::Suffix(vec![])]) - } else { - ClientAuthentication::Unauthenticated - }; - let networks = if cluster_only { - config.networks.iter().copied().map(Into::into).collect() - } else { - vec![ - "0.0.0.0/0".parse::().unwrap().into(), - "::/0".parse::().unwrap().into(), - ] - }; - authzs.insert( - AuthorizationRef::Default(self.as_str()), - ClientAuthorization { - authentication, - networks, - }, - ); + let (authenticated_only, cluster_only) = match self { + DefaultPolicy::Allow { + authenticated_only, + cluster_only, + } => (authenticated_only, cluster_only), + DefaultPolicy::Deny => return authzs, + DefaultPolicy::Audit => (false, false), }; + + let authentication = if authenticated_only { + ClientAuthentication::TlsAuthenticated(vec![IdentityMatch::Suffix(vec![])]) + } else { + ClientAuthentication::Unauthenticated + }; + let networks = if cluster_only { + config.networks.iter().copied().map(Into::into).collect() + } else { + vec![ + "0.0.0.0/0".parse::().unwrap().into(), + "::/0".parse::().unwrap().into(), + ] + }; + authzs.insert( + AuthorizationRef::Default(self.as_str()), + ClientAuthorization { + authentication, + networks, + }, + ); authzs } } @@ -140,6 +148,7 @@ mod test { authenticated_only: false, cluster_only: true, }, + DefaultPolicy::Audit, ] { assert_eq!( default.to_string().parse::().unwrap(), diff --git a/policy-controller/k8s/index/src/inbound/index.rs b/policy-controller/k8s/index/src/inbound/index.rs index a374010014f24..06d3022f91b40 100644 --- a/policy-controller/k8s/index/src/inbound/index.rs +++ b/policy-controller/k8s/index/src/inbound/index.rs @@ -1753,6 +1753,12 @@ impl PolicyIndex { authzs.insert(reference, authz); } + if let Some(p @ DefaultPolicy::Allow { .. }) | Some(p @ DefaultPolicy::Audit) = + server.access_policy + { + authzs.extend(p.default_authzs(&self.cluster_info)); + } + authzs } diff --git a/policy-controller/k8s/index/src/inbound/server.rs b/policy-controller/k8s/index/src/inbound/server.rs index 04c58d47e3729..ac984dd4dcadb 100644 --- a/policy-controller/k8s/index/src/inbound/server.rs +++ b/policy-controller/k8s/index/src/inbound/server.rs @@ -1,4 +1,4 @@ -use crate::ClusterInfo; +use crate::{ClusterInfo, DefaultPolicy}; use linkerd_policy_controller_core::inbound::ProxyProtocol; use linkerd_policy_controller_k8s_api::{ self as k8s, policy::server::Port, policy::server::Selector, @@ -11,6 +11,7 @@ pub(crate) struct Server { pub selector: Selector, pub port_ref: Port, pub protocol: ProxyProtocol, + pub access_policy: Option, } impl Server { @@ -20,6 +21,7 @@ impl Server { selector: srv.spec.selector, port_ref: srv.spec.port, protocol: proxy_protocol(srv.spec.proxy_protocol, cluster), + access_policy: srv.spec.access_policy.and_then(|p| p.parse().ok()), } } } diff --git a/policy-controller/src/admission.rs b/policy-controller/src/admission.rs index 237a4cdd5e803..537b3017561b6 100644 --- a/policy-controller/src/admission.rs +++ b/policy-controller/src/admission.rs @@ -319,7 +319,8 @@ impl Validate for Admission { #[async_trait::async_trait] impl Validate for Admission { - /// Checks that `spec` doesn't select the same pod/ports as other existing Servers + /// Checks that `spec` doesn't select the same pod/ports as other existing Servers, and that + /// `accessPolicy` contains a valid value // // TODO(ver) this isn't rigorous about detecting servers that select the same port if one port // specifies a numeric port and the other specifies the port's name. @@ -346,6 +347,14 @@ impl Validate for Admission { } } + if let Some(Err(_)) = spec + .clone() + .access_policy + .map(|p| p.parse::()) + { + bail!("Invalid accessPolicy \"{}\"", spec.access_policy.unwrap()); + } + Ok(()) } }