istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tools/istio-iptables/pkg/validation/validator.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 validation 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "net" 22 "net/netip" 23 "strconv" 24 "strings" 25 "time" 26 27 "istio.io/istio/pkg/log" 28 "istio.io/istio/tools/istio-iptables/pkg/config" 29 ) 30 31 type ReturnCode int 32 33 const ( 34 DONE ReturnCode = iota 35 ) 36 37 type Validator struct { 38 Config *Config 39 } 40 41 type Config struct { 42 ServerListenAddress []string 43 ServerOriginalPort uint16 44 ServerOriginalIP netip.Addr 45 ServerReadyBarrier chan ReturnCode 46 ProbeTimeout time.Duration 47 } 48 49 type Service struct { 50 Config *Config 51 } 52 53 type Client struct { 54 Config *Config 55 } 56 57 func (validator *Validator) Run() error { 58 log.Infof("Starting iptables validation. This check verifies that iptables rules are properly established for the network.") 59 s := Service{ 60 validator.Config, 61 } 62 sError := make(chan error, 1) 63 sTimer := time.NewTimer(s.Config.ProbeTimeout) 64 defer sTimer.Stop() 65 go func() { 66 sError <- s.Run() 67 }() 68 69 // infinite loop 70 go func() { 71 c := Client{Config: validator.Config} 72 <-c.Config.ServerReadyBarrier 73 for { 74 _ = c.Run() 75 // Avoid spamming the request to the validation server. 76 // Since the TIMEWAIT socket is cleaned up in 60 second, 77 // it's maintaining 60 TIMEWAIT sockets. Not big deal. 78 time.Sleep(time.Second) 79 } 80 }() 81 select { 82 case <-sTimer.C: 83 return fmt.Errorf("validation timeout") 84 case err := <-sError: 85 if err == nil { 86 log.Info("Validation passed, iptables rules established") 87 } else { 88 log.Errorf("Validation failed: %v", err) 89 } 90 return err 91 } 92 } 93 94 // TODO(lambdai): remove this if iptables only need to redirect to outbound proxy port on A call A 95 func genListenerAddress(ip netip.Addr, ports []string) []string { 96 addresses := make([]string, 0, len(ports)) 97 for _, port := range ports { 98 addresses = append(addresses, net.JoinHostPort(ip.String(), port)) 99 } 100 return addresses 101 } 102 103 func NewValidator(config *config.Config) *Validator { 104 // It's tricky here: 105 // Connect to 127.0.0.6 will redirect to 127.0.0.1 106 // Connect to ::6 will redirect to ::1 107 isIPv6 := config.HostIP.Is6() 108 listenIP, _ := netip.AddrFromSlice([]byte{127, 0, 0, 1}) 109 serverIP, _ := netip.AddrFromSlice([]byte{127, 0, 0, 6}) 110 if isIPv6 { 111 listenIP, _ = netip.AddrFromSlice([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) 112 serverIP, _ = netip.AddrFromSlice([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6}) 113 } 114 return &Validator{ 115 Config: &Config{ 116 ServerListenAddress: genListenerAddress(listenIP, []string{config.ProxyPort, config.InboundCapturePort}), 117 ServerOriginalPort: config.IptablesProbePort, 118 ServerOriginalIP: serverIP, 119 ServerReadyBarrier: make(chan ReturnCode, 1), 120 ProbeTimeout: config.ProbeTimeout, 121 }, 122 } 123 } 124 125 // Write human readable response 126 func echo(conn io.WriteCloser, echo []byte) { 127 _, _ = conn.Write(echo) 128 _ = conn.Close() 129 } 130 131 func restoreOriginalAddress(l net.Listener, config *Config, c chan<- ReturnCode) { 132 defer l.Close() 133 for { 134 conn, err := l.Accept() 135 if err != nil { 136 log.Errorf("Listener failed to accept connection: %v", err) 137 continue 138 } 139 _, port, err := GetOriginalDestination(conn) 140 if err != nil { 141 log.Errorf("Error getting original dst: %v", err) 142 conn.Close() 143 continue 144 } 145 146 // echo original port for debugging. 147 // Since the write amount is small it should fit in sock buffer and never blocks. 148 echo(conn, []byte(strconv.Itoa(int(port)))) 149 // Handle connections 150 // Since the write amount is small it should fit in sock buffer and never blocks. 151 if port != config.ServerOriginalPort { 152 // This could be probe request from no where 153 continue 154 } 155 // Server recovers the magical original port 156 c <- DONE 157 return 158 } 159 } 160 161 func (s *Service) Run() error { 162 // at most 2 message: ipv4 and ipv6 163 c := make(chan ReturnCode, 2) 164 hasAtLeastOneListener := false 165 for _, addr := range s.Config.ServerListenAddress { 166 log.Infof("Listening on %v", addr) 167 config := &net.ListenConfig{Control: reuseAddr} 168 169 l, err := config.Listen(context.Background(), "tcp", addr) // bind to the address:port 170 if err != nil { 171 log.Errorf("Error on listening: %v", err) 172 continue 173 } 174 175 hasAtLeastOneListener = true 176 go restoreOriginalAddress(l, s.Config, c) 177 } 178 if hasAtLeastOneListener { 179 s.Config.ServerReadyBarrier <- DONE 180 // bump at least one since we currently support either v4 or v6 181 <-c 182 return nil 183 } 184 return fmt.Errorf("no listener available: %s", strings.Join(s.Config.ServerListenAddress, ",")) 185 } 186 187 func (c *Client) Run() error { 188 laddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0") 189 if err != nil { 190 return err 191 } 192 if c.Config.ServerOriginalIP.Is6() { 193 laddr, err = net.ResolveTCPAddr("tcp", "[::1]:0") 194 if err != nil { 195 return err 196 } 197 } 198 sOriginalPort := fmt.Sprintf("%d", c.Config.ServerOriginalPort) 199 serverOriginalAddress := net.JoinHostPort(c.Config.ServerOriginalIP.String(), sOriginalPort) 200 raddr, err := net.ResolveTCPAddr("tcp", serverOriginalAddress) 201 if err != nil { 202 return err 203 } 204 conn, err := net.DialTCP("tcp", laddr, raddr) 205 if err != nil { 206 log.Errorf("Error connecting to %s: %v", serverOriginalAddress, err) 207 return err 208 } 209 conn.Close() 210 return nil 211 }