istio

Форк
0
218 строк · 7.9 Кб
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 dependencies
16

17
import (
18
	"fmt"
19
	"io"
20
	"os/exec"
21
	"regexp"
22
	"strings"
23

24
	utilversion "k8s.io/apimachinery/pkg/util/version"
25

26
	"istio.io/istio/tools/istio-iptables/pkg/constants"
27
)
28

29
// XTablesExittype is the exit type of xtables commands.
30
type XTablesExittype int
31

32
// Learn from `xtables_exittype` of iptables.
33
// `XTF_ONLY_ONCE`, `XTF_NO_INVERT`, `XTF_BAD_VALUE`, `XTF_ONE_ACTION` will eventually turned out to be a
34
// parameter problem with explicit error message. Thus, we do not need to support them here.
35
const (
36
	// XTablesOtherProblem indicates a problem of other type in xtables
37
	XTablesOtherProblem XTablesExittype = iota + 1
38
	// XTablesParameterProblem indicates a parameter problem in xtables
39
	XTablesParameterProblem
40
	// XTablesVersionProblem indicates a version problem in xtables
41
	XTablesVersionProblem
42
	// XTablesResourceProblem indicates a resource problem in xtables
43
	XTablesResourceProblem
44
)
45

46
var exittypeToString = map[XTablesExittype]string{
47
	XTablesOtherProblem:     "xtables other problem",
48
	XTablesParameterProblem: "xtables parameter problem",
49
	XTablesVersionProblem:   "xtables version problem",
50
	XTablesResourceProblem:  "xtables resource problem",
51
}
52

53
// RealDependencies implementation of interface Dependencies, which is used in production
54
type RealDependencies struct {
55
	NetworkNamespace string
56
	CNIMode          bool
57
}
58

59
const iptablesVersionPattern = `v([0-9]+(\.[0-9]+)+)`
60

61
type IptablesVersion struct {
62
	DetectedBinary        string
63
	DetectedSaveBinary    string
64
	DetectedRestoreBinary string
65
	// the actual version
66
	Version *utilversion.Version
67
	// true if legacy mode, false if nf_tables
68
	Legacy bool
69
	// true if we detected that existing rules are present for this variant (legacy, nft, v6)
70
	ExistingRules bool
71
}
72

73
func (v IptablesVersion) CmdToString(cmd constants.IptablesCmd) string {
74
	switch cmd {
75
	case constants.IPTables:
76
		return v.DetectedBinary
77
	case constants.IPTablesSave:
78
		return v.DetectedSaveBinary
79
	case constants.IPTablesRestore:
80
		return v.DetectedRestoreBinary
81
	default:
82
		return ""
83
	}
84
}
85

86
// IsWriteCmd returns true for all command types that do write actions (and thus need a lock)
87
func (v IptablesVersion) IsWriteCmd(cmd constants.IptablesCmd) bool {
88
	switch cmd {
89
	case constants.IPTables:
90
		return true
91
	case constants.IPTablesRestore:
92
		return true
93
	default:
94
		return false
95
	}
96
}
97

98
// Constants for iptables commands
99
// These should not be used directly/assumed to be present, but should be contextually detected
100
const (
101
	iptablesBin         = "iptables"
102
	iptablesNftBin      = "iptables-nft"
103
	iptablesLegacyBin   = "iptables-legacy"
104
	ip6tablesBin        = "ip6tables"
105
	ip6tablesNftBin     = "ip6tables-nft"
106
	ip6tablesLegacyBin  = "ip6tables-legacy"
107
	iptablesRestoreBin  = "iptables-restore"
108
	ip6tablesRestoreBin = "ip6tables-restore"
109
)
110

111
// It is not sufficient to check for the presence of one binary or the other in $PATH -
112
// we must choose a binary that is
113
// 1. Available in our $PATH
114
// 2. Matches where rules are actually defined in the netns we're operating in
115
// (legacy or nft, with a preference for the latter if both present)
116
//
117
// This is designed to handle situations where, for instance, the host has nft-defined rules, and our default container
118
// binary is `legacy`, or vice-versa - we must match the binaries we have in our $PATH to what rules are actually defined
119
// in our current netns context.
120
//
121
// Q: Why not simply "use the host default binary" at $PATH/iptables?
122
// A: Because we are running in our own container and do not have access to the host default binary.
123
// We are using our local binaries to update host rules, and we must pick the right match.
124
//
125
// Basic selection logic is as follows:
126
// 1. see if we have `nft` binary set in our $PATH
127
// 2. see if we have existing rules in `nft` in our netns
128
// 3. If so, use `nft` binary set
129
// 4. Otherwise, see if we have `legacy` binary set, and use that.
130
// 5. Otherwise, see if we have `iptables` binary set, and use that (detecting whether it's nft or legacy).
131
func (r *RealDependencies) DetectIptablesVersion(ipV6 bool) (IptablesVersion, error) {
132
	// Begin detecting
133
	//
134
	// iptables variants all have ipv6 variants, so decide which set we're looking for
135
	var nftBin, legacyBin, plainBin string
136
	if ipV6 {
137
		nftBin = ip6tablesNftBin
138
		legacyBin = ip6tablesLegacyBin
139
		plainBin = ip6tablesBin
140
	} else {
141
		nftBin = iptablesNftBin
142
		legacyBin = iptablesLegacyBin
143
		plainBin = iptablesBin
144
	}
145

146
	// 1. What binaries we have
147
	// 2. What binary we should use, based on existing rules defined in our current context.
148
	// does the nft binary set exist, and are nft rules present?
149
	nftVer, err := shouldUseBinaryForCurrentContext(nftBin)
150
	if err == nil && nftVer.ExistingRules {
151
		// if so, immediately use it.
152
		return nftVer, nil
153
	}
154
	// does the legacy binary set exist, and are legacy rules present?
155
	legVer, err := shouldUseBinaryForCurrentContext(legacyBin)
156
	if err == nil && legVer.ExistingRules {
157
		// if so, immediately use it
158
		return legVer, nil
159
	}
160
	// regular non-suffixed binary set is our last resort.
161
	//
162
	// If it's there, and rules do not already exist for a specific variant,
163
	// we should use the default non-suffixed binary.
164
	// If it's NOT there, just propagate the error, we can't do anything, no iptables here
165
	return shouldUseBinaryForCurrentContext(plainBin)
166
}
167

168
// TODO BML verify this won't choke on "-save" binaries having slightly diff. version string prefixes
169
func parseIptablesVer(rawVer string) (*utilversion.Version, error) {
170
	versionMatcher := regexp.MustCompile(iptablesVersionPattern)
171
	match := versionMatcher.FindStringSubmatch(rawVer)
172
	if match == nil {
173
		return nil, fmt.Errorf("no iptables version found for: %q", rawVer)
174
	}
175
	version, err := utilversion.ParseGeneric(match[1])
176
	if err != nil {
177
		return nil, fmt.Errorf("iptables version %q is not a valid version string: %v", match[1], err)
178
	}
179
	return version, nil
180
}
181

182
// transformToXTablesErrorMessage returns an updated error message with explicit xtables error hints, if applicable.
183
func transformToXTablesErrorMessage(stderr string, err error) string {
184
	ee, ok := err.(*exec.ExitError)
185
	if !ok {
186
		// Not common, but can happen if file not found error, etc
187
		return err.Error()
188
	}
189
	exitcode := ee.ExitCode()
190
	if errtypeStr, ok := exittypeToString[XTablesExittype(exitcode)]; ok {
191
		// The original stderr is something like:
192
		// `prog_name + prog_vers: error hints`
193
		// `(optional) try help information`.
194
		// e.g.,
195
		// `iptables 1.8.4 (legacy): Couldn't load target 'ISTIO_OUTPUT':No such file or directory`
196
		// `Try 'iptables -h' or 'iptables --help' for more information.`
197
		// Reusing the `error hints` and optional `try help information` parts of the original stderr to form
198
		// an error message with explicit xtables error information.
199
		errStrParts := strings.SplitN(stderr, ":", 2)
200
		errStr := stderr
201
		if len(errStrParts) > 1 {
202
			errStr = errStrParts[1]
203
		}
204
		return fmt.Sprintf("%v: %v", errtypeStr, strings.TrimSpace(errStr))
205
	}
206

207
	return stderr
208
}
209

210
// Run runs a command
211
func (r *RealDependencies) Run(cmd constants.IptablesCmd, iptVer *IptablesVersion, stdin io.ReadSeeker, args ...string) error {
212
	return r.executeXTables(cmd, iptVer, false, stdin, args...)
213
}
214

215
// RunQuietlyAndIgnore runs a command quietly and ignores errors
216
func (r *RealDependencies) RunQuietlyAndIgnore(cmd constants.IptablesCmd, iptVer *IptablesVersion, stdin io.ReadSeeker, args ...string) {
217
	_ = r.executeXTables(cmd, iptVer, true, stdin, args...)
218
}
219

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

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

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

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