diff --git a/.changelog/15050.txt b/.changelog/15050.txt new file mode 100644 index 000000000..2738b7dc0 --- /dev/null +++ b/.changelog/15050.txt @@ -0,0 +1,6 @@ +```release-note:feature +cli: Add `-consul-dns-port` flag to the `consul connect redirect-traffic` command to allow forwarding DNS traffic to a specific Consul DNS port. +``` +```release-note:feature +sdk: Configure `iptables` to forward DNS traffic to a specific DNS port. +``` diff --git a/command/connect/redirecttraffic/redirect_traffic.go b/command/connect/redirecttraffic/redirect_traffic.go index 277c12f69..8b979251f 100644 --- a/command/connect/redirecttraffic/redirect_traffic.go +++ b/command/connect/redirecttraffic/redirect_traffic.go @@ -38,6 +38,7 @@ type cmd struct { // Flags. nodeName string consulDNSIP string + consulDNSPort int proxyUID string proxyID string proxyInboundPort int @@ -55,6 +56,7 @@ func (c *cmd) init() { c.flags.StringVar(&c.nodeName, "node-name", "", "The node name where the proxy service is registered. It requires proxy-id to be specified. This is needed if running in an environment without client agents.") c.flags.StringVar(&c.consulDNSIP, "consul-dns-ip", "", "IP used to reach Consul DNS. If provided, DNS queries will be redirected to Consul.") + c.flags.IntVar(&c.consulDNSPort, "consul-dns-port", 0, "Port used to reach Consul DNS. If provided, DNS queries will be redirected to Consul.") 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.") @@ -136,6 +138,7 @@ type trafficRedirectProxyConfig struct { func (c *cmd) generateConfigFromFlags() (iptables.Config, error) { cfg := iptables.Config{ ConsulDNSIP: c.consulDNSIP, + ConsulDNSPort: c.consulDNSPort, ProxyUserID: c.proxyUID, ProxyInboundPort: c.proxyInboundPort, ProxyOutboundPort: c.proxyOutboundPort, diff --git a/command/connect/redirecttraffic/redirect_traffic_test.go b/command/connect/redirecttraffic/redirect_traffic_test.go index d31b0cffe..f592d4c73 100644 --- a/command/connect/redirecttraffic/redirect_traffic_test.go +++ b/command/connect/redirecttraffic/redirect_traffic_test.go @@ -128,13 +128,14 @@ func TestGenerateConfigFromFlags(t *testing.T) { }, }, { - name: "proxyID with Consul DNS IP provided", + name: "proxyID with Consul DNS IP and port provided", command: func() cmd { var c cmd c.init() c.proxyUID = "1234" c.proxyID = "test-proxy-id" c.consulDNSIP = "10.0.34.16" + c.consulDNSPort = 8600 return c }, consulServices: []api.AgentServiceRegistration{ @@ -151,6 +152,7 @@ func TestGenerateConfigFromFlags(t *testing.T) { }, expCfg: iptables.Config{ ConsulDNSIP: "10.0.34.16", + ConsulDNSPort: 8600, ProxyUserID: "1234", ProxyInboundPort: 20000, ProxyOutboundPort: iptables.DefaultTProxyOutboundPort, diff --git a/sdk/iptables/iptables.go b/sdk/iptables/iptables.go index 83b5c03a1..653fd02b9 100644 --- a/sdk/iptables/iptables.go +++ b/sdk/iptables/iptables.go @@ -2,6 +2,7 @@ package iptables import ( "errors" + "fmt" "strconv" ) @@ -30,6 +31,9 @@ type Config struct { // ConsulDNSIP is the IP for Consul DNS to direct DNS queries to. ConsulDNSIP string + // ConsulDNSPort is the port for Consul DNS to direct DNS queries to. + ConsulDNSPort int + // ProxyUserID is the user ID of the proxy process. ProxyUserID string @@ -107,7 +111,7 @@ func Setup(cfg Config) error { cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputRedirectChain, "-p", "tcp", "-j", "REDIRECT", "--to-port", strconv.Itoa(cfg.ProxyOutboundPort)) // The DNS rules are applied before the rules that directs all TCP traffic, so that the traffic going to port 53 goes through this rule first. - if cfg.ConsulDNSIP != "" { + if cfg.ConsulDNSIP != "" && cfg.ConsulDNSPort == 0 { // Traffic in the DNSChain is directed to the Consul DNS Service IP. cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "udp", "--dport", "53", "-j", "DNAT", "--to-destination", cfg.ConsulDNSIP) cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "tcp", "--dport", "53", "-j", "DNAT", "--to-destination", cfg.ConsulDNSIP) @@ -115,6 +119,19 @@ func Setup(cfg Config) error { // For outbound TCP and UDP traffic going to port 53 (DNS), jump to the DNSChain. cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "udp", "--dport", "53", "-j", DNSChain) cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "tcp", "--dport", "53", "-j", DNSChain) + } else if cfg.ConsulDNSPort != 0 { + consulDNSIP := "127.0.0.1" + if cfg.ConsulDNSIP != "" { + consulDNSIP = cfg.ConsulDNSIP + } + consulDNSHostPort := fmt.Sprintf("%s:%d", consulDNSIP, cfg.ConsulDNSPort) + // Traffic in the DNSChain is directed to the Consul DNS Service IP. + cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "udp", "-d", consulDNSIP, "--dport", "53", "-j", "DNAT", "--to-destination", consulDNSHostPort) + cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "tcp", "-d", consulDNSIP, "--dport", "53", "-j", "DNAT", "--to-destination", consulDNSHostPort) + + // For outbound TCP and UDP traffic going to port 53 (DNS), jump to the DNSChain. Only redirect traffic that's going to consul's DNS IP. + cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "udp", "-d", consulDNSIP, "--dport", "53", "-j", DNSChain) + cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "tcp", "-d", consulDNSIP, "--dport", "53", "-j", DNSChain) } // For outbound TCP traffic jump from OUTPUT chain to PROXY_OUTPUT chain. diff --git a/sdk/iptables/iptables_test.go b/sdk/iptables/iptables_test.go index 1de3122e5..1c79fdf05 100644 --- a/sdk/iptables/iptables_test.go +++ b/sdk/iptables/iptables_test.go @@ -64,6 +64,63 @@ func TestSetup(t *testing.T) { "iptables -t nat -A CONSUL_PROXY_INBOUND -p tcp -j CONSUL_PROXY_IN_REDIRECT", }, }, + { + "Consul DNS port provided", + Config{ + ProxyUserID: "123", + ProxyInboundPort: 20000, + ConsulDNSPort: 8600, + 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 -N CONSUL_DNS_REDIRECT", + "iptables -t nat -A CONSUL_PROXY_REDIRECT -p tcp -j REDIRECT --to-port 15001", + "iptables -t nat -A CONSUL_DNS_REDIRECT -p udp -d 127.0.0.1 --dport 53 -j DNAT --to-destination 127.0.0.1:8600", + "iptables -t nat -A CONSUL_DNS_REDIRECT -p tcp -d 127.0.0.1 --dport 53 -j DNAT --to-destination 127.0.0.1:8600", + "iptables -t nat -A OUTPUT -p udp -d 127.0.0.1 --dport 53 -j CONSUL_DNS_REDIRECT", + "iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 53 -j CONSUL_DNS_REDIRECT", + "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", + }, + }, + { + "Consul DNS IP and port provided", + Config{ + ProxyUserID: "123", + ProxyInboundPort: 20000, + ConsulDNSIP: "10.0.34.16", + ConsulDNSPort: 8600, + 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 -N CONSUL_DNS_REDIRECT", + "iptables -t nat -A CONSUL_PROXY_REDIRECT -p tcp -j REDIRECT --to-port 15001", + "iptables -t nat -A CONSUL_DNS_REDIRECT -p udp -d 10.0.34.16 --dport 53 -j DNAT --to-destination 10.0.34.16:8600", + "iptables -t nat -A CONSUL_DNS_REDIRECT -p tcp -d 10.0.34.16 --dport 53 -j DNAT --to-destination 10.0.34.16:8600", + "iptables -t nat -A OUTPUT -p udp -d 10.0.34.16 --dport 53 -j CONSUL_DNS_REDIRECT", + "iptables -t nat -A OUTPUT -p tcp -d 10.0.34.16 --dport 53 -j CONSUL_DNS_REDIRECT", + "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{ diff --git a/website/content/commands/connect/redirect-traffic.mdx b/website/content/commands/connect/redirect-traffic.mdx index 688429bfb..aa87e69ef 100644 --- a/website/content/commands/connect/redirect-traffic.mdx +++ b/website/content/commands/connect/redirect-traffic.mdx @@ -36,6 +36,8 @@ Usage: `consul connect redirect-traffic [options]` - `-consul-dns-ip` - The IP address of the Consul DNS resolver. If provided, DNS queries will be redirected to the provided IP address for name resolution. +- `-consul-dns-port` - The port of the Consul DNS resolver. If provided, DNS queries will be redirected to the provided IP address for name resolution. + - `-proxy-id` - The [proxy service](/docs/connect/registration/service-registration) ID. This service ID must already be registered with the local agent.