Skip to content

Commit

Permalink
Plugin: SNI DNAT gateway (istio#9166)
Browse files Browse the repository at this point in the history
* Create pod level mTLS inclusion list for destination rules

Signed-off-by: Shriram Rajagopalan <[email protected]>

* updates

Signed-off-by: Shriram Rajagopalan <[email protected]>

* random import

Signed-off-by: Shriram Rajagopalan <[email protected]>

* move dnat to plugin

Signed-off-by: Shriram Rajagopalan <[email protected]>

* more fixes

Signed-off-by: Shriram Rajagopalan <[email protected]>

* Add missing pieces and fix some bugs
  • Loading branch information
rshriram authored Oct 15, 2018
1 parent fc4d8ec commit 18bd3e7
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 86 deletions.
6 changes: 3 additions & 3 deletions install/kubernetes/helm/istio/charts/gateways/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,6 @@ istio-mcgateway:
name: tls
envVariables:
# this is needed to tell pilot to not generate
# mTLS clusters for the mcgateway
ISTIO_META_ISTIO_GATEWAY_MODE: multicluster

# mTLS clusters for the mcgateway except for
# istio-telemetry,istio-policy
ISTIO_META_ISTIO_ROUTER_MODE: "sni-dnat"
1 change: 1 addition & 0 deletions pilot/pkg/bootstrap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ var (
plugin.Health,
plugin.Mixer,
plugin.Envoyfilter,
plugin.Snidnat,
}
)

Expand Down
41 changes: 19 additions & 22 deletions pilot/pkg/model/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,6 @@ const (
Router NodeType = "router"
)

// GatewayMode describes the operating mode of the gateway
type GatewayMode string

const (
// StandardGateway is used for gateways that act as routers respecting routing rules.
StandardGateway GatewayMode = "standard"

// MulticlusterGateway mode is used when the gateway acts as a mute tcp proxy
// routing to clusters based on the SNI value
MulticlusterGateway GatewayMode = "multicluster"
)

// IsApplicationNodeType verifies that the NodeType is one of the declared constants in the model
func IsApplicationNodeType(nType NodeType) bool {
switch nType {
Expand All @@ -127,18 +115,27 @@ func (node *Proxy) GetProxyVersion() (string, bool) {
return version, found
}

// GetGatewayMode returns the mode in which the gateway is operating.
func (node *Proxy) GetGatewayMode() GatewayMode {
if modestr, found := node.Metadata["ISTIO_GATEWAY_MODE"]; found {
mode := GatewayMode(modestr)
switch mode {
case MulticlusterGateway:
return MulticlusterGateway
default:
return StandardGateway
// RouterMode decides the behavior of Istio Gateway (normal or sni-dnat)
type RouterMode string

const (
// StandardRouter is the normal gateway mode
StandardRouter RouterMode = "standard"

// SniDnatRouter is used for bridging two networks
SniDnatRouter RouterMode = "sni-dnat"
)

// GetRouterMode returns the operating mode associated with the router.
// Assumes that the proxy is of type Router
func (node *Proxy) GetRouterMode() RouterMode {
if modestr, found := node.Metadata["ISTIO_ROUTER_MODE"]; found {
switch RouterMode(modestr) {
case SniDnatRouter:
return SniDnatRouter
}
}
return StandardGateway
return StandardRouter
}

// ParseMetadata parses the opaque Metadata from an Envoy Node into string key-value pairs.
Expand Down
64 changes: 35 additions & 29 deletions pilot/pkg/networking/core/v1alpha3/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ import (
v2_cluster "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"
"github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
"github.com/gogo/protobuf/types"

networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pilot/pkg/model"
"istio.io/istio/pilot/pkg/networking/plugin"
"istio.io/istio/pilot/pkg/networking/util"
"istio.io/istio/pkg/log"
)
Expand All @@ -48,29 +48,19 @@ const (
func (configgen *ConfigGeneratorImpl) BuildClusters(env *model.Environment, proxy *model.Proxy, push *model.PushContext) ([]*v2.Cluster, error) {
clusters := make([]*v2.Cluster, 0)

recomputeOutboundClusters := true
if configgen.OutboundClusters != nil {
clusters = append(clusters, configgen.OutboundClusters...)
recomputeOutboundClusters = false
}

if recomputeOutboundClusters {
clusters = append(clusters, configgen.buildOutboundClusters(env, push)...)
}
// If the proxy is a SniDnatRouter router, do not use the cached data
if proxy.Type == model.Router && proxy.GetRouterMode() == model.SniDnatRouter {
clusters = append(clusters, configgen.buildOutboundClusters(env, proxy, push)...)
} else {
recomputeOutboundClusters := true
if configgen.OutboundClusters != nil {
clusters = append(clusters, configgen.OutboundClusters...)
recomputeOutboundClusters = false
}

// If its a multi-cluster gateway, do not enable mTLS
// for clusters, as the gateway is already receiving mTLS
// traffic from downstream
if proxy.GetGatewayMode() == model.MulticlusterGateway {
// TODO: cache this output just like the output
sniClusters := make([]*v2.Cluster, len(clusters))
for _, c := range clusters {
// copy each cluster, unset the tls context
newC := *c
newC.TlsContext = nil
sniClusters = append(sniClusters, &newC)
if recomputeOutboundClusters {
clusters = append(clusters, configgen.buildOutboundClusters(env, proxy, push)...)
}
clusters = sniClusters
}

if proxy.Type == model.Sidecar {
Expand Down Expand Up @@ -109,14 +99,23 @@ func normalizeClusters(push *model.PushContext, proxy *model.Proxy, clusters []*
return out
}

func (configgen *ConfigGeneratorImpl) buildOutboundClusters(env *model.Environment, push *model.PushContext) []*v2.Cluster {
func (configgen *ConfigGeneratorImpl) buildOutboundClusters(env *model.Environment, proxy *model.Proxy, push *model.PushContext) []*v2.Cluster {
clusters := make([]*v2.Cluster, 0)

inputParams := &plugin.InputParams{
Env: env,
Push: push,
Node: proxy,
}

for _, service := range push.Services {
config := push.DestinationRule(service.Hostname)
for _, port := range service.Ports {
if port.Protocol == model.ProtocolUDP {
continue
}
inputParams.Service = service
inputParams.Port = port
hosts := buildClusterHosts(env, service, port.Port)

// create default cluster
Expand All @@ -142,15 +141,15 @@ func (configgen *ConfigGeneratorImpl) buildOutboundClusters(env *model.Environme
applyTrafficPolicy(env, subsetCluster, subset.TrafficPolicy, port)
// call plugins
for _, p := range configgen.Plugins {
p.OnOutboundCluster(env, push, service, port, subsetCluster)
p.OnOutboundCluster(inputParams, subsetCluster)
}
clusters = append(clusters, subsetCluster)
}
}

// call plugins for the default cluster
for _, p := range configgen.Plugins {
p.OnOutboundCluster(env, push, service, port, defaultCluster)
p.OnOutboundCluster(inputParams, defaultCluster)
}
}
}
Expand Down Expand Up @@ -192,19 +191,26 @@ func buildClusterHosts(env *model.Environment, service *model.Service, port int)
return hosts
}

func (configgen *ConfigGeneratorImpl) buildInboundClusters(env *model.Environment, proxy *model.Proxy, push *model.PushContext,
instances []*model.ServiceInstance,
managementPorts []*model.Port) []*v2.Cluster {
func (configgen *ConfigGeneratorImpl) buildInboundClusters(env *model.Environment, proxy *model.Proxy,
push *model.PushContext, instances []*model.ServiceInstance, managementPorts []*model.Port) []*v2.Cluster {

clusters := make([]*v2.Cluster, 0)
inputParams := &plugin.InputParams{
Env: env,
Push: push,
Node: proxy,
}

for _, instance := range instances {
// This cluster name is mainly for stats.
clusterName := model.BuildSubsetKey(model.TrafficDirectionInbound, "", instance.Service.Hostname, instance.Endpoint.ServicePort.Port)
address := util.BuildAddress("127.0.0.1", uint32(instance.Endpoint.Port))
localCluster := buildDefaultCluster(env, clusterName, v2.Cluster_STATIC, []*core.Address{&address})
setUpstreamProtocol(localCluster, instance.Endpoint.ServicePort)
// call plugins
inputParams.ServiceInstance = instance
for _, p := range configgen.Plugins {
p.OnInboundCluster(env, proxy, push, instance.Service, instance.Endpoint.ServicePort, localCluster)
p.OnInboundCluster(inputParams, localCluster)
}

// When users specify circuit breakers, they need to be set on the receiver end
Expand Down
2 changes: 1 addition & 1 deletion pilot/pkg/networking/core/v1alpha3/configgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ func NewConfigGenerator(plugins []plugin.Plugin) *ConfigGeneratorImpl {
}

func (configgen *ConfigGeneratorImpl) BuildSharedPushState(env *model.Environment, push *model.PushContext) error {
configgen.OutboundClusters = configgen.buildOutboundClusters(env, push)
configgen.OutboundClusters = configgen.buildOutboundClusters(env, nil, push)
return nil
}
9 changes: 3 additions & 6 deletions pilot/pkg/networking/core/v1alpha3/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
package v1alpha3

import (
"fmt"
"testing"
"time"

xdsapi "github.com/envoyproxy/go-control-plane/envoy/api/v2"

"fmt"

"istio.io/istio/pilot/pkg/model"
"istio.io/istio/pilot/pkg/networking/core/v1alpha3/fakes"
"istio.io/istio/pilot/pkg/networking/plugin"
Expand Down Expand Up @@ -183,12 +182,10 @@ func (p *fakePlugin) OnInboundListener(in *plugin.InputParams, mutable *plugin.M
return nil
}

func (p *fakePlugin) OnOutboundCluster(env *model.Environment, push *model.PushContext, service *model.Service, servicePort *model.Port,
cluster *xdsapi.Cluster) {
func (p *fakePlugin) OnOutboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

func (p *fakePlugin) OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext, service *model.Service, servicePort *model.Port,
cluster *xdsapi.Cluster) {
func (p *fakePlugin) OnInboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

func (p *fakePlugin) OnOutboundRouteConfiguration(in *plugin.InputParams, routeConfiguration *xdsapi.RouteConfiguration) {
Expand Down
6 changes: 2 additions & 4 deletions pilot/pkg/networking/plugin/authn/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,7 @@ func buildFilter(in *plugin.InputParams, mutable *plugin.MutableObjects) error {
}

// OnInboundCluster implements the Plugin interface method.
func (Plugin) OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext, service *model.Service,
servicePort *model.Port, cluster *xdsapi.Cluster) {
func (Plugin) OnInboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

// OnOutboundRouteConfiguration implements the Plugin interface method.
Expand All @@ -374,6 +373,5 @@ func (Plugin) OnInboundRouteConfiguration(in *plugin.InputParams, route *xdsapi.
}

// OnOutboundCluster implements the Plugin interface method.
func (Plugin) OnOutboundCluster(env *model.Environment, push *model.PushContext, service *model.Service,
servicePort *model.Port, cluster *xdsapi.Cluster) {
func (Plugin) OnOutboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}
6 changes: 2 additions & 4 deletions pilot/pkg/networking/plugin/authz/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,7 @@ func (Plugin) OnInboundListener(in *plugin.InputParams, mutable *plugin.MutableO
}

// OnInboundCluster implements the Plugin interface method.
func (Plugin) OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext, service *model.Service,
servicePort *model.Port, cluster *xdsapi.Cluster) {
func (Plugin) OnInboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

// OnOutboundRouteConfiguration implements the Plugin interface method.
Expand All @@ -387,8 +386,7 @@ func (Plugin) OnInboundRouteConfiguration(in *plugin.InputParams, route *xdsapi.
}

// OnOutboundCluster implements the Plugin interface method.
func (Plugin) OnOutboundCluster(env *model.Environment, push *model.PushContext, service *model.Service,
servicePort *model.Port, cluster *xdsapi.Cluster) {
func (Plugin) OnOutboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

// isServiceInList checks if a given service or namespace is found in the RbacConfig target list.
Expand Down
6 changes: 2 additions & 4 deletions pilot/pkg/networking/plugin/envoyfilter/envoyfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,12 @@ func (envoyfilterplugin) OnInboundListener(in *plugin.InputParams, mutable *plug
}

// OnOutboundCluster implements the Plugin interface method.
func (envoyfilterplugin) OnOutboundCluster(env *model.Environment, push *model.PushContext,
service *model.Service, servicePort *model.Port, cluster *xdsapi.Cluster) {
func (envoyfilterplugin) OnOutboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
// do nothing
}

// OnInboundCluster implements the Plugin interface method.
func (envoyfilterplugin) OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext,
service *model.Service, servicePort *model.Port, cluster *xdsapi.Cluster) {
func (envoyfilterplugin) OnInboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
// do nothing
}

Expand Down
6 changes: 2 additions & 4 deletions pilot/pkg/networking/plugin/health/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ func (Plugin) OnInboundListener(in *plugin.InputParams, mutable *plugin.MutableO
}

// OnInboundCluster implements the Plugin interface method.
func (Plugin) OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext, service *model.Service,
servicePort *model.Port, cluster *xdsapi.Cluster) {
func (Plugin) OnInboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

// OnOutboundRouteConfiguration implements the Plugin interface method.
Expand All @@ -133,8 +132,7 @@ func (Plugin) OnInboundRouteConfiguration(in *plugin.InputParams, route *xdsapi.
}

// OnOutboundCluster implements the Plugin interface method.
func (Plugin) OnOutboundCluster(env *model.Environment, push *model.PushContext, service *model.Service,
servicePort *model.Port, cluster *xdsapi.Cluster) {
func (Plugin) OnOutboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
}

// OnInboundFilterChains is called whenever a plugin needs to setup the filter chains, including relevant filter chain configuration.
Expand Down
6 changes: 2 additions & 4 deletions pilot/pkg/networking/plugin/mixer/mixer.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,12 @@ func (mixerplugin) OnInboundListener(in *plugin.InputParams, mutable *plugin.Mut
}

// OnOutboundCluster implements the Plugin interface method.
func (mixerplugin) OnOutboundCluster(env *model.Environment, push *model.PushContext,
service *model.Service, servicePort *model.Port, cluster *xdsapi.Cluster) {
func (mixerplugin) OnOutboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
// do nothing
}

// OnInboundCluster implements the Plugin interface method.
func (mixerplugin) OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext,
service *model.Service, servicePort *model.Port, cluster *xdsapi.Cluster) {
func (mixerplugin) OnInboundCluster(in *plugin.InputParams, cluster *xdsapi.Cluster) {
// do nothing
}

Expand Down
11 changes: 6 additions & 5 deletions pilot/pkg/networking/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const (
Health = "health"
// Mixer is the name of the mixer plugin passed through the command line
Mixer = "mixer"
// Snidnat is the name of the mixer plugin passed through the command line
Snidnat = "snidnat"
)

// ModelProtocolToListenerProtocol converts from a model.Protocol to its corresponding plugin.ListenerProtocol
Expand Down Expand Up @@ -125,14 +127,13 @@ type Plugin interface {
OnInboundListener(in *InputParams, mutable *MutableObjects) error

// OnOutboundCluster is called whenever a new cluster is added to the CDS output.
// This is called once per push cycle, and not for every sidecar/gateway
OnOutboundCluster(env *model.Environment, push *model.PushContext, service *model.Service, servicePort *model.Port,
cluster *xdsapi.Cluster)
// This is called once per push cycle, and not for every sidecar/gateway, except for gateways with non-standard
// operating modes.
OnOutboundCluster(in *InputParams, cluster *xdsapi.Cluster)

// OnInboundCluster is called whenever a new cluster is added to the CDS output.
// Called for each sidecar
OnInboundCluster(env *model.Environment, node *model.Proxy, push *model.PushContext, service *model.Service, servicePort *model.Port,
cluster *xdsapi.Cluster)
OnInboundCluster(in *InputParams, cluster *xdsapi.Cluster)

// OnOutboundRouteConfiguration is called whenever a new set of virtual hosts (a set of virtual hosts with routes) is
// added to RDS in the outbound path.
Expand Down
2 changes: 2 additions & 0 deletions pilot/pkg/networking/plugin/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"istio.io/istio/pilot/pkg/networking/plugin/envoyfilter"
"istio.io/istio/pilot/pkg/networking/plugin/health"
"istio.io/istio/pilot/pkg/networking/plugin/mixer"
"istio.io/istio/pilot/pkg/networking/plugin/snidnat"
)

var availablePlugins = map[string]plugin.Plugin{
Expand All @@ -32,6 +33,7 @@ var availablePlugins = map[string]plugin.Plugin{
plugin.Envoyfilter: envoyfilter.NewPlugin(),
plugin.Health: health.NewPlugin(),
plugin.Mixer: mixer.NewPlugin(),
plugin.Snidnat: snidnat.NewPlugin(),
}

// NewPlugins returns a slice of default Plugins.
Expand Down
Loading

0 comments on commit 18bd3e7

Please sign in to comment.