in tools/istio-iptables/pkg/capture/run.go [315:572]
func (cfg *IptablesConfigurator) Run() {
defer func() {
// Best effort since we don't know if the commands exist
_ = cfg.ext.Run(constants.IPTABLESSAVE)
if cfg.cfg.EnableInboundIPv6 {
_ = cfg.ext.Run(constants.IP6TABLESSAVE)
}
}()
// Since OUTBOUND_IP_RANGES_EXCLUDE could carry ipv4 and ipv6 ranges
// need to split them in different arrays one for ipv4 and one for ipv6
// in order to not to fail
ipv4RangesExclude, ipv6RangesExclude, err := cfg.separateV4V6(cfg.cfg.OutboundIPRangesExclude)
if err != nil {
panic(err)
}
if ipv4RangesExclude.IsWildcard {
panic("Invalid value for OUTBOUND_IP_RANGES_EXCLUDE")
}
// FixMe: Do we need similar check for ipv6RangesExclude as well ??
ipv4RangesInclude, ipv6RangesInclude, err := cfg.separateV4V6(cfg.cfg.OutboundIPRangesInclude)
if err != nil {
panic(err)
}
redirectDNS := cfg.cfg.RedirectDNS
cfg.logConfig()
cfg.shortCircuitExcludeInterfaces()
// Do not capture internal interface.
cfg.shortCircuitKubeInternalInterface()
// Create a rule for invalid drop in PREROUTING chain in mangle table, so the iptables will drop the out of window packets instead of reset connection .
dropInvalid := cfg.cfg.DropInvalid
if dropInvalid {
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.PREROUTING, constants.MANGLE, "-m", "conntrack", "--ctstate",
"INVALID", "-j", constants.DROP)
}
// Create a new chain for to hit tunnel port directly. Envoy will be listening on port acting as VPN tunnel.
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.NAT, "-p", constants.TCP, "--dport",
cfg.cfg.InboundTunnelPort, "-j", constants.RETURN)
// Create a new chain for redirecting outbound traffic to the common Envoy port.
// In both chains, '-j RETURN' bypasses Envoy and '-j ISTIOREDIRECT'
// redirects to Envoy.
cfg.iptables.AppendRule(iptableslog.UndefinedCommand,
constants.ISTIOREDIRECT, constants.NAT, "-p", constants.TCP, "-j", constants.REDIRECT, "--to-ports", cfg.cfg.ProxyPort)
// Use this chain also for redirecting inbound traffic to the common Envoy port
// when not using TPROXY.
cfg.iptables.AppendRule(iptableslog.InboundCapture, constants.ISTIOINREDIRECT, constants.NAT, "-p", constants.TCP, "-j", constants.REDIRECT,
"--to-ports", cfg.cfg.InboundCapturePort)
cfg.handleInboundPortsInclude()
// TODO: change the default behavior to not intercept any output - user may use http_proxy or another
// iptablesOrFail wrapper (like ufw). Current default is similar with 0.1
// Jump to the ISTIOOUTPUT chain from OUTPUT chain for all tcp traffic, and UDP dns (if enabled)
cfg.iptables.AppendRule(iptableslog.JumpOutbound, constants.OUTPUT, constants.NAT, "-p", constants.TCP, "-j", constants.ISTIOOUTPUT)
// Apply port based exclusions. Must be applied before connections back to self are redirected.
if cfg.cfg.OutboundPortsExclude != "" {
for _, port := range split(cfg.cfg.OutboundPortsExclude) {
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-p", constants.TCP,
"--dport", port, "-j", constants.RETURN)
}
}
// 127.0.0.6/::7 is bind connect from inbound passthrough cluster
cfg.iptables.AppendVersionedRule("127.0.0.6/32", "::6/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "-s", constants.IPVersionSpecific, "-j", constants.RETURN)
for _, uid := range split(cfg.cfg.ProxyUID) {
// Redirect app calls back to itself via Envoy when using the service VIP
// e.g. appN => Envoy (client) => Envoy (server) => appN.
// nolint: lll
if redirectDNS {
// When DNS is enabled, we skip this for port 53. This ensures we do not have:
// app => istio-agent => Envoy inbound => dns server
// Instead, we just have:
// app => istio-agent => dns server
cfg.iptables.AppendVersionedRule("127.0.0.1/32", "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "!", "-d", constants.IPVersionSpecific,
"-p", "tcp", "!", "--dport", "53",
"-m", "owner", "--uid-owner", uid, "-j", constants.ISTIOINREDIRECT)
} else {
cfg.iptables.AppendVersionedRule("127.0.0.1/32", "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "!", "-d", constants.IPVersionSpecific,
"-m", "owner", "--uid-owner", uid, "-j", constants.ISTIOINREDIRECT)
}
// Do not redirect app calls to back itself via Envoy when using the endpoint address
// e.g. appN => appN by lo
// If loopback explicitly set via OutboundIPRangesInclude, then don't return.
if !ipv4RangesInclude.HasLoopBackIP && !ipv6RangesInclude.HasLoopBackIP {
if redirectDNS {
// Users may have a DNS server that is on localhost. In these cases, applications may
// send TCP traffic to the DNS server that we actually *do* want to intercept. To
// handle this case, we exclude port 53 from this rule. Note: We cannot just move the
// port 53 redirection rule further up the list, as we will want to avoid capturing
// DNS requests from the proxy UID/GID
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-o", "lo", "-p", "tcp",
"!", "--dport", "53",
"-m", "owner", "!", "--uid-owner", uid, "-j", constants.RETURN)
} else {
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "-m", "owner", "!", "--uid-owner", uid, "-j", constants.RETURN)
}
}
// Avoid infinite loops. Don't redirect Envoy traffic directly back to
// Envoy for non-loopback traffic.
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-m", "owner", "--uid-owner", uid, "-j", constants.RETURN)
}
for _, gid := range split(cfg.cfg.ProxyGID) {
// Redirect app calls back to itself via Envoy when using the service VIP
// e.g. appN => Envoy (client) => Envoy (server) => appN.
cfg.iptables.AppendVersionedRule("127.0.0.1/32", "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "!", "-d", constants.IPVersionSpecific,
"-m", "owner", "--gid-owner", gid, "-j", constants.ISTIOINREDIRECT)
// Do not redirect app calls to back itself via Envoy when using the endpoint address
// e.g. appN => appN by lo
// If loopback explicitly set via OutboundIPRangesInclude, then don't return.
if !ipv4RangesInclude.HasLoopBackIP && !ipv6RangesInclude.HasLoopBackIP {
if redirectDNS {
// Users may have a DNS server that is on localhost. In these cases, applications may
// send TCP traffic to the DNS server that we actually *do* want to intercept. To
// handle this case, we exclude port 53 from this rule. Note: We cannot just move the
// port 53 redirection rule further up the list, as we will want to avoid capturing
// DNS requests from the proxy UID/GID
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "-p", "tcp",
"!", "--dport", "53",
"-m", "owner", "!", "--gid-owner", gid, "-j", constants.RETURN)
} else {
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-o", "lo", "-m", "owner", "!", "--gid-owner", gid, "-j", constants.RETURN)
}
}
// Avoid infinite loops. Don't redirect Envoy traffic directly back to
// Envoy for non-loopback traffic.
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-m", "owner", "--gid-owner", gid, "-j", constants.RETURN)
}
ownerGroupsFilter := config.ParseInterceptFilter(cfg.cfg.OwnerGroupsInclude, cfg.cfg.OwnerGroupsExclude)
cfg.handleCaptureByOwnerGroup(ownerGroupsFilter)
if redirectDNS {
if cfg.cfg.CaptureAllDNS {
// Redirect all TCP dns traffic on port 53 to the agent on port 15053
// This will be useful for the CNI case where pod DNS server address cannot be decided.
cfg.iptables.AppendRule(iptableslog.UndefinedCommand,
constants.ISTIOOUTPUT, constants.NAT,
"-p", constants.TCP,
"--dport", "53",
"-j", constants.REDIRECT,
"--to-ports", constants.IstioAgentDNSListenerPort)
} else {
for _, s := range cfg.cfg.DNSServersV4 {
// redirect all TCP dns traffic on port 53 to the agent on port 15053 for all servers
// in etc/resolv.conf
// We avoid redirecting all IP ranges to avoid infinite loops when there are local DNS proxies
// such as: app -> istio dns server -> dnsmasq -> upstream
// This ensures that we do not get requests from dnsmasq sent back to the agent dns server in a loop.
// Note: If a user somehow configured etc/resolv.conf to point to dnsmasq and server X, and dnsmasq also
// pointed to server X, this would not work. However, the assumption is that is not a common case.
cfg.iptables.AppendRuleV4(iptableslog.UndefinedCommand,
constants.ISTIOOUTPUT, constants.NAT,
"-p", constants.TCP,
"--dport", "53",
"-d", s+"/32",
"-j", constants.REDIRECT,
"--to-ports", constants.IstioAgentDNSListenerPort)
}
for _, s := range cfg.cfg.DNSServersV6 {
cfg.iptables.AppendRuleV6(iptableslog.UndefinedCommand,
constants.ISTIOOUTPUT, constants.NAT,
"-p", constants.TCP,
"--dport", "53",
"-d", s+"/128",
"-j", constants.REDIRECT,
"--to-ports", constants.IstioAgentDNSListenerPort)
}
}
}
// Skip redirection for Envoy-aware applications and
// container-to-container traffic both of which explicitly use
// localhost.
cfg.iptables.AppendVersionedRule("127.0.0.1/32", "::1/128", iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT,
"-d", constants.IPVersionSpecific, "-j", constants.RETURN)
// Apply outbound IPv4 exclusions. Must be applied before inclusions.
for _, cidr := range ipv4RangesExclude.IPNets {
cfg.iptables.AppendRuleV4(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-d", cidr.String(), "-j", constants.RETURN)
}
for _, cidr := range ipv6RangesExclude.IPNets {
cfg.iptables.AppendRuleV6(iptableslog.UndefinedCommand, constants.ISTIOOUTPUT, constants.NAT, "-d", cidr.String(), "-j", constants.RETURN)
}
cfg.handleOutboundPortsInclude()
cfg.handleOutboundIncludeRules(ipv4RangesInclude, cfg.iptables.AppendRuleV4, cfg.iptables.InsertRuleV4)
cfg.handleOutboundIncludeRules(ipv6RangesInclude, cfg.iptables.AppendRuleV6, cfg.iptables.InsertRuleV6)
if redirectDNS {
HandleDNSUDP(
AppendOps, cfg.iptables, cfg.ext, "",
cfg.cfg.ProxyUID, cfg.cfg.ProxyGID,
cfg.cfg.DNSServersV4, cfg.cfg.DNSServersV6, cfg.cfg.CaptureAllDNS,
ownerGroupsFilter)
}
if cfg.cfg.InboundInterceptionMode == constants.TPROXY {
// save packet mark set by envoy.filters.listener.original_src as connection mark
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.PREROUTING, constants.MANGLE,
"-p", constants.TCP, "-m", "mark", "--mark", cfg.cfg.InboundTProxyMark, "-j", "CONNMARK", "--save-mark")
// If the packet is already marked with 1337, then return. This is to prevent mark envoy --> app traffic again.
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
"-p", constants.TCP, "-o", "lo", "-m", "mark", "--mark", cfg.cfg.InboundTProxyMark, "-j", constants.RETURN)
for _, uid := range split(cfg.cfg.ProxyUID) {
// mark outgoing packets from envoy to workload by pod ip
// app call VIP --> envoy outbound -(mark 1338)-> envoy inbound --> app
cfg.iptables.AppendVersionedRule("127.0.0.1/32", "::1/128", iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
"!", "-d", constants.IPVersionSpecific, "-p", constants.TCP, "-o", "lo",
"-m", "owner", "--uid-owner", uid, "-j", constants.MARK, "--set-mark", outboundMark)
}
for _, gid := range split(cfg.cfg.ProxyGID) {
// mark outgoing packets from envoy to workload by pod ip
// app call VIP --> envoy outbound -(mark 1338)-> envoy inbound --> app
cfg.iptables.AppendVersionedRule("127.0.0.1/32", "::1/128", iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
"!", "-d", constants.IPVersionSpecific, "-p", constants.TCP, "-o", "lo",
"-m", "owner", "--gid-owner", gid, "-j", constants.MARK, "--set-mark", outboundMark)
}
// mark outgoing packets from workload, match it to policy routing entry setup for TPROXY mode
cfg.iptables.AppendRule(iptableslog.UndefinedCommand, constants.OUTPUT, constants.MANGLE,
"-p", constants.TCP, "-m", "connmark", "--mark", cfg.cfg.InboundTProxyMark, "-j", "CONNMARK", "--restore-mark")
// prevent infinite redirect
cfg.iptables.InsertRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 1,
"-p", constants.TCP, "-m", "mark", "--mark", cfg.cfg.InboundTProxyMark, "-j", constants.RETURN)
// prevent intercept traffic from envoy/pilot-agent ==> app by 127.0.0.6 --> podip
cfg.iptables.InsertRuleV4(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 2,
"-p", constants.TCP, "-s", "127.0.0.6/32", "-i", "lo", "-j", constants.RETURN)
cfg.iptables.InsertRuleV6(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 2,
"-p", constants.TCP, "-s", "::6/128", "-i", "lo", "-j", constants.RETURN)
// prevent intercept traffic from app ==> app by pod ip
cfg.iptables.InsertRule(iptableslog.UndefinedCommand, constants.ISTIOINBOUND, constants.MANGLE, 3,
"-p", constants.TCP, "-i", "lo", "-m", "mark", "!", "--mark", outboundMark, "-j", constants.RETURN)
}
cfg.executeCommands()
}