cli: Add new `consul connect redirect-traffic` command for applying traffic redirection rules when Transparent Proxy is enabled. (#9910)
* Add new consul connect redirect-traffic command for applying traffic redirection rules when Transparent Proxy is enabled. * Add new iptables package for applying traffic redirection rules with iptables.
This commit is contained in:
parent
920ba3db39
commit
ff2e70f4ce
|
@ -0,0 +1,6 @@
|
||||||
|
```release-note:feature
|
||||||
|
cli: Add new `consul connect redirect-traffic` command for applying traffic redirection rules when Transparent Proxy is enabled.
|
||||||
|
```
|
||||||
|
```release-note:feature
|
||||||
|
sdk: Add new `iptables` package for applying traffic redirection rules with iptables.
|
||||||
|
```
|
|
@ -18,6 +18,7 @@ import (
|
||||||
envoy_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
|
envoy_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
|
||||||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||||
envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
|
envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
|
||||||
|
"github.com/hashicorp/consul/sdk/iptables"
|
||||||
|
|
||||||
"github.com/golang/protobuf/jsonpb"
|
"github.com/golang/protobuf/jsonpb"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
@ -32,11 +33,6 @@ import (
|
||||||
"github.com/hashicorp/consul/logging"
|
"github.com/hashicorp/consul/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// TODO (freddy) Make this configurable
|
|
||||||
TProxyOutboundPort = 15001
|
|
||||||
)
|
|
||||||
|
|
||||||
// listenersFromSnapshot returns the xDS API representation of the "listeners" in the snapshot.
|
// listenersFromSnapshot returns the xDS API representation of the "listeners" in the snapshot.
|
||||||
func (s *Server) listenersFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
func (s *Server) listenersFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
||||||
if cfgSnap == nil {
|
if cfgSnap == nil {
|
||||||
|
@ -75,7 +71,8 @@ func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap
|
||||||
var outboundListener *envoy_listener_v3.Listener
|
var outboundListener *envoy_listener_v3.Listener
|
||||||
|
|
||||||
if cfgSnap.Proxy.TransparentProxy {
|
if cfgSnap.Proxy.TransparentProxy {
|
||||||
outboundListener = makeListener(OutboundListenerName, "127.0.0.1", TProxyOutboundPort, envoy_core_v3.TrafficDirection_OUTBOUND)
|
// TODO (freddy) Make DefaultTProxyOutboundPort configurable
|
||||||
|
outboundListener = makeListener(OutboundListenerName, "127.0.0.1", iptables.DefaultTProxyOutboundPort, envoy_core_v3.TrafficDirection_OUTBOUND)
|
||||||
outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0)
|
outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0)
|
||||||
outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{
|
outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
ARG CONSUL_IMAGE_VERSION=latest
|
ARG CONSUL_IMAGE_VERSION=latest
|
||||||
FROM consul:${CONSUL_IMAGE_VERSION}
|
FROM consul:${CONSUL_IMAGE_VERSION}
|
||||||
|
RUN apk update && apk add iptables
|
||||||
COPY consul /bin/consul
|
COPY consul /bin/consul
|
||||||
|
|
|
@ -54,6 +54,7 @@ import (
|
||||||
pipebootstrap "github.com/hashicorp/consul/command/connect/envoy/pipe-bootstrap"
|
pipebootstrap "github.com/hashicorp/consul/command/connect/envoy/pipe-bootstrap"
|
||||||
"github.com/hashicorp/consul/command/connect/expose"
|
"github.com/hashicorp/consul/command/connect/expose"
|
||||||
"github.com/hashicorp/consul/command/connect/proxy"
|
"github.com/hashicorp/consul/command/connect/proxy"
|
||||||
|
"github.com/hashicorp/consul/command/connect/redirecttraffic"
|
||||||
"github.com/hashicorp/consul/command/debug"
|
"github.com/hashicorp/consul/command/debug"
|
||||||
"github.com/hashicorp/consul/command/event"
|
"github.com/hashicorp/consul/command/event"
|
||||||
"github.com/hashicorp/consul/command/exec"
|
"github.com/hashicorp/consul/command/exec"
|
||||||
|
@ -77,8 +78,8 @@ import (
|
||||||
kvput "github.com/hashicorp/consul/command/kv/put"
|
kvput "github.com/hashicorp/consul/command/kv/put"
|
||||||
"github.com/hashicorp/consul/command/leave"
|
"github.com/hashicorp/consul/command/leave"
|
||||||
"github.com/hashicorp/consul/command/lock"
|
"github.com/hashicorp/consul/command/lock"
|
||||||
login "github.com/hashicorp/consul/command/login"
|
"github.com/hashicorp/consul/command/login"
|
||||||
logout "github.com/hashicorp/consul/command/logout"
|
"github.com/hashicorp/consul/command/logout"
|
||||||
"github.com/hashicorp/consul/command/maint"
|
"github.com/hashicorp/consul/command/maint"
|
||||||
"github.com/hashicorp/consul/command/members"
|
"github.com/hashicorp/consul/command/members"
|
||||||
"github.com/hashicorp/consul/command/monitor"
|
"github.com/hashicorp/consul/command/monitor"
|
||||||
|
@ -173,6 +174,7 @@ func init() {
|
||||||
Register("connect envoy", func(ui cli.Ui) (cli.Command, error) { return envoy.New(ui), nil })
|
Register("connect envoy", func(ui cli.Ui) (cli.Command, error) { return envoy.New(ui), nil })
|
||||||
Register("connect envoy pipe-bootstrap", func(ui cli.Ui) (cli.Command, error) { return pipebootstrap.New(ui), nil })
|
Register("connect envoy pipe-bootstrap", func(ui cli.Ui) (cli.Command, error) { return pipebootstrap.New(ui), nil })
|
||||||
Register("connect expose", func(ui cli.Ui) (cli.Command, error) { return expose.New(ui), nil })
|
Register("connect expose", func(ui cli.Ui) (cli.Command, error) { return expose.New(ui), nil })
|
||||||
|
Register("connect redirect-traffic", func(ui cli.Ui) (cli.Command, error) { return redirecttraffic.New(ui), nil })
|
||||||
Register("debug", func(ui cli.Ui) (cli.Command, error) { return debug.New(ui, MakeShutdownCh()), nil })
|
Register("debug", func(ui cli.Ui) (cli.Command, error) { return debug.New(ui, MakeShutdownCh()), nil })
|
||||||
Register("event", func(ui cli.Ui) (cli.Command, error) { return event.New(ui), nil })
|
Register("event", func(ui cli.Ui) (cli.Command, error) { return event.New(ui), nil })
|
||||||
Register("exec", func(ui cli.Ui) (cli.Command, error) { return exec.New(ui, MakeShutdownCh()), nil })
|
Register("exec", func(ui cli.Ui) (cli.Command, error) { return exec.New(ui, MakeShutdownCh()), nil })
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
package redirecttraffic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/command/flags"
|
||||||
|
"github.com/hashicorp/consul/sdk/iptables"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(ui cli.Ui) *cmd {
|
||||||
|
ui = &cli.PrefixedUi{
|
||||||
|
OutputPrefix: "==> ",
|
||||||
|
InfoPrefix: " ",
|
||||||
|
ErrorPrefix: "==> ",
|
||||||
|
Ui: ui,
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &cmd{
|
||||||
|
UI: ui,
|
||||||
|
}
|
||||||
|
c.init()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmd struct {
|
||||||
|
UI cli.Ui
|
||||||
|
flags *flag.FlagSet
|
||||||
|
http *flags.HTTPFlags
|
||||||
|
help string
|
||||||
|
client *api.Client
|
||||||
|
|
||||||
|
// Flags.
|
||||||
|
proxyUID string
|
||||||
|
proxyID string
|
||||||
|
proxyInboundPort int
|
||||||
|
proxyOutboundPort int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) init() {
|
||||||
|
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
|
||||||
|
c.flags.StringVar(&c.proxyUID, "proxy-uid", "", "The user ID of the proxy to exclude from traffic redirection.")
|
||||||
|
c.flags.StringVar(&c.proxyID, "proxy-id", "", "The service ID of the proxy service registered with Consul.")
|
||||||
|
c.flags.IntVar(&c.proxyInboundPort, "proxy-inbound-port", 0, "The inbound port that the proxy is listening on.")
|
||||||
|
c.flags.IntVar(&c.proxyOutboundPort, "proxy-outbound-port", iptables.DefaultTProxyOutboundPort,
|
||||||
|
"The outbound port that the proxy is listening on. When not provided, 15001 is used by default.")
|
||||||
|
|
||||||
|
c.http = &flags.HTTPFlags{}
|
||||||
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
|
flags.Merge(c.flags, c.http.NamespaceFlags())
|
||||||
|
c.help = flags.Usage(help, c.flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) Run(args []string) int {
|
||||||
|
if err := c.flags.Parse(args); err != nil {
|
||||||
|
c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.proxyUID == "" {
|
||||||
|
c.UI.Error("-proxy-uid is required")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.proxyID == "" && c.proxyInboundPort == 0 {
|
||||||
|
c.UI.Error("either -proxy-id or -proxy-inbound-port are required")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.proxyID != "" && (c.proxyInboundPort != 0 || c.proxyOutboundPort != iptables.DefaultTProxyOutboundPort) {
|
||||||
|
c.UI.Error("-proxy-inbound-port or -proxy-outbound-port cannot be provided together with -proxy-id. " +
|
||||||
|
"Proxy's inbound and outbound ports are retrieved from the proxy's configuration instead.")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := c.generateConfigFromFlags()
|
||||||
|
if err != nil {
|
||||||
|
c.UI.Error(fmt.Sprintf("Failed to create configuration to apply traffic redirection rules: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iptables.Setup(cfg)
|
||||||
|
if err != nil {
|
||||||
|
c.UI.Error(fmt.Sprintf("Error setting up traffic redirection rules: %s", err.Error()))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
c.UI.Info("Successfully applied traffic redirection rules")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) Synopsis() string {
|
||||||
|
return synopsis
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) Help() string {
|
||||||
|
return c.help
|
||||||
|
}
|
||||||
|
|
||||||
|
// trafficRedirectProxyConfig is a snippet of xds/config.go
|
||||||
|
// with only the configuration values that we need to parse from Proxy.Config
|
||||||
|
// to apply traffic redirection rules.
|
||||||
|
type trafficRedirectProxyConfig struct {
|
||||||
|
BindPort int `mapstructure:"bind_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateConfigFromFlags generates iptables.Config based on command flags.
|
||||||
|
func (c *cmd) generateConfigFromFlags() (iptables.Config, error) {
|
||||||
|
cfg := iptables.Config{ProxyUserID: c.proxyUID}
|
||||||
|
|
||||||
|
// When proxyID is provided, we set up cfg with values
|
||||||
|
// from proxy's service registration in Consul.
|
||||||
|
if c.proxyID != "" {
|
||||||
|
var err error
|
||||||
|
if c.client == nil {
|
||||||
|
c.client, err = c.http.APIClient()
|
||||||
|
if err != nil {
|
||||||
|
return iptables.Config{}, fmt.Errorf("error creating Consul API client: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc, _, err := c.client.Agent().Service(c.proxyID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return iptables.Config{}, fmt.Errorf("failed to fetch proxy service from Consul Agent: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if svc.Proxy == nil {
|
||||||
|
return iptables.Config{}, fmt.Errorf("service %s is not a proxy service", c.proxyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.ProxyInboundPort = svc.Port
|
||||||
|
var trCfg trafficRedirectProxyConfig
|
||||||
|
if err := mapstructure.WeakDecode(svc.Proxy.Config, &trCfg); err != nil {
|
||||||
|
return iptables.Config{}, fmt.Errorf("failed parsing Proxy.Config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if trCfg.BindPort != 0 {
|
||||||
|
cfg.ProxyInboundPort = trCfg.BindPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: Change once it's configurable
|
||||||
|
cfg.ProxyOutboundPort = iptables.DefaultTProxyOutboundPort
|
||||||
|
} else {
|
||||||
|
cfg.ProxyInboundPort = c.proxyInboundPort
|
||||||
|
cfg.ProxyOutboundPort = c.proxyOutboundPort
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const synopsis = "Applies iptables rules for traffic redirection"
|
||||||
|
const help = `
|
||||||
|
Usage: consul connect redirect-traffic [options]
|
||||||
|
|
||||||
|
Applies iptables rules for inbound and outbound traffic redirection.
|
||||||
|
|
||||||
|
Requires that the iptables command line utility is installed.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ consul connect redirect-traffic -proxy-uid 1234 -proxy-id web
|
||||||
|
|
||||||
|
$ consul connect redirect-traffic -proxy-uid 1234 -proxy-inbound-port 20000
|
||||||
|
`
|
|
@ -0,0 +1,280 @@
|
||||||
|
package redirecttraffic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/sdk/iptables"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRun_FlagValidation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
expError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"-proxy-uid is missing",
|
||||||
|
nil,
|
||||||
|
"-proxy-uid is required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-proxy-id and -proxy-inbound-port are missing",
|
||||||
|
[]string{"-proxy-uid=1234"},
|
||||||
|
"either -proxy-id or -proxy-inbound-port are required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-proxy-id and -proxy-inbound-port are provided",
|
||||||
|
[]string{"-proxy-uid=1234", "-proxy-id=test", "-proxy-inbound-port=15000"},
|
||||||
|
"-proxy-inbound-port or -proxy-outbound-port cannot be provided together with -proxy-id.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-proxy-id and -proxy-outbound-port are provided",
|
||||||
|
[]string{"-proxy-uid=1234", "-proxy-id=test", "-proxy-outbound-port=15000"},
|
||||||
|
"-proxy-inbound-port or -proxy-outbound-port cannot be provided together with -proxy-id.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-proxy-id, -proxy-inbound-port and non-default -proxy-outbound-port are provided",
|
||||||
|
[]string{"-proxy-uid=1234", "-proxy-id=test", "-proxy-inbound-port=15000", "-proxy-outbound-port=15001"},
|
||||||
|
"-proxy-inbound-port or -proxy-outbound-port cannot be provided together with -proxy-id.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
ui := cli.NewMockUi()
|
||||||
|
cmd := New(ui)
|
||||||
|
|
||||||
|
code := cmd.Run(c.args)
|
||||||
|
require.Equal(t, code, 1)
|
||||||
|
require.Contains(t, ui.ErrorWriter.String(), c.expError)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateConfigFromFlags(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
command func() cmd
|
||||||
|
proxyService *api.AgentServiceRegistration
|
||||||
|
expCfg iptables.Config
|
||||||
|
expError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"proxyID with service port provided",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyID = "test-proxy-id"
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
&api.AgentServiceRegistration{
|
||||||
|
Kind: api.ServiceKindConnectProxy,
|
||||||
|
ID: "test-proxy-id",
|
||||||
|
Name: "test-proxy",
|
||||||
|
Port: 20000,
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
||||||
|
DestinationServiceName: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
iptables.Config{
|
||||||
|
ProxyUserID: "1234",
|
||||||
|
ProxyInboundPort: 20000,
|
||||||
|
ProxyOutboundPort: iptables.DefaultTProxyOutboundPort,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxyID with bind_port(int) provided",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyID = "test-proxy-id"
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
&api.AgentServiceRegistration{
|
||||||
|
Kind: api.ServiceKindConnectProxy,
|
||||||
|
ID: "test-proxy-id",
|
||||||
|
Name: "test-proxy",
|
||||||
|
Port: 20000,
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
||||||
|
DestinationServiceName: "foo",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"bind_port": 21000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
iptables.Config{
|
||||||
|
ProxyUserID: "1234",
|
||||||
|
ProxyInboundPort: 21000,
|
||||||
|
ProxyOutboundPort: iptables.DefaultTProxyOutboundPort,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxyID with bind_port(string) provided",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyID = "test-proxy-id"
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
&api.AgentServiceRegistration{
|
||||||
|
Kind: api.ServiceKindConnectProxy,
|
||||||
|
ID: "test-proxy-id",
|
||||||
|
Name: "test-proxy",
|
||||||
|
Port: 20000,
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
||||||
|
DestinationServiceName: "foo",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"bind_port": "21000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
iptables.Config{
|
||||||
|
ProxyUserID: "1234",
|
||||||
|
ProxyInboundPort: 21000,
|
||||||
|
ProxyOutboundPort: iptables.DefaultTProxyOutboundPort,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxyID with bind_port(invalid type) provided",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyID = "test-proxy-id"
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
&api.AgentServiceRegistration{
|
||||||
|
Kind: api.ServiceKindConnectProxy,
|
||||||
|
ID: "test-proxy-id",
|
||||||
|
Name: "test-proxy",
|
||||||
|
Port: 20000,
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
||||||
|
DestinationServiceName: "foo",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"bind_port": "invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
iptables.Config{},
|
||||||
|
"failed parsing Proxy.Config: 1 error(s) decoding:\n\n* cannot parse 'bind_port' as int:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxyID provided, but Consul is not reachable",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyID = "test-proxy-id"
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
iptables.Config{},
|
||||||
|
"failed to fetch proxy service from Consul Agent: ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxyID of a non-proxy service",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyID = "test-proxy-id"
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
&api.AgentServiceRegistration{
|
||||||
|
ID: "test-proxy-id",
|
||||||
|
Name: "test-proxy",
|
||||||
|
Port: 20000,
|
||||||
|
Address: "1.1.1.1",
|
||||||
|
},
|
||||||
|
iptables.Config{},
|
||||||
|
"service test-proxy-id is not a proxy service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"only proxy inbound port is provided",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyInboundPort = 15000
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
iptables.Config{
|
||||||
|
ProxyUserID: "1234",
|
||||||
|
ProxyInboundPort: 15000,
|
||||||
|
ProxyOutboundPort: iptables.DefaultTProxyOutboundPort,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxy inbound and outbound ports are provided",
|
||||||
|
func() cmd {
|
||||||
|
var c cmd
|
||||||
|
c.init()
|
||||||
|
c.proxyUID = "1234"
|
||||||
|
c.proxyInboundPort = 15000
|
||||||
|
c.proxyOutboundPort = 16000
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
iptables.Config{
|
||||||
|
ProxyUserID: "1234",
|
||||||
|
ProxyInboundPort: 15000,
|
||||||
|
ProxyOutboundPort: 16000,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
cmd := c.command()
|
||||||
|
if c.proxyService != nil {
|
||||||
|
testServer, err := testutil.NewTestServerConfigT(t, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer testServer.Stop()
|
||||||
|
|
||||||
|
client, err := api.NewClient(&api.Config{Address: testServer.HTTPAddr})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = client.Agent().ServiceRegister(c.proxyService)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cmd.client = client
|
||||||
|
} else {
|
||||||
|
client, err := api.NewClient(&api.Config{Address: "not-reachable"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
cmd.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := cmd.generateConfigFromFlags()
|
||||||
|
|
||||||
|
if c.expError == "" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, c.expCfg, cfg)
|
||||||
|
} else {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), c.expError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ require (
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0
|
github.com/mitchellh/go-testing-interface v1.0.0
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/stretchr/testify v1.4.0 // indirect
|
github.com/stretchr/testify v1.4.0
|
||||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
|
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package iptables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Chain to intercept inbound traffic
|
||||||
|
ProxyInboundChain = "CONSUL_PROXY_INBOUND"
|
||||||
|
|
||||||
|
// Chain to redirect inbound traffic to the proxy
|
||||||
|
ProxyInboundRedirectChain = "CONSUL_PROXY_IN_REDIRECT"
|
||||||
|
|
||||||
|
// Chain to intercept outbound traffic
|
||||||
|
ProxyOutputChain = "CONSUL_PROXY_OUTPUT"
|
||||||
|
|
||||||
|
// Chain to redirect outbound traffic to the proxy
|
||||||
|
ProxyOutputRedirectChain = "CONSUL_PROXY_REDIRECT"
|
||||||
|
|
||||||
|
DefaultTProxyOutboundPort = 15001
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to configure which traffic interception and redirection
|
||||||
|
// rules should be applied with the iptables commands.
|
||||||
|
type Config struct {
|
||||||
|
// ProxyUserID is the user ID of the proxy process.
|
||||||
|
ProxyUserID string
|
||||||
|
|
||||||
|
// ProxyInboundPort is the port of the proxy's inbound listener.
|
||||||
|
ProxyInboundPort int
|
||||||
|
|
||||||
|
// ProxyInboundPort is the port of the proxy's outbound listener.
|
||||||
|
ProxyOutboundPort int
|
||||||
|
|
||||||
|
// IptablesProvider is the Provider that will apply iptables rules.
|
||||||
|
IptablesProvider Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider is an interface for executing iptables rules.
|
||||||
|
type Provider interface {
|
||||||
|
// AddRule adds a rule without executing it.
|
||||||
|
AddRule(name string, args ...string)
|
||||||
|
// ApplyRules executes rules that have been added via AddRule.
|
||||||
|
// This operation is currently not atomic, and if there's an error applying rules,
|
||||||
|
// you may be left in a state where partial rules were applied.
|
||||||
|
ApplyRules() error
|
||||||
|
// Rules returns the list of rules that have been added but not applied yet.
|
||||||
|
Rules() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup will set up iptables interception and redirection rules
|
||||||
|
// based on the configuration provided in cfg.
|
||||||
|
// This implementation was inspired by
|
||||||
|
// https://github.com/openservicemesh/osm/blob/650a1a1dcf081ae90825f3b5dba6f30a0e532725/pkg/injector/iptables.go
|
||||||
|
func Setup(cfg Config) error {
|
||||||
|
if cfg.IptablesProvider == nil {
|
||||||
|
cfg.IptablesProvider = &iptablesExecutor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := validateConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default outbound port if it's not already set.
|
||||||
|
if cfg.ProxyOutboundPort == 0 {
|
||||||
|
cfg.ProxyOutboundPort = DefaultTProxyOutboundPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create chains we will use for redirection.
|
||||||
|
chains := []string{ProxyInboundChain, ProxyInboundRedirectChain, ProxyOutputChain, ProxyOutputRedirectChain}
|
||||||
|
for _, chain := range chains {
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-N", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure outbound rules.
|
||||||
|
{
|
||||||
|
// Redirects outbound TCP traffic hitting PROXY_REDIRECT chain to Envoy's outbound listener port.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputRedirectChain, "-p", "tcp", "-j", "REDIRECT", "--to-port", strconv.Itoa(cfg.ProxyOutboundPort))
|
||||||
|
|
||||||
|
// For outbound TCP traffic jump from OUTPUT chain to PROXY_OUTPUT chain.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "tcp", "-j", ProxyOutputChain)
|
||||||
|
|
||||||
|
// Don't redirect proxy traffic back to itself, return it to the next chain for processing.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputChain, "-m", "owner", "--uid-owner", cfg.ProxyUserID, "-j", "RETURN")
|
||||||
|
|
||||||
|
// Skip localhost traffic, doesn't need to be routed via the proxy.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputChain, "-d", "127.0.0.1/32", "-j", "RETURN")
|
||||||
|
|
||||||
|
// Redirect remaining outbound traffic to Envoy.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputChain, "-j", ProxyOutputRedirectChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure inbound rules.
|
||||||
|
{
|
||||||
|
// Redirects inbound TCP traffic hitting the PROXY_IN_REDIRECT chain to Envoy's inbound listener port.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyInboundRedirectChain, "-p", "tcp", "-j", "REDIRECT", "--to-port", strconv.Itoa(cfg.ProxyInboundPort))
|
||||||
|
|
||||||
|
// For inbound traffic jump from PREROUTING chain to PROXY_INBOUND chain.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "PREROUTING", "-p", "tcp", "-j", ProxyInboundChain)
|
||||||
|
|
||||||
|
// Redirect remaining inbound traffic to Envoy.
|
||||||
|
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyInboundChain, "-p", "tcp", "-j", ProxyInboundRedirectChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.IptablesProvider.ApplyRules()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateConfig(cfg Config) error {
|
||||||
|
if cfg.ProxyUserID == "" {
|
||||||
|
return errors.New("ProxyUserID is required to set up traffic redirection")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ProxyInboundPort == 0 {
|
||||||
|
return errors.New("ProxyInboundPort is required to set up traffic redirection")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package iptables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// iptablesExecutor implements IptablesProvider using exec.Cmd.
|
||||||
|
type iptablesExecutor struct {
|
||||||
|
commands []*exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iptablesExecutor) AddRule(name string, args ...string) {
|
||||||
|
i.commands = append(i.commands, exec.Command(name, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iptablesExecutor) ApplyRules() error {
|
||||||
|
_, err := exec.LookPath("iptables")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range i.commands {
|
||||||
|
var cmdOutput bytes.Buffer
|
||||||
|
cmd.Stdout = &cmdOutput
|
||||||
|
cmd.Stderr = &cmdOutput
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to run command: %s, err: %v, output: %s", cmd.String(), err, string(cmdOutput.Bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iptablesExecutor) Rules() []string {
|
||||||
|
var rules []string
|
||||||
|
for _, cmd := range i.commands {
|
||||||
|
rules = append(rules, cmd.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package iptables
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// iptablesExecutor implements IptablesProvider and errors out on any non-linux OS.
|
||||||
|
type iptablesExecutor struct{}
|
||||||
|
|
||||||
|
func (i *iptablesExecutor) AddRule(_ string, _ ...string) {}
|
||||||
|
|
||||||
|
func (i *iptablesExecutor) ApplyRules() error {
|
||||||
|
return errors.New("applying traffic redirection rules with 'iptables' is not supported on this operating system; only linux OS is supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iptablesExecutor) Rules() []string {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package iptables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetup(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
cfg Config
|
||||||
|
expectedRules []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"no proxy outbound port provided",
|
||||||
|
Config{
|
||||||
|
ProxyUserID: "123",
|
||||||
|
ProxyInboundPort: 20000,
|
||||||
|
IptablesProvider: &fakeIptablesProvider{},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_INBOUND",
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_IN_REDIRECT",
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_OUTPUT",
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_REDIRECT",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_REDIRECT -p tcp -j REDIRECT --to-port 15001",
|
||||||
|
"iptables -t nat -A OUTPUT -p tcp -j CONSUL_PROXY_OUTPUT",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_OUTPUT -m owner --uid-owner 123 -j RETURN",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_OUTPUT -d 127.0.0.1/32 -j RETURN",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_OUTPUT -j CONSUL_PROXY_REDIRECT",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_IN_REDIRECT -p tcp -j REDIRECT --to-port 20000",
|
||||||
|
"iptables -t nat -A PREROUTING -p tcp -j CONSUL_PROXY_INBOUND",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_INBOUND -p tcp -j CONSUL_PROXY_IN_REDIRECT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"proxy outbound port is provided",
|
||||||
|
Config{
|
||||||
|
ProxyUserID: "123",
|
||||||
|
ProxyInboundPort: 20000,
|
||||||
|
ProxyOutboundPort: 21000,
|
||||||
|
IptablesProvider: &fakeIptablesProvider{},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_INBOUND",
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_IN_REDIRECT",
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_OUTPUT",
|
||||||
|
"iptables -t nat -N CONSUL_PROXY_REDIRECT",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_REDIRECT -p tcp -j REDIRECT --to-port 21000",
|
||||||
|
"iptables -t nat -A OUTPUT -p tcp -j CONSUL_PROXY_OUTPUT",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_OUTPUT -m owner --uid-owner 123 -j RETURN",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_OUTPUT -d 127.0.0.1/32 -j RETURN",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_OUTPUT -j CONSUL_PROXY_REDIRECT",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_IN_REDIRECT -p tcp -j REDIRECT --to-port 20000",
|
||||||
|
"iptables -t nat -A PREROUTING -p tcp -j CONSUL_PROXY_INBOUND",
|
||||||
|
"iptables -t nat -A CONSUL_PROXY_INBOUND -p tcp -j CONSUL_PROXY_IN_REDIRECT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
err := Setup(c.cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, c.expectedRules, c.cfg.IptablesProvider.Rules())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetup_errors(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
cfg Config
|
||||||
|
expErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"no proxy UID",
|
||||||
|
Config{
|
||||||
|
IptablesProvider: &iptablesExecutor{},
|
||||||
|
},
|
||||||
|
"ProxyUserID is required to set up traffic redirection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no proxy inbound port",
|
||||||
|
Config{
|
||||||
|
ProxyUserID: "123",
|
||||||
|
ProxyOutboundPort: 21000,
|
||||||
|
IptablesProvider: &iptablesExecutor{},
|
||||||
|
},
|
||||||
|
"ProxyInboundPort is required to set up traffic redirection",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
err := Setup(c.cfg)
|
||||||
|
require.EqualError(t, err, c.expErr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeIptablesProvider struct {
|
||||||
|
rules []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeIptablesProvider) AddRule(name string, args ...string) {
|
||||||
|
var rule []string
|
||||||
|
rule = append(rule, name)
|
||||||
|
rule = append(rule, args...)
|
||||||
|
|
||||||
|
f.rules = append(f.rules, strings.Join(rule, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeIptablesProvider) ApplyRules() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeIptablesProvider) Rules() []string {
|
||||||
|
return f.rules
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
---
|
---
|
||||||
layout: commands
|
layout: commands
|
||||||
page_title: 'Commands: Connect Proxy'
|
page_title: 'Commands: Connect Envoy'
|
||||||
description: The connect proxy subcommand is used to run the Envoy proxy for Connect.
|
sidebar_title: envoy
|
||||||
|
description: The connect envoy subcommand is used to generate a bootstrap configuration for Envoy.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Consul Connect Envoy
|
# Consul Connect Envoy
|
||||||
|
@ -90,7 +91,7 @@ proxy configuration needed.
|
||||||
|
|
||||||
- `-prometheus-scrape-path` - Sets the path where Envoy will expose metrics on the
|
- `-prometheus-scrape-path` - Sets the path where Envoy will expose metrics on the
|
||||||
`envoy_prometheus_bind_addr` listener. Default is `/metrics`. For example, if `envoy_prometheus_bind_addr`
|
`envoy_prometheus_bind_addr` listener. Default is `/metrics`. For example, if `envoy_prometheus_bind_addr`
|
||||||
is `0.0.0.0:20200`, and this flag is set to `/scrape-metrics`, prometheus metrics would
|
is `0.0.0.0:20200`, and this flag is set to `/scrape-metrics`, prometheus metrics would
|
||||||
be scrapable at `0.0.0.0:20200/scrape-metrics`.
|
be scrapable at `0.0.0.0:20200/scrape-metrics`.
|
||||||
Only applicable when `envoy_prometheus_bind_addr` is set in proxy config.
|
Only applicable when `envoy_prometheus_bind_addr` is set in proxy config.
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,11 @@ Usage: consul connect <subcommand> [options] [args]
|
||||||
For more examples, ask for subcommand help or view the documentation.
|
For more examples, ask for subcommand help or view the documentation.
|
||||||
|
|
||||||
Subcommands:
|
Subcommands:
|
||||||
ca Interact with the Consul Connect Certificate Authority (CA)
|
ca Interact with the Consul Connect Certificate Authority (CA)
|
||||||
envoy Runs or Configures Envoy as a Connect proxy
|
envoy Runs or Configures Envoy as a Connect proxy
|
||||||
expose Expose a Connect-enabled service through an Ingress gateway
|
expose Expose a Connect-enabled service through an Ingress gateway
|
||||||
proxy Runs a Consul Connect proxy
|
proxy Runs a Consul Connect proxy
|
||||||
|
redirect-traffic Applies iptables rules for traffic redirection
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, examples, and usage about a subcommand, click on the name
|
For more information, examples, and usage about a subcommand, click on the name
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
layout: commands
|
||||||
|
page_title: 'Commands: Connect Redirect Traffic'
|
||||||
|
sidebar_title: redirect-traffic
|
||||||
|
description: >
|
||||||
|
The connect redirect-traffic subcommand is used to apply traffic redirection rules
|
||||||
|
when using Connect in Transparent Proxy mode.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Consul Connect Redirect Traffic
|
||||||
|
|
||||||
|
Command: `consul connect redirect-traffic`
|
||||||
|
|
||||||
|
The connect redirect-traffic command is used to apply traffic redirection rules to enforce
|
||||||
|
all traffic to go through the [Envoy proxy](https://envoyproxy.io) when using [Consul
|
||||||
|
Service Mesh](/docs/connect/) in the Transparent Proxy mode.
|
||||||
|
|
||||||
|
This command requires `iptables` command line utility to be installed,
|
||||||
|
and as a result, this command can currently only run on linux.
|
||||||
|
The user running the command needs to have `NET_ADMIN` capability.
|
||||||
|
|
||||||
|
By default, this command will apply rules to intercept and redirect all inbound and outbound
|
||||||
|
TCP traffic to the Envoy's inbound and outbound ports accordingly.
|
||||||
|
|
||||||
|
When `proxy-id` is specified, additional exclusion rules will be applied based on proxy's
|
||||||
|
configuration stored in the local Consul agent. This includes redirecting to the proxy's
|
||||||
|
inbound and outbound ports specified in the service registration.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Usage: `consul connect redirect-traffic [options]`
|
||||||
|
|
||||||
|
#### API Options
|
||||||
|
|
||||||
|
@include 'http_api_options_client.mdx'
|
||||||
|
|
||||||
|
#### Options for Traffic Redirection Rules
|
||||||
|
|
||||||
|
- `-proxy-id` - The [proxy service](/docs/connect/registration/service-registration) ID.
|
||||||
|
This service ID must already be registered with the local agent.
|
||||||
|
|
||||||
|
- `-proxy-inbound-port` - The inbound port that the proxy is listening on.
|
||||||
|
|
||||||
|
- `-proxy-outbound-port` - The outbound port that the proxy is listening on. When not provided, 15001 is used by default.
|
||||||
|
|
||||||
|
- `-proxy-uid` - The user ID of the proxy to exclude from traffic redirection.
|
||||||
|
|
||||||
|
#### Enterprise Options
|
||||||
|
|
||||||
|
@include 'http_api_namespace_options.mdx'
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Basic Rules
|
||||||
|
|
||||||
|
The default traffic redirection rules can be applied with:
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul connect redirect-traffic \
|
||||||
|
-proxy-uid 1234 \
|
||||||
|
-proxy-inbound-port 20000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Registered Proxy Configuration
|
||||||
|
|
||||||
|
To automatically apply rules based on proxy's service registration, use the following command:
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul connect redirect-traffic -proxy-uid 1234 -proxy-id web
|
||||||
|
```
|
||||||
|
|
||||||
|
This command assumes that the proxy service is registered with the local agent
|
||||||
|
and that the local agent is reachable.
|
Loading…
Reference in New Issue