istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tools/istio-iptables/pkg/config/config.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package config 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net" 21 "net/netip" 22 "os/user" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/miekg/dns" 28 29 "istio.io/istio/pkg/env" 30 "istio.io/istio/pkg/log" 31 netutil "istio.io/istio/pkg/util/net" 32 "istio.io/istio/tools/istio-iptables/pkg/constants" 33 ) 34 35 func DefaultConfig() *Config { 36 return &Config{ 37 RestoreFormat: true, 38 ProxyPort: "15001", 39 InboundCapturePort: "15006", 40 InboundTunnelPort: "15008", 41 InboundTProxyMark: "1337", 42 InboundTProxyRouteTable: "133", 43 IptablesProbePort: constants.DefaultIptablesProbePortUint, 44 ProbeTimeout: constants.DefaultProbeTimeout, 45 OwnerGroupsInclude: constants.OwnerGroupsInclude.DefaultValue, 46 OwnerGroupsExclude: constants.OwnerGroupsExclude.DefaultValue, 47 HostIPv4LoopbackCidr: constants.HostIPv4LoopbackCidr.DefaultValue, 48 } 49 } 50 51 // Command line options 52 // nolint: maligned 53 type Config struct { 54 ProxyPort string `json:"PROXY_PORT"` 55 InboundCapturePort string `json:"INBOUND_CAPTURE_PORT"` 56 InboundTunnelPort string `json:"INBOUND_TUNNEL_PORT"` 57 ProxyUID string `json:"PROXY_UID"` 58 ProxyGID string `json:"PROXY_GID"` 59 InboundInterceptionMode string `json:"INBOUND_INTERCEPTION_MODE"` 60 InboundTProxyMark string `json:"INBOUND_TPROXY_MARK"` 61 InboundTProxyRouteTable string `json:"INBOUND_TPROXY_ROUTE_TABLE"` 62 InboundPortsInclude string `json:"INBOUND_PORTS_INCLUDE"` 63 InboundPortsExclude string `json:"INBOUND_PORTS_EXCLUDE"` 64 OwnerGroupsInclude string `json:"OUTBOUND_OWNER_GROUPS_INCLUDE"` 65 OwnerGroupsExclude string `json:"OUTBOUND_OWNER_GROUPS_EXCLUDE"` 66 OutboundPortsInclude string `json:"OUTBOUND_PORTS_INCLUDE"` 67 OutboundPortsExclude string `json:"OUTBOUND_PORTS_EXCLUDE"` 68 OutboundIPRangesInclude string `json:"OUTBOUND_IPRANGES_INCLUDE"` 69 OutboundIPRangesExclude string `json:"OUTBOUND_IPRANGES_EXCLUDE"` 70 KubeVirtInterfaces string `json:"KUBE_VIRT_INTERFACES"` 71 ExcludeInterfaces string `json:"EXCLUDE_INTERFACES"` 72 IptablesProbePort uint16 `json:"IPTABLES_PROBE_PORT"` 73 ProbeTimeout time.Duration `json:"PROBE_TIMEOUT"` 74 DryRun bool `json:"DRY_RUN"` 75 RestoreFormat bool `json:"RESTORE_FORMAT"` 76 SkipRuleApply bool `json:"SKIP_RULE_APPLY"` 77 RunValidation bool `json:"RUN_VALIDATION"` 78 RedirectDNS bool `json:"REDIRECT_DNS"` 79 DropInvalid bool `json:"DROP_INVALID"` 80 CaptureAllDNS bool `json:"CAPTURE_ALL_DNS"` 81 EnableIPv6 bool `json:"ENABLE_INBOUND_IPV6"` 82 DNSServersV4 []string `json:"DNS_SERVERS_V4"` 83 DNSServersV6 []string `json:"DNS_SERVERS_V6"` 84 NetworkNamespace string `json:"NETWORK_NAMESPACE"` 85 CNIMode bool `json:"CNI_MODE"` 86 TraceLogging bool `json:"IPTABLES_TRACE_LOGGING"` 87 DualStack bool `json:"DUAL_STACK"` 88 HostIP netip.Addr `json:"HOST_IP"` 89 HostIPv4LoopbackCidr string `json:"HOST_IPV4_LOOPBACK_CIDR"` 90 } 91 92 func (c *Config) String() string { 93 output, err := json.MarshalIndent(c, "", "\t") 94 if err != nil { 95 log.Errorf("Unable to marshal config object: %v", err) 96 } 97 return string(output) 98 } 99 100 func (c *Config) Print() { 101 var b strings.Builder 102 b.WriteString(fmt.Sprintf("PROXY_PORT=%s\n", c.ProxyPort)) 103 b.WriteString(fmt.Sprintf("PROXY_INBOUND_CAPTURE_PORT=%s\n", c.InboundCapturePort)) 104 b.WriteString(fmt.Sprintf("PROXY_TUNNEL_PORT=%s\n", c.InboundTunnelPort)) 105 b.WriteString(fmt.Sprintf("PROXY_UID=%s\n", c.ProxyUID)) 106 b.WriteString(fmt.Sprintf("PROXY_GID=%s\n", c.ProxyGID)) 107 b.WriteString(fmt.Sprintf("INBOUND_INTERCEPTION_MODE=%s\n", c.InboundInterceptionMode)) 108 b.WriteString(fmt.Sprintf("INBOUND_TPROXY_MARK=%s\n", c.InboundTProxyMark)) 109 b.WriteString(fmt.Sprintf("INBOUND_TPROXY_ROUTE_TABLE=%s\n", c.InboundTProxyRouteTable)) 110 b.WriteString(fmt.Sprintf("INBOUND_PORTS_INCLUDE=%s\n", c.InboundPortsInclude)) 111 b.WriteString(fmt.Sprintf("INBOUND_PORTS_EXCLUDE=%s\n", c.InboundPortsExclude)) 112 b.WriteString(fmt.Sprintf("OUTBOUND_OWNER_GROUPS_INCLUDE=%s\n", c.OwnerGroupsInclude)) 113 b.WriteString(fmt.Sprintf("OUTBOUND_OWNER_GROUPS_EXCLUDE=%s\n", c.OwnerGroupsExclude)) 114 b.WriteString(fmt.Sprintf("OUTBOUND_IP_RANGES_INCLUDE=%s\n", c.OutboundIPRangesInclude)) 115 b.WriteString(fmt.Sprintf("OUTBOUND_IP_RANGES_EXCLUDE=%s\n", c.OutboundIPRangesExclude)) 116 b.WriteString(fmt.Sprintf("OUTBOUND_PORTS_INCLUDE=%s\n", c.OutboundPortsInclude)) 117 b.WriteString(fmt.Sprintf("OUTBOUND_PORTS_EXCLUDE=%s\n", c.OutboundPortsExclude)) 118 b.WriteString(fmt.Sprintf("KUBE_VIRT_INTERFACES=%s\n", c.KubeVirtInterfaces)) 119 // TODO consider renaming this env var to ENABLE_IPV6 - nothing about it is specific to "INBOUND" 120 b.WriteString(fmt.Sprintf("ENABLE_INBOUND_IPV6=%t\n", c.EnableIPv6)) 121 // TODO remove this flag - "dual stack" should just mean 122 // - supports IPv6 123 // - supports pods with more than one podIP 124 // The former already has a flag, the latter is something we should do by default and is a bug where we do not 125 b.WriteString(fmt.Sprintf("DUAL_STACK=%t\n", c.DualStack)) 126 b.WriteString(fmt.Sprintf("DNS_CAPTURE=%t\n", c.RedirectDNS)) 127 b.WriteString(fmt.Sprintf("DROP_INVALID=%t\n", c.DropInvalid)) 128 b.WriteString(fmt.Sprintf("CAPTURE_ALL_DNS=%t\n", c.CaptureAllDNS)) 129 b.WriteString(fmt.Sprintf("DNS_SERVERS=%s,%s\n", c.DNSServersV4, c.DNSServersV6)) 130 b.WriteString(fmt.Sprintf("NETWORK_NAMESPACE=%s\n", c.NetworkNamespace)) 131 b.WriteString(fmt.Sprintf("CNI_MODE=%s\n", strconv.FormatBool(c.CNIMode))) 132 b.WriteString(fmt.Sprintf("EXCLUDE_INTERFACES=%s\n", c.ExcludeInterfaces)) 133 log.Infof("Istio iptables variables:\n%s", b.String()) 134 } 135 136 func (c *Config) Validate() error { 137 if err := ValidateOwnerGroups(c.OwnerGroupsInclude, c.OwnerGroupsExclude); err != nil { 138 return err 139 } 140 return ValidateIPv4LoopbackCidr(c.HostIPv4LoopbackCidr) 141 } 142 143 var envoyUserVar = env.Register(constants.EnvoyUser, "istio-proxy", "Envoy proxy username") 144 145 func (c *Config) FillConfigFromEnvironment() error { 146 // Fill in env-var only options 147 c.OwnerGroupsInclude = constants.OwnerGroupsInclude.Get() 148 c.OwnerGroupsExclude = constants.OwnerGroupsExclude.Get() 149 150 c.HostIPv4LoopbackCidr = constants.HostIPv4LoopbackCidr.Get() 151 152 // TODO: Make this more configurable, maybe with an allowlist of users to be captured for output instead of a denylist. 153 if c.ProxyUID == "" { 154 usr, err := user.Lookup(envoyUserVar.Get()) 155 var userID string 156 // Default to the UID of ENVOY_USER 157 if err != nil { 158 userID = constants.DefaultProxyUID 159 } else { 160 userID = usr.Uid 161 } 162 c.ProxyUID = userID 163 } 164 165 // For TPROXY as its uid and gid are same. 166 if c.ProxyGID == "" { 167 c.ProxyGID = c.ProxyUID 168 } 169 // Detect whether IPv6 is enabled by checking if the pod's IP address is IPv4 or IPv6. 170 // TODO remove this check, it will break with more than one pod IP 171 hostIP, isIPv6, err := getLocalIP(c.DualStack) 172 if err != nil { 173 return err 174 } 175 176 c.HostIP = hostIP 177 c.EnableIPv6 = isIPv6 178 179 // Lookup DNS nameservers. We only do this if DNS is enabled in case of some obscure theoretical 180 // case where reading /etc/resolv.conf could fail. 181 // If capture all DNS option is enabled, we don't need to read from the dns resolve conf. All 182 // traffic to port 53 will be captured. 183 if c.RedirectDNS && !c.CaptureAllDNS { 184 dnsConfig, err := dns.ClientConfigFromFile("/etc/resolv.conf") 185 if err != nil { 186 return fmt.Errorf("failed to load /etc/resolv.conf: %v", err) 187 } 188 c.DNSServersV4, c.DNSServersV6 = netutil.IPsSplitV4V6(dnsConfig.Servers) 189 } 190 return nil 191 } 192 193 // mock net.InterfaceAddrs to make its unit test become available 194 var ( 195 LocalIPAddrs = net.InterfaceAddrs 196 ) 197 198 // getLocalIP returns one of the local IP address and it should support IPv6 or not 199 func getLocalIP(dualStack bool) (netip.Addr, bool, error) { 200 var isIPv6 bool 201 var ipAddrs []netip.Addr 202 addrs, err := LocalIPAddrs() 203 if err != nil { 204 return netip.Addr{}, isIPv6, err 205 } 206 207 for _, a := range addrs { 208 if ipnet, ok := a.(*net.IPNet); ok { 209 ip := ipnet.IP 210 ipAddr, ok := netip.AddrFromSlice(ip) 211 if !ok { 212 continue 213 } 214 // unwrap the IPv4-mapped IPv6 address 215 unwrapAddr := ipAddr.Unmap() 216 if !unwrapAddr.IsLoopback() && !unwrapAddr.IsLinkLocalUnicast() && !unwrapAddr.IsLinkLocalMulticast() { 217 isIPv6 = unwrapAddr.Is6() 218 ipAddrs = append(ipAddrs, unwrapAddr) 219 if !dualStack { 220 return unwrapAddr, isIPv6, nil 221 } 222 if isIPv6 { 223 break 224 } 225 } 226 } 227 } 228 229 if len(ipAddrs) > 0 { 230 return ipAddrs[0], isIPv6, nil 231 } 232 233 return netip.Addr{}, isIPv6, fmt.Errorf("no valid local IP address found") 234 }