istio

Форк
0
211 строк · 5.5 Кб
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
}
212

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.