in pilot/pkg/networking/core/v1alpha3/gateway.go [61:185]
func (configgen *ConfigGeneratorImpl) buildGatewayListeners(builder *ListenerBuilder) *ListenerBuilder {
if builder.node.MergedGateway == nil {
log.Debugf("buildGatewayListeners: no gateways for router %v", builder.node.ID)
return builder
}
mergedGateway := builder.node.MergedGateway
log.Debugf("buildGatewayListeners: gateways after merging: %v", mergedGateway)
actualWildcard, _ := getActualWildcardAndLocalHost(builder.node)
errs := istiomultierror.New()
// Mutable objects keyed by listener name so that we can build listeners at the end.
mutableopts := make(map[string]mutableListenerOpts)
proxyConfig := builder.node.Metadata.ProxyConfigOrDefault(builder.push.Mesh.DefaultConfig)
for _, port := range mergedGateway.ServerPorts {
// Skip ports we cannot bind to. Note that MergeGateways will already translate Service port to
// targetPort, which handles the common case of exposing ports like 80 and 443 but listening on
// higher numbered ports.
if builder.node.IsUnprivileged() && port.Number < 1024 {
log.Warnf("buildGatewayListeners: skipping privileged gateway port %d for node %s as it is an unprivileged pod",
port.Number, builder.node.ID)
continue
}
bind := actualWildcard
if len(port.Bind) > 0 {
bind = port.Bind
}
// NOTE: There is no gating here to check for the value of the QUIC feature flag. However,
// they are created in MergeGatways only when the flag is set. So when it is turned off, the
// MergedQUICTransportServers would be nil so that no listener would be created. It is written this way
// to make testing a little easier.
transportToServers := map[istionetworking.TransportProtocol]map[model.ServerPort]*model.MergedServers{
istionetworking.TransportProtocolTCP: mergedGateway.MergedServers,
istionetworking.TransportProtocolQUIC: mergedGateway.MergedQUICTransportServers,
}
for transport, gwServers := range transportToServers {
if gwServers == nil {
log.Debugf("buildGatewayListeners: no gateway-server for transport %s at port %d", transport.String(), port)
continue
}
// on a given port, we can either have plain text HTTP servers or
// HTTPS/TLS servers with SNI. We cannot have a mix of http and https server on same port.
// We can also have QUIC on a given port along with HTTPS/TLS on a given port. It does not
// cause port-conflict as they use different transport protocols
opts := &buildListenerOpts{
push: builder.push,
proxy: builder.node,
bind: bind,
port: &model.Port{Port: int(port.Number)},
bindToPort: true,
class: istionetworking.ListenerClassGateway,
transport: transport,
}
lname := getListenerName(bind, int(port.Number), transport)
p := protocol.Parse(port.Protocol)
serversForPort := gwServers[port]
if serversForPort == nil {
continue
}
var newFilterChains []istionetworking.FilterChain
switch transport {
case istionetworking.TransportProtocolTCP:
newFilterChains = configgen.buildGatewayTCPBasedFilterChains(builder, p, port, opts, serversForPort, proxyConfig, mergedGateway)
case istionetworking.TransportProtocolQUIC:
// Currently, we just assume that QUIC is HTTP/3 although that does not
// have to be the case (it is just the most common case now, in the future
// we will support more cases)
newFilterChains = configgen.buildGatewayHTTP3FilterChains(builder, serversForPort, mergedGateway, proxyConfig, opts)
}
var mutable *MutableListener
if mopts, exists := mutableopts[lname]; !exists {
mutable = &MutableListener{
MutableObjects: istionetworking.MutableObjects{
// Note: buildListener creates filter chains but does not populate the filters in the chain; that's what
// this is for.
FilterChains: newFilterChains,
},
}
mutableopts[lname] = mutableListenerOpts{mutable: mutable, opts: opts}
} else {
mopts.opts.filterChainOpts = append(mopts.opts.filterChainOpts, opts.filterChainOpts...)
mopts.mutable.MutableObjects.FilterChains = append(mopts.mutable.MutableObjects.FilterChains, newFilterChains...)
mutable = mopts.mutable
}
for cnum := range mutable.FilterChains {
if mutable.FilterChains[cnum].ListenerProtocol == istionetworking.ListenerProtocolTCP {
mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, builder.authzCustomBuilder.BuildTCP()...)
mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, builder.authzBuilder.BuildTCP()...)
}
}
}
}
listeners := make([]*listener.Listener, 0)
for _, ml := range mutableopts {
ml.mutable.Listener = buildListener(*ml.opts, core.TrafficDirection_OUTBOUND)
log.Debugf("buildGatewayListeners: marshaling listener %q with %d filter chains",
ml.mutable.Listener.GetName(), len(ml.mutable.Listener.GetFilterChains()))
// Filters are serialized one time into an opaque struct once we have the complete list.
if err := ml.mutable.build(builder, *ml.opts); err != nil {
errs = multierror.Append(errs, fmt.Errorf("gateway omitting listener %q due to: %v", ml.mutable.Listener.Name, err.Error()))
continue
}
listeners = append(listeners, ml.mutable.Listener)
}
// We'll try to return any listeners we successfully marshaled; if we have none, we'll emit the error we built up
err := errs.ErrorOrNil()
if err != nil {
// we have some listeners to return, but we also have some errors; log them
log.Info(err.Error())
}
if len(mutableopts) == 0 {
log.Warnf("gateway has zero listeners for node %v", builder.node.ID)
return builder
}
builder.gatewayListeners = listeners
return builder
}