in pilot/pkg/networking/core/v1alpha3/listener.go [1216:1376]
func buildListener(opts buildListenerOpts, trafficDirection core.TrafficDirection) *listener.Listener {
filterChains := make([]*listener.FilterChain, 0, len(opts.filterChainOpts))
listenerFiltersMap := make(map[string]bool)
var listenerFilters []*listener.ListenerFilter
// add a TLS inspector if we need to detect ServerName or ALPN
// (this is not applicable for QUIC listeners)
needTLSInspector := false
if opts.transport == istionetworking.TransportProtocolTCP {
for _, chain := range opts.filterChainOpts {
needsALPN := chain.tlsContext != nil && chain.tlsContext.CommonTlsContext != nil && len(chain.tlsContext.CommonTlsContext.AlpnProtocols) > 0
if len(chain.sniHosts) > 0 || needsALPN {
needTLSInspector = true
break
}
}
}
if opts.proxy.GetInterceptionMode() == model.InterceptionTproxy && trafficDirection == core.TrafficDirection_INBOUND {
listenerFiltersMap[wellknown.OriginalSource] = true
listenerFilters = append(listenerFilters, xdsfilters.OriginalSrc)
}
// We add a TLS inspector when http inspector is needed for outbound only. This
// is because if we ever set ALPN in the match without
// transport_protocol=raw_buffer, Envoy will automatically inject a tls
// inspector: https://github.com/envoyproxy/envoy/issues/13601. This leads to
// excessive logging and loss of control over the config For inbound this is not
// needed, since we are explicitly setting transport protocol in every single
// match. We can do this for outbound as well, at which point this could be
// removed, but have not yet
if opts.transport == istionetworking.TransportProtocolTCP &&
(needTLSInspector || (opts.class == istionetworking.ListenerClassSidecarOutbound && opts.needHTTPInspector)) {
listenerFiltersMap[wellknown.TlsInspector] = true
listenerFilters = append(listenerFilters, xdsfilters.TLSInspector)
}
// TODO: For now we assume that only HTTP/3 is used over QUIC. Revisit this in the future
if opts.needHTTPInspector && opts.transport == istionetworking.TransportProtocolTCP {
listenerFiltersMap[wellknown.HttpInspector] = true
listenerFilters = append(listenerFilters, xdsfilters.HTTPInspector)
}
for _, chain := range opts.filterChainOpts {
for _, filter := range chain.listenerFilters {
if _, exist := listenerFiltersMap[filter.Name]; !exist {
listenerFiltersMap[filter.Name] = true
listenerFilters = append(listenerFilters, filter)
}
}
match := &listener.FilterChainMatch{}
needMatch := false
if chain.match != nil {
needMatch = true
match = chain.match
}
if len(chain.sniHosts) > 0 {
fullWildcardFound := false
for _, h := range chain.sniHosts {
if h == "*" {
fullWildcardFound = true
// If we have a host with *, it effectively means match anything, i.e.
// no SNI based matching for this host.
break
}
}
if !fullWildcardFound {
chain.sniHosts = append([]string{}, chain.sniHosts...)
sort.Stable(sort.StringSlice(chain.sniHosts))
match.ServerNames = chain.sniHosts
}
}
if len(chain.destinationCIDRs) > 0 {
chain.destinationCIDRs = append([]string{}, chain.destinationCIDRs...)
sort.Stable(sort.StringSlice(chain.destinationCIDRs))
for _, d := range chain.destinationCIDRs {
cidr := util.ConvertAddressToCidr(d)
if cidr != nil && cidr.AddressPrefix != constants.UnspecifiedIP {
match.PrefixRanges = append(match.PrefixRanges, cidr)
}
}
}
if !needMatch && filterChainMatchEmpty(match) {
match = nil
}
var transportSocket *core.TransportSocket
switch opts.transport {
case istionetworking.TransportProtocolTCP:
transportSocket = buildDownstreamTLSTransportSocket(chain.tlsContext)
case istionetworking.TransportProtocolQUIC:
transportSocket = buildDownstreamQUICTransportSocket(chain.tlsContext)
}
filterChains = append(filterChains, &listener.FilterChain{
FilterChainMatch: match,
TransportSocket: transportSocket,
})
}
var res *listener.Listener
switch opts.transport {
case istionetworking.TransportProtocolTCP:
var bindToPort *wrappers.BoolValue
var connectionBalance *listener.Listener_ConnectionBalanceConfig
if !opts.bindToPort {
bindToPort = proto.BoolFalse
}
// only use to exact_balance for tcp outbound listeners; virtualOutbound listener should
// not have this set per Envoy docs for redirected listeners
if opts.proxy.Metadata.OutboundListenerExactBalance && trafficDirection == core.TrafficDirection_OUTBOUND {
connectionBalance = &listener.Listener_ConnectionBalanceConfig{
BalanceType: &listener.Listener_ConnectionBalanceConfig_ExactBalance_{
ExactBalance: &listener.Listener_ConnectionBalanceConfig_ExactBalance{},
},
}
}
res = &listener.Listener{
// TODO: need to sanitize the opts.bind if its a UDS socket, as it could have colons, that envoy doesn't like
Name: getListenerName(opts.bind, opts.port.Port, istionetworking.TransportProtocolTCP),
Address: util.BuildAddress(opts.bind, uint32(opts.port.Port)),
TrafficDirection: trafficDirection,
ListenerFilters: listenerFilters,
FilterChains: filterChains,
BindToPort: bindToPort,
ConnectionBalanceConfig: connectionBalance,
}
if opts.proxy.Type != model.Router {
res.ListenerFiltersTimeout = opts.push.Mesh.ProtocolDetectionTimeout
if res.ListenerFiltersTimeout != nil {
res.ContinueOnListenerFiltersTimeout = true
}
}
case istionetworking.TransportProtocolQUIC:
// TODO: switch on TransportProtocolQUIC is in too many places now. Once this is a bit
// mature, refactor some of these to an interface so that they kick off the process
// of building listener, filter chains, serializing etc based on transport protocol
listenerName := getListenerName(opts.bind, opts.port.Port, istionetworking.TransportProtocolQUIC)
log.Debugf("buildListener: building UDP/QUIC listener %s", listenerName)
res = &listener.Listener{
Name: listenerName,
Address: util.BuildNetworkAddress(opts.bind, uint32(opts.port.Port), istionetworking.TransportProtocolQUIC),
TrafficDirection: trafficDirection,
FilterChains: filterChains,
UdpListenerConfig: &listener.UdpListenerConfig{
// TODO: Maybe we should add options in MeshConfig to
// configure QUIC options - it should look similar
// to the H2 protocol options.
QuicOptions: &listener.QuicProtocolOptions{},
DownstreamSocketConfig: &core.UdpSocketConfig{},
},
EnableReusePort: proto.BoolTrue,
}
}
accessLogBuilder.setListenerAccessLog(opts.push, opts.proxy, res, opts.class)
return res
}