Documentation
Introduction
- Overview
- Getting Started
- Support for K8s Installers
- Deploying on Kind
- Deploying on Minikube
- Configuration
- Installing with Helm
Cloud Deployment
Reference
- Antrea Network Policy
- Antctl
- Architecture
- Traffic Encryption (Ipsec / WireGuard)
- Securing Control Plane
- Security considerations
- Troubleshooting
- OS-specific Known Issues
- OVS Pipeline
- Feature Gates
- Antrea Proxy
- Network Flow Visibility
- Traceflow Guide
- NoEncap and Hybrid Traffic Modes
- Egress Guide
- NodePortLocal Guide
- Antrea IPAM Guide
- Exposing Services of type LoadBalancer
- Traffic Control
- BGP Support
- Versioning
- Antrea API Groups
- Antrea API Reference
Windows
Integrations
Cookbooks
Multicluster
Developer Guide
Project Information
Network Flow Visibility in Antrea
Table of Contents
- Overview
- Flow Exporter
- Flow Aggregator
- Quick Deployment
- Flow Collectors
Overview
Antrea is a Kubernetes network plugin that provides network connectivity and security features for Pod workloads. Considering the scale and dynamism of Kubernetes workloads in a cluster, Network Flow Visibility helps in the management and configuration of Kubernetes resources such as Network Policy, Services, Pods etc., and thereby provides opportunities to enhance the performance and security aspects of Pod workloads.
For visualizing the network flows, Antrea monitors the flows in Linux conntrack module. These flows are converted to flow records, and then flow records are post-processed before they are sent to the configured external flow collector. High-level design is given below:
Flow Exporter
In Antrea, the basic building block for the Network Flow Visibility is the Flow Exporter. Flow Exporter operates within Antrea Agent; it builds and maintains a connection store by polling and dumping flows from conntrack module periodically. Connections from the connection store are exported to the Flow Aggregator Service using the IPFIX protocol, and for this purpose we use the IPFIX exporter process from the go-ipfix library.
Configuration
In addition to enabling the Flow Exporter feature gate (if needed), you need to
ensure that the flowExporter.enable flag is set to true in the Antrea Agent
configuration.
Your antrea-agent ConfigMap should look like this:
antrea-agent.conf: |
# FeatureGates is a map of feature names to bools that enable or disable experimental features.
featureGates:
# Enable flowexporter which exports polled conntrack connections as IPFIX flow records from each agent to a configured collector.
FlowExporter: true
flowExporter:
# Enable FlowExporter, a feature used to export polled conntrack connections as
# IPFIX flow records from each agent to a configured collector. To enable this
# feature, you need to set "enable" to true, and ensure that the FlowExporter
# feature gate is also enabled.
enable: true
# Provide the IPFIX collector address as a string with format <HOST>:[<PORT>][:<PROTO>].
# HOST can either be the DNS name, IP, or Service name of the Flow Collector. If
# using an IP, it can be either IPv4 or IPv6. However, IPv6 address should be
# wrapped with []. When the collector is running in-cluster as a Service, set
# <HOST> to <Service namespace>/<Service name>. For example,
# "flow-aggregator/flow-aggregator" can be provided to connect to the Antrea
# Flow Aggregator Service.
# If PORT is empty, we default to 4739. If no PROTO is given, we consider "tls" as default.
# We support "grpc", "tls", "tcp" and "udp" protocols. "tls" is used for securing
# communication between flow exporter and flow aggregator. When "grpc" is configured,
# mTLS will always be used.
flowCollectorAddr: "flow-aggregator/flow-aggregator:4739:tls"
# Provide flow poll interval as a duration string. This determines how often the
# flow exporter dumps connections from the conntrack module. Flow poll interval
# should be greater than or equal to 1s (one second).
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
flowPollInterval: "5s"
# Provide the active flow export timeout, which is the timeout after which a flow
# record is sent to the collector for active flows. Thus, for flows with a continuous
# stream of packets, a flow record will be exported to the collector once the elapsed
# time since the last export event is equal to the value of this timeout.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
activeFlowExportTimeout: "5s"
# Provide the idle flow export timeout, which is the timeout after which a flow
# record is sent to the collector for idle flows. A flow is considered idle if no
# packet matching this flow has been observed since the last export event.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
idleFlowExportTimeout: "15s"
# Provide the set of protocols to filter specific flows that will be
# exported. Invalid protocols will not error and instead a warning is
# logged on the antrea agent. By default the full set of supported
# protocols are exported which are:
# "tcp", "udp", "sctp"
protocolFilter: nil
Please note that the default value for flowExporter.flowCollectorAddr is
"flow-aggregator/flow-aggregator:4739:tls", which enables the Flow Exporter to connect
the Flow Aggregator Service, assuming it is running in the same K8 cluster with the Name
and Namespace set to flow-aggregator. If you deploy the Flow Aggregator Service with
a different Name and Namespace, then set flowExporter.flowCollectorAddr appropriately.
Please note that the default values for
flowExporter.flowPollInterval, flowExporter.activeFlowExportTimeout, and
flowExporter.idleFlowExportTimeout parameters are set to 5s, 5s, and 15s, respectively.
TLS communication between the Flow Exporter and the Flow Aggregator is enabled by default.
Please modify them as per your requirements.
Configuration pre Antrea v1.13
Prior to the Antrea v1.13 release, the flowExporter option group in the
Antrea Agent configuration did not exist. To enable the Flow Exporter feature,
one simply needed to enable the feature gate, and the Flow Exporter related
configuration could be configured using the (now deprecated) flowCollectorAddr,
flowPollInterval, activeFlowExportTimeout, idleFlowExportTimeout
parameters.
IPFIX Information Elements (IEs) in a Flow Record
There are 43 IPFIX IEs in each exported flow record, which are defined in the IANA-assigned IE registry, the Reverse IANA-assigned IE registry and the Antrea IE registry. The reverse IEs are used to provide bi-directional information about the flow. The Enterprise ID is 0 for IANA-assigned IE registry, 29305 for reverse IANA IE registry, 56506 for Antrea IE registry. All the IEs used by the Antrea Flow Exporter are listed below:
IEs from IANA-assigned IE Registry
| IPFIX Information Element | Field ID | Type |
|---|---|---|
| flowStartSeconds | 150 | dateTimeSeconds |
| flowEndSeconds | 151 | dateTimeSeconds |
| flowEndReason | 136 | unsigned8 |
| sourceIPv4Address | 8 | ipv4Address |
| destinationIPv4Address | 12 | ipv4Address |
| sourceIPv6Address | 27 | ipv6Address |
| destinationIPv6Address | 28 | ipv6Address |
| sourceTransportPort | 7 | unsigned16 |
| destinationTransportPort | 11 | unsigned16 |
| protocolIdentifier | 4 | unsigned8 |
| packetTotalCount | 86 | unsigned64 |
| octetTotalCount | 85 | unsigned64 |
| packetDeltaCount | 2 | unsigned64 |
| octetDeltaCount | 1 | unsigned64 |
IEs from Reverse IANA-assigned IE Registry
| IPFIX Information Element | Field ID | Type |
|---|---|---|
| reversePacketTotalCount | 86 | unsigned64 |
| reverseOctetTotalCount | 85 | unsigned64 |
| reversePacketDeltaCount | 2 | unsigned64 |
| reverseOctetDeltaCount | 1 | unsigned64 |
IEs from Antrea IE Registry
| IPFIX Information Element | Field ID | Type | Description |
|---|---|---|---|
| sourcePodNamespace | 100 | string | |
| sourcePodName | 101 | string | |
| destinationPodNamespace | 102 | string | |
| destinationPodName | 103 | string | |
| sourceNodeName | 104 | string | |
| destinationNodeName | 105 | string | |
| destinationClusterIPv4 | 106 | ipv4Address | DEPRECATED in favor of destinationServiceIPv4. |
| destinationClusterIPv6 | 107 | ipv6Address | DEPRECATED in favor of destinationServiceIPv6. |
| destinationServiceIPv4 | 168 | ipv4Address | |
| destinationServiceIPv6 | 169 | ipv6Address | |
| proxySnatIPv4 | 170 | ipv4Address | The IPv4 SNAT address (e.g. by the Antrea gateway for an inter-Node “from external” flow). Unset (0.0.0.0) if the connection was not SNAT’d. |
| proxySnatIPv6 | 171 | ipv6Address | Same as proxySnatIPv4, for IPv6. Unset (::) if the connection was not SNAT’d. |
| proxySnatPort | 172 | unsigned16 | The port the connection’s source was translated to via SNAT, alongside proxySnatIPv4 / proxySnatIPv6. Unset (0) if the connection was not SNAT’d. |
| destinationServicePort | 108 | unsigned16 | |
| destinationServicePortName | 109 | string | |
| ingressNetworkPolicyName | 110 | string | Name of the ingress network policy applied to the destination Pod for this flow. |
| ingressNetworkPolicyNamespace | 111 | string | Namespace of the ingress network policy applied to the destination Pod for this flow. |
| ingressNetworkPolicyType | 115 | unsigned8 | 1 stands for Kubernetes Network Policy. 2 stands for Antrea Network Policy. 3 stands for Antrea Cluster Network Policy. |
| ingressNetworkPolicyRuleName | 141 | string | Name of the ingress network policy Rule applied to the destination Pod for this flow. |
| egressNetworkPolicyName | 112 | string | Name of the egress network policy applied to the source Pod for this flow. |
| egressNetworkPolicyNamespace | 113 | string | Namespace of the egress network policy applied to the source Pod for this flow. |
| egressNetworkPolicyType | 118 | unsigned8 | |
| egressNetworkPolicyRuleName | 142 | string | Name of the egress network policy rule applied to the source Pod for this flow. |
| ingressNetworkPolicyRuleAction | 139 | unsigned8 | 1 stands for Allow. 2 stands for Drop. 3 stands for Reject. |
| egressNetworkPolicyRuleAction | 140 | unsigned8 | |
| tcpState | 136 | string | The state of the TCP connection. The states are: LISTEN, SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT, and CLOSED. |
| flowType | 137 | unsigned8 | 1 stands for Intra-Node. 2 stands for Inter-Node. 3 stands for To External. 4 stands for From External. |
| egressName | 153 | string | Name of the Egress applied to this flow, if any. |
| egressIP | 154 | string | SNAT IP address used for the Egress applied to this flow. |
| egressNodeName | 157 | string | Name of the Node that currently holds the Egress IP for the Egress applied to this flow. |
Supported Capabilities
Types of Flows and Associated Information
Currently, the Flow Exporter feature provides visibility for Pod-to-Pod, Pod-to-Service and Pod-to-External network flows along with the associated statistics such as data throughput (bits per second), packet throughput (packets per second), cumulative byte count and cumulative packet count. Pod-To-Service flow visibility is supported only when Antrea Proxy enabled, which is the case by default starting with Antrea v0.11. In the future, we will enable the support for External-To-Service flows.
Kubernetes information such as Node name, Pod name, Pod Namespace, Service name, NetworkPolicy name and NetworkPolicy Namespace, is added to the flow records. Network Policy Rule Action (Allow, Reject, Drop) is also supported for both Antrea-native NetworkPolicies and K8s NetworkPolicies. For K8s NetworkPolicies, connections dropped due to isolated Pod behavior will be assigned the Drop action. For flow records that are exported from any given Antrea Agent, the Flow Exporter only provides the information of Kubernetes entities that are local to the Antrea Agent. In other words, flow records are only complete for intra-Node flows, but incomplete for inter-Node flows. It is the responsibility of the Flow Aggregator to correlate flows from the source and destination Nodes and produce complete flow records.
Both Flow Exporter and Flow Aggregator are supported in IPv4 clusters, IPv6 clusters and dual-stack clusters.
Connection Metrics
We support following connection metrics as Prometheus metrics that are exposed
through
Antrea Agent apiserver endpoint:
antrea_agent_conntrack_total_connection_count,
antrea_agent_conntrack_antrea_connection_count,
antrea_agent_denied_connection_count,
antrea_agent_conntrack_max_connection_count, and
antrea_agent_flow_collector_reconnection_count
Flow Aggregator
The Flow Aggregator consists of a K8s Deployment and Service. It has 2 main modes of operation:
Aggregate(default): in this mode, the Flow Aggregator stores, correlates and aggregates the flow records received from the Flow Exporter of Antrea Agents. For more information about this mode, including installation instructions, refer to the Aggregate Mode section.Proxy(new in Antrea v2.3): in this mode, the Flow Aggregator operates statelessly, and the flow records received from the Flow Exporter of Antrea Agents are sent directly to an IPFIX collector, without buffering or correlation / aggregation. For more information about this mode, including installation instructions, refer to the Proxy Mode section.
The Flow Aggregator is implemented as an IPFIX mediator. It consists of an IPFIX
Collector Process, an IPFIX Intermediate Process, and an IPFIX Exporter
Process. We use the
go-ipfix library to
implement the Flow Aggregator. The “Intermediate Process” differs greatly based
on whether the Flow Aggregator operates in Aggregate or Proxy mode.
Deciding which mode to use
If you are looking to export “raw” IPFIX records to an external IPFIX collector,
and want to minimize the footprint of the Flow Aggregator, you may be interested
in the Proxy mode. Note that the external IPFIX collector will be responsible
for correlating flow records generated by the source and destination K8s Nodes,
based on the 5-tuple (if the source and destination Nodes are the same, flow
records will be sent by a single Flow Exporter). Note that the same information
will not be present based on whether the record was exported from the source or
destination. For example, for Pod-to-Service traffic, destination Service
information will only be included in the source records. See
#6773 for more background on
why Proxy mode was introduced. Note that Proxy mode cannot be used with a
non-IPFIX destination collector, and is only available starting with Antrea v2.3.
Otherwise, the default Aggregate mode is probably right for you. It supports
all available destination collectors (e.g., ClickHouse), and its output is
easier to consume as the source and destination records have already been
correlated / aggregated.
Aggregate Mode
Installation
To deploy a released version of the Flow Aggregator, pick a deployment manifest from the
list of releases. For any
given release <TAG> (e.g. v2.3.0), you can deploy Flow Aggregator as follows:
kubectl apply -f https://github.com/antrea-io/antrea/releases/download/<TAG>/flow-aggregator.yml
We recommend updating the manifest with your desired configuration before applying it.
The following configuration parameters have to be provided through the Flow
Aggregator ConfigMap.
flowCollector is mandatory for
go-ipfix collector, and
clickHouse is mandatory for
Grafana Flow Collector.
We provide an example value for this parameter in the following snippet.
- If you have deployed the
go-ipfix collector,
then please set
flowCollector.enabletotrueand use the address forflowCollector.address:<Ipfix-Collector Cluster IP>:<port>:<tcp|udp> - If you have deployed the
Grafana Flow Collector,
then please enable the collector by setting
clickHouse.enabletotrue. If it is deployed following the deployment steps, the ClickHouse server is already exposed via a K8s Service, and no further configuration is required. If a different FQDN or IP is desired, please use the URL forclickHouse.databaseURLin the following format:<protocol>://<ClickHouse server FQDN or IP>:<ClickHouse port>.
Configuring secure connections to the ClickHouse database
Starting with Antrea v1.13, you can enable TLS when connecting to the ClickHouse
Server by setting clickHouse.databaseURL with protocol tls or https.
You can also change the value of clickHouse.tls.insecureSkipVerify to
determine whether to skip the verification of the server’s certificate.
If you want to provide a custom CA certificate, you can set
clickHouse.tls.caCert to true and the flow Aggregator will read the
certificate key pair from theclickhouse-ca Secret.
Make sure to follow the following form when creating the clickhouse-ca Secret
with the custom CA certificate:
apiVersion: v1
kind: Secret
metadata:
name: clickhouse-ca
namespace: flow-aggregator
data:
ca.crt: <BASE64 ENCODED CA CERTIFICATE>
You can use kubectl apply -f <PATH TO SECRET YAML> to create the above Secret,
or use kubectl create secret:
kubectl create secret generic clickhouse-ca -n flow-aggregator --from-file=ca.crt=<PATH TO CA CERTIFICATE>
Prior to Antrea v1.13, secure connections to ClickHouse are not supported, and TCP is the only supported protocol when connecting to the ClickHouse server from the Flow Aggregator.
Example of flow-aggregator.conf
flow-aggregator.conf: |
# Provide the active flow record timeout as a duration string. This determines
# how often the flow aggregator exports the active flow records to the flow
# collector. Thus, for flows with a continuous stream of packets, a flow record
# will be exported to the collector once the elapsed time since the last export
# event in the flow aggregator is equal to the value of this timeout.
# Note that a record is only exported if a matching flow record has been
# received from an Antrea Agent since the last export, hence this timeout is
# only honored as long as it is not smaller than the Agent's
# activeFlowExportTimeout ("5s" by default), which is the case with the
# default configuration.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
activeFlowRecordTimeout: 60s
# Provide the inactive flow record timeout as a duration string. This determines
# how long the flow aggregator retains the aggregated record for a flow after the
# last matching record was received: once this timeout expires, the aggregated
# record is deleted, and a subsequent record for the same flow starts a new
# aggregation. When this timeout is greater than activeFlowRecordTimeout (which is
# the case by default), the deletion never causes a record to be exported, as any
# new data has already been exported when the active flow record timeout last
# expired.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
inactiveFlowRecordTimeout: 90s
# Provide the transport protocol for the flow aggregator collecting process, which is tls, tcp or udp.
aggregatorTransportProtocol: "tls"
# Provide an extra DNS name or IP address of flow aggregator for generating TLS certificate.
flowAggregatorAddress: ""
# recordContents enables configuring some fields in the flow records. Fields can
# be excluded to reduce record size, but some features or external tooling may
# depend on these fields.
recordContents:
# Determine whether source and destination Pod labels will be included in the flow records.
podLabels: false
# apiServer contains APIServer related configuration options.
apiServer:
# The port for the flow-aggregator APIServer to serve on.
apiPort: 10348
# Comma-separated list of Cipher Suites. If omitted, the default Go Cipher Suites will be used.
# https://golang.org/pkg/crypto/tls/#pkg-constants
# Note that TLS1.3 Cipher Suites cannot be added to the list. But the apiserver will always
# prefer TLS1.3 Cipher Suites whenever possible.
tlsCipherSuites: ""
# TLS min version from: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13.
tlsMinVersion: ""
# flowCollector contains external IPFIX or JSON collector related configuration options.
flowCollector:
# Enable is the switch to enable exporting flow records to external flow collector.
enable: false
# Provide the flow collector address as string with format <IP>:<port>[:<proto>], where proto is tcp or udp.
# If no L4 transport proto is given, we consider tcp as default.
address: ""
# Provide the 32-bit Observation Domain ID which will uniquely identify this instance of the flow
# aggregator to an external flow collector. If omitted, an Observation Domain ID will be generated
# from the persistent cluster UUID generated by Antrea. Failing that (e.g. because the cluster UUID
# is not available), a value will be randomly generated, which may vary across restarts of the flow
# aggregator.
#observationDomainID:
# Provide format for records sent to the configured flow collector.
# Supported formats are IPFIX and JSON.
recordFormat: "IPFIX"
# clickHouse contains ClickHouse related configuration options.
clickHouse:
# Enable is the switch to enable exporting flow records to ClickHouse.
enable: false
# Database is the name of database where Antrea "flows" table is created.
database: "default"
# DatabaseURL is the url to the database. Provide the database URL as a string with format
# <Protocol>://<ClickHouse server FQDN or IP>:<ClickHouse port>. The protocol has to be
# one of the following: "tcp", "tls", "http", "https". When "tls" or "https" is used, tls
# will be enabled.
databaseURL: "tcp://clickhouse-clickhouse.flow-visibility.svc:9000"
# TLS configuration options, when using TLS to connect to the ClickHouse service.
tls:
# InsecureSkipVerify determines whether to skip the verification of the server's certificate chain and host name.
# Default is false.
insecureSkipVerify: false
# CACert indicates whether to use custom CA certificate. Default root CAs will be used if this field is false.
# If true, a Secret named "clickhouse-ca" must be provided with the following keys:
# ca.crt: <CA certificate>
caCert: false
# Debug enables debug logs from ClickHouse sql driver.
debug: false
# Compress enables lz4 compression when committing flow records.
compress: true
# CommitInterval is the periodical interval between batch commit of flow records to DB.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
# The minimum interval is 1s based on ClickHouse documentation for best performance.
commitInterval: "8s"
Please note that the default values for activeFlowRecordTimeout,
inactiveFlowRecordTimeout, aggregatorTransportProtocol parameters are set to
60s, 90s and tls respectively. Please make sure that
aggregatorTransportProtocol and protocol of flowCollectorAddr in
agent-agent.conf are set to tls to guarantee secure communication works
properly. Protocol of flowCollectorAddr and aggregatorTransportProtocol must
always match, so TLS must either be enabled for both sides or disabled for both
sides. Please modify the parameters as per your requirements.
Please note that the default value for recordContents.podLabels is false,
which indicates source and destination Pod labels will not be included in the
flow records exported to flowCollector and clickHouse. If you would like
to include them, you can modify the value to true.
Please note that the default value for apiServer.apiPort is 10348, which
is the port used to expose the Flow Aggregator’s APIServer. Please modify the
parameters as per your requirements.
Please note that the default value for clickHouse.commitInterval is 8s,
which is based on experiment results to achieve best ClickHouse write
performance and data retention. Based on ClickHouse recommendation for best
performance, this interval is required be no shorter than 1s. Also note
that flow aggregator has a cache limit of ~500k records for ClickHouse-Grafana
collector. If clickHouse.commitInterval is set to a value too large, there’s
a risk of losing records.
IPFIX Information Elements (IEs) in an Aggregated Flow Record
In addition to IPFIX information elements provided in the above section, the Flow Aggregator adds the following fields to the flow records.
IEs from Antrea IE Registry
| IPFIX Information Element | Field ID | Type | Description |
|---|---|---|---|
| packetTotalCountFromSourceNode | 120 | unsigned64 | The cumulative number of packets for this flow as reported by the source Node, since the flow started. |
| octetTotalCountFromSourceNode | 121 | unsigned64 | The cumulative number of octets for this flow as reported by the source Node, since the flow started. |
| packetDeltaCountFromSourceNode | 122 | unsigned64 | The number of packets for this flow as reported by the source Node, since the previous report for this flow at the observation point. |
| octetDeltaCountFromSourceNode | 123 | unsigned64 | The number of octets for this flow as reported by the source Node, since the previous report for this flow at the observation point. |
| reversePacketTotalCountFromSourceNode | 124 | unsigned64 | The cumulative number of reverse packets for this flow as reported by the source Node, since the flow started. |
| reverseOctetTotalCountFromSourceNode | 125 | unsigned64 | The cumulative number of reverse octets for this flow as reported by the source Node, since the flow started. |
| reversePacketDeltaCountFromSourceNode | 126 | unsigned64 | The number of reverse packets for this flow as reported by the source Node, since the previous report for this flow at the observation point. |
| reverseOctetDeltaCountFromSourceNode | 127 | unsigned64 | The number of reverse octets for this flow as reported by the source Node, since the previous report for this flow at the observation point. |
| packetTotalCountFromDestinationNode | 128 | unsigned64 | The cumulative number of packets for this flow as reported by the destination Node, since the flow started. |
| octetTotalCountFromDestinationNode | 129 | unsigned64 | The cumulative number of octets for this flow as reported by the destination Node, since the flow started. |
| packetDeltaCountFromDestinationNode | 130 | unsigned64 | The number of packets for this flow as reported by the destination Node, since the previous report for this flow at the observation point. |
| octetDeltaCountFromDestinationNode | 131 | unsigned64 | The number of octets for this flow as reported by the destination Node, since the previous report for this flow at the observation point. |
| reversePacketTotalCountFromDestinationNode | 132 | unsigned64 | The cumulative number of reverse packets for this flow as reported by the destination Node, since the flow started. |
| reverseOctetTotalCountFromDestinationNode | 133 | unsigned64 | The cumulative number of reverse octets for this flow as reported by the destination Node, since the flow started. |
| reversePacketDeltaCountFromDestinationNode | 134 | unsigned64 | The number of reverse packets for this flow as reported by the destination Node, since the previous report for this flow at the observation point. |
| reverseOctetDeltaCountFromDestinationNode | 135 | unsigned64 | The number of reverse octets for this flow as reported by the destination Node, since the previous report for this flow at the observation point. |
| sourcePodLabels | 143 | string | |
| destinationPodLabels | 144 | string | |
| throughput | 145 | unsigned64 | The average amount of traffic flowing from source to destination, since the previous report for this flow at the observation point. The unit is bits per second. |
| reverseThroughput | 146 | unsigned64 | The average amount of reverse traffic flowing from destination to source, since the previous report for this flow at the observation point. The unit is bits per second. |
| throughputFromSourceNode | 147 | unsigned64 | The average amount of traffic flowing from source to destination, since the previous report for this flow at the observation point, based on the records sent from the source Node. The unit is bits per second. |
| throughputFromDestinationNode | 148 | unsigned64 | The average amount of traffic flowing from source to destination, since the previous report for this flow at the observation point, based on the records sent from the destination Node. The unit is bits per second. |
| reverseThroughputFromSourceNode | 149 | unsigned64 | The average amount of reverse traffic flowing from destination to source, since the previous report for this flow at the observation point, based on the records sent from the source Node. The unit is bits per second. |
| reverseThroughputFromDestinationNode | 150 | unsigned64 | The average amount of reverse traffic flowing from destination to source, since the previous report for this flow at the observation point, based on the records sent from the destination Node. The unit is bits per second. |
| flowEndSecondsFromSourceNode | 151 | unsigned32 | The absolute timestamp of the last packet of this flow, based on the records sent from the source Node. The unit is seconds. |
| flowEndSecondsFromDestinationNode | 152 | unsigned32 | The absolute timestamp of the last packet of this flow, based on the records sent from the destination Node. The unit is seconds. |
| clusterId | 158 | string | UUID of the cluster as generated by the Antrea Controller, in string format. |
Supported Capabilities
Storage of Flow Records
Flow Aggregator stores the received flow records from Antrea Agents in a hash map, where the flow key is 5-tuple of a network connection. 5-tuple consists of Source IP, Destination IP, Source Port, Destination Port and Transport protocol. Therefore, Flow Aggregator maintains one flow record for any given connection, and this flow record gets updated till the connection in the Kubernetes cluster becomes invalid.
Correlation of Flow Records
In the case of inter-Node flows, there are two flow records, one from the source Node, where the flow originates from, and another one from the destination Node, where the destination Pod resides. Both the flow records contain incomplete information as mentioned here. Flow Aggregator provides support for the correlation of the flow records from the source Node and the destination Node, and it exports a single flow record with complete information for both inter-Node and intra-Node flows.
Aggregation of Flow Records
Flow Aggregator aggregates the flow records that belong to a single connection. As part of aggregation, fields such as flow timestamps, flow statistics etc. are updated. For the purpose of updating flow statistics fields, Flow Aggregator introduces the new fields in Antrea Enterprise IPFIX registry corresponding to the Source Node and Destination Node, so that flow statistics from different Nodes can be preserved.
Antctl Support
antctl can access the Flow Aggregator API to dump flow records and print metrics about flow record processing. Refer to the antctl documentation for more information.
Proxy Mode (v2.3 and above)
Installation
To deploy a released version of the Flow Aggregator, pick a deployment manifest from the
list of releases. For any
given release <TAG> (e.g. v2.3.0), you can deploy Flow Aggregator as follows:
kubectl apply -f https://github.com/antrea-io/antrea/releases/download/<TAG>/flow-aggregator.yml
We recommend updating the manifest with your desired configuration before
applying it. In order to proxy records to an external collector, you will need
to populate the flowCollector section of the Flow Aggregator ConfigMap.
Alternatively, you can use Helm to easily install the latest released version of the Flow Aggregator:
# If you are installing an Antrea Helm chart for the first time:
helm repo add antrea https://charts.antrea.io
# To update available charts:
helm repo update
helm install flow-aggregator antrea/flow-aggregator --set mode=Proxy,flowCollector.enable=true,flowCollector.address="<addr:port:proto>" -n flow-aggregator --create-namespace
In Proxy mode, not all configuration parameters are applicable.
flowCollector is the only supported collector, and activeFlowRecordTimeout /
inactiveFlowRecordTimeout are not applicable. The
FlowStreamService is not supported either: it is
disabled by default in Proxy mode, and the Flow Aggregator refuses to start if
flowStreamService.enable is set to true. If you start from the release
manifest, remove the flowstream-grpc port and the
observability.antrea.io/flow-stream-service label from the Service.
IPFIX Information Elements (IEs) in a Proxied Flow Record
In addition to IPFIX information elements provided in the above section, the Flow Aggregator adds the following fields to the flow records before forwarding them to the external collector:
| IPFIX Registry | IPFIX Information Element | Field ID | Type | Description |
|---|---|---|---|---|
| Antrea | sourcePodLabels* | 143 | string | K8s labels for the source Pod *if recordContents.podLabels is true. |
| destinationPodLabels* | 144 | string | K8s labels for the destination Pod *if recordContents.podLabels is true. |
|
| clusterId | 158 | string | UUID of the cluster as generated by the Antrea Controller, in string format. | |
| IANA | flowDirection | 61 | unsigned8 | The direction of the flow as observed by the Flow Exporter: 0x00 (ingress flow), 0x01 (egress flow), 0xff (direction N/A such as for intra-Node flows). |
| originalExporterIPv4Address | 403 | ipv4Address | The IPv4 address (if any) used by the Flow Exporter in the Antrea Agent. | |
| originalExporterIPv6Address | 404 | ipv6Address | The IPv6 address (if any) used by the Flow Exporter in the Antrea Agent. | |
| originalObservationDomainId | 405 | unsigned32 | The Observation Domain ID originally reported by the Flow Exporter in the Antrea Agent. |
FlowStreamService (alpha)
The Flow Aggregator can expose a gRPC server-streaming API, FlowStreamService,
on port 14740, which streams flow records out of its in-memory record buffer to
consumers such as antrea-ui: historical records first, then live ones if the
client asked to follow. It is alpha, and enabled by default in Aggregate mode.
It is not supported in Proxy mode: records are not correlated in that mode, so
a record only carries the policy information of the Node which exported it,
while the redaction described in
What a client sees of each endpoint
relies on records describing the whole connection. It can be disabled with:
flowStreamService:
enable: false
The connection uses server-side TLS with the same self-signed certificate as the gRPC collector.
When the FlowStreamService is enabled, the Flow Aggregator Service carries the
label observability.antrea.io/flow-stream-service: "true"; it does not carry
it otherwise. As a cluster can run several Flow Aggregators, and only some of
them may expose the FlowStreamService (for example, a Flow Aggregator running
in Proxy mode for IPFIX export does not), clients can use this label to discover
the FlowStreamService to connect to, instead of hardcoding its Namespace and name:
kubectl get services -A -l observability.antrea.io/flow-stream-service=true
Authenticating to the FlowStreamService
Clients must present a Kubernetes credential, as one of:
- a bearer token in gRPC metadata, as
authorization: Bearer <token>, which the Flow Aggregator validates with aTokenReview.kubectl create token <serviceaccount>and, in a cluster whose kube-apiserver trusts an OIDC issuer, an OIDC ID token both work. - an X.509 client certificate, presented as the TLS client credential of the gRPC
connection, which the Flow Aggregator verifies against the cluster’s client CA
bundle — the one kube-apiserver publishes in the
extension-apiserver-authenticationConfigMap. This covers the clients whose only credential is a short-lived client certificate, such as one issued by a Pinniped Concierge. Note that the Flow Aggregator’s own client certificate is signed by its self-signed CA rather than the cluster’s, so it cannot be used here.
A call carrying both is identified by its certificate, which is the order kube-apiserver itself tries them in; if that certificate does not verify, the bearer token is tried next.
A stream whose credential is missing, malformed or invalid is rejected with
UNAUTHENTICATED before any record is read.
Authorizing flow visibility
What a client then receives is decided by ordinary Kubernetes RBAC on a virtual
resource, flows in API group observability.antrea.io. Nothing serves
that resource: it exists to be named in a Role or ClusterRole, the same way
kubelet authorizes its own endpoints through nodes/proxy. The Flow Aggregator
resolves each stream with SubjectAccessReviews, for which its ServiceAccount is
already bound to system:auth-delegator.
Two permissions exist:
| To let a subject… | Grant in a Namespace |
|---|---|
| stream that Namespace’s flows, and see full detail on its endpoints | flows, verbs list and watch |
| recognize that Namespace’s workloads inside records the subject receives through other Namespaces | flows/identity, verb get |
flows in a Namespace implies flows/identity there: an endpoint in that
Namespace is disclosed in full without a separate identity check, provided the
flows verbs held there include the one the stream was opened with (watch
for a following stream, list otherwise). Granting flows/identity on its own
is for the remaining case — being recognizable inside records someone receives
through a different Namespace.
list authorizes a stream that drains the record buffer and closes; watch
authorizes one that then follows live records. Granting only list therefore
yields history-only access. watch is what the gRPC API’s follow flag maps
onto.
A client must name the scope it wants to observe. There are two ways to consume flows:
- a subject with cluster-wide visibility sets the cluster-wide flag and streams the flows of every Namespace, including flows with no Namespace at all, over a single stream. This is a single cluster-wide check, which only a ClusterRoleBinding can satisfy;
- a subject with visibility in individual Namespaces names one Namespace per request, and opens one stream per Namespace it wants to observe.
A request naming more than one Namespace is rejected with INVALID_ARGUMENT, and
a request naming a Namespace the client may not observe is rejected with
PERMISSION_DENIED. Kubernetes offers no reverse lookup from a subject to the
Namespaces it may access, so “show me everything I am allowed to see” is not
something the Flow Aggregator can answer, which is why the scope is always explicit.
The same distinction surfaces in the Antrea UI: selecting a Namespace is mandatory
on the flow visibility tab unless the user has cluster-wide visibility.
A record is streamed if either of its endpoints is in the Namespaces the client was authorized for. Requiring both would hide exactly the cross-Namespace flows users are looking for — “my egress to a service I cannot see was dropped” is the common case.
Two ClusterRoles are shipped by default: antrea-flow-viewer, which grants
flows, and antrea-flow-identity-viewer, which grants flows/identity. Each
can be bound directly, in a Namespace or cluster-wide, and each is also
aggregated into a built-in role, as the paragraphs after these examples describe.
Examples:
# Let the "network-ops" group stream flows in the "frontend" Namespace.
kubectl create rolebinding flow-viewer -n frontend \
--clusterrole antrea-flow-viewer --group network-ops
# Let it stream every flow in the cluster, including flows with no Namespace at all.
kubectl create clusterrolebinding flow-viewer \
--clusterrole antrea-flow-viewer --group network-ops
# Let a group with no read access at all recognize kube-system's workloads inside
# records they already receive.
kubectl create rolebinding flow-identity -n kube-system \
--clusterrole antrea-flow-identity-viewer --group some-group-without-read-access
antrea-flow-viewer carries rbac.authorization.k8s.io/aggregate-to-admin, so a
Namespace administrator observes their own Namespace’s flows without any of the
bindings above as long as they hold the “admin” role to that Namespace: a Namespace’s
flows are treated as part of what owning that Namespace already entails. A true
cluster administrator, holding * on *, has cluster-wide visibility for the
same reason.
antrea-flow-identity-viewer carries rbac.authorization.k8s.io/aggregate-to-view
instead, so who can already read the Namespace’s objects automatically gets flows/identity
in that namespace, with no separate binding.
Both roles are cluster singletons: their names are fixed, they name no
ServiceAccount and no release, and one copy of each serves every Flow Aggregator
in the cluster. A second Helm release in the same cluster must instead set
flowStreamService.installClusterRoles=false and bind against the roles the first
release installed, since Helm will not adopt objects another release owns. A
release in which the FlowStreamService is disabled, as is always the case in
Proxy mode, does not install the roles.
Ownership follows the release that installed them, so uninstalling that release
deletes both roles even while other Flow Aggregator releases are still running.
To hand them to a surviving release, set installClusterRoles=true there and
helm upgrade it: the roles are recreated and that release becomes their owner.
Do this only once the previous owner is gone, since Helm will not let one
release adopt what another still owns.
Existing bindings fail closed during this period. RBAC resolves a binding’s
roleRef when it authorizes a request rather than when the binding is created,
so a binding whose role is missing grants nothing in the meanwhile, and resumes
working once the role is back.
Who currently holds flow visibility can be audited with:
kubectl get rolebindings,clusterrolebindings -A -o json | \
jq '.items[] | select(.roleRef.name | startswith("antrea-flow-"))'
What a client sees of each endpoint
Each endpoint of a record — source and destination — is disclosed independently, according to the client’s permissions in that endpoint’s Namespace. A single record could carry one endpoint in full and the other redacted.
| Tier | Fields | Requires |
|---|---|---|
| Flow | addresses, ports, protocol, statistics and throughput, timestamps, flow type, direction, end reason, TCP state, the type and action of the network policies evaluated on the endpoint’s side, and the endpoint’s Pod Namespace when neither ingress nor egress policy action is DROP or REJECT |
receiving the record at all |
| Identity | endpoint’s Pod Namespace regardless of policy action, Pod name/UID/labels, the destination Service’s destination_service_port, destination_service_port_name, destination_service_uid and destination_service_ip (plus the deprecated destination_cluster_ip), and the network policy namespace/name/UID/rule name |
get flows/identity in the endpoint’s Namespace |
| Full | Node name/UID, Egress name/IP/Node. Node co-tenancy with the client’s own workloads can survive redaction at Flow/Identity tiers, as the flow type paragraph below explains | the endpoint’s Namespace is one the stream was authorized for, or the client holds the stream’s verb on flows there |
A destination Service’s IP sits at the Identity tier rather than the Flow tier,
because it maps back to the Service it belongs to, so granting flows/identity
in a Namespace discloses the IPs of that Namespace’s Services along with their
names. There is no field carrying a Service’s name on its own: the name rides
inside destination_service_port_name, which the Flow Exporter sets to
namespace/name:portName, so that one field carries the Service’s Namespace and
name as well as the port’s name.
Those Service fields follow the tier of the destination endpoint, which is
resolved from destination_pod_namespace — the Namespace of the Pod that
received the connection, after DNAT. The Service’s own Namespace, the ns/
prefix of destination_service_port_name, is normally that same Namespace, but
the two can differ, because a Service’s endpoints are not constrained to its own
Namespace: a selector-less Service in ns-a can be pointed at a Pod IP in ns-b
by a manually created EndpointSlice, which AntreaProxy accepts. If a Service
has endpoints in another Namespace, its name, UID and ClusterIP are disclosed to
whoever may observe flows in the Namespace its backends live in, whatever the
Service’s own Namespace has granted — the Service fields were tiered against
ns-b’s consent, not ns-a’s. This is a documented caveat rather than something
the tiering tries to undo: creating such a Service takes EndpointSlice write
access in ns-a, and a Service’s name and ClusterIP are discoverable through
cluster DNS by any Pod anyway.
The converse also holds. A destination endpoint with no Namespace at all, e.g. a
hostNetwork backend, or a Multicluster endpoint in another cluster — sits at the
Flow tier on every non-cluster-wide stream, so the Service fields are withheld even
on a stream opened for the Service’s own Namespace. Nothing is over-disclosed there;
the client simply cannot see which of its own Services such a flow went through.
The flow type is deliberately not redacted, so FLOW_TYPE_INTRA_NODE still
reports that the two endpoints share a Node. Paired with an endpoint the client
can place — its own Pods, whose Nodes it can read from the downward API with no
Antrea grant at all — that discloses co-tenancy with the client’s own workloads
even where the peer’s Node name was withheld, and the flow_types filter yields
the same bit without reading the field. This is a deliberate trade: the
internal-versus-external distinction the flow type carries is what lets a client
tell a genuinely external endpoint from one whose Namespace was withheld, and
collapsing INTRA_NODE to INTER_NODE would make the field mean something
different on every stream. The peer’s Node is never named.
Each endpoint’s tier is reported on the record itself (source_disclosure /
destination_disclosure), so that a client can tell a field that was withheld
from one the Flow Aggregator never had — an empty policy Namespace, for
instance, is what a cluster-scoped policy legitimately looks like. FULL is the
zero value of both fields, so a record that was never redacted, and every record
reaching a cluster-wide stream, reports both endpoints as fully disclosed
without the Flow Aggregator having to modify anything.
An unidentified endpoint keeps its Namespace only if the connection was allowed:
withholding it for denied connections is what keeps a client from scanning the
Pod CIDR and mapping IPs to Namespaces by reading back its own denied flows.
Anything other than an explicit drop or reject counts as allowed, including “no
policy applied at all”, so the mitigation only bites where the traffic really was
denied by a policy: in a cluster without default-deny, scan probes not explicitly
dropped or rejected read as allowed and disclose the peer Namespace. That is an
accepted limit rather than an oversight. Namespace names may also be discoverable
through CoreDNS reverse lookups for Service IPs and their endpoint IPs, though
this does not cover every Pod.
A few fields belong to the record rather than to either endpoint. ipfix, which
carries the IP of the Node that exported the record, is withheld unless both
endpoints are disclosed in full: it is present on every record, including a
Pod-to-Pod flow where neither Node is named. Because it is a record-level field,
an endpoint can lose it from a record while its own source_disclosure /
destination_disclosure still reports FULL: a marker describes an endpoint,
not the record it arrives on.
proxy_snat_ip and proxy_snat_port are not redacted, even though they too
hold a Node address. Only a from-external flow carries them — they are the
address the ingress Node masqueraded the external client to, which is what the
destination Pod actually observed as its peer. Such a flow has no source
Namespace for a stream to be authorized for, so it only ever reaches a stream
through its destination, a Namespace that stream may observe in full.
A rule action and the policy type are disclosed at every tier, even for a
policy the client may not otherwise know about: losing them would lose “why did my
connection fail”, which is most of the troubleshooting value, and knowing whether
a cluster-scoped or a namespaced policy dropped the connection tells a client
whether to escalate to the platform team or to the peer. The policy’s identity
(its Namespace, name, UID and rule name) comes with the Identity tier, so that
“which of your policies dropped my traffic” is answerable once the peer’s
Namespace has granted flows/identity. Below that tier it stays hidden, which is
what keeps a client from mapping the policy set of a Namespace that never
consented to being identified.
Consequently, the same flow can look different depending on which Namespace a
stream was opened for. Take a subject holding antrea-flow-viewer in ns-a and
ns-b: it sees an ns-a-to-ns-b flow with both endpoints in full on either
stream, since it could have opened the same stream for the peer’s Namespace
anyway. A subject holding antrea-flow-viewer in ns-a only sees the ns-b
endpoint at the Identity tier if it holds antrea-flow-identity-viewer in ns-b,
and at the Flow tier otherwise. The verb matters too: a subject holding only
list on flows in ns-b sees the ns-b endpoint in full on a non-following
ns-a stream, but not on a following one, which falls back to what
flows/identity there yields. A stream is always opened with exactly the scope
the client named — a request is authorized in full or rejected outright, never
narrowed — so there is nothing for the server to report back.
What administrators should know
- A grant on a shared infrastructure Namespace approximates a cluster-wide
grant.
kube-system, ingress, monitoring and service-mesh control planes have connections to nearly everything, so flow visibility there exposes flows involving nearly every workload. - A wildcard Role confers flow visibility, and full disclosure with it. A
namespaced Role granting
apiGroups: ["*"], resources: ["*"], as tenants are sometimes given, includesflowsin that Namespace. Its holder therefore sees that Namespace’s endpoints in full, Node and Egress details included, even in records received through some other Namespace, as long as the Role grants the verb that stream was opened with. Bindingadminthere has the same effect, becauseantrea-flow-vieweris aggregated to it. Bindingvieworeditdoes not: they stop at the Identity tier. RBAC cannot express “not reachable via a wildcard”, and there is no query for “who can do X”, so a cluster that hands out wildcard Roles should scan for inadvertentflowsgrants. - A Service with endpoints in another Namespace discloses itself there. The Service fields of a record are tiered by the Namespace of the Pod that received the connection, so a Service whose EndpointSlices point outside its own Namespace is named to whoever may observe flows where its backends are. See the paragraph on the Service tier above.
- Revoking a grant takes up to 11 minutes to end an established stream. Authorization decisions, both allow and deny, are cached for 10 minutes — the shortest lifetime Kubernetes gives a projected ServiceAccount token — and an established stream re-checks its own scope once a minute, which is normally answered from that cache. The two add up in the worst case. Granting access is subject to the same delay. Opening a new stream is never served from a stale allow decision beyond the 10-minute window, and fails closed if the API server is unreachable; an established stream survives a control-plane blip. Peer disclosure that rests on a cluster-wide grant is kept the same way while its re-check cannot complete; disclosure that rests on a grant in the peer’s own Namespace falls back to what the subject holds cluster-wide instead, which is the Flow tier if it holds nothing there.
- Flow visibility is not derived from read access to the objects involved, but
identity is. A subject granted
watch flowsin a Namespace sees Pod names and labels for it even withoutget podsthere —flowsand object read access are separate grants.flows/identityis the exception:antrea-flow-identity-vieweris aggregated toview, so anyone bound to the built-inview,editoradminRole in a Namespace already holds it there, with noantrea-flow-*binding to show up in the audit command above. - Identity changes mid-stream are invisible. The credential is resolved when the stream opens, so a token revoked or a group membership changed afterwards takes effect on the next connection, not on the current one.
Version skew between Flow Aggregator and Antrea Agent
As a rule, we recommend keeping the Flow Aggregator and the Antrea Agent at the same (minor) version. During upgrades, there will be a small time windows during which this is not possible. Prior to Antrea v2.3, the Flow Aggregator Pod may crash (and restart) in case of version mismatch, e.g., because of the introduction of new Information Elements. Starting with Antrea v2.3, the Flow Aggregator should be able to handle older or newer Agents gracefully. If possible, we do recommend upgrading the Flow Aggregator Deployment last (i.e., after all Antrea Agents have been upgraded). The version skew between the Flow Aggregator and Antrea Agents should be at most 4 minor versions, which also corresponds to the maximum supported version “delta” for Antrea upgrades, as per our versioning policy.
Quick Deployment
If you would like to quickly try Network Flow Visibility feature, you can deploy Antrea, the Flow Aggregator Service, the Grafana Flow Collector on the Vagrant setup.
Image-building Steps
Build required image under antrea by using make command:
make
make flow-aggregator-image
Deployment Steps
Given any external IPFIX flow collector, you can deploy Antrea and the Flow Aggregator Service on a default Vagrant setup by running the following commands:
./infra/vagrant/provision.sh
./infra/vagrant/push_antrea.sh --flow-collector <externalFlowCollectorAddress>
If you would like to deploy the Grafana Flow Collector, you can run the following command:
./infra/vagrant/provision.sh
./infra/vagrant/push_antrea.sh --flow-collector Grafana
Flow Collectors
Here we list two choices the external configured flow collector: go-ipfix collector and Grafana flow collector. For each collector, we introduce how to deploy it and how to output or visualize the collected flow records information.
Go-ipfix Collector
Deployment Steps
The go-ipfix collector can be built from go-ipfix library. It is used to collect, decode and log the IPFIX records.
- To deploy a released version of the go-ipfix collector, please choose one
deployment manifest from the list of releases (supported after v0.5.2).
For any given release
(e.g. v0.5.2), you can deploy the collector as follows:
kubectl apply -f https://github.com/vmware/go-ipfix/releases/download/<TAG>/ipfix-collector.yaml
- To deploy the latest version of the go-ipfix collector (built from the main branch), use the checked-in deployment manifest:
kubectl apply -f https://raw.githubusercontent.com/vmware/go-ipfix/main/build/yamls/ipfix-collector.yaml
Go-ipfix collector also supports customization on its parameters: port and protocol. Please follow the go-ipfix documentation to configure those parameters if needed.
Output Flow Records
To output the flow records collected by the go-ipfix collector, use the command below:
kubectl logs <ipfix-collector-pod-name> -n ipfix
Grafana Flow Collector (migrated)
Starting with Antrea v1.8, support for the Grafana Flow Collector has been migrated to Theia.
The Grafana Flow Collector was added in Antrea v1.6.0. In Antrea v1.7.0, we start to move the network observability and analytics functionalities of Antrea to Project Theia, including the Grafana Flow Collector. Going forward, further development of the Grafana Flow Collector will be in the Theia repo. For the up-to-date version of Grafana Flow Collector and other Theia features, please refer to the Theia document.
ELK Flow Collector (removed)
Starting with Antrea v1.7, support for the ELK Flow Collector has been removed. Please consider using the Grafana Flow Collector instead, which is actively maintained.