Skip to content

Commit

Permalink
Policy controller suggestions (for #8795) (#8858)
Browse files Browse the repository at this point in the history
* Policy controller suggestions

* core: Use `http` crate instead of `hyper`'s re-export. `http` is just
  the core types. `hyper` includes client/server infrastructure which
  isn't needed. We already pull in both so there's practically no
  functional difference.
* core: Rename `Hostname` to `HostMatch` to be consistent with API
  types.
* core: Rename `HttpRoute`, `HttpFilter`, etc to `Inbound*`. These types
  are specific to inbound policies. We wouldn't use the same types for
  outbound policies.
* core: Split individual filter types from the `InboundFilter` type so
  that the `InboundFilter` type doesn't hold all of the details for all
  of the filters.
* core: Make `HeaderMatch` hold `HeaderName` and `HeaderValue` so that
  we can rely on the validation from these libraries. Notably,
  `Headervalue` does not necessarily hold a string.
* core: Make `QueryParamMatch` an enum, since the `Value` type would
  only have that one use now.

* index: Rename `RouteBinding` to `InboundRouteBinding`, as it holds
  inbound-specific route configuration.
* index: Add a `InboundParentRef` type that describes a validated parent
  reference.
* index: Update `InboundRouteBinding::try_from` to validate parent
  references and fail reading routes that do not reference servers.

* grpc: Move general `http_route` conversions into a dedicated module
  (to simplify inbound coverters).

* Cleanup imports as much as possible, shortening module names with
  aliases where possible. Because we're frequently converting between
  different representations of the same types, it's helpful to reference
  the modules explicitly rather than relying on large sets of imports.
* Where possible, we destructure types to document that we are handling
  all fields on a type.
* Update deny.toml for git dependency

Signed-off-by: Oliver Gould <ver@buoyant.io>

* Upgrade to moment 2.29.4 (#8856)

Signed-off-by: Alex Leong <alex@buoyant.io>

* build(deps): bump google.golang.org/grpc from 1.47.0 to 1.48.0 (#8857)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.47.0 to 1.48.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](grpc/grpc-go@v1.47.0...v1.48.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add port to helm Values (#8855)

Fix `linkerd-viz` helm chart documentation for jaeger integration.

Adds miss port to jaeger url example in `value.yaml`. This port is required to
allow the dashboard to proxy to the jaeger instance. This brings the example
given in the `values.yaml` file in line with the web docs.

Closes #8851

Signed-off-by: Harry Walter <harry@bluebamboostudios.com>

* policy: Index authorization policies with no authentications (#8865)

In 1a0c1c3 we updated the admission controller to allow
`AuthorizationPolicy` resources with an empty
`requiredAuthenticationRefs`. But we did NOT update the indexer, so we
would allow these resources to be created but then fail to honor them in
the API.

To fix this:

1. The `AuthorizationPolicy` admission controller is updated to exercise
   the indexer's validation so that it is impossible to admit resources
   that will be discarded by the indexer;
2. An e2e test is added to exercise this configuration;
3. The indexer's validation is updated to accept resources with no
   authentications.

Signed-off-by: Oliver Gould <ver@buoyant.io>

* Simply ignore non-server parent refs when indexing

Signed-off-by: Oliver Gould <ver@buoyant.io>

Co-authored-by: Alex Leong <alex@buoyant.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Harry Walter <harry.walter@lqdinternet.com>
  • Loading branch information
4 people committed Jul 13, 2022
1 parent 1df6259 commit 26cc102
Show file tree
Hide file tree
Showing 21 changed files with 463 additions and 340 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock
Expand Up @@ -982,7 +982,7 @@ dependencies = [
"anyhow",
"async-trait",
"futures",
"hyper",
"http",
"ipnet",
"regex",
]
Expand Down Expand Up @@ -1033,6 +1033,7 @@ dependencies = [
"linkerd-policy-controller-k8s-api",
"maplit",
"parking_lot",
"thiserror",
"tokio",
"tokio-stream",
"tokio-test",
Expand Down Expand Up @@ -1069,7 +1070,7 @@ dependencies = [
[[package]]
name = "linkerd2-proxy-api"
version = "0.5.0"
source = "git+https://github.com/linkerd/linkerd2-proxy-api?branch=ver/rautz#c8d0904fc73ab1f4279878b2b7e5113d7b1a1169"
source = "git+https://github.com/linkerd/linkerd2-proxy-api?branch=ver/rautz#6e97a203d8245fc258cf14ae4304738aa555aa90"
dependencies = [
"http",
"ipnet",
Expand Down
6 changes: 6 additions & 0 deletions deny.toml
Expand Up @@ -49,3 +49,9 @@ unknown-registry = "deny"
unknown-git = "deny"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
allow-git = []

[sources.allow-org]
github = [
"linkerd",
]

2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -38,7 +38,7 @@ require (
go.opencensus.io v0.23.0
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/tools v0.1.11
google.golang.org/grpc v1.47.0
google.golang.org/grpc v1.48.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
google.golang.org/protobuf v1.28.0
helm.sh/helm/v3 v3.9.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -1583,8 +1583,8 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
Expand Down
2 changes: 1 addition & 1 deletion policy-controller/core/Cargo.toml
Expand Up @@ -10,6 +10,6 @@ ahash = "0.7"
anyhow = "1"
async-trait = "0.1"
futures = { version = "0.3", default-features = false, features = ["std"] }
hyper = { version = "0.14" }
http = "0.2"
ipnet = "2"
regex = "1"
108 changes: 63 additions & 45 deletions policy-controller/core/src/http_route.rs
@@ -1,45 +1,57 @@
use ahash::AHashMap as HashMap;
use anyhow::Result;
pub use hyper::http::{uri::Scheme, Method, StatusCode};
pub use http::{
header::{HeaderName, HeaderValue},
uri::Scheme,
Method, StatusCode,
};
use regex::Regex;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HttpRoute {
pub hostnames: Vec<Hostname>,
pub rules: Vec<HttpRouteRule>,
pub struct InboundHttpRoute {
pub hostnames: Vec<HostMatch>,
pub rules: Vec<InboundHttpRouteRule>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Hostname {
pub enum HostMatch {
Exact(String),
Suffix { reverse_labels: Vec<String> },
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HttpRouteRule {
pub struct InboundHttpRouteRule {
pub matches: Vec<HttpRouteMatch>,
pub filters: Vec<HttpFilter>,
pub filters: Vec<InboundFilter>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HttpFilter {
RequestHeaderModifier {
add: HashMap<String, String>,
set: HashMap<String, String>,
remove: Vec<String>,
},
RequestRedirect {
scheme: Option<Scheme>,
host: Option<String>,
path: Option<PathModifier>,
port: Option<u32>,
status: Option<StatusCode>,
},
HttpFailureInjector {
status: StatusCode,
message: String,
ratio: Ratio,
},
pub enum InboundFilter {
RequestHeaderModifier(RequestHeaderModifierFilter),
RequestRedirect(RequestRedirectFilter),
FailureInjector(FailureInjectorFilter),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RequestHeaderModifierFilter {
pub add: Vec<(HeaderName, HeaderValue)>,
pub set: Vec<(HeaderName, HeaderValue)>,
pub remove: Vec<HeaderName>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RequestRedirectFilter {
pub scheme: Option<Scheme>,
pub host: Option<String>,
pub path: Option<PathModifier>,
pub port: Option<u32>,
pub status: Option<StatusCode>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FailureInjectorFilter {
pub status: StatusCode,
pub message: String,
pub ratio: Ratio,
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -69,23 +81,19 @@ pub enum PathMatch {
Regex(Regex),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HeaderMatch {
pub name: String,
pub value: Value,
#[derive(Clone, Debug)]
pub enum HeaderMatch {
Exact(HeaderName, HeaderValue),
Regex(HeaderName, Regex),
}

#[derive(Clone, Debug)]
pub enum Value {
Exact(String),
Regex(Regex),
pub enum QueryParamMatch {
Exact(String, String),
Regex(String, Regex),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryParamMatch {
pub name: String,
pub value: Value,
}
// === impl PathMatch ===

impl PartialEq for PathMatch {
fn eq(&self, other: &Self) -> bool {
Expand All @@ -106,20 +114,30 @@ impl PathMatch {
}
}

impl PartialEq for Value {
// === impl HeaderMatch ===

impl PartialEq for HeaderMatch {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Exact(l0), Self::Exact(r0)) => l0 == r0,
(Self::Regex(l0), Self::Regex(r0)) => l0.as_str() == r0.as_str(),
(Self::Exact(n0, v0), Self::Exact(n1, v1)) => n0 == n1 && v0 == v1,
(Self::Regex(n0, r0), Self::Regex(n1, r1)) => n0 == n1 && r0.as_str() == r1.as_str(),
_ => false,
}
}
}

impl Eq for Value {}
impl Eq for HeaderMatch {}

impl Value {
pub fn regex(s: &str) -> Result<Self> {
Ok(Self::Regex(Regex::new(s)?))
// === impl QueryParamMatch ===

impl PartialEq for QueryParamMatch {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Exact(n0, v0), Self::Exact(n1, v1)) => n0 == n1 && v0 == v1,
(Self::Regex(n0, r0), Self::Regex(n1, r1)) => n0 == n1 && r0.as_str() == r1.as_str(),
_ => false,
}
}
}

impl Eq for QueryParamMatch {}
6 changes: 4 additions & 2 deletions policy-controller/core/src/lib.rs
Expand Up @@ -5,7 +5,9 @@ pub mod http_route;
mod identity_match;
mod network_match;

pub use self::{http_route::HttpRoute, identity_match::IdentityMatch, network_match::NetworkMatch};
pub use self::{
http_route::InboundHttpRoute, identity_match::IdentityMatch, network_match::NetworkMatch,
};
use ahash::AHashMap as HashMap;
use anyhow::Result;
use futures::prelude::*;
Expand All @@ -29,7 +31,7 @@ pub struct InboundServer {

pub protocol: ProxyProtocol,
pub authorizations: HashMap<AuthorizationRef, ClientAuthorization>,
pub http_routes: HashMap<String, HttpRoute>,
pub http_routes: HashMap<String, InboundHttpRoute>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down
136 changes: 136 additions & 0 deletions policy-controller/grpc/src/http_route.rs
@@ -0,0 +1,136 @@
use linkerd2_proxy_api::{http_route as proto, http_types};
use linkerd_policy_controller_core::http_route::{
FailureInjectorFilter, HeaderMatch, HostMatch, HttpRouteMatch, PathMatch, PathModifier,
QueryParamMatch, RequestHeaderModifierFilter, RequestRedirectFilter,
};

pub(crate) fn convert_host_match(h: HostMatch) -> proto::HostMatch {
proto::HostMatch {
r#match: Some(match h {
HostMatch::Exact(host) => proto::host_match::Match::Exact(host),
HostMatch::Suffix { reverse_labels } => {
proto::host_match::Match::Suffix(proto::host_match::Suffix {
reverse_labels: reverse_labels.to_vec(),
})
}
}),
}
}

pub(crate) fn convert_match(
HttpRouteMatch {
headers,
path,
query_params,
method,
}: HttpRouteMatch,
) -> proto::HttpRouteMatch {
let headers = headers
.into_iter()
.map(|hm| match hm {
HeaderMatch::Exact(name, value) => proto::HeaderMatch {
name: name.to_string(),
value: Some(proto::header_match::Value::Exact(value.as_bytes().to_vec())),
},
HeaderMatch::Regex(name, re) => proto::HeaderMatch {
name: name.to_string(),
value: Some(proto::header_match::Value::Regex(re.to_string())),
},
})
.collect();

let path = path.map(|path| proto::PathMatch {
kind: Some(match path {
PathMatch::Exact(path) => proto::path_match::Kind::Exact(path),
PathMatch::Prefix(prefix) => proto::path_match::Kind::Prefix(prefix),
PathMatch::Regex(regex) => proto::path_match::Kind::Regex(regex.to_string()),
}),
});

let query_params = query_params
.into_iter()
.map(|qpm| match qpm {
QueryParamMatch::Exact(name, value) => proto::QueryParamMatch {
name,
value: Some(proto::query_param_match::Value::Exact(value)),
},
QueryParamMatch::Regex(name, re) => proto::QueryParamMatch {
name,
value: Some(proto::query_param_match::Value::Regex(re.to_string())),
},
})
.collect();

proto::HttpRouteMatch {
headers,
path,
query_params,
method: method.map(Into::into),
}
}

pub(crate) fn convert_failure_injector_filter(
FailureInjectorFilter {
status,
message,
ratio,
}: FailureInjectorFilter,
) -> proto::HttpFailureInjector {
proto::HttpFailureInjector {
status: u32::from(status.as_u16()),
message,
ratio: Some(proto::Ratio {
numerator: ratio.numerator,
denominator: ratio.denominator,
}),
}
}

pub(crate) fn convert_header_modifier_filter(
RequestHeaderModifierFilter { add, set, remove }: RequestHeaderModifierFilter,
) -> proto::RequestHeaderModifier {
proto::RequestHeaderModifier {
add: Some(http_types::Headers {
headers: add
.into_iter()
.map(|(n, v)| http_types::headers::Header {
name: n.to_string(),
value: v.as_bytes().to_owned(),
})
.collect(),
}),
set: Some(http_types::Headers {
headers: set
.into_iter()
.map(|(n, v)| http_types::headers::Header {
name: n.to_string(),
value: v.as_bytes().to_owned(),
})
.collect(),
}),
remove: remove.into_iter().map(|n| n.to_string()).collect(),
}
}

pub(crate) fn convert_redirect_filter(
RequestRedirectFilter {
scheme,
host,
path,
port,
status,
}: RequestRedirectFilter,
) -> proto::RequestRedirect {
proto::RequestRedirect {
scheme: scheme.map(|ref s| s.into()),
host: host.unwrap_or_default(),
path: path.map(|pm| proto::PathModifier {
replace: Some(match pm {
PathModifier::Full(p) => proto::path_modifier::Replace::Full(p),
PathModifier::Prefix(p) => proto::path_modifier::Replace::Prefix(p),
}),
}),
port: port.unwrap_or_default(),
status: u32::from(status.unwrap_or_default().as_u16()),
}
}

0 comments on commit 26cc102

Please sign in to comment.