diff --git a/tembo-operator/justfile b/tembo-operator/justfile index 94a3268c1..344b77a6b 100644 --- a/tembo-operator/justfile +++ b/tembo-operator/justfile @@ -21,6 +21,10 @@ install-traefik: helm upgrade --install --namespace=traefik --version=20.8.0 --values=./testdata/traefik-values.yaml traefik traefik/traefik # https://github.com/traefik/traefik-helm-chart/issues/757#issuecomment-1753995542 kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.0.0-beta2/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml + # Temporary until we upgrade to v3. + # First, we have both CRD versions installed, but only using old version. + # Then, we upgrade traefik, which cuts over to using the new CRDs, at that point this can be deleted. + kubectl apply -f ./testdata/traefik-v3.yaml install-operator: just install-cert-manager diff --git a/tembo-operator/src/app_service/ingress_v3.rs b/tembo-operator/src/app_service/ingress_v3.rs new file mode 100644 index 000000000..4d5aade65 --- /dev/null +++ b/tembo-operator/src/app_service/ingress_v3.rs @@ -0,0 +1,558 @@ +// TODO: Delete file after API migration +use super::{ + manager::to_delete, + types::{AppService, Middleware, COMPONENT_NAME}, +}; +use crate::traefik::ingress_route_crd_v3::{ + IngressRoute, IngressRouteRoutes, IngressRouteRoutesKind, IngressRouteRoutesMiddlewares, + IngressRouteRoutesServices, IngressRouteRoutesServicesKind, IngressRouteSpec, IngressRouteTls, +}; +use crate::traefik::ingress_route_tcp_crd_v3::{ + IngressRouteTCP, IngressRouteTCPRoutes, IngressRouteTCPRoutesMiddlewares, + IngressRouteTCPRoutesServices, IngressRouteTCPSpec, IngressRouteTCPTls, +}; +use crate::traefik::middlewares_crd_v3::{ + Middleware as TraefikMiddleware, MiddlewareHeaders, MiddlewareReplacePathRegex, MiddlewareSpec, + MiddlewareStripPrefix, +}; +use crate::Result; +use k8s_openapi::apimachinery::pkg::{apis::meta::v1::OwnerReference, util::intstr::IntOrString}; +use kube::{ + api::{Api, ListParams, ObjectMeta, Patch, PatchParams}, + Client, +}; +use std::collections::BTreeMap; +use tracing::{debug, error}; + +use crate::app_service::types::IngressType; + +#[derive(Clone, Debug)] +pub struct MiddleWareWrapper { + pub name: String, + pub mw: TraefikMiddleware, +} + +fn generate_ingress( + coredb_name: &str, + namespace: &str, + oref: OwnerReference, + routes: Vec, + entry_points: Vec, +) -> IngressRoute { + let mut labels: BTreeMap = BTreeMap::new(); + labels.insert("component".to_owned(), COMPONENT_NAME.to_string()); + labels.insert("coredb.io/name".to_owned(), coredb_name.to_string()); + + IngressRoute { + metadata: ObjectMeta { + // using coredb name, since we'll have 1x ingress per coredb + name: Some(coredb_name.to_owned()), + namespace: Some(namespace.to_owned()), + owner_references: Some(vec![oref]), + labels: Some(labels.clone()), + ..ObjectMeta::default() + }, + spec: IngressRouteSpec { + entry_points: Some(entry_points), + routes, + tls: Some(IngressRouteTls::default()), + }, + } +} + +fn generate_ingress_tcp( + name: &str, + namespace: &str, + oref: OwnerReference, + routes: Vec, + entry_points: Vec, +) -> IngressRouteTCP { + let mut labels: BTreeMap = BTreeMap::new(); + labels.insert("component".to_owned(), COMPONENT_NAME.to_string()); + labels.insert("coredb.io/name".to_owned(), name.to_string()); + + IngressRouteTCP { + metadata: ObjectMeta { + // using coredb name, since we'll have 1x ingress per coredb + name: Some(name.to_string()), + namespace: Some(namespace.to_owned()), + owner_references: Some(vec![oref]), + labels: Some(labels.clone()), + ..ObjectMeta::default() + }, + spec: IngressRouteTCPSpec { + entry_points: Some(entry_points), + routes, + tls: Some(IngressRouteTCPTls { + passthrough: Some(true), + ..IngressRouteTCPTls::default() + }), + }, + } +} + +// creates traefik middleware objects +// named `-` +fn generate_middlewares( + coredb_name: &str, + namespace: &str, + oref: OwnerReference, + middlewares: Vec, +) -> Vec { + let mut traefik_middlwares: Vec = Vec::new(); + let mut labels: BTreeMap = BTreeMap::new(); + labels.insert("component".to_owned(), COMPONENT_NAME.to_string()); + labels.insert("coredb.io/name".to_owned(), coredb_name.to_string()); + + for mw in middlewares { + let traefik_mw = match mw { + Middleware::CustomRequestHeaders(mw) => { + let mw_name = format!("{}-{}", coredb_name, mw.name); + let mwh = MiddlewareHeaders { + custom_request_headers: Some(mw.config), + ..MiddlewareHeaders::default() + }; + let tmw = TraefikMiddleware { + metadata: ObjectMeta { + name: Some(mw_name.clone()), + namespace: Some(namespace.to_owned()), + owner_references: Some(vec![oref.clone()]), + labels: Some(labels.clone()), + ..ObjectMeta::default() + }, + spec: MiddlewareSpec { + headers: Some(mwh), + ..MiddlewareSpec::default() + }, + }; + MiddleWareWrapper { + name: mw_name, + mw: tmw, + } + } + Middleware::StripPrefix(mw) => { + let mw_name = format!("{}-{}", coredb_name, mw.name); + let mwsp = MiddlewareStripPrefix { + force_slash: None, + prefixes: Some(mw.config), + }; + let tmw = TraefikMiddleware { + metadata: ObjectMeta { + name: Some(mw_name.clone()), + namespace: Some(namespace.to_owned()), + owner_references: Some(vec![oref.clone()]), + labels: Some(labels.clone()), + ..ObjectMeta::default() + }, + spec: MiddlewareSpec { + strip_prefix: Some(mwsp), + ..MiddlewareSpec::default() + }, + }; + MiddleWareWrapper { + name: mw_name, + mw: tmw, + } + } + Middleware::ReplacePathRegex(mw) => { + let mw_name = format!("{}-{}", coredb_name, mw.name); + let mwrpr = MiddlewareReplacePathRegex { + regex: Some(mw.config.regex), + replacement: Some(mw.config.replacement), + }; + let tmw = TraefikMiddleware { + metadata: ObjectMeta { + name: Some(mw_name.clone()), + namespace: Some(namespace.to_owned()), + owner_references: Some(vec![oref.clone()]), + labels: Some(labels.clone()), + ..ObjectMeta::default() + }, + spec: MiddlewareSpec { + replace_path_regex: Some(mwrpr), + ..MiddlewareSpec::default() + }, + }; + MiddleWareWrapper { + name: mw_name, + mw: tmw, + } + } + }; + traefik_middlwares.push(traefik_mw); + } + traefik_middlwares +} + +// generates Kubernetes IngressRoute template for an appService +// maps the specified +pub fn generate_ingress_routes( + appsvc: &AppService, + resource_name: &str, + namespace: &str, + host_matcher: String, + coredb_name: &str, +) -> Option> { + match appsvc.routing.clone() { + Some(routings) => { + let mut routes: Vec = Vec::new(); + for route in routings.iter() { + match route.ingress_path.clone() { + Some(path) => { + if route.ingress_type.clone()?.eq(&IngressType::tcp) { + // Do not create IngressRouteRoutes for TCP ingress type + debug!("Skipping IngressRouteRoutes for TCP ingress type"); + continue; + } + + let matcher = format!("{host_matcher} && PathPrefix(`{}`)", path); + let middlewares: Option> = + route.middlewares.clone().map(|names| { + names + .into_iter() + .map(|m| IngressRouteRoutesMiddlewares { + name: format!("{}-{}", &coredb_name, m), + namespace: Some(namespace.to_owned()), + }) + .collect() + }); + let route = IngressRouteRoutes { + kind: IngressRouteRoutesKind::Rule, + r#match: matcher.clone(), + services: Some(vec![IngressRouteRoutesServices { + name: resource_name.to_string(), + port: Some(IntOrString::Int(route.port as i32)), + // namespace attribute is NOT a kubernetes namespace + // it is the Traefik provider namespace: https://doc.traefik.io/traefik/v3.0/providers/overview/#provider-namespace + // https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware + namespace: None, + kind: Some(IngressRouteRoutesServicesKind::Service), + ..IngressRouteRoutesServices::default() + }]), + middlewares, + priority: None, + syntax: None, + }; + routes.push(route); + } + None => { + // do not create ingress when there is no path provided + continue; + } + } + } + Some(routes) + } + None => None, + } +} + +pub fn generate_ingress_tcp_routes( + appsvc: &AppService, + resource_name: &str, + namespace: &str, + host_matcher_tcp: String, + coredb_name: &str, +) -> Option> { + match appsvc.routing.clone() { + Some(routings) => { + let mut routes: Vec = Vec::new(); + for route in routings.iter() { + match route.ingress_path.clone() { + Some(_path) => { + if !route.ingress_type.clone()?.eq(&IngressType::tcp) { + // Do not create IngressRouteTCPRoutes for non-TCP ingress type + debug!("Skipping IngressRouteTCPRoutes for non-TCP ingress type"); + continue; + } + + let middlewares: Option> = + route.middlewares.clone().map(|names| { + names + .into_iter() + .map(|m| IngressRouteTCPRoutesMiddlewares { + name: format!("{}-{}", &coredb_name, m), + namespace: Some(namespace.to_owned()), + }) + .collect() + }); + let route = IngressRouteTCPRoutes { + r#match: host_matcher_tcp.clone(), + services: Some(vec![IngressRouteTCPRoutesServices { + name: resource_name.to_string(), + port: IntOrString::Int(route.port as i32), + namespace: None, + ..IngressRouteTCPRoutesServices::default() + }]), + middlewares, + priority: None, + syntax: None, + }; + routes.push(route); + } + None => { + // do not create ingress when there is no path provided + continue; + } + } + } + Some(routes) + } + None => None, + } +} + +pub async fn reconcile_ingress( + client: Client, + coredb_name: &str, + ns: &str, + oref: OwnerReference, + desired_routes: Vec, + desired_middlewares: Vec, + entry_points: Vec, +) -> Result<(), kube::Error> { + let ingress_api: Api = Api::namespaced(client.clone(), ns); + + let middleware_api: Api = Api::namespaced(client.clone(), ns); + let desired_middlewares = + generate_middlewares(coredb_name, ns, oref.clone(), desired_middlewares); + let actual_mw_names = get_middlewares(client.clone(), ns, coredb_name).await?; + let desired_mw_names = desired_middlewares + .iter() + .map(|mw| mw.name.clone()) + .collect::>(); + if let Some(to_delete) = to_delete(desired_mw_names, actual_mw_names) { + for d in to_delete { + match middleware_api.delete(&d, &Default::default()).await { + Ok(_) => { + debug!("ns: {}, successfully deleted Middleware: {}", ns, d); + } + Err(e) => { + error!( + "ns: {}, Failed to delete Middleware: {}, error: {}", + ns, d, e + ); + } + } + } + } + for desired_mw in desired_middlewares { + match apply_middleware(middleware_api.clone(), &desired_mw.name, &desired_mw.mw).await { + Ok(_) => { + debug!( + "ns: {}, successfully applied Middleware: {}", + ns, desired_mw.name + ); + } + Err(e) => { + error!( + "ns: {}, Failed to apply Middleware: {}, error: {}", + ns, desired_mw.name, e + ); + } + } + } + + let ingress = generate_ingress(coredb_name, ns, oref, desired_routes.clone(), entry_points); + if desired_routes.is_empty() { + // we don't need an IngressRoute when there are no routes + let lp = ListParams::default().labels("component=appService"); + // Check if there are any IngressRoute objects with the label component=appService and delete them + let ingress_routes = ingress_api.list(&lp).await?; + if let Some(ingress_route) = ingress_routes.into_iter().next() { + match ingress_api + .delete(&ingress_route.metadata.name.unwrap(), &Default::default()) + .await + { + Ok(_) => { + debug!( + "ns: {}, successfully deleted IngressRoute: {}", + ns, coredb_name + ); + return Ok(()); + } + Err(e) => { + error!( + "ns: {}, Failed to delete IngressRoute: {}, error: {}", + ns, coredb_name, e + ); + return Err(e); + } + } + } + return Ok(()); + } + match apply_ingress_route(ingress_api, coredb_name, &ingress).await { + Ok(_) => { + debug!("Updated/applied IngressRoute for {}.{}", ns, coredb_name,); + Ok(()) + } + Err(e) => { + error!( + "Failed to update/apply IngressRoute {}.{}: {}", + ns, coredb_name, e + ); + Err(e) + } + } +} + +pub async fn reconcile_ingress_tcp( + client: Client, + coredb_name: &str, + ns: &str, + oref: OwnerReference, + desired_routes: Vec, + // TODO: this should be a MiddlewareTCP + desired_middlewares: Vec, + entry_points_tcp: Vec, + app_name: &str, +) -> Result<(), kube::Error> { + let ingress_api: Api = Api::namespaced(client.clone(), ns); + let name = format!("{}-{}", coredb_name, app_name); + + let middleware_api: Api = Api::namespaced(client.clone(), ns); + let desired_middlewares = generate_middlewares(&name, ns, oref.clone(), desired_middlewares); + let actual_mw_names = get_middlewares(client.clone(), ns, &name).await?; + let desired_mw_names = desired_middlewares + .iter() + .map(|mw| mw.name.clone()) + .collect::>(); + if let Some(to_delete) = to_delete(desired_mw_names, actual_mw_names) { + for d in to_delete { + match middleware_api.delete(&d, &Default::default()).await { + Ok(_) => { + debug!("ns: {}, successfully deleted Middleware: {}", ns, d); + } + Err(e) => { + error!( + "ns: {}, Failed to delete Middleware: {}, error: {}", + ns, d, e + ); + } + } + } + } + for desired_mw in desired_middlewares { + match apply_middleware(middleware_api.clone(), &desired_mw.name, &desired_mw.mw).await { + Ok(_) => { + debug!( + "ns: {}, successfully applied Middleware: {}", + ns, desired_mw.name + ); + } + Err(e) => { + error!( + "ns: {}, Failed to apply Middleware: {}, error: {}", + ns, desired_mw.name, e + ); + } + } + } + + let ingress = generate_ingress_tcp(&name, ns, oref, desired_routes.clone(), entry_points_tcp); + if desired_routes.is_empty() { + // we don't need an IngressRouteTCP when there are no routes + let lp = ListParams::default().labels("component=appService"); + // Check if there are any IngressRouteTCP objects with the label component=appService and delete them + let ingress_tcp_routes = ingress_api.list(&lp).await?; + if let Some(ingress_tcp_route) = ingress_tcp_routes.into_iter().next() { + match ingress_api + .delete( + &ingress_tcp_route.metadata.name.unwrap(), + &Default::default(), + ) + .await + { + Ok(_) => { + debug!( + "ns: {}, successfully deleted IngressRouteTCP: {}", + ns, &name + ); + return Ok(()); + } + Err(e) => { + error!( + "ns: {}, Failed to delete IngressRouteTCP: {}, error: {}", + ns, &name, e + ); + return Err(e); + } + } + } + return Ok(()); + } + match apply_ingress_route_tcp(ingress_api, &name, &ingress).await { + Ok(_) => { + debug!("Updated/applied IngressRouteTCP for {}.{}", ns, &name,); + Ok(()) + } + Err(e) => { + error!( + "Failed to update/apply IngressRouteTCP {}.{}: {}", + ns, &name, e + ); + Err(e) + } + } +} + +async fn apply_middleware( + mw_api: Api, + mw_name: &str, + mw: &TraefikMiddleware, +) -> Result { + let patch_parameters = PatchParams::apply("cntrlr").force(); + mw_api + .patch(mw_name, &patch_parameters, &Patch::Apply(&mw)) + .await +} + +async fn apply_ingress_route( + ingress_api: Api, + ingress_name: &str, + ingress_route: &IngressRoute, +) -> Result { + let patch_parameters = PatchParams::apply("cntrlr").force(); + ingress_api + .patch( + ingress_name, + &patch_parameters, + &Patch::Apply(&ingress_route), + ) + .await +} + +async fn apply_ingress_route_tcp( + ingress_api: Api, + ingress_name: &str, + ingress_route_tcp: &IngressRouteTCP, +) -> Result { + let patch_parameters = PatchParams::apply("cntrlr").force(); + ingress_api + .patch( + ingress_name, + &patch_parameters, + &Patch::Apply(&ingress_route_tcp), + ) + .await +} + +async fn get_middlewares( + client: Client, + namespace: &str, + coredb_name: &str, +) -> Result, kube::Error> { + let label_selector = format!( + "component={},coredb.io/name={}", + COMPONENT_NAME, coredb_name + ); + let deployent_api: Api = Api::namespaced(client, namespace); + let lp = ListParams::default().labels(&label_selector).timeout(10); + let deployments = deployent_api.list(&lp).await?; + Ok(deployments + .items + .iter() + .map(|d| d.metadata.name.to_owned().expect("no name on resource")) + .collect()) +} diff --git a/tembo-operator/src/app_service/manager_v3.rs b/tembo-operator/src/app_service/manager_v3.rs new file mode 100644 index 000000000..ecb38f8f7 --- /dev/null +++ b/tembo-operator/src/app_service/manager_v3.rs @@ -0,0 +1,1088 @@ +// TODO: Delete this file after API migration +use crate::traefik::ingress_route_crd_v3::IngressRouteRoutes; +use crate::{ + apis::coredb_types::CoreDB, cloudnativepg::placement::cnpg_placement::PlacementConfig, Context, + Error, Result, +}; +use k8s_openapi::{ + api::{ + apps::v1::{Deployment, DeploymentSpec}, + core::v1::{ + Capabilities, Container, ContainerPort, EnvVar, EnvVarSource, HTTPGetAction, + PodSecurityContext, PodSpec, PodTemplateSpec, Probe, Secret, SecretKeySelector, + SecretVolumeSource, SecurityContext, Service, ServicePort, ServiceSpec, Volume, + VolumeMount, + }, + }, + apimachinery::pkg::{ + apis::meta::v1::{LabelSelector, OwnerReference}, + util::intstr::IntOrString, + }, + ByteString, +}; +use kube::{ + api::{Api, ListParams, ObjectMeta, Patch, PatchParams, ResourceExt}, + runtime::controller::Action, + Client, Resource, +}; +use lazy_static::lazy_static; +use std::{collections::BTreeMap, sync::Arc, time::Duration}; + +use crate::{ + app_service::ingress_v3::{generate_ingress_tcp_routes, reconcile_ingress_tcp}, + traefik::ingress_route_tcp_crd_v3::IngressRouteTCPRoutes, +}; +use tracing::{debug, error, warn}; + +use super::{ + ingress_v3::{generate_ingress_routes, reconcile_ingress}, + types::{AppService, EnvVarRef, Middleware, COMPONENT_NAME}, +}; + +use crate::{app_service::types::IngressType, secret::fetch_all_decoded_data_from_secret}; + +const APP_CONTAINER_PORT_PREFIX: &str = "app-"; + +lazy_static! { + static ref FORWARDED_ENV_VARS: Vec = { + let mut env_vars = Vec::new(); + for (key, value) in std::env::vars() { + if key.starts_with("TEMBO_APPS_DEFAULT_ENV_") { + let new_key = key.replace("TEMBO_APPS_DEFAULT_ENV_", "TEMBO_"); + env_vars.push(EnvVar { + name: new_key, + value: Some(value), + ..EnvVar::default() + }); + } + } + env_vars + }; +} + +// private wrapper to hold the AppService Resources +#[derive(Clone, Debug)] +struct AppServiceResources { + deployment: Deployment, + name: String, + service: Option, + ingress_routes: Option>, + ingress_tcp_routes: Option>, + entry_points: Option>, + entry_points_tcp: Option>, + podmonitor: Option, +} + +// generates Kubernetes Deployment and Service templates for a AppService +fn generate_resource( + appsvc: &AppService, + coredb_name: &str, + namespace: &str, + oref: OwnerReference, + domain: Option, + annotations: &BTreeMap, + placement: Option, +) -> AppServiceResources { + let resource_name = format!("{}-{}", coredb_name, appsvc.name.clone()); + let service = appsvc.routing.as_ref().map(|_| { + generate_service( + appsvc, + coredb_name, + &resource_name, + namespace, + oref.clone(), + annotations, + ) + }); + let deployment = generate_deployment( + appsvc, + coredb_name, + &resource_name, + namespace, + oref.clone(), + annotations, + placement.clone(), + ); + + let maybe_podmonitor = generate_podmonitor(appsvc, &resource_name, namespace, annotations); + + // If DATA_PLANE_BASEDOMAIN is not set, don't generate IngressRoutes, IngressRouteTCPs, or EntryPoints + if domain.is_none() { + return AppServiceResources { + deployment, + name: resource_name, + service, + ingress_routes: None, + ingress_tcp_routes: None, + entry_points: None, + entry_points_tcp: None, + podmonitor: maybe_podmonitor, + }; + } + // It's safe to unwrap domain here because we've already checked if it's None + let host_matcher = format!( + "Host(`{subdomain}.{domain}`)", + subdomain = coredb_name, + domain = domain.clone().unwrap() + ); + let ingress_routes = generate_ingress_routes( + appsvc, + &resource_name, + namespace, + host_matcher.clone(), + coredb_name, + ); + + let host_matcher_tcp = format!( + "HostSNI(`{subdomain}.{domain}`)", + subdomain = coredb_name, + domain = domain.unwrap() + ); + + let ingress_tcp_routes = generate_ingress_tcp_routes( + appsvc, + &resource_name, + namespace, + host_matcher_tcp, + coredb_name, + ); + // fetch entry points where ingress type is http + let entry_points: Option> = appsvc.routing.as_ref().map(|routes| { + routes + .iter() + .filter_map(|route| { + if route.ingress_type == Some(IngressType::http) { + route.entry_points.clone() + } else { + None + } + }) + .flatten() + .collect() + }); + + // fetch tcp entry points where ingress type is tcp + let entry_points_tcp: Option> = appsvc.routing.as_ref().map(|routes| { + routes + .iter() + .filter_map(|route| { + if route.ingress_type == Some(IngressType::tcp) { + route.entry_points.clone() + } else { + None + } + }) + .flatten() + .collect() + }); + + AppServiceResources { + deployment, + name: resource_name, + service, + ingress_routes, + ingress_tcp_routes, + entry_points, + entry_points_tcp, + podmonitor: maybe_podmonitor, + } +} + +// templates the Kubernetes Service for an AppService +fn generate_service( + appsvc: &AppService, + coredb_name: &str, + resource_name: &str, + namespace: &str, + oref: OwnerReference, + annotations: &BTreeMap, +) -> Service { + let mut selector_labels: BTreeMap = BTreeMap::new(); + + selector_labels.insert("app".to_owned(), resource_name.to_string()); + selector_labels.insert("component".to_owned(), COMPONENT_NAME.to_string()); + selector_labels.insert("coredb.io/name".to_owned(), coredb_name.to_string()); + + let mut labels = selector_labels.clone(); + labels.insert("component".to_owned(), COMPONENT_NAME.to_owned()); + + let ports = match appsvc.routing.as_ref() { + Some(routing) => { + // de-dupe any ports because we can have multiple appService routing configs for the same port + // but we only need one ServicePort per port + let distinct_ports = routing + .iter() + .map(|r| r.port) + .collect::>(); + + let ports: Vec = distinct_ports + .into_iter() + .map(|p| ServicePort { + port: p as i32, + // there can be more than one ServicePort per Service + // these must be unique, so we'll use the port number + name: Some(format!("{APP_CONTAINER_PORT_PREFIX}{p}")), + target_port: None, + ..ServicePort::default() + }) + .collect(); + Some(ports) + } + None => None, + }; + Service { + metadata: ObjectMeta { + name: Some(resource_name.to_owned()), + namespace: Some(namespace.to_owned()), + labels: Some(labels.clone()), + owner_references: Some(vec![oref]), + annotations: Some(annotations.clone()), + ..ObjectMeta::default() + }, + spec: Some(ServiceSpec { + ports, + selector: Some(selector_labels.clone()), + ..ServiceSpec::default() + }), + ..Service::default() + } +} + +// templates a single Kubernetes Deployment for an AppService +fn generate_deployment( + appsvc: &AppService, + coredb_name: &str, + resource_name: &str, + namespace: &str, + oref: OwnerReference, + annotations: &BTreeMap, + placement: Option, +) -> Deployment { + let mut labels: BTreeMap = BTreeMap::new(); + labels.insert("app".to_owned(), resource_name.to_string()); + labels.insert("component".to_owned(), COMPONENT_NAME.to_string()); + labels.insert("coredb.io/name".to_owned(), coredb_name.to_string()); + + let deployment_metadata = ObjectMeta { + name: Some(resource_name.to_string()), + namespace: Some(namespace.to_owned()), + labels: Some(labels.clone()), + owner_references: Some(vec![oref]), + annotations: Some(annotations.clone()), + ..ObjectMeta::default() + }; + + let (readiness_probe, liveness_probe) = match appsvc.probes.clone() { + Some(probes) => { + let readiness_probe = Probe { + http_get: Some(HTTPGetAction { + path: Some(probes.readiness.path), + port: IntOrString::Int(probes.readiness.port), + ..HTTPGetAction::default() + }), + initial_delay_seconds: Some(probes.readiness.initial_delay_seconds as i32), + ..Probe::default() + }; + let liveness_probe = Probe { + http_get: Some(HTTPGetAction { + path: Some(probes.liveness.path), + port: IntOrString::Int(probes.liveness.port), + ..HTTPGetAction::default() + }), + initial_delay_seconds: Some(probes.liveness.initial_delay_seconds as i32), + ..Probe::default() + }; + (Some(readiness_probe), Some(liveness_probe)) + } + None => (None, None), + }; + + // container ports + let container_ports = if let Some(routings) = appsvc.routing.as_ref() { + let distinct_ports = routings + .iter() + .map(|r| r.port) + .collect::>(); + let container_ports: Vec = distinct_ports + .into_iter() + .map(|p| ContainerPort { + name: Some(format!("{APP_CONTAINER_PORT_PREFIX}{p}")), + container_port: p as i32, + protocol: Some("TCP".to_string()), + ..ContainerPort::default() + }) + .collect(); + Some(container_ports) + } else { + None + }; + + // https://tembo.io/docs/tembo-cloud/security/#tenant-isolation + // These configs are the same as CNPG configs + let security_context = SecurityContext { + run_as_user: Some(65534), + allow_privilege_escalation: Some(false), + capabilities: Some(Capabilities { + drop: Some(vec!["ALL".to_string()]), + ..Capabilities::default() + }), + privileged: Some(false), + run_as_non_root: Some(true), + // This part maybe we disable if we need + // or we can mount ephemeral or persistent + // volumes if we need to write somewhere + read_only_root_filesystem: Some(true), + ..SecurityContext::default() + }; + + // ensure hyphen in env var name (cdb name allows hyphen) + let cdb_name_env = coredb_name.to_uppercase().replace('-', "_"); + + // map postgres connection secrets to env vars + // mapping directly to env vars instead of using a SecretEnvSource + // so that we can select which secrets to map into appService + // generally, the system roles (e.g. postgres-exporter role) should not be injected to the appService + // these three are the only secrets that are mapped into the container + let r_conn = format!("{}_R_CONNECTION", cdb_name_env); + let ro_conn = format!("{}_RO_CONNECTION", cdb_name_env); + let rw_conn = format!("{}_RW_CONNECTION", cdb_name_env); + let apps_connection_secret_name = format!("{}-apps", coredb_name); + + // map the secrets we inject to appService containers + let default_app_envs = vec![ + EnvVar { + name: r_conn, + value_from: Some(EnvVarSource { + secret_key_ref: Some(SecretKeySelector { + name: Some(apps_connection_secret_name.clone()), + key: "r_uri".to_string(), + ..SecretKeySelector::default() + }), + ..EnvVarSource::default() + }), + ..EnvVar::default() + }, + EnvVar { + name: ro_conn, + value_from: Some(EnvVarSource { + secret_key_ref: Some(SecretKeySelector { + name: Some(apps_connection_secret_name.clone()), + key: "ro_uri".to_string(), + ..SecretKeySelector::default() + }), + ..EnvVarSource::default() + }), + ..EnvVar::default() + }, + EnvVar { + name: rw_conn, + value_from: Some(EnvVarSource { + secret_key_ref: Some(SecretKeySelector { + name: Some(apps_connection_secret_name.clone()), + key: "rw_uri".to_string(), + ..SecretKeySelector::default() + }), + ..EnvVarSource::default() + }), + ..EnvVar::default() + }, + ]; + + // map the user provided env vars + // users can map certain secrets to env vars of their choice + let mut env_vars: Vec = Vec::new(); + if let Some(envs) = appsvc.env.clone() { + for env in envs { + let evar: Option = match (env.value, env.value_from_platform) { + // Value provided + (Some(e), _) => Some(EnvVar { + name: env.name, + value: Some(e), + ..EnvVar::default() + }), + // EnvVarRef provided, and no Value + (None, Some(e)) => { + let secret_key = match e { + EnvVarRef::ReadOnlyConnection => "ro_uri", + EnvVarRef::ReadWriteConnection => "rw_uri", + }; + Some(EnvVar { + name: env.name, + value_from: Some(EnvVarSource { + secret_key_ref: Some(SecretKeySelector { + name: Some(apps_connection_secret_name.clone()), + key: secret_key.to_string(), + ..SecretKeySelector::default() + }), + ..EnvVarSource::default() + }), + ..EnvVar::default() + }) + } + // everything missing, skip it + _ => { + error!( + "ns: {}, AppService: {}, env var: {} is missing value or valueFromPlatform", + namespace, resource_name, env.name + ); + None + } + }; + if let Some(e) = evar { + env_vars.push(e); + } + } + } + + // Check for tembo.io/instance_id and tembo.io/organization_id annotations + if let Some(instance_id) = annotations.get("tembo.io/instance_id") { + env_vars.push(EnvVar { + name: "TEMBO_INSTANCE_ID".to_string(), + value: Some(instance_id.clone()), + ..EnvVar::default() + }); + } + + if let Some(organization_id) = annotations.get("tembo.io/organization_id") { + env_vars.push(EnvVar { + name: "TEMBO_ORG_ID".to_string(), + value: Some(organization_id.clone()), + ..EnvVar::default() + }); + } + + // Add the pre-loaded forwarded environment variables + env_vars.extend(FORWARDED_ENV_VARS.iter().cloned()); + + // combine the secret env vars and those provided in spec by user + env_vars.extend(default_app_envs); + + // Create volume vec and add certs volume from secret + let mut volumes: Vec = Vec::new(); + let mut volume_mounts: Vec = Vec::new(); + + // If USE_SHARED_CA is not set, we don't need to mount the certs + match std::env::var("USE_SHARED_CA") { + Ok(_) => { + // Create volume and add it to volumes vec + let certs_volume = Volume { + name: "tembo-certs".to_string(), + secret: Some(SecretVolumeSource { + secret_name: Some(format!("{}-server1", coredb_name)), + ..SecretVolumeSource::default() + }), + ..Volume::default() + }; + volumes.push(certs_volume); + + // Create volume mounts vec and add certs volume mount + let certs_volume_mount = VolumeMount { + name: "tembo-certs".to_string(), + mount_path: "/tembo/certs".to_string(), + read_only: Some(true), + ..VolumeMount::default() + }; + volume_mounts.push(certs_volume_mount); + } + Err(_) => { + warn!("USE_SHARED_CA not set, skipping certs volume mount"); + } + } + + let mut pod_security_context: Option = None; + // Add any user provided volumes / volume mounts + if let Some(storage) = appsvc.storage.clone() { + // when there are user specified volumes, we need to let kubernetes modify permissions of those volumes + pod_security_context = Some(PodSecurityContext { + fs_group: Some(65534), + ..PodSecurityContext::default() + }); + if let Some(vols) = storage.volumes { + volumes.extend(vols); + } + if let Some(vols) = storage.volume_mounts { + volume_mounts.extend(vols); + } + } + + let affinity = placement.as_ref().and_then(|p| p.combine_affinity_items()); + let node_selector = placement.as_ref().and_then(|p| p.node_selector.clone()); + let tolerations = placement.as_ref().map(|p| p.tolerations.clone()); + let topology_spread_constraints = placement + .as_ref() + .and_then(|p| p.topology_spread_constraints.clone()); + + let pod_spec = PodSpec { + affinity, + containers: vec![Container { + args: appsvc.args.clone(), + command: appsvc.command.clone(), + env: Some(env_vars), + image: Some(appsvc.image.clone()), + name: appsvc.name.clone(), + ports: container_ports, + resources: Some(appsvc.resources.clone()), + readiness_probe, + liveness_probe, + security_context: Some(security_context), + volume_mounts: Some(volume_mounts), + ..Container::default() + }], + node_selector, + tolerations, + topology_spread_constraints, + volumes: Some(volumes), + security_context: pod_security_context, + ..PodSpec::default() + }; + + let pod_template_spec = PodTemplateSpec { + metadata: Some(deployment_metadata.clone()), + spec: Some(pod_spec), + }; + + let deployment_spec = DeploymentSpec { + selector: LabelSelector { + match_labels: Some(labels.clone()), + ..LabelSelector::default() + }, + template: pod_template_spec, + ..DeploymentSpec::default() + }; + Deployment { + metadata: deployment_metadata, + spec: Some(deployment_spec), + ..Deployment::default() + } +} + +// gets all names of AppService Deployments in the namespace that have the label "component=AppService" +async fn get_appservice_deployments( + client: &Client, + namespace: &str, + coredb_name: &str, +) -> Result, Error> { + let label_selector = format!( + "component={},coredb.io/name={}", + COMPONENT_NAME, coredb_name + ); + let deployent_api: Api = Api::namespaced(client.clone(), namespace); + let lp = ListParams::default().labels(&label_selector).timeout(10); + let deployments = deployent_api.list(&lp).await.map_err(Error::KubeError)?; + Ok(deployments + .items + .iter() + .filter_map(|d| d.metadata.name.clone()) + .collect()) +} + +/// Retrieves all AppService component Deployments in the namespace +/// +/// This function should return all available deployments with an AppService label +/// and return the actual Deployment struct for each as a vector. This allows us +/// to use the full current state of the deployment rather than simply the name. +pub async fn get_appservice_deployment_objects( + client: &Client, + namespace: &str, + coredb_name: &str, +) -> Result, Error> { + let label_selector = format!( + "component={},coredb.io/name={}", + COMPONENT_NAME, coredb_name + ); + let deployent_api: Api = Api::namespaced(client.clone(), namespace); + let lp = ListParams::default().labels(&label_selector).timeout(10); + let deployments = deployent_api.list(&lp).await.map_err(Error::KubeError)?; + Ok(deployments.items) +} + +// gets all names of AppService Services in the namespace +// that have the label "component=AppService" and belong to the coredb +async fn get_appservice_services( + client: &Client, + namespace: &str, + coredb_name: &str, +) -> Result, Error> { + let label_selector = format!( + "component={},coredb.io/name={}", + COMPONENT_NAME, coredb_name + ); + let deployent_api: Api = Api::namespaced(client.clone(), namespace); + let lp = ListParams::default().labels(&label_selector).timeout(10); + let services = deployent_api.list(&lp).await.map_err(Error::KubeError)?; + Ok(services + .items + .iter() + .filter_map(|d| d.metadata.name.clone()) + .collect()) +} + +// determines AppService deployments +pub fn to_delete(desired: Vec, actual: Vec) -> Option> { + let mut to_delete: Vec = Vec::new(); + for a in actual { + // if actual not in desired, put it in the delete vev + if !desired.contains(&a) { + to_delete.push(a); + } + } + if to_delete.is_empty() { + None + } else { + Some(to_delete) + } +} + +async fn apply_resources(resources: Vec, client: &Client, ns: &str) -> bool { + let deployment_api: Api = Api::namespaced(client.clone(), ns); + let ps = PatchParams::apply("cntrlr").force(); + + let mut has_errors: bool = false; + + // apply desired resources + for res in resources { + match deployment_api + .patch(&res.name, &ps, &Patch::Apply(&res.deployment)) + .await + .map_err(Error::KubeError) + { + Ok(_) => { + debug!("ns: {}, applied AppService Deployment: {}", ns, res.name); + } + Err(e) => { + // TODO: find a better way to handle single error without stopping all reconciliation of AppService + has_errors = true; + error!( + "ns: {}, failed to apply AppService Deployment: {}, error: {}", + ns, res.name, e + ); + } + } + if res.service.is_none() { + continue; + } + + let service_api: Api = Api::namespaced(client.clone(), ns); + match service_api + .patch(&res.name, &ps, &Patch::Apply(&res.service)) + .await + .map_err(Error::KubeError) + { + Ok(_) => { + debug!("ns: {}, applied AppService Service: {}", ns, res.name); + } + Err(e) => { + // TODO: find a better way to handle single error without stopping all reconciliation of AppService + has_errors = true; + error!( + "ns: {}, failed to apply AppService Service: {}, error: {}", + ns, res.name, e + ); + } + } + + let podmon_api: Api = Api::namespaced(client.clone(), ns); + if let Some(mut pmon) = res.podmonitor { + // assign ownership of the PodMonitor to the Service + // if Service is deleted, so is the PodMonitor + let meta = service_api.get(&res.name).await; + if let Ok(svc) = meta { + let uid = svc.metadata.uid.unwrap_or_default(); + let oref = OwnerReference { + api_version: "v1".to_string(), + kind: "Service".to_string(), + name: res.name.clone(), + uid, + controller: Some(true), + block_owner_deletion: Some(true), + }; + pmon.metadata.owner_references = Some(vec![oref]); + } + match podmon_api + .patch(&res.name, &ps, &Patch::Apply(&pmon)) + .await + .map_err(Error::KubeError) + { + Ok(_) => { + debug!("ns: {}, applied PodMonitor: {}", ns, res.name); + } + Err(e) => { + has_errors = true; + error!( + "ns: {}, failed to apply PodMonitor for AppService: {}, error: {}", + ns, res.name, e + ); + } + } + } else { + match podmon_api.delete(&res.name, &Default::default()).await.ok() { + Some(_) => { + debug!("ns: {}, deleted PodMonitor: {}", ns, res.name); + } + None => { + debug!("ns: {}, PodMonitor does not exist: {}", ns, res.name); + } + } + } + } + has_errors +} + +// generate_appsvc_annotations generates the annotations for the AppService resources +fn generate_appsvc_annotations(cdb: &CoreDB) -> BTreeMap { + cdb.metadata.annotations.as_ref().map_or_else( + || { + debug!( + "failed to generate annotations for AppService: {}, error: No annotations found", + cdb.name_any() + ); + BTreeMap::new() + }, + |annotations| { + annotations + .iter() + .map(|(k, v)| { + if k == "tembo.io/org_id" { + // Change key to "tembo.io/organization_id" if it matches "tembo.io/org_id" + ("tembo.io/organization_id".to_string(), v.clone()) + } else { + // Otherwise, clone the key and value as is + (k.clone(), v.clone()) + } + }) + .collect() + }, + ) +} + +pub async fn reconcile_app_services( + cdb: &CoreDB, + ctx: Arc, + placement: Option, +) -> Result<(), Action> { + let client = ctx.client.clone(); + let ns = cdb.namespace().unwrap(); + let coredb_name = cdb.name_any(); + let oref = cdb.controller_owner_ref(&()).unwrap(); + let deployment_api: Api = Api::namespaced(client.clone(), &ns); + let service_api: Api = Api::namespaced(client.clone(), &ns); + + // Generate labels to attach to the AppService resources + let annotations = generate_appsvc_annotations(cdb); + + let desired_deployments = match cdb.spec.app_services.clone() { + Some(appsvcs) => appsvcs + .iter() + .map(|a| format!("{}-{}", coredb_name, a.name.clone())) + .collect(), + None => { + debug!("No AppServices found in Instance: {}", ns); + vec![] + } + }; + + match prepare_apps_connection_secret(ctx.client.clone(), cdb).await { + Ok(_) => {} + Err(_) => { + error!( + "Failed to prepare Apps Connection Secret for CoreDB: {}", + coredb_name + ); + return Err(Action::requeue(Duration::from_secs(300))); + } + }; + + // only deploy the Kubernetes Service when there are routing configurations + // we need one service per PORT, not necessarily 1 per AppService route + let desired_services = match cdb.spec.app_services.clone() { + Some(appsvcs) => { + let mut desired_svc: Vec = Vec::new(); + for appsvc in appsvcs.iter() { + if appsvc.routing.as_ref().is_some() { + let svc_name = format!("{}-{}", coredb_name, appsvc.name); + desired_svc.push(svc_name.clone()); + } + } + desired_svc + } + None => { + vec![] + } + }; + // TODO: we can improve our overall error handling design + // for app_service reconciliation, not stop all reconciliation if an operation on a single AppService fails + // however, we do want to requeue if there are any error + // currently there are no expected errors in this path + // for simplicity, we will return a requeue Action if there are errors + let mut has_errors: bool = false; + + let actual_deployments = match get_appservice_deployments(&client, &ns, &coredb_name).await { + Ok(deployments) => deployments, + Err(e) => { + has_errors = true; + error!("ns: {}, failed to get AppService Deployments: {}", ns, e); + vec![] + } + }; + let actual_services = match get_appservice_services(&client, &ns, &coredb_name).await { + Ok(services) => services, + Err(e) => { + has_errors = true; + error!("ns: {}, failed to get AppService Services: {}", ns, e); + vec![] + } + }; + + // reap any AppService Deployments that are no longer desired + if let Some(to_delete) = to_delete(desired_deployments, actual_deployments) { + for d in to_delete { + match deployment_api.delete(&d, &Default::default()).await { + Ok(_) => { + debug!("ns: {}, successfully deleted AppService: {}", ns, d); + } + Err(e) => { + has_errors = true; + error!( + "ns: {}, Failed to delete AppService: {}, error: {}", + ns, d, e + ); + } + } + } + } + + // reap any AppService that are no longer desired + if let Some(to_delete) = to_delete(desired_services, actual_services) { + for d in to_delete { + match service_api.delete(&d, &Default::default()).await { + Ok(_) => { + debug!("ns: {}, successfully deleted AppService: {}", ns, d); + } + Err(e) => { + has_errors = true; + error!( + "ns: {}, Failed to delete AppService: {}, error: {}", + ns, d, e + ); + } + } + } + } + + let appsvcs = match cdb.spec.app_services.clone() { + Some(appsvcs) => appsvcs, + None => { + debug!("ns: {}, No AppServices found in spec", ns); + vec![] + } + }; + + let domain = match std::env::var("DATA_PLANE_BASEDOMAIN") { + Ok(domain) => Some(domain), + Err(_) => { + warn!("DATA_PLANE_BASEDOMAIN not set, skipping ingress reconciliation"); + None + } + }; + // Iterate over each AppService and process routes + let resources: Vec = appsvcs + .iter() + .map(|appsvc| { + generate_resource( + appsvc, + &coredb_name, + &ns, + oref.clone(), + domain.to_owned(), + &annotations, + placement.clone(), + ) + }) + .collect(); + let apply_errored = apply_resources(resources.clone(), &client, &ns).await; + + let desired_routes: Vec = resources + .iter() + .filter_map(|r| r.ingress_routes.clone()) + .flatten() + .collect(); + + let desired_tcp_routes: Vec = resources + .iter() + .filter_map(|r| r.ingress_tcp_routes.clone()) + .flatten() + .collect(); + + let desired_middlewares = appsvcs + .iter() + .filter_map(|appsvc| appsvc.middlewares.clone()) + .flatten() + .collect::>(); + + let desired_entry_points = resources + .iter() + .filter_map(|r| r.entry_points.clone()) + .flatten() + .collect::>(); + + let desired_entry_points_tcp = resources + .iter() + .filter_map(|r| r.entry_points_tcp.clone()) + .flatten() + .collect::>(); + + // Only reconcile IngressRoute and IngressRouteTCP if DATA_PLANE_BASEDOMAIN is set + if domain.is_some() { + match reconcile_ingress( + client.clone(), + &coredb_name, + &ns, + oref.clone(), + desired_routes, + desired_middlewares.clone(), + desired_entry_points, + ) + .await + { + Ok(_) => { + debug!("Updated/applied IngressRoute for {}.{}", ns, coredb_name,); + } + Err(e) => { + error!( + "Failed to update/apply IngressRoute {}.{}: {}", + ns, coredb_name, e + ); + has_errors = true; + } + } + + for appsvc in appsvcs.iter() { + let app_name = appsvc.name.clone(); + + match reconcile_ingress_tcp( + client.clone(), + &coredb_name, + &ns, + oref.clone(), + desired_tcp_routes.clone(), + // TODO: fill with actual MiddlewareTCPs when it is supported + // first supported MiddlewareTCP will be for custom domains + vec![], + desired_entry_points_tcp.clone(), + &app_name, + ) + .await + { + Ok(_) => { + debug!("Updated/applied IngressRouteTCP for {}.{}", ns, coredb_name,); + } + Err(e) => { + error!( + "Failed to update/apply IngressRouteTCP {}.{}: {}", + ns, coredb_name, e + ); + has_errors = true; + } + } + } + } + if has_errors || apply_errored { + return Err(Action::requeue(Duration::from_secs(300))); + } + Ok(()) +} + +pub async fn prepare_apps_connection_secret(client: Client, cdb: &CoreDB) -> Result<(), Error> { + let namespace = cdb.namespace().unwrap(); + let cdb_name = cdb.metadata.name.clone().unwrap(); + let secret_name = format!("{}-connection", cdb_name); + let new_secret_name = format!("{}-apps", cdb_name); + + let secrets_api: Api = Api::namespaced(client.clone(), &namespace); + + // Fetch the original secret + let original_secret_data = + fetch_all_decoded_data_from_secret(secrets_api.clone(), secret_name.to_string()).await?; + + // Modify the secret data + let mut new_secret_data = BTreeMap::new(); + for (key, value) in original_secret_data { + match key.as_str() { + "r_uri" | "ro_uri" | "rw_uri" => { + let new_value = format!("{}?application_name=tembo-apps", value); + new_secret_data.insert(key, new_value); + } + _ => {} + }; + } + + // Encode the modified secret data + let encoded_secret_data: BTreeMap = new_secret_data + .into_iter() + .map(|(k, v)| (k, ByteString(v.into_bytes()))) + .collect(); + + // Create a new secret with the modified data + let new_secret = Secret { + data: Some(encoded_secret_data), + metadata: kube::api::ObjectMeta { + name: Some(new_secret_name.to_string()), + namespace: Some(namespace.to_string()), + ..Default::default() + }, + ..Default::default() + }; + + // Apply the new secret + let patch_params = PatchParams::apply("cntrlr").force(); + secrets_api + .patch(&new_secret_name, &patch_params, &Patch::Apply(&new_secret)) + .await?; + + Ok(()) +} + +use crate::prometheus::podmonitor_crd as podmon; + +fn generate_podmonitor( + appsvc: &AppService, + resource_name: &str, + namespace: &str, + annotations: &BTreeMap, +) -> Option { + let metrics = appsvc.metrics.clone()?; + + let mut selector_labels: BTreeMap = BTreeMap::new(); + selector_labels.insert("app".to_owned(), resource_name.to_string()); + + let mut labels = selector_labels.clone(); + labels.insert("component".to_owned(), COMPONENT_NAME.to_owned()); + labels.insert("coredb.io/name".to_owned(), namespace.to_owned()); + + let podmon_metadata = ObjectMeta { + name: Some(resource_name.to_string()), + namespace: Some(namespace.to_owned()), + labels: Some(labels.clone()), + annotations: Some(annotations.clone()), + ..ObjectMeta::default() + }; + + let metrics_endpoint = podmon::PodMonitorPodMetricsEndpoints { + path: Some(metrics.path), + port: Some(format!("{APP_CONTAINER_PORT_PREFIX}{}", metrics.port)), + ..podmon::PodMonitorPodMetricsEndpoints::default() + }; + + let pmonspec = podmon::PodMonitorSpec { + pod_metrics_endpoints: Some(vec![metrics_endpoint]), + selector: podmon::PodMonitorSelector { + match_labels: Some(selector_labels.clone()), + ..podmon::PodMonitorSelector::default() + }, + ..podmon::PodMonitorSpec::default() + }; + Some(podmon::PodMonitor { + metadata: podmon_metadata, + spec: pmonspec, + }) +} diff --git a/tembo-operator/src/app_service/mod.rs b/tembo-operator/src/app_service/mod.rs index c68c7aede..d8cf97770 100644 --- a/tembo-operator/src/app_service/mod.rs +++ b/tembo-operator/src/app_service/mod.rs @@ -1,3 +1,5 @@ pub mod ingress; +pub mod ingress_v3; pub mod manager; +pub mod manager_v3; pub mod types; diff --git a/tembo-operator/src/controller.rs b/tembo-operator/src/controller.rs index 01b33af8d..a712f6ad0 100644 --- a/tembo-operator/src/controller.rs +++ b/tembo-operator/src/controller.rs @@ -4,6 +4,8 @@ use futures::stream::StreamExt; use crate::{ apis::coredb_types::{CoreDB, CoreDBStatus, VolumeSnapshot}, app_service::manager::reconcile_app_services, + // TODO: Delete after API migration + app_service::manager_v3::reconcile_app_services as reconcile_app_services_v3, cloudnativepg::{ archive::wal::reconcile_last_archive_status, backups::Backup, @@ -19,10 +21,14 @@ use crate::{ extensions::database_queries::is_not_restarting, heartbeat::reconcile_heartbeat, ingress::reconcile_postgres_ing_route_tcp, + ingress_v3::reconcile_postgres_ing_route_tcp as reconcile_postgres_ing_route_tcp_v3, postgres_certificates::reconcile_certificates, psql::{PsqlCommand, PsqlOutput}, secret::{reconcile_postgres_role_secret, reconcile_secret}, - telemetry, Error, Metrics, Result, + telemetry, + Error, + Metrics, + Result, }; use k8s_openapi::{ api::core::v1::{Namespace, Pod}, @@ -47,6 +53,10 @@ use crate::{ configmap::reconcile_generic_metrics_configmap, extensions::{database_queries::list_config_params, reconcile_extensions}, ingress::{reconcile_extra_postgres_ing_route_tcp, reconcile_ip_allowlist_middleware}, + ingress_v3::{ + reconcile_extra_postgres_ing_route_tcp as reconcile_extra_postgres_ing_route_tcp_v3, + reconcile_ip_allowlist_middleware as reconcile_ip_allowlist_middleware_v3, + }, network_policies::reconcile_network_policies, postgres_exporter::reconcile_metrics_configmap, trunk::{extensions_that_require_load, reconcile_trunk_configmap}, @@ -211,6 +221,13 @@ impl CoreDB { error!("Error reconciling MiddlewareTCP for {}: {:?}", name, e); Action::requeue(Duration::from_secs(300)) })?; + // TODO: Delete after API migration + let middleware_name_v3 = reconcile_ip_allowlist_middleware_v3(self, ctx.clone()) + .await + .map_err(|e| { + error!("Error reconciling MiddlewareTCP for {}: {:?}", name, e); + Action::requeue(Duration::from_secs(300)) + })?; let service_name_read_only = format!("{}-ro", self.name_any().as_str()); let prefix_read_only = format!("{}-ro-", self.name_any().as_str()); @@ -234,6 +251,26 @@ impl CoreDB { // IngressRouteTCP does not have expected errors during reconciliation. Action::requeue(Duration::from_secs(300)) })?; + // TODO: Delete after API migration + reconcile_postgres_ing_route_tcp_v3( + self, + ctx.clone(), + &read_only_subdomain, + basedomain.as_str(), + ns.as_str(), + prefix_read_only.as_str(), + service_name_read_only.as_str(), + IntOrString::Int(5432), + vec![middleware_name_v3.clone()], + ) + .await + .map_err(|e| { + error!("Error reconciling postgres ingress route: {:?}", e); + // For unexpected errors, we should requeue for several minutes at least, + // for expected, "waiting" type of requeuing, those should be shorter, just a few seconds. + // IngressRouteTCP does not have expected errors during reconciliation. + Action::requeue(Duration::from_secs(300)) + })?; let service_name_read_write = format!("{}-rw", self.name_any().as_str()); let prefix_read_write = format!("{}-rw-", self.name_any().as_str()); @@ -256,6 +293,26 @@ impl CoreDB { // IngressRouteTCP does not have expected errors during reconciliation. Action::requeue(Duration::from_secs(300)) })?; + // TODO: Delete after API migration + reconcile_postgres_ing_route_tcp_v3( + self, + ctx.clone(), + self.name_any().as_str(), + basedomain.as_str(), + ns.as_str(), + prefix_read_write.as_str(), + service_name_read_write.as_str(), + IntOrString::Int(5432), + vec![middleware_name_v3.clone()], + ) + .await + .map_err(|e| { + error!("Error reconciling postgres ingress route: {:?}", e); + // For unexpected errors, we should requeue for several minutes at least, + // for expected, "waiting" type of requeuing, those should be shorter, just a few seconds. + // IngressRouteTCP does not have expected errors during reconciliation. + Action::requeue(Duration::from_secs(300)) + })?; reconcile_extra_postgres_ing_route_tcp( self, @@ -273,6 +330,23 @@ impl CoreDB { // IngressRouteTCP does not have expected errors during reconciliation. Action::requeue(Duration::from_secs(300)) })?; + // TODO: Delete after API migration + reconcile_extra_postgres_ing_route_tcp_v3( + self, + ctx.clone(), + ns.as_str(), + service_name_read_write.as_str(), + IntOrString::Int(5432), + vec![middleware_name_v3.clone()], + ) + .await + .map_err(|e| { + error!("Error reconciling extra postgres ingress route: {:?}", e); + // For unexpected errors, we should requeue for several minutes at least, + // for expected, "waiting" type of requeuing, those should be shorter, just a few seconds. + // IngressRouteTCP does not have expected errors during reconciliation. + Action::requeue(Duration::from_secs(300)) + })?; // If pooler is enabled, reconcile ingress route tcp for pooler if self.spec.connectionPooler.enabled { let name_pooler = format!("{}-pooler", self.name_any().as_str()); @@ -296,6 +370,26 @@ impl CoreDB { // IngressRouteTCP does not have expected errors during reconciliation. Action::requeue(Duration::from_secs(300)) })?; + // TODO: Delete after API migration + reconcile_postgres_ing_route_tcp_v3( + self, + ctx.clone(), + name_pooler.as_str(), + basedomain.as_str(), + ns.as_str(), + prefix_pooler.as_str(), + name_pooler.as_str(), + IntOrString::Int(5432), + vec![middleware_name.clone()], + ) + .await + .map_err(|e| { + error!("Error reconciling pooler ingress route: {:?}", e); + // For unexpected errors, we should requeue for several minutes at least, + // for expected, "waiting" type of requeuing, those should be shorter, just a few seconds. + // IngressRouteTCP does not have expected errors during reconciliation. + Action::requeue(Duration::from_secs(300)) + })?; } } Err(_e) => { @@ -309,6 +403,7 @@ impl CoreDB { // Superuser connection info reconcile_secret(self, ctx.clone()).await?; reconcile_app_services(self, ctx.clone(), placement_config.clone()).await?; + reconcile_app_services_v3(self, ctx.clone(), placement_config.clone()).await?; if self .spec diff --git a/tembo-operator/src/ingress_v3.rs b/tembo-operator/src/ingress_v3.rs new file mode 100644 index 000000000..e3a943bce --- /dev/null +++ b/tembo-operator/src/ingress_v3.rs @@ -0,0 +1,341 @@ +// TODO: Delete after API migration +use crate::traefik::ingress_route_tcp_crd_v3::{ + IngressRouteTCP, IngressRouteTCPRoutes, IngressRouteTCPRoutesMiddlewares, + IngressRouteTCPRoutesServices, IngressRouteTCPSpec, IngressRouteTCPTls, +}; +use k8s_openapi::apimachinery::pkg::{ + apis::meta::v1::{ObjectMeta, OwnerReference}, + util::intstr::IntOrString, +}; +use kube::{ + api::{DeleteParams, Patch, PatchParams}, + Api, Resource, ResourceExt, +}; +use regex::Regex; +use std::sync::Arc; + +use crate::{ + apis::coredb_types::CoreDB, + errors::OperatorError, + traefik::middleware_tcp_crd_v3::{MiddlewareTCP, MiddlewareTCPIpAllowList, MiddlewareTCPSpec}, + Context, +}; +use tracing::{debug, error, info}; + +pub const VALID_IPV4_CIDR_BLOCK: &str = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/(3[0-2]|2[0-9]|1[0-9]|[0-9]))?$"; + +fn postgres_ingress_route_tcp( + name: String, + namespace: String, + owner_reference: OwnerReference, + matcher: String, + service_name: String, + middleware_names: Vec, + port: IntOrString, +) -> IngressRouteTCP { + let mut middlewares = vec![]; + for middleware_name in middleware_names { + middlewares.push(IngressRouteTCPRoutesMiddlewares { + name: middleware_name.clone(), + // Warning: 'namespace' field does not mean kubernetes namespace, + // it means Traefik 'provider' namespace. + // The IngressRouteTCP will by default look in the same Kubernetes namespace, + // so this should be set to None. + // https://doc.traefik.io/traefik/providers/overview/#provider-namespace + namespace: None, + }); + } + let middlewares = Some(middlewares); + + IngressRouteTCP { + metadata: ObjectMeta { + name: Some(name), + namespace: Some(namespace), + owner_references: Some(vec![owner_reference]), + ..ObjectMeta::default() + }, + spec: IngressRouteTCPSpec { + entry_points: Some(vec!["postgresql".to_string()]), + routes: vec![IngressRouteTCPRoutes { + r#match: matcher, + services: Some(vec![IngressRouteTCPRoutesServices { + name: service_name, + port, + ..IngressRouteTCPRoutesServices::default() + }]), + middlewares, + priority: None, + syntax: None, + }], + tls: Some(IngressRouteTCPTls { + passthrough: Some(true), + ..IngressRouteTCPTls::default() + }), + }, + } +} + +// For end-user provided, extra domain names, +// we allow for update and deletion of domain names. +pub async fn reconcile_extra_postgres_ing_route_tcp( + cdb: &CoreDB, + ctx: Arc, + namespace: &str, + service_name_read_write: &str, + port: IntOrString, + middleware_names: Vec, +) -> Result<(), OperatorError> { + let mut extra_domain_names = cdb.spec.extra_domains_rw.clone().unwrap_or_default(); + // Ensure always same order + extra_domain_names.sort(); + let matchers = extra_domain_names + .iter() + .map(|domain_name| format!("HostSNI(`{}`)", domain_name)) + .collect::>(); + let matcher_actual = matchers.join(" || "); + let ingress_route_tcp_name = format!("extra-{}-rw", cdb.name_any()); + let owner_reference = cdb.controller_owner_ref(&()).unwrap(); + + let ingress_route_tcp_to_apply = postgres_ingress_route_tcp( + ingress_route_tcp_name.clone(), + namespace.to_string(), + owner_reference, + matcher_actual, + service_name_read_write.to_string(), + middleware_names, + port, + ); + let ingress_route_tcp_api: Api = + Api::namespaced(ctx.client.clone(), namespace); + if !extra_domain_names.is_empty() { + apply_ingress_route_tcp( + ingress_route_tcp_api, + namespace, + &ingress_route_tcp_name, + &ingress_route_tcp_to_apply, + ) + .await + } else { + delete_ingress_route_tcp(ingress_route_tcp_api, namespace, &ingress_route_tcp_name).await + } +} + +// For end-user provided, extra domain names, +// we allow for update and deletion of domain names. +pub async fn reconcile_ip_allowlist_middleware( + cdb: &CoreDB, + ctx: Arc, +) -> Result { + let ip_allow_list_middleware = generate_ip_allow_list_middleware_tcp(cdb); + let middleware_name = ip_allow_list_middleware + .metadata + .name + .clone() + .expect("middleware is always named"); + let namespace = &ip_allow_list_middleware + .metadata + .namespace + .clone() + .expect("namespace is always set"); + + let middleware_api: Api = Api::namespaced(ctx.client.clone(), namespace); + + let patch = Patch::Apply(&ip_allow_list_middleware); + let patch_parameters = PatchParams::apply("cntrlr").force(); + match middleware_api + .patch(&middleware_name.clone(), &patch_parameters, &patch) + .await + { + Ok(_) => { + debug!( + "Updated MiddlewareTCP {}.{}", + middleware_name.clone(), + &namespace + ); + } + Err(e) => { + // serialize then log json of middlewaretcp + let serialized = serde_json::to_string(&ip_allow_list_middleware).unwrap_or_default(); + error!( + "Failed to update MiddlewareTCP {}.{}: {} \n {}", + middleware_name, namespace, e, serialized + ); + return Err(OperatorError::IngressRouteTcpError); + } + } + + Ok(middleware_name) +} + +async fn apply_ingress_route_tcp( + ingress_route_tcp_api: Api, + namespace: &str, + ingress_route_tcp_name: &String, + ingress_route_tcp_to_apply: &IngressRouteTCP, +) -> Result<(), OperatorError> { + let patch: Patch<&&IngressRouteTCP> = Patch::Apply(&ingress_route_tcp_to_apply); + let patch_parameters = PatchParams::apply("cntrlr").force(); + match ingress_route_tcp_api + .patch(&ingress_route_tcp_name.clone(), &patch_parameters, &patch) + .await + { + Ok(_) => { + info!( + "Updated postgres IngressRouteTCP {}.{}", + ingress_route_tcp_name.clone(), + namespace + ); + } + Err(e) => { + error!( + "Failed to update postgres IngressRouteTCP {}.{}: {}", + ingress_route_tcp_name, namespace, e + ); + return Err(OperatorError::IngressRouteTcpError); + } + } + Ok(()) +} + +async fn delete_ingress_route_tcp( + ingress_route_tcp_api: Api, + namespace: &str, + ingress_route_tcp_name: &String, +) -> Result<(), OperatorError> { + // Check if the resource exists + if ingress_route_tcp_api + .get(&ingress_route_tcp_name.clone()) + .await + .is_ok() + { + // If it exists, proceed with the deletion + let delete_parameters = DeleteParams::default(); + match ingress_route_tcp_api + .delete(&ingress_route_tcp_name.clone(), &delete_parameters) + .await + { + Ok(_) => { + info!( + "Deleted IngressRouteTCP {}.{}", + ingress_route_tcp_name.clone(), + namespace + ); + } + Err(e) => { + error!( + "Failed to delete IngressRouteTCP {}.{}: {}", + ingress_route_tcp_name, namespace, e + ); + return Err(OperatorError::IngressRouteTcpError); + } + } + } else { + debug!( + "IngressRouteTCP {}.{} was not found. Assuming it's already deleted.", + ingress_route_tcp_name, namespace + ); + } + Ok(()) +} + +pub async fn reconcile_postgres_ing_route_tcp( + cdb: &CoreDB, + ctx: Arc, + subdomain: &str, + basedomain: &str, + namespace: &str, + ingress_name_prefix: &str, + service_name: &str, + port: IntOrString, + middleware_names: Vec, +) -> Result<(), OperatorError> { + let client = ctx.client.clone(); + // Initialize kube api for ingress route tcp + let ingress_route_tcp_api: Api = Api::namespaced(client, namespace); + let owner_reference = cdb.controller_owner_ref(&()).unwrap(); + let ingress_route_tcp_name = format!("{}0", ingress_name_prefix); + let newest_matcher = format!("HostSNI(`{subdomain}.{basedomain}`)"); + + let ingress_route_tcp_to_apply = postgres_ingress_route_tcp( + ingress_route_tcp_name.clone(), + namespace.to_string(), + owner_reference.clone(), + newest_matcher.clone(), + service_name.to_string(), + middleware_names.clone(), + port.clone(), + ); + + // Apply this ingress route tcp + apply_ingress_route_tcp( + ingress_route_tcp_api.clone(), + namespace, + &ingress_route_tcp_name, + &ingress_route_tcp_to_apply, + ) + .await?; + + Ok(()) +} + +fn generate_ip_allow_list_middleware_tcp(cdb: &CoreDB) -> MiddlewareTCP { + let source_range = match cdb.spec.ip_allow_list.clone() { + None => { + vec![] + } + Some(ips) => ips, + }; + + let mut valid_ips = valid_cidrs(&source_range); + + for ip in &source_range { + if !valid_ips.contains(ip) { + error!( + "Invalid IP address or CIDR block '{}' on DB {}, skipping", + ip, + cdb.name_any() + ); + } + } + + if valid_ips.is_empty() { + // If IP allow list is not specified, allow all IPs + debug!( + "No valid IP addresses or CIDR blocks specified for DB {}, allowing all IPs", + cdb.name_any() + ); + valid_ips.push("0.0.0.0/0".to_string()); + } + + let owner_references = cdb.controller_owner_ref(&()).map(|oref| vec![oref]); + + MiddlewareTCP { + metadata: ObjectMeta { + name: Some(cdb.name_any()), + namespace: cdb.namespace(), + owner_references, + ..Default::default() + }, + spec: MiddlewareTCPSpec { + ip_allow_list: Some(MiddlewareTCPIpAllowList { + source_range: Some(valid_ips), + }), + ..Default::default() + }, + } +} + +pub fn valid_cidrs(source_range: &[String]) -> Vec { + // Validate each IP address or CIDR block against the regex + let cidr_regex = + Regex::new(VALID_IPV4_CIDR_BLOCK).expect("Failed to compile regex for IPv4 CIDR block"); + let mut valid_ips = Vec::new(); + for ip in source_range.iter() { + if !cidr_regex.is_match(ip) { + } else { + valid_ips.push(ip.clone()); + } + } + valid_ips.sort(); + valid_ips +} diff --git a/tembo-operator/src/lib.rs b/tembo-operator/src/lib.rs index 2355cc543..e3fdd2ad2 100644 --- a/tembo-operator/src/lib.rs +++ b/tembo-operator/src/lib.rs @@ -25,6 +25,7 @@ mod deployment_postgres_exporter; pub mod fixtures; pub mod heartbeat; pub mod ingress; +pub mod ingress_v3; pub mod traefik; pub use traefik::ingress_route_crd; mod certmanager; diff --git a/tembo-operator/src/traefik/ingress_route_crd_v3.rs b/tembo-operator/src/traefik/ingress_route_crd_v3.rs new file mode 100644 index 000000000..66eab3f7b --- /dev/null +++ b/tembo-operator/src/traefik/ingress_route_crd_v3.rs @@ -0,0 +1,278 @@ +// WARNING: generated by kopium - manual changes will be overwritten +// kopium command: kopium -A --derive Default ingressroutes.traefik.io +// kopium version: 0.20.1 + +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; + +/// IngressRouteSpec defines the desired state of IngressRoute. +#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +#[kube( + group = "traefik.io", + version = "v1alpha1", + kind = "IngressRoute", + plural = "ingressroutes" +)] +#[kube(namespaced)] +#[kube(derive = "Default")] +pub struct IngressRouteSpec { + /// EntryPoints defines the list of entry point names to bind to. + /// Entry points have to be configured in the static configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/ + /// Default: all. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "entryPoints" + )] + pub entry_points: Option>, + /// Routes defines the list of routes. + pub routes: Vec, + /// TLS defines the TLS configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tls: Option, +} + +/// Route holds the HTTP route configuration. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteRoutes { + /// Kind defines the kind of the route. + /// Rule is the only supported kind. + pub kind: IngressRouteRoutesKind, + /// Match defines the router's rule. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule + #[serde(rename = "match")] + pub r#match: String, + /// Middlewares defines the list of references to Middleware resources. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware + #[serde(default, skip_serializing_if = "Option::is_none")] + pub middlewares: Option>, + /// Priority defines the router's priority. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + /// Services defines the list of Service. + /// It can contain any combination of TraefikService and/or reference to a Kubernetes Service. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub services: Option>, + /// Syntax defines the router's rule syntax. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax + #[serde(default, skip_serializing_if = "Option::is_none")] + pub syntax: Option, +} + +/// Route holds the HTTP route configuration. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default)] +pub enum IngressRouteRoutesKind { + #[default] + Rule, +} + +/// MiddlewareRef is a reference to a Middleware resource. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteRoutesMiddlewares { + /// Name defines the name of the referenced Middleware resource. + pub name: String, + /// Namespace defines the namespace of the referenced Middleware resource. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +/// Service defines an upstream HTTP service to proxy traffic to. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteRoutesServices { + /// Kind defines the kind of the Service. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + /// Name defines the name of the referenced Kubernetes Service or TraefikService. + /// The differentiation between the two is specified in the Kind field. + pub name: String, + /// Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + /// NativeLB controls, when creating the load-balancer, + /// whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + /// The Kubernetes Service itself does load-balance to the pods. + /// By default, NativeLB is false. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "nativeLB")] + pub native_lb: Option, + /// PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + /// By default, passHostHeader is true. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "passHostHeader" + )] + pub pass_host_header: Option, + /// Port defines the port of a Kubernetes Service. + /// This can be a reference to a named port. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + /// ResponseForwarding defines how Traefik forwards the response from the upstream Kubernetes Service to the client. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "responseForwarding" + )] + pub response_forwarding: Option, + /// Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + /// It defaults to https when Kubernetes Service port is 443, http otherwise. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheme: Option, + /// ServersTransport defines the name of ServersTransport resource to use. + /// It allows to configure the transport between Traefik and your servers. + /// Can only be used on a Kubernetes Service. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "serversTransport" + )] + pub servers_transport: Option, + /// Sticky defines the sticky sessions configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sticky: Option, + /// Strategy defines the load balancing strategy between the servers. + /// RoundRobin is the only supported value at the moment. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub strategy: Option, + /// Weight defines the weight and should only be specified when Name references a TraefikService object + /// (and to be precise, one that embeds a Weighted Round Robin). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub weight: Option, +} + +/// Service defines an upstream HTTP service to proxy traffic to. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] +pub enum IngressRouteRoutesServicesKind { + Service, + TraefikService, +} + +/// ResponseForwarding defines how Traefik forwards the response from the upstream Kubernetes Service to the client. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteRoutesServicesResponseForwarding { + /// FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + /// A negative value means to flush immediately after each write to the client. + /// This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + /// for such responses, writes are flushed to the client immediately. + /// Default: 100ms + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "flushInterval" + )] + pub flush_interval: Option, +} + +/// Sticky defines the sticky sessions configuration. +/// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteRoutesServicesSticky { + /// Cookie defines the sticky cookie configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cookie: Option, +} + +/// Cookie defines the sticky cookie configuration. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteRoutesServicesStickyCookie { + /// HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "httpOnly")] + pub http_only: Option, + /// MaxAge indicates the number of seconds until the cookie expires. + /// When set to a negative number, the cookie expires immediately. + /// When set to zero, the cookie never expires. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "maxAge")] + pub max_age: Option, + /// Name defines the Cookie name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + /// SameSite defines the same site policy. + /// More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + #[serde(default, skip_serializing_if = "Option::is_none", rename = "sameSite")] + pub same_site: Option, + /// Secure defines whether the cookie can only be transmitted over an encrypted connection (i.e. HTTPS). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secure: Option, +} + +/// TLS defines the TLS configuration. +/// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTls { + /// CertResolver defines the name of the certificate resolver to use. + /// Cert resolvers have to be configured in the static configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "certResolver" + )] + pub cert_resolver: Option, + /// Domains defines the list of domains that will be used to issue certificates. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains + #[serde(default, skip_serializing_if = "Option::is_none")] + pub domains: Option>, + /// Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. + /// If not defined, the `default` TLSOption is used. + /// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options + #[serde(default, skip_serializing_if = "Option::is_none")] + pub options: Option, + /// SecretName is the name of the referenced Kubernetes Secret to specify the certificate details. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "secretName" + )] + pub secret_name: Option, + /// Store defines the reference to the TLSStore, that will be used to store certificates. + /// Please note that only `default` TLSStore can be used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub store: Option, +} + +/// Domain holds a domain name with SANs. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTlsDomains { + /// Main defines the main domain name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub main: Option, + /// SANs defines the subject alternative domain names. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sans: Option>, +} + +/// Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. +/// If not defined, the `default` TLSOption is used. +/// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTlsOptions { + /// Name defines the name of the referenced TLSOption. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption + pub name: String, + /// Namespace defines the namespace of the referenced TLSOption. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +/// Store defines the reference to the TLSStore, that will be used to store certificates. +/// Please note that only `default` TLSStore can be used. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTlsStore { + /// Name defines the name of the referenced TLSStore. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore + pub name: String, + /// Namespace defines the namespace of the referenced TLSStore. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} diff --git a/tembo-operator/src/traefik/ingress_route_tcp_crd_v3.rs b/tembo-operator/src/traefik/ingress_route_tcp_crd_v3.rs new file mode 100644 index 000000000..e2e6b9e69 --- /dev/null +++ b/tembo-operator/src/traefik/ingress_route_tcp_crd_v3.rs @@ -0,0 +1,209 @@ +// WARNING: generated by kopium - manual changes will be overwritten +// kopium command: kopium -A --derive Default ingressroutetcps.traefik.io +// kopium version: 0.20.1 + +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; + +/// IngressRouteTCPSpec defines the desired state of IngressRouteTCP. +#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +#[kube( + group = "traefik.io", + version = "v1alpha1", + kind = "IngressRouteTCP", + plural = "ingressroutetcps" +)] +#[kube(namespaced)] +#[kube(derive = "Default")] +pub struct IngressRouteTCPSpec { + /// EntryPoints defines the list of entry point names to bind to. + /// Entry points have to be configured in the static configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/ + /// Default: all. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "entryPoints" + )] + pub entry_points: Option>, + /// Routes defines the list of routes. + pub routes: Vec, + /// TLS defines the TLS configuration on a layer 4 / TCP Route. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tls: Option, +} + +/// RouteTCP holds the TCP route configuration. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPRoutes { + /// Match defines the router's rule. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule_1 + #[serde(rename = "match")] + pub r#match: String, + /// Middlewares defines the list of references to MiddlewareTCP resources. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub middlewares: Option>, + /// Priority defines the router's priority. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + /// Services defines the list of TCP services. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub services: Option>, + /// Syntax defines the router's rule syntax. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub syntax: Option, +} + +/// ObjectReference is a generic reference to a Traefik resource. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPRoutesMiddlewares { + /// Name defines the name of the referenced Traefik resource. + pub name: String, + /// Namespace defines the namespace of the referenced Traefik resource. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +/// ServiceTCP defines an upstream TCP service to proxy traffic to. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPRoutesServices { + /// Name defines the name of the referenced Kubernetes Service. + pub name: String, + /// Namespace defines the namespace of the referenced Kubernetes Service. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + /// NativeLB controls, when creating the load-balancer, + /// whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + /// The Kubernetes Service itself does load-balance to the pods. + /// By default, NativeLB is false. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "nativeLB")] + pub native_lb: Option, + /// Port defines the port of a Kubernetes Service. + /// This can be a reference to a named port. + pub port: IntOrString, + /// ProxyProtocol defines the PROXY protocol configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "proxyProtocol" + )] + pub proxy_protocol: Option, + /// ServersTransport defines the name of ServersTransportTCP resource to use. + /// It allows to configure the transport between Traefik and your servers. + /// Can only be used on a Kubernetes Service. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "serversTransport" + )] + pub servers_transport: Option, + /// TerminationDelay defines the deadline that the proxy sets, after one of its connected peers indicates + /// it has closed the writing capability of its connection, to close the reading capability as well, + /// hence fully terminating the connection. + /// It is a duration in milliseconds, defaulting to 100. + /// A negative value means an infinite deadline (i.e. the reading capability is never closed). + /// Deprecated: TerminationDelay is not supported APIVersion traefik.io/v1, please use ServersTransport to configure the TerminationDelay instead. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "terminationDelay" + )] + pub termination_delay: Option, + /// TLS determines whether to use TLS when dialing with the backend. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tls: Option, + /// Weight defines the weight used when balancing requests between multiple Kubernetes Service. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub weight: Option, +} + +/// ProxyProtocol defines the PROXY protocol configuration. +/// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPRoutesServicesProxyProtocol { + /// Version defines the PROXY Protocol version to use. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, +} + +/// TLS defines the TLS configuration on a layer 4 / TCP Route. +/// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1 +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPTls { + /// CertResolver defines the name of the certificate resolver to use. + /// Cert resolvers have to be configured in the static configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "certResolver" + )] + pub cert_resolver: Option, + /// Domains defines the list of domains that will be used to issue certificates. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains + #[serde(default, skip_serializing_if = "Option::is_none")] + pub domains: Option>, + /// Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. + /// If not defined, the `default` TLSOption is used. + /// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options + #[serde(default, skip_serializing_if = "Option::is_none")] + pub options: Option, + /// Passthrough defines whether a TLS router will terminate the TLS connection. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub passthrough: Option, + /// SecretName is the name of the referenced Kubernetes Secret to specify the certificate details. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "secretName" + )] + pub secret_name: Option, + /// Store defines the reference to the TLSStore, that will be used to store certificates. + /// Please note that only `default` TLSStore can be used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub store: Option, +} + +/// Domain holds a domain name with SANs. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPTlsDomains { + /// Main defines the main domain name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub main: Option, + /// SANs defines the subject alternative domain names. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sans: Option>, +} + +/// Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. +/// If not defined, the `default` TLSOption is used. +/// More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPTlsOptions { + /// Name defines the name of the referenced Traefik resource. + pub name: String, + /// Namespace defines the namespace of the referenced Traefik resource. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +/// Store defines the reference to the TLSStore, that will be used to store certificates. +/// Please note that only `default` TLSStore can be used. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct IngressRouteTCPTlsStore { + /// Name defines the name of the referenced Traefik resource. + pub name: String, + /// Namespace defines the namespace of the referenced Traefik resource. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} diff --git a/tembo-operator/src/traefik/middleware_tcp_crd_v3.rs b/tembo-operator/src/traefik/middleware_tcp_crd_v3.rs new file mode 100644 index 000000000..9b2dadc37 --- /dev/null +++ b/tembo-operator/src/traefik/middleware_tcp_crd_v3.rs @@ -0,0 +1,88 @@ +// WARNING: generated by kopium - manual changes will be overwritten +// kopium command: kopium -A --derive Default middlewaretcps.traefik.io +// kopium version: 0.20.1 + +#[allow(unused_imports)] +mod prelude { + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; + +/// MiddlewareTCPSpec defines the desired state of a MiddlewareTCP. +#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +#[kube( + group = "traefik.io", + version = "v1alpha1", + kind = "MiddlewareTCP", + plural = "middlewaretcps" +)] +#[kube(namespaced)] +#[kube(derive = "Default")] +pub struct MiddlewareTCPSpec { + /// InFlightConn defines the InFlightConn middleware configuration. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "inFlightConn" + )] + pub in_flight_conn: Option, + /// IPAllowList defines the IPAllowList middleware configuration. + /// This middleware accepts/refuses connections based on the client IP. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/ipallowlist/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipAllowList" + )] + pub ip_allow_list: Option, + /// IPWhiteList defines the IPWhiteList middleware configuration. + /// This middleware accepts/refuses connections based on the client IP. + /// Deprecated: please use IPAllowList instead. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/ipwhitelist/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipWhiteList" + )] + pub ip_white_list: Option, +} + +/// InFlightConn defines the InFlightConn middleware configuration. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareTCPInFlightConn { + /// Amount defines the maximum amount of allowed simultaneous connections. + /// The middleware closes the connection if there are already amount connections opened. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub amount: Option, +} + +/// IPAllowList defines the IPAllowList middleware configuration. +/// This middleware accepts/refuses connections based on the client IP. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/ipallowlist/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareTCPIpAllowList { + /// SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sourceRange" + )] + pub source_range: Option>, +} + +/// IPWhiteList defines the IPWhiteList middleware configuration. +/// This middleware accepts/refuses connections based on the client IP. +/// Deprecated: please use IPAllowList instead. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/ipwhitelist/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareTCPIpWhiteList { + /// SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sourceRange" + )] + pub source_range: Option>, +} diff --git a/tembo-operator/src/traefik/middlewares_crd_v3.rs b/tembo-operator/src/traefik/middlewares_crd_v3.rs new file mode 100644 index 000000000..d29443bd1 --- /dev/null +++ b/tembo-operator/src/traefik/middlewares_crd_v3.rs @@ -0,0 +1,1334 @@ +// WARNING: generated by kopium - manual changes will be overwritten +// kopium command: kopium -A --derive Default middlewares.traefik.io +// kopium version: 0.20.1 + +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; + pub use std::collections::BTreeMap; +} +use self::prelude::*; + +/// MiddlewareSpec defines the desired state of a Middleware. +#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +#[kube( + group = "traefik.io", + version = "v1alpha1", + kind = "Middleware", + plural = "middlewares" +)] +#[kube(namespaced)] +#[kube(derive = "Default")] +pub struct MiddlewareSpec { + /// AddPrefix holds the add prefix middleware configuration. + /// This middleware updates the path of a request before forwarding it. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/ + #[serde(default, skip_serializing_if = "Option::is_none", rename = "addPrefix")] + pub add_prefix: Option, + /// BasicAuth holds the basic auth middleware configuration. + /// This middleware restricts access to your services to known users. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/ + #[serde(default, skip_serializing_if = "Option::is_none", rename = "basicAuth")] + pub basic_auth: Option, + /// Buffering holds the buffering middleware configuration. + /// This middleware retries or limits the size of requests that can be forwarded to backends. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes + #[serde(default, skip_serializing_if = "Option::is_none")] + pub buffering: Option, + /// Chain holds the configuration of the chain middleware. + /// This middleware enables to define reusable combinations of other pieces of middleware. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/ + #[serde(default, skip_serializing_if = "Option::is_none")] + pub chain: Option, + /// CircuitBreaker holds the circuit breaker configuration. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "circuitBreaker" + )] + pub circuit_breaker: Option, + /// Compress holds the compress middleware configuration. + /// This middleware compresses responses before sending them to the client, using gzip compression. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/ + #[serde(default, skip_serializing_if = "Option::is_none")] + pub compress: Option, + /// ContentType holds the content-type middleware configuration. + /// This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "contentType" + )] + pub content_type: Option, + /// DigestAuth holds the digest auth middleware configuration. + /// This middleware restricts access to your services to known users. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "digestAuth" + )] + pub digest_auth: Option, + /// ErrorPage holds the custom error middleware configuration. + /// This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/ + #[serde(default, skip_serializing_if = "Option::is_none")] + pub errors: Option, + /// ForwardAuth holds the forward auth middleware configuration. + /// This middleware delegates the request authentication to a Service. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "forwardAuth" + )] + pub forward_auth: Option, + /// GrpcWeb holds the gRPC web middleware configuration. + /// This middleware converts a gRPC web request to an HTTP/2 gRPC request. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "grpcWeb")] + pub grpc_web: Option, + /// Headers holds the headers middleware configuration. + /// This middleware manages the requests and responses headers. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders + #[serde(default, skip_serializing_if = "Option::is_none")] + pub headers: Option, + /// InFlightReq holds the in-flight request middleware configuration. + /// This middleware limits the number of requests being processed and served concurrently. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "inFlightReq" + )] + pub in_flight_req: Option, + /// IPAllowList holds the IP allowlist middleware configuration. + /// This middleware accepts / refuses requests based on the client IP. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipAllowList" + )] + pub ip_allow_list: Option, + /// Deprecated: please use IPAllowList instead. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipWhiteList" + )] + pub ip_white_list: Option, + /// PassTLSClientCert holds the pass TLS client cert middleware configuration. + /// This middleware adds the selected data from the passed client TLS certificate to a header. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "passTLSClientCert" + )] + pub pass_tls_client_cert: Option, + /// Plugin defines the middleware plugin configuration. + /// More info: https://doc.traefik.io/traefik/plugins/ + #[serde(default, skip_serializing_if = "Option::is_none")] + pub plugin: Option>, + /// RateLimit holds the rate limit configuration. + /// This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/ + #[serde(default, skip_serializing_if = "Option::is_none", rename = "rateLimit")] + pub rate_limit: Option, + /// RedirectRegex holds the redirect regex middleware configuration. + /// This middleware redirects a request using regex matching and replacement. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "redirectRegex" + )] + pub redirect_regex: Option, + /// RedirectScheme holds the redirect scheme middleware configuration. + /// This middleware redirects requests from a scheme/port to another. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "redirectScheme" + )] + pub redirect_scheme: Option, + /// ReplacePath holds the replace path middleware configuration. + /// This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "replacePath" + )] + pub replace_path: Option, + /// ReplacePathRegex holds the replace path regex middleware configuration. + /// This middleware replaces the path of a URL using regex matching and replacement. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "replacePathRegex" + )] + pub replace_path_regex: Option, + /// Retry holds the retry middleware configuration. + /// This middleware reissues requests a given number of times to a backend server if that server does not reply. + /// As soon as the server answers, the middleware stops retrying, regardless of the response status. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/ + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + /// StripPrefix holds the strip prefix middleware configuration. + /// This middleware removes the specified prefixes from the URL path. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "stripPrefix" + )] + pub strip_prefix: Option, + /// StripPrefixRegex holds the strip prefix regex middleware configuration. + /// This middleware removes the matching prefixes from the URL path. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/ + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "stripPrefixRegex" + )] + pub strip_prefix_regex: Option, +} + +/// AddPrefix holds the add prefix middleware configuration. +/// This middleware updates the path of a request before forwarding it. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareAddPrefix { + /// Prefix is the string to add before the current path in the requested URL. + /// It should include a leading slash (/). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub prefix: Option, +} + +/// BasicAuth holds the basic auth middleware configuration. +/// This middleware restricts access to your services to known users. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareBasicAuth { + /// HeaderField defines a header field to store the authenticated user. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "headerField" + )] + pub header_field: Option, + /// Realm allows the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme. + /// Default: traefik. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub realm: Option, + /// RemoveHeader sets the removeHeader option to true to remove the authorization header before forwarding the request to your service. + /// Default: false. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "removeHeader" + )] + pub remove_header: Option, + /// Secret is the name of the referenced Kubernetes Secret containing user credentials. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secret: Option, +} + +/// Buffering holds the buffering middleware configuration. +/// This middleware retries or limits the size of requests that can be forwarded to backends. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareBuffering { + /// MaxRequestBodyBytes defines the maximum allowed body size for the request (in bytes). + /// If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a 413 (Request Entity Too Large) response. + /// Default: 0 (no maximum). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "maxRequestBodyBytes" + )] + pub max_request_body_bytes: Option, + /// MaxResponseBodyBytes defines the maximum allowed response size from the service (in bytes). + /// If the response exceeds the allowed size, it is not forwarded to the client. The client gets a 500 (Internal Server Error) response instead. + /// Default: 0 (no maximum). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "maxResponseBodyBytes" + )] + pub max_response_body_bytes: Option, + /// MemRequestBodyBytes defines the threshold (in bytes) from which the request will be buffered on disk instead of in memory. + /// Default: 1048576 (1Mi). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "memRequestBodyBytes" + )] + pub mem_request_body_bytes: Option, + /// MemResponseBodyBytes defines the threshold (in bytes) from which the response will be buffered on disk instead of in memory. + /// Default: 1048576 (1Mi). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "memResponseBodyBytes" + )] + pub mem_response_body_bytes: Option, + /// RetryExpression defines the retry conditions. + /// It is a logical combination of functions with operators AND (&&) and OR (||). + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#retryexpression + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "retryExpression" + )] + pub retry_expression: Option, +} + +/// Chain holds the configuration of the chain middleware. +/// This middleware enables to define reusable combinations of other pieces of middleware. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareChain { + /// Middlewares is the list of MiddlewareRef which composes the chain. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub middlewares: Option>, +} + +/// MiddlewareRef is a reference to a Middleware resource. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareChainMiddlewares { + /// Name defines the name of the referenced Middleware resource. + pub name: String, + /// Namespace defines the namespace of the referenced Middleware resource. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +/// CircuitBreaker holds the circuit breaker configuration. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareCircuitBreaker { + /// CheckPeriod is the interval between successive checks of the circuit breaker condition (when in standby state). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "checkPeriod" + )] + pub check_period: Option, + /// Expression is the condition that triggers the tripped state. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expression: Option, + /// FallbackDuration is the duration for which the circuit breaker will wait before trying to recover (from a tripped state). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "fallbackDuration" + )] + pub fallback_duration: Option, + /// RecoveryDuration is the duration for which the circuit breaker will try to recover (as soon as it is in recovering state). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "recoveryDuration" + )] + pub recovery_duration: Option, +} + +/// Compress holds the compress middleware configuration. +/// This middleware compresses responses before sending them to the client, using gzip compression. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareCompress { + /// ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing. + /// `application/grpc` is always excluded. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "excludedContentTypes" + )] + pub excluded_content_types: Option>, + /// IncludedContentTypes defines the list of content types to compare the Content-Type header of the responses before compressing. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "includedContentTypes" + )] + pub included_content_types: Option>, + /// MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. + /// Default: 1024. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "minResponseBodyBytes" + )] + pub min_response_body_bytes: Option, +} + +/// ContentType holds the content-type middleware configuration. +/// This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareContentType { + /// AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, + /// be automatically set to a value derived from the contents of the response. + /// Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "autoDetect" + )] + pub auto_detect: Option, +} + +/// DigestAuth holds the digest auth middleware configuration. +/// This middleware restricts access to your services to known users. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareDigestAuth { + /// HeaderField defines a header field to store the authenticated user. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "headerField" + )] + pub header_field: Option, + /// Realm allows the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme. + /// Default: traefik. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub realm: Option, + /// RemoveHeader defines whether to remove the authorization header before forwarding the request to the backend. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "removeHeader" + )] + pub remove_header: Option, + /// Secret is the name of the referenced Kubernetes Secret containing user credentials. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secret: Option, +} + +/// ErrorPage holds the custom error middleware configuration. +/// This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareErrors { + /// Query defines the URL for the error page (hosted by service). + /// The {status} variable can be used in order to insert the status code in the URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub query: Option, + /// Service defines the reference to a Kubernetes Service that will serve the error page. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service + #[serde(default, skip_serializing_if = "Option::is_none")] + pub service: Option, + /// Status defines which status or range of statuses should result in an error page. + /// It can be either a status code as a number (500), + /// as multiple comma-separated numbers (500,502), + /// as ranges by separating two codes with a dash (500-599), + /// or a combination of the two (404,418,500-599). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub status: Option>, +} + +/// Service defines the reference to a Kubernetes Service that will serve the error page. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareErrorsService { + /// Kind defines the kind of the Service. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + /// Name defines the name of the referenced Kubernetes Service or TraefikService. + /// The differentiation between the two is specified in the Kind field. + pub name: String, + /// Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + /// NativeLB controls, when creating the load-balancer, + /// whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + /// The Kubernetes Service itself does load-balance to the pods. + /// By default, NativeLB is false. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "nativeLB")] + pub native_lb: Option, + /// PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + /// By default, passHostHeader is true. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "passHostHeader" + )] + pub pass_host_header: Option, + /// Port defines the port of a Kubernetes Service. + /// This can be a reference to a named port. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + /// ResponseForwarding defines how Traefik forwards the response from the upstream Kubernetes Service to the client. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "responseForwarding" + )] + pub response_forwarding: Option, + /// Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + /// It defaults to https when Kubernetes Service port is 443, http otherwise. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheme: Option, + /// ServersTransport defines the name of ServersTransport resource to use. + /// It allows to configure the transport between Traefik and your servers. + /// Can only be used on a Kubernetes Service. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "serversTransport" + )] + pub servers_transport: Option, + /// Sticky defines the sticky sessions configuration. + /// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sticky: Option, + /// Strategy defines the load balancing strategy between the servers. + /// RoundRobin is the only supported value at the moment. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub strategy: Option, + /// Weight defines the weight and should only be specified when Name references a TraefikService object + /// (and to be precise, one that embeds a Weighted Round Robin). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub weight: Option, +} + +/// Service defines the reference to a Kubernetes Service that will serve the error page. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] +pub enum MiddlewareErrorsServiceKind { + Service, + TraefikService, +} + +/// ResponseForwarding defines how Traefik forwards the response from the upstream Kubernetes Service to the client. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareErrorsServiceResponseForwarding { + /// FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + /// A negative value means to flush immediately after each write to the client. + /// This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + /// for such responses, writes are flushed to the client immediately. + /// Default: 100ms + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "flushInterval" + )] + pub flush_interval: Option, +} + +/// Sticky defines the sticky sessions configuration. +/// More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareErrorsServiceSticky { + /// Cookie defines the sticky cookie configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cookie: Option, +} + +/// Cookie defines the sticky cookie configuration. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareErrorsServiceStickyCookie { + /// HTTPOnly defines whether the cookie can be accessed by client-side APIs, such as JavaScript. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "httpOnly")] + pub http_only: Option, + /// MaxAge indicates the number of seconds until the cookie expires. + /// When set to a negative number, the cookie expires immediately. + /// When set to zero, the cookie never expires. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "maxAge")] + pub max_age: Option, + /// Name defines the Cookie name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + /// SameSite defines the same site policy. + /// More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + #[serde(default, skip_serializing_if = "Option::is_none", rename = "sameSite")] + pub same_site: Option, + /// Secure defines whether the cookie can only be transmitted over an encrypted connection (i.e. HTTPS). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secure: Option, +} + +/// ForwardAuth holds the forward auth middleware configuration. +/// This middleware delegates the request authentication to a Service. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareForwardAuth { + /// AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "addAuthCookiesToResponse" + )] + pub add_auth_cookies_to_response: Option>, + /// Address defines the authentication server address. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub address: Option, + /// AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. + /// If not set or empty then all request headers are passed. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "authRequestHeaders" + )] + pub auth_request_headers: Option>, + /// AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "authResponseHeaders" + )] + pub auth_response_headers: Option>, + /// AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "authResponseHeadersRegex" + )] + pub auth_response_headers_regex: Option, + /// TLS defines the configuration used to secure the connection to the authentication server. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tls: Option, + /// TrustForwardHeader defines whether to trust (ie: forward) all X-Forwarded-* headers. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "trustForwardHeader" + )] + pub trust_forward_header: Option, +} + +/// TLS defines the configuration used to secure the connection to the authentication server. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareForwardAuthTls { + /// Deprecated: TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "caOptional" + )] + pub ca_optional: Option, + /// CASecret is the name of the referenced Kubernetes Secret containing the CA to validate the server certificate. + /// The CA certificate is extracted from key `tls.ca` or `ca.crt`. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "caSecret")] + pub ca_secret: Option, + /// CertSecret is the name of the referenced Kubernetes Secret containing the client certificate. + /// The client certificate is extracted from the keys `tls.crt` and `tls.key`. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "certSecret" + )] + pub cert_secret: Option, + /// InsecureSkipVerify defines whether the server certificates should be validated. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "insecureSkipVerify" + )] + pub insecure_skip_verify: Option, +} + +/// GrpcWeb holds the gRPC web middleware configuration. +/// This middleware converts a gRPC web request to an HTTP/2 gRPC request. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareGrpcWeb { + /// AllowOrigins is a list of allowable origins. + /// Can also be a wildcard origin "*". + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "allowOrigins" + )] + pub allow_origins: Option>, +} + +/// Headers holds the headers middleware configuration. +/// This middleware manages the requests and responses headers. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareHeaders { + /// AccessControlAllowCredentials defines whether the request can include user credentials. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlAllowCredentials" + )] + pub access_control_allow_credentials: Option, + /// AccessControlAllowHeaders defines the Access-Control-Request-Headers values sent in preflight response. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlAllowHeaders" + )] + pub access_control_allow_headers: Option>, + /// AccessControlAllowMethods defines the Access-Control-Request-Method values sent in preflight response. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlAllowMethods" + )] + pub access_control_allow_methods: Option>, + /// AccessControlAllowOriginList is a list of allowable origins. Can also be a wildcard origin "*". + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlAllowOriginList" + )] + pub access_control_allow_origin_list: Option>, + /// AccessControlAllowOriginListRegex is a list of allowable origins written following the Regular Expression syntax (https://golang.org/pkg/regexp/). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlAllowOriginListRegex" + )] + pub access_control_allow_origin_list_regex: Option>, + /// AccessControlExposeHeaders defines the Access-Control-Expose-Headers values sent in preflight response. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlExposeHeaders" + )] + pub access_control_expose_headers: Option>, + /// AccessControlMaxAge defines the time that a preflight request may be cached. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "accessControlMaxAge" + )] + pub access_control_max_age: Option, + /// AddVaryHeader defines whether the Vary header is automatically added/updated when the AccessControlAllowOriginList is set. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "addVaryHeader" + )] + pub add_vary_header: Option, + /// AllowedHosts defines the fully qualified list of allowed domain names. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "allowedHosts" + )] + pub allowed_hosts: Option>, + /// BrowserXSSFilter defines whether to add the X-XSS-Protection header with the value 1; mode=block. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "browserXssFilter" + )] + pub browser_xss_filter: Option, + /// ContentSecurityPolicy defines the Content-Security-Policy header value. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "contentSecurityPolicy" + )] + pub content_security_policy: Option, + /// ContentTypeNosniff defines whether to add the X-Content-Type-Options header with the nosniff value. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "contentTypeNosniff" + )] + pub content_type_nosniff: Option, + /// CustomBrowserXSSValue defines the X-XSS-Protection header value. + /// This overrides the BrowserXssFilter option. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "customBrowserXSSValue" + )] + pub custom_browser_xss_value: Option, + /// CustomFrameOptionsValue defines the X-Frame-Options header value. + /// This overrides the FrameDeny option. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "customFrameOptionsValue" + )] + pub custom_frame_options_value: Option, + /// CustomRequestHeaders defines the header names and values to apply to the request. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "customRequestHeaders" + )] + pub custom_request_headers: Option>, + /// CustomResponseHeaders defines the header names and values to apply to the response. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "customResponseHeaders" + )] + pub custom_response_headers: Option>, + /// Deprecated: FeaturePolicy option is deprecated, please use PermissionsPolicy instead. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "featurePolicy" + )] + pub feature_policy: Option, + /// ForceSTSHeader defines whether to add the STS header even when the connection is HTTP. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "forceSTSHeader" + )] + pub force_sts_header: Option, + /// FrameDeny defines whether to add the X-Frame-Options header with the DENY value. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "frameDeny")] + pub frame_deny: Option, + /// HostsProxyHeaders defines the header keys that may hold a proxied hostname value for the request. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "hostsProxyHeaders" + )] + pub hosts_proxy_headers: Option>, + /// IsDevelopment defines whether to mitigate the unwanted effects of the AllowedHosts, SSL, and STS options when developing. + /// Usually testing takes place using HTTP, not HTTPS, and on localhost, not your production domain. + /// If you would like your development environment to mimic production with complete Host blocking, SSL redirects, + /// and STS headers, leave this as false. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "isDevelopment" + )] + pub is_development: Option, + /// PermissionsPolicy defines the Permissions-Policy header value. + /// This allows sites to control browser features. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "permissionsPolicy" + )] + pub permissions_policy: Option, + /// PublicKey is the public key that implements HPKP to prevent MITM attacks with forged certificates. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "publicKey")] + pub public_key: Option, + /// ReferrerPolicy defines the Referrer-Policy header value. + /// This allows sites to control whether browsers forward the Referer header to other sites. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "referrerPolicy" + )] + pub referrer_policy: Option, + /// Deprecated: SSLForceHost option is deprecated, please use RedirectRegex instead. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sslForceHost" + )] + pub ssl_force_host: Option, + /// Deprecated: SSLHost option is deprecated, please use RedirectRegex instead. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "sslHost")] + pub ssl_host: Option, + /// SSLProxyHeaders defines the header keys with associated values that would indicate a valid HTTPS request. + /// It can be useful when using other proxies (example: "X-Forwarded-Proto": "https"). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sslProxyHeaders" + )] + pub ssl_proxy_headers: Option>, + /// Deprecated: SSLRedirect option is deprecated, please use EntryPoint redirection or RedirectScheme instead. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sslRedirect" + )] + pub ssl_redirect: Option, + /// Deprecated: SSLTemporaryRedirect option is deprecated, please use EntryPoint redirection or RedirectScheme instead. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sslTemporaryRedirect" + )] + pub ssl_temporary_redirect: Option, + /// STSIncludeSubdomains defines whether the includeSubDomains directive is appended to the Strict-Transport-Security header. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "stsIncludeSubdomains" + )] + pub sts_include_subdomains: Option, + /// STSPreload defines whether the preload flag is appended to the Strict-Transport-Security header. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "stsPreload" + )] + pub sts_preload: Option, + /// STSSeconds defines the max-age of the Strict-Transport-Security header. + /// If set to 0, the header is not set. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "stsSeconds" + )] + pub sts_seconds: Option, +} + +/// InFlightReq holds the in-flight request middleware configuration. +/// This middleware limits the number of requests being processed and served concurrently. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareInFlightReq { + /// Amount defines the maximum amount of allowed simultaneous in-flight request. + /// The middleware responds with HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub amount: Option, + /// SourceCriterion defines what criterion is used to group requests as originating from a common source. + /// If several strategies are defined at the same time, an error will be raised. + /// If none are set, the default is to use the requestHost. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sourceCriterion" + )] + pub source_criterion: Option, +} + +/// SourceCriterion defines what criterion is used to group requests as originating from a common source. +/// If several strategies are defined at the same time, an error will be raised. +/// If none are set, the default is to use the requestHost. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareInFlightReqSourceCriterion { + /// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipStrategy" + )] + pub ip_strategy: Option, + /// RequestHeaderName defines the name of the header used to group incoming requests. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHeaderName" + )] + pub request_header_name: Option, + /// RequestHost defines whether to consider the request Host as the source. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHost" + )] + pub request_host: Option, +} + +/// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareInFlightReqSourceCriterionIpStrategy { + /// Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub depth: Option, + /// ExcludedIPs configures Traefik to scan the X-Forwarded-For header and select the first IP not in the list. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "excludedIPs" + )] + pub excluded_i_ps: Option>, +} + +/// IPAllowList holds the IP allowlist middleware configuration. +/// This middleware accepts / refuses requests based on the client IP. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareIpAllowList { + /// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipStrategy" + )] + pub ip_strategy: Option, + /// RejectStatusCode defines the HTTP status code used for refused requests. + /// If not set, the default is 403 (Forbidden). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "rejectStatusCode" + )] + pub reject_status_code: Option, + /// SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sourceRange" + )] + pub source_range: Option>, +} + +/// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareIpAllowListIpStrategy { + /// Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub depth: Option, + /// ExcludedIPs configures Traefik to scan the X-Forwarded-For header and select the first IP not in the list. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "excludedIPs" + )] + pub excluded_i_ps: Option>, +} + +/// Deprecated: please use IPAllowList instead. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareIpWhiteList { + /// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipStrategy" + )] + pub ip_strategy: Option, + /// SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sourceRange" + )] + pub source_range: Option>, +} + +/// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareIpWhiteListIpStrategy { + /// Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub depth: Option, + /// ExcludedIPs configures Traefik to scan the X-Forwarded-For header and select the first IP not in the list. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "excludedIPs" + )] + pub excluded_i_ps: Option>, +} + +/// PassTLSClientCert holds the pass TLS client cert middleware configuration. +/// This middleware adds the selected data from the passed client TLS certificate to a header. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewarePassTlsClientCert { + /// Info selects the specific client certificate details you want to add to the X-Forwarded-Tls-Client-Cert-Info header. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub info: Option, + /// PEM sets the X-Forwarded-Tls-Client-Cert header with the certificate. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pem: Option, +} + +/// Info selects the specific client certificate details you want to add to the X-Forwarded-Tls-Client-Cert-Info header. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewarePassTlsClientCertInfo { + /// Issuer defines the client certificate issuer details to add to the X-Forwarded-Tls-Client-Cert-Info header. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub issuer: Option, + /// NotAfter defines whether to add the Not After information from the Validity part. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "notAfter")] + pub not_after: Option, + /// NotBefore defines whether to add the Not Before information from the Validity part. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "notBefore")] + pub not_before: Option, + /// Sans defines whether to add the Subject Alternative Name information from the Subject Alternative Name part. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sans: Option, + /// SerialNumber defines whether to add the client serialNumber information. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "serialNumber" + )] + pub serial_number: Option, + /// Subject defines the client certificate subject details to add to the X-Forwarded-Tls-Client-Cert-Info header. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub subject: Option, +} + +/// Issuer defines the client certificate issuer details to add to the X-Forwarded-Tls-Client-Cert-Info header. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewarePassTlsClientCertInfoIssuer { + /// CommonName defines whether to add the organizationalUnit information into the issuer. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "commonName" + )] + pub common_name: Option, + /// Country defines whether to add the country information into the issuer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub country: Option, + /// DomainComponent defines whether to add the domainComponent information into the issuer. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "domainComponent" + )] + pub domain_component: Option, + /// Locality defines whether to add the locality information into the issuer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub locality: Option, + /// Organization defines whether to add the organization information into the issuer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub organization: Option, + /// Province defines whether to add the province information into the issuer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub province: Option, + /// SerialNumber defines whether to add the serialNumber information into the issuer. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "serialNumber" + )] + pub serial_number: Option, +} + +/// Subject defines the client certificate subject details to add to the X-Forwarded-Tls-Client-Cert-Info header. +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewarePassTlsClientCertInfoSubject { + /// CommonName defines whether to add the organizationalUnit information into the subject. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "commonName" + )] + pub common_name: Option, + /// Country defines whether to add the country information into the subject. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub country: Option, + /// DomainComponent defines whether to add the domainComponent information into the subject. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "domainComponent" + )] + pub domain_component: Option, + /// Locality defines whether to add the locality information into the subject. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub locality: Option, + /// Organization defines whether to add the organization information into the subject. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub organization: Option, + /// OrganizationalUnit defines whether to add the organizationalUnit information into the subject. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "organizationalUnit" + )] + pub organizational_unit: Option, + /// Province defines whether to add the province information into the subject. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub province: Option, + /// SerialNumber defines whether to add the serialNumber information into the subject. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "serialNumber" + )] + pub serial_number: Option, +} + +/// RateLimit holds the rate limit configuration. +/// This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareRateLimit { + /// Average is the maximum rate, by default in requests/s, allowed for the given source. + /// It defaults to 0, which means no rate limiting. + /// The rate is actually defined by dividing Average by Period. So for a rate below 1req/s, + /// one needs to define a Period larger than a second. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub average: Option, + /// Burst is the maximum number of requests allowed to arrive in the same arbitrarily small period of time. + /// It defaults to 1. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub burst: Option, + /// Period, in combination with Average, defines the actual maximum rate, such as: + /// r = Average / Period. It defaults to a second. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub period: Option, + /// SourceCriterion defines what criterion is used to group requests as originating from a common source. + /// If several strategies are defined at the same time, an error will be raised. + /// If none are set, the default is to use the request's remote address field (as an ipStrategy). + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "sourceCriterion" + )] + pub source_criterion: Option, +} + +/// SourceCriterion defines what criterion is used to group requests as originating from a common source. +/// If several strategies are defined at the same time, an error will be raised. +/// If none are set, the default is to use the request's remote address field (as an ipStrategy). +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareRateLimitSourceCriterion { + /// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + /// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "ipStrategy" + )] + pub ip_strategy: Option, + /// RequestHeaderName defines the name of the header used to group incoming requests. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHeaderName" + )] + pub request_header_name: Option, + /// RequestHost defines whether to consider the request Host as the source. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHost" + )] + pub request_host: Option, +} + +/// IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareRateLimitSourceCriterionIpStrategy { + /// Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub depth: Option, + /// ExcludedIPs configures Traefik to scan the X-Forwarded-For header and select the first IP not in the list. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "excludedIPs" + )] + pub excluded_i_ps: Option>, +} + +/// RedirectRegex holds the redirect regex middleware configuration. +/// This middleware redirects a request using regex matching and replacement. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareRedirectRegex { + /// Permanent defines whether the redirection is permanent (301). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub permanent: Option, + /// Regex defines the regex used to match and capture elements from the request URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub regex: Option, + /// Replacement defines how to modify the URL to have the new target URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub replacement: Option, +} + +/// RedirectScheme holds the redirect scheme middleware configuration. +/// This middleware redirects requests from a scheme/port to another. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareRedirectScheme { + /// Permanent defines whether the redirection is permanent (301). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub permanent: Option, + /// Port defines the port of the new URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + /// Scheme defines the scheme of the new URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheme: Option, +} + +/// ReplacePath holds the replace path middleware configuration. +/// This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareReplacePath { + /// Path defines the path to use as replacement in the request URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, +} + +/// ReplacePathRegex holds the replace path regex middleware configuration. +/// This middleware replaces the path of a URL using regex matching and replacement. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareReplacePathRegex { + /// Regex defines the regular expression used to match and capture the path from the request URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub regex: Option, + /// Replacement defines the replacement path format, which can include captured variables. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub replacement: Option, +} + +/// Retry holds the retry middleware configuration. +/// This middleware reissues requests a given number of times to a backend server if that server does not reply. +/// As soon as the server answers, the middleware stops retrying, regardless of the response status. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareRetry { + /// Attempts defines how many times the request should be retried. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub attempts: Option, + /// InitialInterval defines the first wait time in the exponential backoff series. + /// The maximum interval is calculated as twice the initialInterval. + /// If unspecified, requests will be retried immediately. + /// The value of initialInterval should be provided in seconds or as a valid duration format, + /// see https://pkg.go.dev/time#ParseDuration. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "initialInterval" + )] + pub initial_interval: Option, +} + +/// StripPrefix holds the strip prefix middleware configuration. +/// This middleware removes the specified prefixes from the URL path. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareStripPrefix { + /// Deprecated: ForceSlash option is deprecated, please remove any usage of this option. + /// ForceSlash ensures that the resulting stripped path is not the empty string, by replacing it with / when necessary. + /// Default: true. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "forceSlash" + )] + pub force_slash: Option, + /// Prefixes defines the prefixes to strip from the request URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub prefixes: Option>, +} + +/// StripPrefixRegex holds the strip prefix regex middleware configuration. +/// This middleware removes the matching prefixes from the URL path. +/// More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/ +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] +pub struct MiddlewareStripPrefixRegex { + /// Regex defines the regular expression to match the path prefix from the request URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub regex: Option>, +} diff --git a/tembo-operator/src/traefik/mod.rs b/tembo-operator/src/traefik/mod.rs index 368eeb3f9..61212d333 100644 --- a/tembo-operator/src/traefik/mod.rs +++ b/tembo-operator/src/traefik/mod.rs @@ -1,4 +1,8 @@ pub mod ingress_route_crd; +pub mod ingress_route_crd_v3; pub mod ingress_route_tcp_crd; +pub mod ingress_route_tcp_crd_v3; pub mod middleware_tcp_crd; +pub mod middleware_tcp_crd_v3; pub mod middlewares_crd; +pub mod middlewares_crd_v3; diff --git a/tembo-operator/testdata/traefik-v3.yaml b/tembo-operator/testdata/traefik-v3.yaml new file mode 100644 index 000000000..cdfbc0cae --- /dev/null +++ b/tembo-operator/testdata/traefik-v3.yaml @@ -0,0 +1,3823 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: accesscontrolpolicies.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: AccessControlPolicy + listKind: AccessControlPolicyList + plural: accesscontrolpolicies + singular: accesscontrolpolicy + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: AccessControlPolicy defines an access control policy. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: AccessControlPolicySpec configures an access control policy. + properties: + apiKey: + description: AccessControlPolicyAPIKey configure an APIKey control + policy. + properties: + forwardHeaders: + additionalProperties: + type: string + description: ForwardHeaders instructs the middleware to forward + key metadata as header values upon successful authentication. + type: object + keySource: + description: KeySource defines how to extract API keys from requests. + properties: + cookie: + description: Cookie is the name of a cookie. + type: string + header: + description: Header is the name of a header. + type: string + headerAuthScheme: + description: |- + HeaderAuthScheme sets an optional auth scheme when Header is set to "Authorization". + If set, this scheme is removed from the token, and all requests not including it are dropped. + type: string + query: + description: Query is the name of a query parameter. + type: string + type: object + keys: + description: Keys define the set of authorized keys to access + a protected resource. + items: + description: AccessControlPolicyAPIKeyKey defines an API key. + properties: + id: + description: ID is the unique identifier of the key. + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds arbitrary metadata for this + key, can be used by ForwardHeaders. + type: object + value: + description: Value is the SHAKE-256 hash (using 64 bytes) + of the API key. + type: string + required: + - id + - value + type: object + type: array + required: + - keySource + type: object + basicAuth: + description: AccessControlPolicyBasicAuth holds the HTTP basic authentication + configuration. + properties: + forwardUsernameHeader: + type: string + realm: + type: string + stripAuthorizationHeader: + type: boolean + users: + items: + type: string + type: array + type: object + jwt: + description: AccessControlPolicyJWT configures a JWT access control + policy. + properties: + claims: + type: string + forwardHeaders: + additionalProperties: + type: string + type: object + jwksFile: + type: string + jwksUrl: + type: string + publicKey: + type: string + signingSecret: + type: string + signingSecretBase64Encoded: + type: boolean + stripAuthorizationHeader: + type: boolean + tokenQueryKey: + type: string + type: object + oAuthIntro: + description: AccessControlOAuthIntro configures an OAuth 2.0 Token + Introspection access control policy. + properties: + claims: + type: string + clientConfig: + description: AccessControlOAuthIntroClientConfig configures the + OAuth 2.0 client for issuing token introspection requests. + properties: + headers: + additionalProperties: + type: string + description: Headers to set when sending requests to the Authorization + Server. + type: object + maxRetries: + default: 3 + description: MaxRetries defines the number of retries for + introspection requests. + type: integer + timeoutSeconds: + default: 5 + description: TimeoutSeconds configures the maximum amount + of seconds to wait before giving up on requests. + type: integer + tls: + description: TLS configures TLS communication with the Authorization + Server. + properties: + ca: + description: CA sets the CA bundle used to sign the Authorization + Server certificate. + type: string + insecureSkipVerify: + description: |- + InsecureSkipVerify skips the Authorization Server certificate validation. + For testing purposes only, do not use in production. + type: boolean + type: object + tokenTypeHint: + description: |- + TokenTypeHint is a hint to pass to the Authorization Server. + See https://tools.ietf.org/html/rfc7662#section-2.1 for more information. + type: string + url: + description: URL of the Authorization Server. + type: string + required: + - url + type: object + forwardHeaders: + additionalProperties: + type: string + type: object + tokenSource: + description: |- + TokenSource describes how to extract tokens from HTTP requests. + If multiple sources are set, the order is the following: header > query > cookie. + properties: + cookie: + description: Cookie is the name of a cookie. + type: string + header: + description: Header is the name of a header. + type: string + headerAuthScheme: + description: |- + HeaderAuthScheme sets an optional auth scheme when Header is set to "Authorization". + If set, this scheme is removed from the token, and all requests not including it are dropped. + type: string + query: + description: Query is the name of a query parameter. + type: string + type: object + required: + - clientConfig + - tokenSource + type: object + oidc: + description: AccessControlPolicyOIDC holds the OIDC authentication + configuration. + properties: + authParams: + additionalProperties: + type: string + type: object + claims: + type: string + clientId: + type: string + disableAuthRedirectionPaths: + items: + type: string + type: array + forwardHeaders: + additionalProperties: + type: string + type: object + issuer: + type: string + logoutUrl: + type: string + redirectUrl: + type: string + scopes: + items: + type: string + type: array + secret: + description: |- + SecretReference represents a Secret Reference. It has enough information to retrieve secret + in any namespace + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + session: + description: Session holds session configuration. + properties: + domain: + type: string + path: + type: string + refresh: + type: boolean + sameSite: + type: string + secure: + type: boolean + type: object + stateCookie: + description: StateCookie holds state cookie configuration. + properties: + domain: + type: string + path: + type: string + sameSite: + type: string + secure: + type: boolean + type: object + type: object + oidcGoogle: + description: AccessControlPolicyOIDCGoogle holds the Google OIDC authentication + configuration. + properties: + authParams: + additionalProperties: + type: string + type: object + clientId: + type: string + emails: + description: Emails are the allowed emails to connect. + items: + type: string + minItems: 1 + type: array + forwardHeaders: + additionalProperties: + type: string + type: object + logoutUrl: + type: string + redirectUrl: + type: string + secret: + description: |- + SecretReference represents a Secret Reference. It has enough information to retrieve secret + in any namespace + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + session: + description: Session holds session configuration. + properties: + domain: + type: string + path: + type: string + refresh: + type: boolean + sameSite: + type: string + secure: + type: boolean + type: object + stateCookie: + description: StateCookie holds state cookie configuration. + properties: + domain: + type: string + path: + type: string + sameSite: + type: string + secure: + type: boolean + type: object + type: object + type: object + status: + description: The current status of this access control policy. + properties: + specHash: + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiaccesses.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIAccess + listKind: APIAccessList + plural: apiaccesses + singular: apiaccess + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: APIAccess defines who can access to a set of APIs. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: The desired behavior of this APIAccess. + properties: + apiSelector: + description: |- + APISelector selects the APIs that will be accessible to the configured audience. + Multiple APIAccesses can select the same set of APIs. + This field is optional and follows standard label selector semantics. + An empty APISelector matches any API. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + apis: + description: |- + APIs defines a set of APIs that will be accessible to the configured audience. + Multiple APIAccesses can select the same APIs. + When combined with APISelector, this set of APIs is appended to the matching APIs. + items: + description: APIReference references an API. + properties: + name: + description: Name of the API. + maxLength: 253 + type: string + required: + - name + type: object + maxItems: 100 + type: array + x-kubernetes-validations: + - message: duplicated apis + rule: self.all(x, self.exists_one(y, x.name == y.name)) + everyone: + description: Everyone indicates that all users will have access to + the selected APIs. + type: boolean + groups: + description: Groups are the consumer groups that will gain access + to the selected APIs. + items: + type: string + type: array + operationFilter: + description: |- + OperationFilter specifies the allowed operations on APIs and APIVersions. + If not set, all operations are available. + An empty OperationFilter prohibits all operations. + properties: + include: + description: Include defines the names of OperationSets that will + be accessible. + items: + type: string + maxItems: 100 + type: array + type: object + type: object + x-kubernetes-validations: + - message: groups and everyone are mutually exclusive + rule: '(has(self.everyone) && has(self.groups)) ? !(self.everyone && + self.groups.size() > 0) : true' + status: + description: The current status of this APIAccess. + properties: + hash: + description: Hash is a hash representing the APIAccess. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiportals.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIPortal + listKind: APIPortalList + plural: apiportals + singular: apiportal + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: APIPortal defines a developer portal for accessing the documentation + of APIs. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: The desired behavior of this APIPortal. + properties: + description: + description: Description of the APIPortal. + type: string + title: + description: Title is the public facing name of the APIPortal. + type: string + trustedUrls: + description: TrustedURLs are the urls that are trusted by the OAuth + 2.0 authorization server. + items: + type: string + maxItems: 1 + minItems: 1 + type: array + x-kubernetes-validations: + - message: must be a valid URLs + rule: self.all(x, isURL(x)) + ui: + description: UI holds the UI customization options. + properties: + logoUrl: + description: LogoURL is the public URL of the logo. + type: string + type: object + required: + - trustedUrls + type: object + status: + description: The current status of this APIPortal. + properties: + hash: + description: Hash is a hash representing the APIPortal. + type: string + oidc: + description: OIDC is the OIDC configuration for accessing the exposed + APIPortal WebUI. + properties: + clientId: + description: ClientID is the OIDC ClientID for accessing the exposed + APIPortal WebUI. + type: string + issuer: + description: Issuer is the OIDC issuer for accessing the exposed + APIPortal WebUI. + type: string + secretName: + description: SecretName is the name of the secret containing the + OIDC ClientSecret for accessing the exposed APIPortal WebUI. + type: string + type: object + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiratelimits.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIRateLimit + listKind: APIRateLimitList + plural: apiratelimits + singular: apiratelimit + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: APIRateLimit defines how group of consumers are rate limited + on a set of APIs. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: The desired behavior of this APIRateLimit. + properties: + apiSelector: + description: |- + APISelector selects the APIs that will be rate limited. + Multiple APIRateLimits can select the same set of APIs. + This field is optional and follows standard label selector semantics. + An empty APISelector matches any API. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + apis: + description: |- + APIs defines a set of APIs that will be rate limited. + Multiple APIRateLimits can select the same APIs. + When combined with APISelector, this set of APIs is appended to the matching APIs. + items: + description: APIReference references an API. + properties: + name: + description: Name of the API. + maxLength: 253 + type: string + required: + - name + type: object + maxItems: 100 + type: array + x-kubernetes-validations: + - message: duplicated apis + rule: self.all(x, self.exists_one(y, x.name == y.name)) + everyone: + description: |- + Everyone indicates that all users will, by default, be rate limited with this configuration. + If an APIRateLimit explicitly target a group, the default rate limit will be ignored. + type: boolean + groups: + description: |- + Groups are the consumer groups that will be rate limited. + Multiple APIRateLimits can target the same set of consumer groups, the most restrictive one applies. + When a consumer belongs to multiple groups, the least restrictive APIRateLimit applies. + items: + type: string + type: array + limit: + description: Limit is the maximum number of token in the bucket. + type: integer + x-kubernetes-validations: + - message: must be a positive number + rule: self >= 0 + period: + description: Period is the unit of time for the Limit. + format: duration + type: string + x-kubernetes-validations: + - message: must be between 1s and 1h + rule: self >= duration('1s') && self <= duration('1h') + strategy: + description: |- + Strategy defines how the bucket state will be synchronized between the different Traefik Hub instances. + It can be, either "local" or "distributed". + enum: + - local + - distributed + type: string + required: + - limit + type: object + x-kubernetes-validations: + - message: groups and everyone are mutually exclusive + rule: '(has(self.everyone) && has(self.groups)) ? !(self.everyone && + self.groups.size() > 0) : true' + status: + description: The current status of this APIRateLimit. + properties: + hash: + description: Hash is a hash representing the APIRateLimit. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apis.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: API + listKind: APIList + plural: apis + singular: api + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + API defines an HTTP interface that is exposed to external clients. It specifies the supported versions + and provides instructions for accessing its documentation. Once instantiated, an API object is associated + with an Ingress, IngressRoute, or HTTPRoute resource, enabling the exposure of the described API to the outside world. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: APISpec describes the API. + properties: + openApiSpec: + description: OpenAPISpec defines the API contract as an OpenAPI specification. + properties: + operationSets: + description: OperationSets defines the sets of operations to be + referenced for granular filtering in APIAccesses. + items: + description: |- + OperationSet gives a name to a set of matching OpenAPI operations. + This set of operations can then be referenced for granular filtering in APIAccesses. + properties: + matchers: + description: Matchers defines a list of alternative rules + for matching OpenAPI operations. + items: + description: OperationMatcher defines criteria for matching + an OpenAPI operation. + minProperties: 1 + properties: + methods: + description: Methods specifies the HTTP methods to + be included for selection. + items: + type: string + maxItems: 10 + type: array + path: + description: Path specifies the exact path of the + operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathPrefix: + description: PathPrefix specifies the path prefix + of the operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathRegex: + description: PathRegex specifies a regular expression + pattern for matching operations based on their paths. + type: string + type: object + x-kubernetes-validations: + - message: path, pathPrefix and pathRegex are mutually + exclusive + rule: '[has(self.path), has(self.pathPrefix), has(self.pathRegex)].filter(x, + x).size() <= 1' + maxItems: 100 + minItems: 1 + type: array + name: + description: Name is the name of the OperationSet to reference + in APIAccesses. + maxLength: 253 + type: string + required: + - matchers + - name + type: object + maxItems: 100 + type: array + override: + description: Override holds data used to override OpenAPI specification. + properties: + servers: + items: + properties: + url: + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + required: + - url + type: object + maxItems: 100 + minItems: 1 + type: array + required: + - servers + type: object + path: + description: |- + Path specifies the endpoint path within the Kubernetes Service where the OpenAPI specification can be obtained. + The Service queried is determined by the associated Ingress, IngressRoute, or HTTPRoute resource to which the API is attached. + It's important to note that this option is incompatible if the Ingress or IngressRoute specifies multiple backend services. + The Path must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + url: + description: |- + URL is a Traefik Hub agent accessible URL for obtaining the OpenAPI specification. + The URL must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + type: object + x-kubernetes-validations: + - message: path or url must be defined + rule: has(self.path) || has(self.url) + versions: + description: Versions are the different APIVersions available. + items: + description: APIVersionRef references an APIVersion. + properties: + name: + description: Name of the APIVersion. + maxLength: 253 + type: string + required: + - name + type: object + maxItems: 100 + minItems: 1 + type: array + type: object + status: + description: The current status of this API. + properties: + hash: + description: Hash is a hash representing the API. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiversions.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIVersion + listKind: APIVersionList + plural: apiversions + singular: apiversion + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.title + name: Title + type: string + - jsonPath: .spec.release + name: Release + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: APIVersion defines a version of an API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: The desired behavior of this APIVersion. + properties: + openApiSpec: + description: OpenAPISpec defines the API contract as an OpenAPI specification. + properties: + operationSets: + description: OperationSets defines the sets of operations to be + referenced for granular filtering in APIAccesses. + items: + description: |- + OperationSet gives a name to a set of matching OpenAPI operations. + This set of operations can then be referenced for granular filtering in APIAccesses. + properties: + matchers: + description: Matchers defines a list of alternative rules + for matching OpenAPI operations. + items: + description: OperationMatcher defines criteria for matching + an OpenAPI operation. + minProperties: 1 + properties: + methods: + description: Methods specifies the HTTP methods to + be included for selection. + items: + type: string + maxItems: 10 + type: array + path: + description: Path specifies the exact path of the + operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathPrefix: + description: PathPrefix specifies the path prefix + of the operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathRegex: + description: PathRegex specifies a regular expression + pattern for matching operations based on their paths. + type: string + type: object + x-kubernetes-validations: + - message: path, pathPrefix and pathRegex are mutually + exclusive + rule: '[has(self.path), has(self.pathPrefix), has(self.pathRegex)].filter(x, + x).size() <= 1' + maxItems: 100 + minItems: 1 + type: array + name: + description: Name is the name of the OperationSet to reference + in APIAccesses. + maxLength: 253 + type: string + required: + - matchers + - name + type: object + maxItems: 100 + type: array + override: + description: Override holds data used to override OpenAPI specification. + properties: + servers: + items: + properties: + url: + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + required: + - url + type: object + maxItems: 100 + minItems: 1 + type: array + required: + - servers + type: object + path: + description: |- + Path specifies the endpoint path within the Kubernetes Service where the OpenAPI specification can be obtained. + The Service queried is determined by the associated Ingress, IngressRoute, or HTTPRoute resource to which the API is attached. + It's important to note that this option is incompatible if the Ingress or IngressRoute specifies multiple backend services. + The Path must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + url: + description: |- + URL is a Traefik Hub agent accessible URL for obtaining the OpenAPI specification. + The URL must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + type: object + x-kubernetes-validations: + - message: path or url must be defined + rule: has(self.path) || has(self.url) + release: + description: |- + Release is the version number of the API. + This value must follow the SemVer format: https://semver.org/ + maxLength: 100 + type: string + x-kubernetes-validations: + - message: must be a valid semver version + rule: self.matches(r"""^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$""") + title: + description: Title is the public facing name of the APIVersion. + type: string + required: + - release + type: object + status: + description: The current status of this APIVersion. + properties: + hash: + description: Hash is a hash representing the APIVersion. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: ingressroutes.traefik.io +spec: + group: traefik.io + names: + kind: IngressRoute + listKind: IngressRouteList + plural: ingressroutes + singular: ingressroute + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressRoute is the CRD implementation of a Traefik HTTP Router. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IngressRouteSpec defines the desired state of IngressRoute. + properties: + entryPoints: + description: |- + EntryPoints defines the list of entry point names to bind to. + Entry points have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/ + Default: all. + items: + type: string + type: array + routes: + description: Routes defines the list of routes. + items: + description: Route holds the HTTP route configuration. + properties: + kind: + description: |- + Kind defines the kind of the route. + Rule is the only supported kind. + enum: + - Rule + type: string + match: + description: |- + Match defines the router's rule. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule + type: string + middlewares: + description: |- + Middlewares defines the list of references to Middleware resources. + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-middleware + items: + description: MiddlewareRef is a reference to a Middleware + resource. + properties: + name: + description: Name defines the name of the referenced Middleware + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Middleware resource. + type: string + required: + - name + type: object + type: array + priority: + description: |- + Priority defines the router's priority. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority + type: integer + services: + description: |- + Services defines the list of Service. + It can contain any combination of TraefikService and/or reference to a Kubernetes Service. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to + the client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as + JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie + can only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + type: array + syntax: + description: |- + Syntax defines the router's rule syntax. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax + type: string + required: + - kind + - match + type: object + type: array + tls: + description: |- + TLS defines the TLS configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls + properties: + certResolver: + description: |- + CertResolver defines the name of the certificate resolver to use. + Cert resolvers have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers + type: string + domains: + description: |- + Domains defines the list of domains that will be used to issue certificates. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains + items: + description: Domain holds a domain name with SANs. + properties: + main: + description: Main defines the main domain name. + type: string + sans: + description: SANs defines the subject alternative domain + names. + items: + type: string + type: array + type: object + type: array + options: + description: |- + Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. + If not defined, the `default` TLSOption is used. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options + properties: + name: + description: |- + Name defines the name of the referenced TLSOption. + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption + type: string + namespace: + description: |- + Namespace defines the namespace of the referenced TLSOption. + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsoption + type: string + required: + - name + type: object + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + store: + description: |- + Store defines the reference to the TLSStore, that will be used to store certificates. + Please note that only `default` TLSStore can be used. + properties: + name: + description: |- + Name defines the name of the referenced TLSStore. + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore + type: string + namespace: + description: |- + Namespace defines the namespace of the referenced TLSStore. + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-tlsstore + type: string + required: + - name + type: object + type: object + required: + - routes + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: ingressroutetcps.traefik.io +spec: + group: traefik.io + names: + kind: IngressRouteTCP + listKind: IngressRouteTCPList + plural: ingressroutetcps + singular: ingressroutetcp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressRouteTCP is the CRD implementation of a Traefik TCP Router. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IngressRouteTCPSpec defines the desired state of IngressRouteTCP. + properties: + entryPoints: + description: |- + EntryPoints defines the list of entry point names to bind to. + Entry points have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/ + Default: all. + items: + type: string + type: array + routes: + description: Routes defines the list of routes. + items: + description: RouteTCP holds the TCP route configuration. + properties: + match: + description: |- + Match defines the router's rule. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rule_1 + type: string + middlewares: + description: Middlewares defines the list of references to MiddlewareTCP + resources. + items: + description: ObjectReference is a generic reference to a Traefik + resource. + properties: + name: + description: Name defines the name of the referenced Traefik + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Traefik resource. + type: string + required: + - name + type: object + type: array + priority: + description: |- + Priority defines the router's priority. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1 + type: integer + services: + description: Services defines the list of TCP services. + items: + description: ServiceTCP defines an upstream TCP service to + proxy traffic to. + properties: + name: + description: Name defines the name of the referenced Kubernetes + Service. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + proxyProtocol: + description: |- + ProxyProtocol defines the PROXY protocol configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#proxy-protocol + properties: + version: + description: Version defines the PROXY Protocol version + to use. + type: integer + type: object + serversTransport: + description: |- + ServersTransport defines the name of ServersTransportTCP resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + terminationDelay: + description: |- + TerminationDelay defines the deadline that the proxy sets, after one of its connected peers indicates + it has closed the writing capability of its connection, to close the reading capability as well, + hence fully terminating the connection. + It is a duration in milliseconds, defaulting to 100. + A negative value means an infinite deadline (i.e. the reading capability is never closed). + Deprecated: TerminationDelay is not supported APIVersion traefik.io/v1, please use ServersTransport to configure the TerminationDelay instead. + type: integer + tls: + description: TLS determines whether to use TLS when dialing + with the backend. + type: boolean + weight: + description: Weight defines the weight used when balancing + requests between multiple Kubernetes Service. + type: integer + required: + - name + - port + type: object + type: array + syntax: + description: |- + Syntax defines the router's rule syntax. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1 + type: string + required: + - match + type: object + type: array + tls: + description: |- + TLS defines the TLS configuration on a layer 4 / TCP Route. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#tls_1 + properties: + certResolver: + description: |- + CertResolver defines the name of the certificate resolver to use. + Cert resolvers have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/https/acme/#certificate-resolvers + type: string + domains: + description: |- + Domains defines the list of domains that will be used to issue certificates. + More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#domains + items: + description: Domain holds a domain name with SANs. + properties: + main: + description: Main defines the main domain name. + type: string + sans: + description: SANs defines the subject alternative domain + names. + items: + type: string + type: array + type: object + type: array + options: + description: |- + Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. + If not defined, the `default` TLSOption is used. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options + properties: + name: + description: Name defines the name of the referenced Traefik + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Traefik resource. + type: string + required: + - name + type: object + passthrough: + description: Passthrough defines whether a TLS router will terminate + the TLS connection. + type: boolean + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + store: + description: |- + Store defines the reference to the TLSStore, that will be used to store certificates. + Please note that only `default` TLSStore can be used. + properties: + name: + description: Name defines the name of the referenced Traefik + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Traefik resource. + type: string + required: + - name + type: object + type: object + required: + - routes + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: ingressrouteudps.traefik.io +spec: + group: traefik.io + names: + kind: IngressRouteUDP + listKind: IngressRouteUDPList + plural: ingressrouteudps + singular: ingressrouteudp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressRouteUDP is a CRD implementation of a Traefik UDP Router. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IngressRouteUDPSpec defines the desired state of a IngressRouteUDP. + properties: + entryPoints: + description: |- + EntryPoints defines the list of entry point names to bind to. + Entry points have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/entrypoints/ + Default: all. + items: + type: string + type: array + routes: + description: Routes defines the list of routes. + items: + description: RouteUDP holds the UDP route configuration. + properties: + services: + description: Services defines the list of UDP services. + items: + description: ServiceUDP defines an upstream UDP service to + proxy traffic to. + properties: + name: + description: Name defines the name of the referenced Kubernetes + Service. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + weight: + description: Weight defines the weight used when balancing + requests between multiple Kubernetes Service. + type: integer + required: + - name + - port + type: object + type: array + type: object + type: array + required: + - routes + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: middlewares.traefik.io +spec: + group: traefik.io + names: + kind: Middleware + listKind: MiddlewareList + plural: middlewares + singular: middleware + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Middleware is the CRD implementation of a Traefik Middleware. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/overview/ + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: MiddlewareSpec defines the desired state of a Middleware. + properties: + addPrefix: + description: |- + AddPrefix holds the add prefix middleware configuration. + This middleware updates the path of a request before forwarding it. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/addprefix/ + properties: + prefix: + description: |- + Prefix is the string to add before the current path in the requested URL. + It should include a leading slash (/). + type: string + type: object + basicAuth: + description: |- + BasicAuth holds the basic auth middleware configuration. + This middleware restricts access to your services to known users. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/ + properties: + headerField: + description: |- + HeaderField defines a header field to store the authenticated user. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield + type: string + realm: + description: |- + Realm allows the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme. + Default: traefik. + type: string + removeHeader: + description: |- + RemoveHeader sets the removeHeader option to true to remove the authorization header before forwarding the request to your service. + Default: false. + type: boolean + secret: + description: Secret is the name of the referenced Kubernetes Secret + containing user credentials. + type: string + type: object + buffering: + description: |- + Buffering holds the buffering middleware configuration. + This middleware retries or limits the size of requests that can be forwarded to backends. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#maxrequestbodybytes + properties: + maxRequestBodyBytes: + description: |- + MaxRequestBodyBytes defines the maximum allowed body size for the request (in bytes). + If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a 413 (Request Entity Too Large) response. + Default: 0 (no maximum). + format: int64 + type: integer + maxResponseBodyBytes: + description: |- + MaxResponseBodyBytes defines the maximum allowed response size from the service (in bytes). + If the response exceeds the allowed size, it is not forwarded to the client. The client gets a 500 (Internal Server Error) response instead. + Default: 0 (no maximum). + format: int64 + type: integer + memRequestBodyBytes: + description: |- + MemRequestBodyBytes defines the threshold (in bytes) from which the request will be buffered on disk instead of in memory. + Default: 1048576 (1Mi). + format: int64 + type: integer + memResponseBodyBytes: + description: |- + MemResponseBodyBytes defines the threshold (in bytes) from which the response will be buffered on disk instead of in memory. + Default: 1048576 (1Mi). + format: int64 + type: integer + retryExpression: + description: |- + RetryExpression defines the retry conditions. + It is a logical combination of functions with operators AND (&&) and OR (||). + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/buffering/#retryexpression + type: string + type: object + chain: + description: |- + Chain holds the configuration of the chain middleware. + This middleware enables to define reusable combinations of other pieces of middleware. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/chain/ + properties: + middlewares: + description: Middlewares is the list of MiddlewareRef which composes + the chain. + items: + description: MiddlewareRef is a reference to a Middleware resource. + properties: + name: + description: Name defines the name of the referenced Middleware + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Middleware resource. + type: string + required: + - name + type: object + type: array + type: object + circuitBreaker: + description: CircuitBreaker holds the circuit breaker configuration. + properties: + checkPeriod: + anyOf: + - type: integer + - type: string + description: CheckPeriod is the interval between successive checks + of the circuit breaker condition (when in standby state). + x-kubernetes-int-or-string: true + expression: + description: Expression is the condition that triggers the tripped + state. + type: string + fallbackDuration: + anyOf: + - type: integer + - type: string + description: FallbackDuration is the duration for which the circuit + breaker will wait before trying to recover (from a tripped state). + x-kubernetes-int-or-string: true + recoveryDuration: + anyOf: + - type: integer + - type: string + description: RecoveryDuration is the duration for which the circuit + breaker will try to recover (as soon as it is in recovering + state). + x-kubernetes-int-or-string: true + type: object + compress: + description: |- + Compress holds the compress middleware configuration. + This middleware compresses responses before sending them to the client, using gzip compression. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/compress/ + properties: + excludedContentTypes: + description: |- + ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing. + `application/grpc` is always excluded. + items: + type: string + type: array + includedContentTypes: + description: IncludedContentTypes defines the list of content + types to compare the Content-Type header of the responses before + compressing. + items: + type: string + type: array + minResponseBodyBytes: + description: |- + MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. + Default: 1024. + type: integer + type: object + contentType: + description: |- + ContentType holds the content-type middleware configuration. + This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. + properties: + autoDetect: + description: |- + AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, + be automatically set to a value derived from the contents of the response. + Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. + type: boolean + type: object + digestAuth: + description: |- + DigestAuth holds the digest auth middleware configuration. + This middleware restricts access to your services to known users. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/digestauth/ + properties: + headerField: + description: |- + HeaderField defines a header field to store the authenticated user. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/basicauth/#headerfield + type: string + realm: + description: |- + Realm allows the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme. + Default: traefik. + type: string + removeHeader: + description: RemoveHeader defines whether to remove the authorization + header before forwarding the request to the backend. + type: boolean + secret: + description: Secret is the name of the referenced Kubernetes Secret + containing user credentials. + type: string + type: object + errors: + description: |- + ErrorPage holds the custom error middleware configuration. + This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/ + properties: + query: + description: |- + Query defines the URL for the error page (hosted by service). + The {status} variable can be used in order to insert the status code in the URL. + type: string + service: + description: |- + Service defines the reference to a Kubernetes Service that will serve the error page. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/errorpages/#service + properties: + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie can + be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + status: + description: |- + Status defines which status or range of statuses should result in an error page. + It can be either a status code as a number (500), + as multiple comma-separated numbers (500,502), + as ranges by separating two codes with a dash (500-599), + or a combination of the two (404,418,500-599). + items: + type: string + type: array + type: object + forwardAuth: + description: |- + ForwardAuth holds the forward auth middleware configuration. + This middleware delegates the request authentication to a Service. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/ + properties: + addAuthCookiesToResponse: + description: AddAuthCookiesToResponse defines the list of cookies + to copy from the authentication server response to the response. + items: + type: string + type: array + address: + description: Address defines the authentication server address. + type: string + authRequestHeaders: + description: |- + AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. + If not set or empty then all request headers are passed. + items: + type: string + type: array + authResponseHeaders: + description: AuthResponseHeaders defines the list of headers to + copy from the authentication server response and set on forwarded + request, replacing any existing conflicting headers. + items: + type: string + type: array + authResponseHeadersRegex: + description: |- + AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#authresponseheadersregex + type: string + tls: + description: TLS defines the configuration used to secure the + connection to the authentication server. + properties: + caOptional: + description: 'Deprecated: TLS client authentication is a server + side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634).' + type: boolean + caSecret: + description: |- + CASecret is the name of the referenced Kubernetes Secret containing the CA to validate the server certificate. + The CA certificate is extracted from key `tls.ca` or `ca.crt`. + type: string + certSecret: + description: |- + CertSecret is the name of the referenced Kubernetes Secret containing the client certificate. + The client certificate is extracted from the keys `tls.crt` and `tls.key`. + type: string + insecureSkipVerify: + description: InsecureSkipVerify defines whether the server + certificates should be validated. + type: boolean + type: object + trustForwardHeader: + description: 'TrustForwardHeader defines whether to trust (ie: + forward) all X-Forwarded-* headers.' + type: boolean + type: object + grpcWeb: + description: |- + GrpcWeb holds the gRPC web middleware configuration. + This middleware converts a gRPC web request to an HTTP/2 gRPC request. + properties: + allowOrigins: + description: |- + AllowOrigins is a list of allowable origins. + Can also be a wildcard origin "*". + items: + type: string + type: array + type: object + headers: + description: |- + Headers holds the headers middleware configuration. + This middleware manages the requests and responses headers. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/headers/#customrequestheaders + properties: + accessControlAllowCredentials: + description: AccessControlAllowCredentials defines whether the + request can include user credentials. + type: boolean + accessControlAllowHeaders: + description: AccessControlAllowHeaders defines the Access-Control-Request-Headers + values sent in preflight response. + items: + type: string + type: array + accessControlAllowMethods: + description: AccessControlAllowMethods defines the Access-Control-Request-Method + values sent in preflight response. + items: + type: string + type: array + accessControlAllowOriginList: + description: AccessControlAllowOriginList is a list of allowable + origins. Can also be a wildcard origin "*". + items: + type: string + type: array + accessControlAllowOriginListRegex: + description: AccessControlAllowOriginListRegex is a list of allowable + origins written following the Regular Expression syntax (https://golang.org/pkg/regexp/). + items: + type: string + type: array + accessControlExposeHeaders: + description: AccessControlExposeHeaders defines the Access-Control-Expose-Headers + values sent in preflight response. + items: + type: string + type: array + accessControlMaxAge: + description: AccessControlMaxAge defines the time that a preflight + request may be cached. + format: int64 + type: integer + addVaryHeader: + description: AddVaryHeader defines whether the Vary header is + automatically added/updated when the AccessControlAllowOriginList + is set. + type: boolean + allowedHosts: + description: AllowedHosts defines the fully qualified list of + allowed domain names. + items: + type: string + type: array + browserXssFilter: + description: BrowserXSSFilter defines whether to add the X-XSS-Protection + header with the value 1; mode=block. + type: boolean + contentSecurityPolicy: + description: ContentSecurityPolicy defines the Content-Security-Policy + header value. + type: string + contentTypeNosniff: + description: ContentTypeNosniff defines whether to add the X-Content-Type-Options + header with the nosniff value. + type: boolean + customBrowserXSSValue: + description: |- + CustomBrowserXSSValue defines the X-XSS-Protection header value. + This overrides the BrowserXssFilter option. + type: string + customFrameOptionsValue: + description: |- + CustomFrameOptionsValue defines the X-Frame-Options header value. + This overrides the FrameDeny option. + type: string + customRequestHeaders: + additionalProperties: + type: string + description: CustomRequestHeaders defines the header names and + values to apply to the request. + type: object + customResponseHeaders: + additionalProperties: + type: string + description: CustomResponseHeaders defines the header names and + values to apply to the response. + type: object + featurePolicy: + description: 'Deprecated: FeaturePolicy option is deprecated, + please use PermissionsPolicy instead.' + type: string + forceSTSHeader: + description: ForceSTSHeader defines whether to add the STS header + even when the connection is HTTP. + type: boolean + frameDeny: + description: FrameDeny defines whether to add the X-Frame-Options + header with the DENY value. + type: boolean + hostsProxyHeaders: + description: HostsProxyHeaders defines the header keys that may + hold a proxied hostname value for the request. + items: + type: string + type: array + isDevelopment: + description: |- + IsDevelopment defines whether to mitigate the unwanted effects of the AllowedHosts, SSL, and STS options when developing. + Usually testing takes place using HTTP, not HTTPS, and on localhost, not your production domain. + If you would like your development environment to mimic production with complete Host blocking, SSL redirects, + and STS headers, leave this as false. + type: boolean + permissionsPolicy: + description: |- + PermissionsPolicy defines the Permissions-Policy header value. + This allows sites to control browser features. + type: string + publicKey: + description: PublicKey is the public key that implements HPKP + to prevent MITM attacks with forged certificates. + type: string + referrerPolicy: + description: |- + ReferrerPolicy defines the Referrer-Policy header value. + This allows sites to control whether browsers forward the Referer header to other sites. + type: string + sslForceHost: + description: 'Deprecated: SSLForceHost option is deprecated, please + use RedirectRegex instead.' + type: boolean + sslHost: + description: 'Deprecated: SSLHost option is deprecated, please + use RedirectRegex instead.' + type: string + sslProxyHeaders: + additionalProperties: + type: string + description: |- + SSLProxyHeaders defines the header keys with associated values that would indicate a valid HTTPS request. + It can be useful when using other proxies (example: "X-Forwarded-Proto": "https"). + type: object + sslRedirect: + description: 'Deprecated: SSLRedirect option is deprecated, please + use EntryPoint redirection or RedirectScheme instead.' + type: boolean + sslTemporaryRedirect: + description: 'Deprecated: SSLTemporaryRedirect option is deprecated, + please use EntryPoint redirection or RedirectScheme instead.' + type: boolean + stsIncludeSubdomains: + description: STSIncludeSubdomains defines whether the includeSubDomains + directive is appended to the Strict-Transport-Security header. + type: boolean + stsPreload: + description: STSPreload defines whether the preload flag is appended + to the Strict-Transport-Security header. + type: boolean + stsSeconds: + description: |- + STSSeconds defines the max-age of the Strict-Transport-Security header. + If set to 0, the header is not set. + format: int64 + type: integer + type: object + inFlightReq: + description: |- + InFlightReq holds the in-flight request middleware configuration. + This middleware limits the number of requests being processed and served concurrently. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/ + properties: + amount: + description: |- + Amount defines the maximum amount of allowed simultaneous in-flight request. + The middleware responds with HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy). + format: int64 + type: integer + sourceCriterion: + description: |- + SourceCriterion defines what criterion is used to group requests as originating from a common source. + If several strategies are defined at the same time, an error will be raised. + If none are set, the default is to use the requestHost. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/inflightreq/#sourcecriterion + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position + (starting from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the + X-Forwarded-For header and select the first IP not in + the list. + items: + type: string + type: array + type: object + requestHeaderName: + description: RequestHeaderName defines the name of the header + used to group incoming requests. + type: string + requestHost: + description: RequestHost defines whether to consider the request + Host as the source. + type: boolean + type: object + type: object + ipAllowList: + description: |- + IPAllowList holds the IP allowlist middleware configuration. + This middleware accepts / refuses requests based on the client IP. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/ + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position (starting + from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the X-Forwarded-For + header and select the first IP not in the list. + items: + type: string + type: array + type: object + rejectStatusCode: + description: |- + RejectStatusCode defines the HTTP status code used for refused requests. + If not set, the default is 403 (Forbidden). + type: integer + sourceRange: + description: SourceRange defines the set of allowed IPs (or ranges + of allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + ipWhiteList: + description: 'Deprecated: please use IPAllowList instead.' + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position (starting + from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the X-Forwarded-For + header and select the first IP not in the list. + items: + type: string + type: array + type: object + sourceRange: + description: SourceRange defines the set of allowed IPs (or ranges + of allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + passTLSClientCert: + description: |- + PassTLSClientCert holds the pass TLS client cert middleware configuration. + This middleware adds the selected data from the passed client TLS certificate to a header. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/passtlsclientcert/ + properties: + info: + description: Info selects the specific client certificate details + you want to add to the X-Forwarded-Tls-Client-Cert-Info header. + properties: + issuer: + description: Issuer defines the client certificate issuer + details to add to the X-Forwarded-Tls-Client-Cert-Info header. + properties: + commonName: + description: CommonName defines whether to add the organizationalUnit + information into the issuer. + type: boolean + country: + description: Country defines whether to add the country + information into the issuer. + type: boolean + domainComponent: + description: DomainComponent defines whether to add the + domainComponent information into the issuer. + type: boolean + locality: + description: Locality defines whether to add the locality + information into the issuer. + type: boolean + organization: + description: Organization defines whether to add the organization + information into the issuer. + type: boolean + province: + description: Province defines whether to add the province + information into the issuer. + type: boolean + serialNumber: + description: SerialNumber defines whether to add the serialNumber + information into the issuer. + type: boolean + type: object + notAfter: + description: NotAfter defines whether to add the Not After + information from the Validity part. + type: boolean + notBefore: + description: NotBefore defines whether to add the Not Before + information from the Validity part. + type: boolean + sans: + description: Sans defines whether to add the Subject Alternative + Name information from the Subject Alternative Name part. + type: boolean + serialNumber: + description: SerialNumber defines whether to add the client + serialNumber information. + type: boolean + subject: + description: Subject defines the client certificate subject + details to add to the X-Forwarded-Tls-Client-Cert-Info header. + properties: + commonName: + description: CommonName defines whether to add the organizationalUnit + information into the subject. + type: boolean + country: + description: Country defines whether to add the country + information into the subject. + type: boolean + domainComponent: + description: DomainComponent defines whether to add the + domainComponent information into the subject. + type: boolean + locality: + description: Locality defines whether to add the locality + information into the subject. + type: boolean + organization: + description: Organization defines whether to add the organization + information into the subject. + type: boolean + organizationalUnit: + description: OrganizationalUnit defines whether to add + the organizationalUnit information into the subject. + type: boolean + province: + description: Province defines whether to add the province + information into the subject. + type: boolean + serialNumber: + description: SerialNumber defines whether to add the serialNumber + information into the subject. + type: boolean + type: object + type: object + pem: + description: PEM sets the X-Forwarded-Tls-Client-Cert header with + the certificate. + type: boolean + type: object + plugin: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: |- + Plugin defines the middleware plugin configuration. + More info: https://doc.traefik.io/traefik/plugins/ + type: object + rateLimit: + description: |- + RateLimit holds the rate limit configuration. + This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ratelimit/ + properties: + average: + description: |- + Average is the maximum rate, by default in requests/s, allowed for the given source. + It defaults to 0, which means no rate limiting. + The rate is actually defined by dividing Average by Period. So for a rate below 1req/s, + one needs to define a Period larger than a second. + format: int64 + type: integer + burst: + description: |- + Burst is the maximum number of requests allowed to arrive in the same arbitrarily small period of time. + It defaults to 1. + format: int64 + type: integer + period: + anyOf: + - type: integer + - type: string + description: |- + Period, in combination with Average, defines the actual maximum rate, such as: + r = Average / Period. It defaults to a second. + x-kubernetes-int-or-string: true + sourceCriterion: + description: |- + SourceCriterion defines what criterion is used to group requests as originating from a common source. + If several strategies are defined at the same time, an error will be raised. + If none are set, the default is to use the request's remote address field (as an ipStrategy). + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position + (starting from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the + X-Forwarded-For header and select the first IP not in + the list. + items: + type: string + type: array + type: object + requestHeaderName: + description: RequestHeaderName defines the name of the header + used to group incoming requests. + type: string + requestHost: + description: RequestHost defines whether to consider the request + Host as the source. + type: boolean + type: object + type: object + redirectRegex: + description: |- + RedirectRegex holds the redirect regex middleware configuration. + This middleware redirects a request using regex matching and replacement. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectregex/#regex + properties: + permanent: + description: Permanent defines whether the redirection is permanent + (301). + type: boolean + regex: + description: Regex defines the regex used to match and capture + elements from the request URL. + type: string + replacement: + description: Replacement defines how to modify the URL to have + the new target URL. + type: string + type: object + redirectScheme: + description: |- + RedirectScheme holds the redirect scheme middleware configuration. + This middleware redirects requests from a scheme/port to another. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/redirectscheme/ + properties: + permanent: + description: Permanent defines whether the redirection is permanent + (301). + type: boolean + port: + description: Port defines the port of the new URL. + type: string + scheme: + description: Scheme defines the scheme of the new URL. + type: string + type: object + replacePath: + description: |- + ReplacePath holds the replace path middleware configuration. + This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepath/ + properties: + path: + description: Path defines the path to use as replacement in the + request URL. + type: string + type: object + replacePathRegex: + description: |- + ReplacePathRegex holds the replace path regex middleware configuration. + This middleware replaces the path of a URL using regex matching and replacement. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/replacepathregex/ + properties: + regex: + description: Regex defines the regular expression used to match + and capture the path from the request URL. + type: string + replacement: + description: Replacement defines the replacement path format, + which can include captured variables. + type: string + type: object + retry: + description: |- + Retry holds the retry middleware configuration. + This middleware reissues requests a given number of times to a backend server if that server does not reply. + As soon as the server answers, the middleware stops retrying, regardless of the response status. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/retry/ + properties: + attempts: + description: Attempts defines how many times the request should + be retried. + type: integer + initialInterval: + anyOf: + - type: integer + - type: string + description: |- + InitialInterval defines the first wait time in the exponential backoff series. + The maximum interval is calculated as twice the initialInterval. + If unspecified, requests will be retried immediately. + The value of initialInterval should be provided in seconds or as a valid duration format, + see https://pkg.go.dev/time#ParseDuration. + x-kubernetes-int-or-string: true + type: object + stripPrefix: + description: |- + StripPrefix holds the strip prefix middleware configuration. + This middleware removes the specified prefixes from the URL path. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefix/ + properties: + forceSlash: + description: |- + Deprecated: ForceSlash option is deprecated, please remove any usage of this option. + ForceSlash ensures that the resulting stripped path is not the empty string, by replacing it with / when necessary. + Default: true. + type: boolean + prefixes: + description: Prefixes defines the prefixes to strip from the request + URL. + items: + type: string + type: array + type: object + stripPrefixRegex: + description: |- + StripPrefixRegex holds the strip prefix regex middleware configuration. + This middleware removes the matching prefixes from the URL path. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/stripprefixregex/ + properties: + regex: + description: Regex defines the regular expression to match the + path prefix from the request URL. + items: + type: string + type: array + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: middlewaretcps.traefik.io +spec: + group: traefik.io + names: + kind: MiddlewareTCP + listKind: MiddlewareTCPList + plural: middlewaretcps + singular: middlewaretcp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/overview/ + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: MiddlewareTCPSpec defines the desired state of a MiddlewareTCP. + properties: + inFlightConn: + description: InFlightConn defines the InFlightConn middleware configuration. + properties: + amount: + description: |- + Amount defines the maximum amount of allowed simultaneous connections. + The middleware closes the connection if there are already amount connections opened. + format: int64 + type: integer + type: object + ipAllowList: + description: |- + IPAllowList defines the IPAllowList middleware configuration. + This middleware accepts/refuses connections based on the client IP. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/ipallowlist/ + properties: + sourceRange: + description: SourceRange defines the allowed IPs (or ranges of + allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + ipWhiteList: + description: |- + IPWhiteList defines the IPWhiteList middleware configuration. + This middleware accepts/refuses connections based on the client IP. + Deprecated: please use IPAllowList instead. + More info: https://doc.traefik.io/traefik/v3.0/middlewares/tcp/ipwhitelist/ + properties: + sourceRange: + description: SourceRange defines the allowed IPs (or ranges of + allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: serverstransports.traefik.io +spec: + group: traefik.io + names: + kind: ServersTransport + listKind: ServersTransportList + plural: serverstransports + singular: serverstransport + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + ServersTransport is the CRD implementation of a ServersTransport. + If no serversTransport is specified, the default@internal will be used. + The default@internal serversTransport is created from the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#serverstransport_1 + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ServersTransportSpec defines the desired state of a ServersTransport. + properties: + certificatesSecrets: + description: CertificatesSecrets defines a list of secret storing + client certificates for mTLS. + items: + type: string + type: array + disableHTTP2: + description: DisableHTTP2 disables HTTP/2 for connections with backend + servers. + type: boolean + forwardingTimeouts: + description: ForwardingTimeouts defines the timeouts for requests + forwarded to the backend servers. + properties: + dialTimeout: + anyOf: + - type: integer + - type: string + description: DialTimeout is the amount of time to wait until a + connection to a backend server can be established. + x-kubernetes-int-or-string: true + idleConnTimeout: + anyOf: + - type: integer + - type: string + description: IdleConnTimeout is the maximum period for which an + idle HTTP keep-alive connection will remain open before closing + itself. + x-kubernetes-int-or-string: true + pingTimeout: + anyOf: + - type: integer + - type: string + description: PingTimeout is the timeout after which the HTTP/2 + connection will be closed if a response to ping is not received. + x-kubernetes-int-or-string: true + readIdleTimeout: + anyOf: + - type: integer + - type: string + description: ReadIdleTimeout is the timeout after which a health + check using ping frame will be carried out if no frame is received + on the HTTP/2 connection. + x-kubernetes-int-or-string: true + responseHeaderTimeout: + anyOf: + - type: integer + - type: string + description: ResponseHeaderTimeout is the amount of time to wait + for a server's response headers after fully writing the request + (including its body, if any). + x-kubernetes-int-or-string: true + type: object + insecureSkipVerify: + description: InsecureSkipVerify disables SSL certificate verification. + type: boolean + maxIdleConnsPerHost: + description: MaxIdleConnsPerHost controls the maximum idle (keep-alive) + to keep per-host. + type: integer + peerCertURI: + description: PeerCertURI defines the peer cert URI used to match against + SAN URI during the peer certificate verification. + type: string + rootCAsSecrets: + description: RootCAsSecrets defines a list of CA secret used to validate + self-signed certificate. + items: + type: string + type: array + serverName: + description: ServerName defines the server name used to contact the + server. + type: string + spiffe: + description: Spiffe defines the SPIFFE configuration. + properties: + ids: + description: IDs defines the allowed SPIFFE IDs (takes precedence + over the SPIFFE TrustDomain). + items: + type: string + type: array + trustDomain: + description: TrustDomain defines the allowed SPIFFE trust domain. + type: string + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: serverstransporttcps.traefik.io +spec: + group: traefik.io + names: + kind: ServersTransportTCP + listKind: ServersTransportTCPList + plural: serverstransporttcps + singular: serverstransporttcp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + ServersTransportTCP is the CRD implementation of a TCPServersTransport. + If no tcpServersTransport is specified, a default one named default@internal will be used. + The default@internal tcpServersTransport can be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#serverstransport_3 + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ServersTransportTCPSpec defines the desired state of a ServersTransportTCP. + properties: + dialKeepAlive: + anyOf: + - type: integer + - type: string + description: DialKeepAlive is the interval between keep-alive probes + for an active network connection. If zero, keep-alive probes are + sent with a default value (currently 15 seconds), if supported by + the protocol and operating system. Network protocols or operating + systems that do not support keep-alives ignore this field. If negative, + keep-alive probes are disabled. + x-kubernetes-int-or-string: true + dialTimeout: + anyOf: + - type: integer + - type: string + description: DialTimeout is the amount of time to wait until a connection + to a backend server can be established. + x-kubernetes-int-or-string: true + terminationDelay: + anyOf: + - type: integer + - type: string + description: TerminationDelay defines the delay to wait before fully + terminating the connection, after one connected peer has closed + its writing capability. + x-kubernetes-int-or-string: true + tls: + description: TLS defines the TLS configuration + properties: + certificatesSecrets: + description: CertificatesSecrets defines a list of secret storing + client certificates for mTLS. + items: + type: string + type: array + insecureSkipVerify: + description: InsecureSkipVerify disables TLS certificate verification. + type: boolean + peerCertURI: + description: |- + MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. + PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. + type: string + rootCAsSecrets: + description: RootCAsSecrets defines a list of CA secret used to + validate self-signed certificates. + items: + type: string + type: array + serverName: + description: ServerName defines the server name used to contact + the server. + type: string + spiffe: + description: Spiffe defines the SPIFFE configuration. + properties: + ids: + description: IDs defines the allowed SPIFFE IDs (takes precedence + over the SPIFFE TrustDomain). + items: + type: string + type: array + trustDomain: + description: TrustDomain defines the allowed SPIFFE trust + domain. + type: string + type: object + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: tlsoptions.traefik.io +spec: + group: traefik.io + names: + kind: TLSOption + listKind: TLSOptionList + plural: tlsoptions + singular: tlsoption + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#tls-options + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: TLSOptionSpec defines the desired state of a TLSOption. + properties: + alpnProtocols: + description: |- + ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#alpn-protocols + items: + type: string + type: array + cipherSuites: + description: |- + CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#cipher-suites + items: + type: string + type: array + clientAuth: + description: ClientAuth defines the server's policy for TLS Client + Authentication. + properties: + clientAuthType: + description: ClientAuthType defines the client authentication + type to apply. + enum: + - NoClientCert + - RequestClientCert + - RequireAnyClientCert + - VerifyClientCertIfGiven + - RequireAndVerifyClientCert + type: string + secretNames: + description: SecretNames defines the names of the referenced Kubernetes + Secret storing certificate details. + items: + type: string + type: array + type: object + curvePreferences: + description: |- + CurvePreferences defines the preferred elliptic curves in a specific order. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#curve-preferences + items: + type: string + type: array + maxVersion: + description: |- + MaxVersion defines the maximum TLS version that Traefik will accept. + Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13. + Default: None. + type: string + minVersion: + description: |- + MinVersion defines the minimum TLS version that Traefik will accept. + Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13. + Default: VersionTLS10. + type: string + preferServerCipherSuites: + description: |- + PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's. + It is enabled automatically when minVersion or maxVersion is set. + Deprecated: https://github.com/golang/go/issues/45430 + type: boolean + sniStrict: + description: SniStrict defines whether Traefik allows connections + from clients connections that do not specify a server_name extension. + type: boolean + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: tlsstores.traefik.io +spec: + group: traefik.io + names: + kind: TLSStore + listKind: TLSStoreList + plural: tlsstores + singular: tlsstore + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + TLSStore is the CRD implementation of a Traefik TLS Store. + For the time being, only the TLSStore named default is supported. + This means that you cannot have two stores that are named default in different Kubernetes namespaces. + More info: https://doc.traefik.io/traefik/v3.0/https/tls/#certificates-stores + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: TLSStoreSpec defines the desired state of a TLSStore. + properties: + certificates: + description: Certificates is a list of secret names, each secret holding + a key/certificate pair to add to the store. + items: + description: Certificate holds a secret name for the TLSStore resource. + properties: + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + required: + - secretName + type: object + type: array + defaultCertificate: + description: DefaultCertificate defines the default certificate configuration. + properties: + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + required: + - secretName + type: object + defaultGeneratedCert: + description: DefaultGeneratedCert defines the default generated certificate + configuration. + properties: + domain: + description: Domain is the domain definition for the DefaultCertificate. + properties: + main: + description: Main defines the main domain name. + type: string + sans: + description: SANs defines the subject alternative domain names. + items: + type: string + type: array + type: object + resolver: + description: Resolver is the name of the resolver that will be + used to issue the DefaultCertificate. + type: string + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: traefikservices.traefik.io +spec: + group: traefik.io + names: + kind: TraefikService + listKind: TraefikServiceList + plural: traefikservices + singular: traefikservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + TraefikService is the CRD implementation of a Traefik Service. + TraefikService object allows to: + - Apply weight to Services on load-balancing + - Mirror traffic on services + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#kind-traefikservice + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: TraefikServiceSpec defines the desired state of a TraefikService. + properties: + mirroring: + description: Mirroring defines the Mirroring service configuration. + properties: + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + maxBodySize: + description: |- + MaxBodySize defines the maximum size allowed for the body of the request. + If the body is larger, the request is not mirrored. + Default value is -1, which means unlimited size. + format: int64 + type: integer + mirrors: + description: Mirrors defines the list of mirrors where Traefik + will duplicate the traffic. + items: + description: MirrorService holds the mirror configuration. + properties: + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + percent: + description: |- + Percent defines the part of the traffic to mirror. + Supported values: 0 to 100. + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + type: array + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards the + response from the upstream Kubernetes Service to the client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie can be + accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can only + be transmitted over an encrypted connection (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + weighted: + description: Weighted defines the Weighted Round Robin configuration. + properties: + services: + description: Services defines the list of Kubernetes Service and/or + TraefikService to load-balance, with weight. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.0/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + type: array + sticky: + description: |- + Sticky defines whether sticky sessions are enabled. + More info: https://doc.traefik.io/traefik/v3.0/routing/providers/kubernetes-crd/#stickiness-and-load-balancing + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie can be + accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can only + be transmitted over an encrypted connection (i.e. HTTPS). + type: boolean + type: object + type: object + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true