// Copyright 2018 Istio Authors
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

syntax = "proto3";

import "google/api/field_behavior.proto";
import "google/protobuf/struct.proto";
import "networking/v1alpha3/sidecar.proto";

// $title: Envoy Filter
// $description: Customizing Envoy configuration generated by Istio.
// $location: https://istio.io/docs/reference/config/networking/envoy-filter.html
// $aliases: [/docs/reference/config/networking/v1alpha3/envoy-filter]

// `EnvoyFilter` provides a mechanism to customize the Envoy
// configuration generated by Istio Pilot. Use EnvoyFilter to modify
// values for certain fields, add specific filters, or even add
// entirely new listeners, clusters, etc. This feature must be used
// with care, as incorrect configurations could potentially
// destabilize the entire mesh. Unlike other Istio networking objects,
// EnvoyFilters are additively applied. Any number of EnvoyFilters can
// exist for a given workload in a specific namespace. The order of
// application of these EnvoyFilters is as follows: all EnvoyFilters
// in the config [root
// namespace](https://istio.io/docs/reference/config/istio.mesh.v1alpha1/#MeshConfig),
// followed by all matching EnvoyFilters in the workload's namespace.
//
// **NOTE 1**: Since this is break glass configuration, there will not
// be any backward compatibility across different Istio releases. In
// other words, this configuration is subject to change based on
// internal implementation of Istio networking subsystem.
//
// **NOTE 2**: The envoy configuration provided through this mechanism
// should be carefully monitored across Istio proxy version upgrades,
// to ensure that deprecated fields are removed and replaced
// appropriately.
//
// **NOTE 3**: When multiple EnvoyFilters are bound to the same
// workload in a given namespace, all patches will be processed
// sequentially in order of creation time.  The behavior is undefined
// if multiple EnvoyFilter configurations conflict with each other.
//
// **NOTE 4**: *_To apply an EnvoyFilter resource to all workloads
// (sidecars and gateways) in the system, define the resource in the
// config [root
// namespace](https://istio.io/docs/reference/config/istio.mesh.v1alpha1/#MeshConfig),
// without a workloadSelector.
//
// The example below declares a global default EnvoyFilter resource in
// the root namespace called `istio-config`, that adds a custom
// protocol filter on all sidecars in the system, for outbound port
// 9307. The filter should be added before the terminating tcp_proxy
// filter to take effect. In addition, it sets a 30s idle timeout for
// all HTTP connections in both gateays and sidecars.
//
// ```yaml
// apiVersion: networking.istio.io/v1alpha3
// kind: EnvoyFilter
// metadata:
//   name: custom-protocol
//   namespace: istio-config # as defined in meshConfig resource.
// spec:
//   configPatches:
//   - applyTo: NETWORK_FILTER
//     match:
//       context: SIDECAR_OUTBOUND # will match outbound listeners in all sidecars
//       listener:
//         portNumber: 9307
//         filterChain:
//           filter:
//             name: "envoy.tcp_proxy"
//     patch:
//       operation: INSERT_BEFORE
//       value:
//         name: "envoy.config.filter.network.custom_protocol"
//         config:
//          ...
//   - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy
//     match:
//       # context omitted so that this applies to both sidecars and gateways
//       listener:
//         filterChain:
//           filter:
//             name: "envoy.http_connection_manager"
//     patch:
//       operation: MERGE
//       value:
//         typed_config:
//           "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager"
//           idle_timeout: 30s
//```
//
// The following example enables Envoy's Lua filter for all inbound
// HTTP calls arriving at service port 8080 of the reviews service pod
// with labels "app: reviews", in the bookinfo namespace. The lua
// filter calls out to an external service internal.org.net:8888 that
// requires a special cluster definition in envoy. The cluster is also
// added to the sidecar as part of this configuration.
//
// ```yaml
// apiVersion: networking.istio.io/v1alpha3
// kind: EnvoyFilter
// metadata:
//   name: reviews-lua
//   namespace: bookinfo
// spec:
//   workloadSelector:
//     labels:
//       app: reviews
//   configPatches:
//     # The first patch adds the lua filter to the listener/http connection manager
//   - applyTo: HTTP_FILTER
//     match:
//       context: SIDECAR_INBOUND
//       listener:
//         portNumber: 8080
//         filterChain:
//           filter:
//             name: "envoy.http_connection_manager"
//             subFilter:
//               name: "envoy.router"
//     patch:
//       operation: INSERT_BEFORE
//       value: # lua filter specification
//        name: envoy.lua
//        config:
//          inlineCode: |
//            function envoy_on_request(request_handle)
//              -- Make an HTTP call to an upstream host with the following headers, body, and timeout.
//              local headers, body = request_handle:httpCall(
//               "lua_cluster",
//               {
//                [":method"] = "POST",
//                [":path"] = "/acl",
//                [":authority"] = "internal.org.net"
//               },
//              "authorize call",
//              5000)
//            end
//   # The second patch adds the cluster that is referenced by the lua code
//   # cds match is omitted as a new cluster is being added
//   - applyTo: CLUSTER
//     match:
//       context: SIDECAR_OUTBOUND
//     patch:
//       operation: ADD
//       value: # cluster specification
//         name: "lua_cluster"
//         type: STRICT_DNS
//         connect_timeout: 0.5s
//         lb_policy: ROUND_ROBIN
//         hosts:
//         - socket_address:
//             protocol: TCP
//             address: "internal.org.net"
//             port_value: 8888
//
// ```
//
// The following example overwrites certain fields (HTTP idle timeout
// and X-Forward-For trusted hops) in the HTTP connection manager in a
// listener on the ingress gateway in istio-system namespace for the
// SNI host app.example.com:
//
// ```yaml
// apiVersion: networking.istio.io/v1alpha3
// kind: EnvoyFilter
// metadata:
//   name: hcm-tweaks
//   namespace: istio-system
// spec:
//   workloadSelector:
//     labels:
//       istio: ingress-gateway
//   configPatches:
//   - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy
//     match:
//       context: GATEWAY
//       listener:
//         filterChain:
//           sni: app.example.com
//           filter:
//             name: "envoy.http_connection_manager"
//     patch:
//       operation: MERGE
//       value:
//         idle_timeout: 30s
//         xff_num_trusted_hops: 5
//```
//
package istio.networking.v1alpha3;

option go_package = "istio.io/api/networking/v1alpha3";

// EnvoyFilter provides a mechanism to customize the Envoy configuration
// generated by Istio Pilot.
//
// <!-- go code generation tags
// +kubetype-gen
// +kubetype-gen:groupVersion=networking.istio.io/v1alpha3
// +genclient
// +k8s:deepcopy-gen=true
// -->
message EnvoyFilter {
  // Deprecated.
  // Select a listener to add the filter to based on the match conditions.
  // All conditions specified in the ListenerMatch must be met for the filter
  // to be applied to a listener.
  // $hide_from_docs
  message DeprecatedListenerMatch {
    // The service port/gateway port to which traffic is being
    // sent/received. If not specified, matches all listeners. Even though
    // inbound listeners are generated for the instance/pod ports, only
    // service ports should be used to match listeners.
    uint32 port_number = 1;

    // Instead of using specific port numbers, a set of ports matching a
    // given port name prefix can be selected. E.g., "mongo" selects ports
    // named mongo-port, mongo, mongoDB, MONGO, etc. Matching is case
    // insensitive.
    string port_name_prefix = 2;

    enum ListenerType {
      // All listeners
      ANY = 0;

      // Inbound listener in sidecar
      SIDECAR_INBOUND = 1;

      // Outbound listener in sidecar
      SIDECAR_OUTBOUND = 2;

      // Gateway listener
      GATEWAY = 3;
    };

    // Inbound vs outbound sidecar listener or gateway listener. If not specified,
    // matches all listeners.
    ListenerType listener_type = 3;

    enum ListenerProtocol {
      // All protocols
      ALL = 0;
      // HTTP or HTTPS (with termination) / HTTP2/gRPC
      HTTP = 1;
      // Any non-HTTP listener
      TCP = 2;
    };

    // Selects a class of listeners for the same protocol. Use the protocol
    // selection to select all HTTP listeners (includes HTTP2/gRPC/HTTPS
    // where Envoy terminates TLS) or all TCP listeners (includes HTTPS
    // passthrough using SNI). When adding a HTTP filter, the listenerProtocol
    // should be set to HTTP.
    ListenerProtocol listener_protocol = 4;

    // One or more IP addresses to which the listener is bound. If
    // specified, should match at least one address in the list.
    repeated string address = 5;
  };

  // Deprecated.
  // Indicates the relative index in the filter chain where the filter should be inserted.
  // $hide_from_docs
  message InsertPosition {

    // Index/position in the filter chain.
    enum Index {
      // Insert first
      FIRST = 0;

      // Insert last
      LAST = 1;

      // Insert before the named filter.
      BEFORE = 2;
      // Insert after the named filter.
      AFTER = 3;
    };

    // Position of this filter in the filter chain.
    Index index = 1;

    // If BEFORE or AFTER position is specified, specify the name of the
    // filter relative to which this filter should be inserted.
    string relative_to = 2;
  };

  // Deprecated.
  // Envoy filters to be added to a network or http filter chain.
  // $hide_from_docs
  message Filter {
    // Filter will be added to the listener only if the match
    // conditions are true.  If not specified, the filters will be
    // applied to all listeners where possible, potentially resulting
    // in invalid configurations. It is recommended to specify the
    // listener match criteria for all filter insertions.
    DeprecatedListenerMatch listener_match = 1;

    // Insert position in the filter chain. Defaults to FIRST
    InsertPosition insert_position = 2;

    enum FilterType {
      // placeholder
      INVALID = 0;

      // Http filter
      HTTP = 1;

      // Network filter
      NETWORK = 2;
    };

    // The type of filter to instantiate.
    FilterType filter_type = 3 [(google.api.field_behavior) = REQUIRED];

    // The name of the filter to instantiate. The name must match a supported
    // filter _compiled into_ Envoy.
    string filter_name = 4 [(google.api.field_behavior) = REQUIRED];

    // Filter specific configuration which depends on the filter being
    // instantiated.
    google.protobuf.Struct filter_config = 5 [(google.api.field_behavior) = REQUIRED];
  };

  // ApplyTo specifies where in the Envoy configuration, the given patch should be applied.
  enum ApplyTo {

    INVALID = 0;

    // Applies the patch to the listener.
    LISTENER = 1;

    // Applies the patch to the filter chain.
    FILTER_CHAIN = 2;

    // Applies the patch to the network filter chain, to modify an
    // existing filter or add a new filter.
    NETWORK_FILTER = 3;

    // Applies the patch to the HTTP filter chain in the http
    // connection manager, to modify an existing filter or add a new
    // filter.
    HTTP_FILTER = 4;

    // Applies the patch to the Route configuration (rds output)
    // inside a HTTP connection manager. This does not apply to the
    // virtual host. Currently, only MERGE operation is allowed on the
    // route configuration objects.
    ROUTE_CONFIGURATION = 5;

    // Applies the patch to a virtual host inside a route configuration.
    VIRTUAL_HOST = 6;

    // Applies the patch to a route object inside the matched virtual
    // host in a route configuration. Currently, only MERGE operation
    // is allowed on the route objects.
    HTTP_ROUTE = 7;

    // Applies the patch to a cluster in a CDS output. Also used to add new clusters.
    CLUSTER = 8;
  };

  // PatchContext selects a class of configurations based on the
  // traffic flow direction and workload type.
  enum PatchContext {
    // All listeners/routes/clusters in both sidecars and gateways.
    ANY = 0;

    // Inbound listener/route/cluster in sidecar.
    SIDECAR_INBOUND = 1;

    // Outbound listener/route/cluster in sidecar.
    SIDECAR_OUTBOUND = 2;

    // Gateway listener/route/cluster.
    GATEWAY = 3;
  };

  // One or more properties of the proxy to match on.
  message ProxyMatch {
    // A regular expression in golang regex format (RE2) that can be
    // used to select proxies using a specific version of istio
    // proxy. The Istio version for a given proxy is obtained from the
    // node metadata field ISTIO_VERSION supplied by the proxy when
    // connecting to Pilot. This value is embedded as an environment
    // variable (ISTIO_META_ISTIO_VERSION) in the Istio proxy docker
    // image. Custom proxy implementations should provide this metadata
    // variable to take advantage of the Istio version check option.
    string proxy_version = 1;

    // Match on the node metadata supplied by a proxy when connecting
    // to Istio Pilot. Note that while Envoy's node metadata is of
    // type Struct, only string key-value pairs are processed by
    // Pilot. All keys specified in the metadata must match with exact
    // values. The match will fail if any of the specified keys are
    // absent or the values fail to match.
    map<string, string> metadata = 2;
  };

  // Conditions specified in ClusterMatch must be met for the patch
  // to be applied to a cluster.
  message ClusterMatch {
    // The service port for which this cluster was generated.  If
    // omitted, applies to clusters for any port.
    uint32 port_number = 1;

    // The fully qualified service name for this cluster. If omitted,
    // applies to clusters for any service. For services defined
    // through service entries, the service name is same as the hosts
    // defined in the service entry.
    string service = 2;

    // The subset associated with the service. If omitted, applies to
    // clusters for any subset of a service.
    string subset = 3;

    // The exact name of the cluster to match. To match a specific
    // cluster by name, such as the internally generated "Passthrough"
    // cluster, leave all fields in clusterMatch empty, except the
    // name.
    string name = 4;
  };

  // Conditions specified in RouteConfigurationMatch must be met for
  // the patch to be applied to a route configuration object or a
  // specific virtual host within the route configuration.
  message RouteConfigurationMatch {
    // Match a specific route inside a virtual host in a route configuration.
    message RouteMatch {
      // The Route objects generated by default are named as
      // "default".  Route objects generated using a virtual service
      // will carry the name used in the virtual service's HTTP
      // routes.
      string name = 1;

      // Action refers to the route action taken by Envoy when a http route matches.
      enum Action {
	// All three route actions
	ANY = 0;
	// Route traffic to a cluster / weighted clusters.
	ROUTE = 1;
	// Redirect request.
	REDIRECT = 2;
	// directly respond to a request with specific payload.
	DIRECT_RESPONSE = 3;
      };

      // Match a route with specific action type.
      Action action = 2;
    }

    // Match a specific virtual host inside a route configuration.
    message VirtualHostMatch {
      // The VirtualHosts objects generated by Istio are named as
      // host:port, where the host typically corresponds to the
      // VirtualService's host field or the hostname of a service in the
      // registry.
      string name = 1;

      // Match a specific route within the virtual host.
      RouteMatch route = 2;
    }

    // The service port number or gateway server port number for which
    // this route configuration was generated. If omitted, applies to
    // route configurations for all ports.
    uint32 port_number = 1;

    // Applicable only for GATEWAY context. The gateway server port
    // name for which this route configuration was generated.
    string port_name = 2;

    // The Istio gateway config's namespace/name for which this route
    // configuration was generated. Applies only if the context is
    // GATEWAY. Should be in the namespace/name format. Use this field
    // in conjunction with the portNumber and portName to accurately
    // select the Envoy route configuration for a specific HTTPS
    // server within a gateway config object.
    string gateway = 3;

    // Match a specific virtual host in a route configuration and
    // apply the patch to the virtual host.
    VirtualHostMatch vhost = 4;

    // Route configuration name to match on. Can be used to match a
    // specific route configuration by name, such as the internally
    // generated "http_proxy" route configuration for all sidecars.
    string name = 5;
  };

  // Conditions specified in a listener match must be met for the
  // patch to be applied to a specific listener across all filter
  // chains, or a specific filter chain inside the listener.
  message ListenerMatch {
    // For listeners with multiple filter chains (e.g., inbound
    // listeners on sidecars with permissive mTLS, gateway listeners
    // with multiple SNI matches), the filter chain match can be used
    // to select a specific filter chain to patch.
    message FilterChainMatch {
      // The name assigned to the filter chain.
      string name = 1;

      // The SNI value used by a filter chain's match condition.  This
      // condition will evaluate to false if the filter chain has no
      // sni match.
      string sni = 2;

      // Applies only to SIDECAR_INBOUND context. If non-empty, a
      // transport protocol to consider when determining a filter
      // chain match.  This value will be compared against the
      // transport protocol of a new connection, when it's detected by
      // the tls_inspector listener filter.
      //
      // Accepted values include:
      //
      // * `raw_buffer` - default, used when no transport protocol is detected.
      // * `tls` - set when TLS protocol is detected by the TLS inspector.
      string transport_protocol = 3;

      // Applies only to sidecars. If non-empty, a comma separated set
      // of application protocols to consider when determining a
      // filter chain match.  This value will be compared against the
      // application protocols of a new connection, when it's detected
      // by one of the listener filters such as the http_inspector.
      //
      // Accepted values include: h2,http/1.1,http/1.0
      string application_protocols = 4;

      // The name of a specific filter to apply the patch to. Set this
      // to envoy.http_connection_manager to add a filter or apply a
      // patch to the HTTP connection manager.
      FilterMatch filter = 5;
    };

    // Conditions to match a specific filter within a filter chain.
    message FilterMatch {
      // The filter name to match on.
      string name = 1;
      // The next level filter within this filter to match
      // upon. Typically used for HTTP Connection Manager filters and
      // Thrift filters.
      SubFilterMatch sub_filter = 2;
    };

    // Conditions to match a specific filter within another
    // filter. This field is typically useful to match a HTTP filter
    // inside the envoy.http_connection_manager network filter. This
    // could also be applicable for thrift filters.
    message SubFilterMatch {
      // The filter name to match on.
      string name = 1;
    };

    // The service port/gateway port to which traffic is being
    // sent/received. If not specified, matches all listeners. Even though
    // inbound listeners are generated for the instance/pod ports, only
    // service ports should be used to match listeners.
    uint32 port_number = 1;

    // Instead of using specific port numbers, a set of ports matching
    // a given service's port name can be selected. Matching is case
    // insensitive.
    // Not implemented.
    // $hide_from_docs
    string port_name = 2;

    // Match a specific filter chain in a listener. If specified, the
    // patch will be applied to the filter chain (and a specific
    // filter if specified) and not to other filter chains in the
    // listener.
    FilterChainMatch filter_chain = 3;

    // Match a specific listener by its name. The listeners generated
    // by Pilot are typically named as IP:Port.
    string name = 4;
  };

  // Patch specifies how the selected object should be modified.
  message Patch {

    // Operation denotes how the patch should be applied to the selected
    // configuration.
    enum Operation {
      INVALID = 0;

      // Merge the provided config with the generated config using
      // json merge semantics.
      MERGE = 1;

      // Add the provided config to an existing list (of listeners,
      // clusters, virtual hosts, network filters, or http
      // filters). This operation will be ignored when applyTo is set
      // to ROUTE_CONFIGURATION, or HTTP_ROUTE.
      ADD = 2;

      // Remove the selected object from the list (of listeners,
      // clusters, virtual hosts, network filters, or http
      // filters). Does not require a value to be specified. This
      // operation will be ignored when applyTo is set to
      // ROUTE_CONFIGURATION, or HTTP_ROUTE.
      REMOVE = 3;

      // Insert operation on an array of named objects. This operation
      // is typically useful only in the context of filters, where the
      // order of filters matter. For clusters and virtual hosts,
      // order of the element in the array does not matter. Insert
      // before the selected filter or sub filter. If no filter is
      // selected, the specified filter will be inserted at the front
      // of the list.
      INSERT_BEFORE = 4;

      // Insert operation on an array of named objects. This operation
      // is typically useful only in the context of filters, where the
      // order of filters matter. For clusters and virtual hosts,
      // order of the element in the array does not matter. Insert
      // after the selected filter or sub filter. If no filter is
      // selected, the specified filter will be inserted at the end
      // of the list.
      INSERT_AFTER = 5;
    }

    // Determines how the patch should be applied.
    Operation operation = 1;

    // The JSON config of the object being patched. This will be merged using
    // json merge semantics with the existing proto in the path.
    google.protobuf.Struct value = 2;
  };

  // One or more match conditions to be met before a patch is applied
  // to the generated configuration for a given proxy.
  message EnvoyConfigObjectMatch {
    // The specific config generation context to match on. Istio Pilot
    // generates envoy configuration in the context of a gateway,
    // inbound traffic to sidecar and outbound traffic from sidecar.
    PatchContext context = 1;

    // Match on properties associated with a proxy.
    ProxyMatch proxy = 2;

    oneof object_types {
      // Match on envoy listener attributes.
      ListenerMatch listener = 3;
      // Match on envoy HTTP route configuration attributes.
      RouteConfigurationMatch route_configuration = 4;
      // Match on envoy cluster attributes.
      ClusterMatch cluster = 5;
    }
  };

  // Changes to be made to various envoy config objects.
  message EnvoyConfigObjectPatch {
    // Specifies where in the Envoy configuration, the patch should be
    // applied.  The match is expected to select the appropriate
    // object based on applyTo.  For example, an applyTo with
    // HTTP_FILTER is expected to have a match condition on the
    // listeners, with a network filter selection on
    // envoy.http_connection_manager and a sub filter selection on the
    // HTTP filter relative to which the insertion should be
    // performed. Similarly, an applyTo on CLUSTER should have a match
    // (if provided) on the cluster and not on a listener.
    ApplyTo apply_to = 1;

    // Match on listener/route configuration/cluster.
    EnvoyConfigObjectMatch match = 2;

    // The patch to apply along with the operation.
    Patch patch = 3;
  }

  // Deprecated. Use workload_selector instead.
  // $hide_from_docs
  map<string, string> workload_labels = 1 [deprecated = true];
  // $hide_from_docs
  repeated Filter filters = 2 [deprecated = true];

  // Criteria used to select the specific set of pods/VMs on which
  // this patch configuration should be applied. If omitted, the set
  // of patches in this configuration will be applied to all workload
  // instances in the same namespace.  If omitted, the EnvoyFilter
  // patches will be applied to all workloads in the same
  // namespace. If the EnvoyFilter is present in the config root
  // namespace, it will be applied to all applicable workloads in any
  // namespace.
  WorkloadSelector workload_selector = 3;

  // One or more patches with match conditions.
  repeated EnvoyConfigObjectPatch config_patches = 4 [(google.api.field_behavior) = REQUIRED];
}
