1
// Copyright Istio Authors
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
24
"google.golang.org/protobuf/types/known/durationpb"
27
"istio.io/istio/pilot/pkg/util/network"
28
"istio.io/istio/pkg/env"
29
common_features "istio.io/istio/pkg/features"
30
"istio.io/istio/pkg/log"
39
type ProxyConfig struct {
41
ComponentLogLevel string
45
// TODO: outlier log path configuration belongs to mesh ProxyConfig
52
DrainDuration *durationpb.Duration
55
// For unit testing, in combination with NoEnvoy prevents agent.Run from blocking
59
// Is the proxy in Dual Stack environment
63
// NewProxy creates an instance of the proxy control commands
64
func NewProxy(cfg ProxyConfig) Proxy {
65
// inject tracing flag for higher levels
67
logLevel, componentLogs := splitComponentLog(cfg.LogLevel)
69
args = append(args, "-l", logLevel)
71
if len(componentLogs) > 0 {
72
args = append(args, "--component-log-level", strings.Join(componentLogs, ","))
73
} else if cfg.ComponentLogLevel != "" {
74
// Use the old setting if we don't set any component log levels in LogLevel
75
args = append(args, "--component-log-level", cfg.ComponentLogLevel)
78
// Explicitly enable core dumps. This may be desirable more often (by default), but for now we only set it in VM tests.
79
if enableEnvoyCoreDump {
80
args = append(args, "--enable-core-dump")
88
// splitComponentLog breaks down an argument string into a log level (ie "info") and component log levels (ie "misc:error").
89
// This allows using a single log level API, with the same semantics as Istio's logging, to configure Envoy which
90
// has two different settings
91
func splitComponentLog(level string) (string, []string) {
92
levels := strings.Split(level, ",")
94
var componentLogs []string
95
for _, sl := range levels {
96
spl := strings.Split(sl, ":")
99
} else if len(spl) == 2 {
100
componentLogs = append(componentLogs, sl)
102
log.Warnf("dropping invalid log level: %v", sl)
105
return logLevel, componentLogs
108
func (e *envoy) Drain(skipExit bool) error {
109
adminPort := uint32(e.AdminPort)
111
err := DrainListeners(adminPort, e.Sidecar, skipExit)
113
log.Infof("failed draining listeners for Envoy on port %d: %v", adminPort, err)
118
func (e *envoy) UpdateConfig(config []byte) error {
119
return os.WriteFile(e.ConfigPath, config, 0o666)
122
func (e *envoy) args(fname string, overrideFname string) []string {
123
proxyLocalAddressType := "v4"
124
if network.AllIPv6(e.NodeIPs) {
125
proxyLocalAddressType = "v6"
127
startupArgs := []string{
129
"--drain-time-s", fmt.Sprint(int(e.DrainDuration.AsDuration().Seconds())),
130
"--drain-strategy", "immediate", // Clients are notified as soon as the drain process starts.
131
"--local-address-ip-version", proxyLocalAddressType,
132
// Reduce default flush interval from 10s to 1s. The access log buffer size is 64k and each log is ~256 bytes
133
// This means access logs will be written once we have ~250 requests, or ever 1s, which ever comes first.
134
// Reducing this to 1s optimizes for UX while retaining performance.
135
// At low QPS access logs are unlikely a bottleneck, and these users will now see logs after 1s rather than 10s.
136
// At high QPS (>250 QPS) we will log the same amount as we will log due to exceeding buffer size, rather
137
// than the flush interval.
138
"--file-flush-interval-msec", "1000",
139
"--disable-hot-restart", // We don't use it, so disable it to simplify Envoy's logic
140
"--allow-unknown-static-fields",
143
startupArgs = append(startupArgs, e.extraArgs...)
145
if overrideFname != "" {
146
s, err := readToJSON(overrideFname)
148
log.Warnf("Failed to read bootstrap override: %v", err)
150
// Despite the name Envoy also accepts JSON string
151
startupArgs = append(startupArgs, "--config-yaml", s)
155
if e.Concurrency > 0 {
156
startupArgs = append(startupArgs, "--concurrency", fmt.Sprint(e.Concurrency))
162
// readToJSON reads a config file, in YAML or JSON, and returns JSON string
163
func readToJSON(fname string) (string, error) {
164
bytes, err := os.ReadFile(fname)
166
return "", fmt.Errorf("failed to read file: %s, %v", fname, err)
169
converted, err := yaml.YAMLToJSON(bytes)
171
return "", fmt.Errorf("failed to convert to JSON: %s, %v", fname, err)
173
return string(converted), nil
177
istioBootstrapOverrideVar = env.Register("ISTIO_BOOTSTRAP_OVERRIDE", "", "")
178
enableEnvoyCoreDump = env.Register("ISTIO_ENVOY_ENABLE_CORE_DUMP", false, "").Get()
181
func (e *envoy) Run(abort <-chan error) error {
182
// spin up a new Envoy process
183
args := e.args(e.ConfigPath, istioBootstrapOverrideVar.Get())
184
log.Infof("Envoy command: %v", args)
187
cmd := exec.Command(e.BinaryPath, args...)
188
cmd.Env = os.Environ()
189
if common_features.CompliancePolicy == common_features.FIPS_140_2 {
190
// Limit the TLSv1.2 ciphers in google_grpc client in Envoy to the compliant ciphers.
191
cmd.Env = append(cmd.Env,
192
"GRPC_SSL_CIPHER_SUITES=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384")
194
cmd.Stdout = os.Stdout
195
cmd.Stderr = os.Stderr
197
cmd.SysProcAttr = &syscall.SysProcAttr{}
198
cmd.SysProcAttr.Credential = &syscall.Credential{
204
if err := cmd.Start(); err != nil {
207
done := make(chan error, 1)
214
log.Warnf("Aborting proxy")
215
if errKill := cmd.Process.Kill(); errKill != nil {
216
log.Warnf("killing proxy caused an error %v", errKill)
224
func (e *envoy) Cleanup() {
226
if err := os.Remove(e.ConfigPath); err != nil {
227
log.Warnf("Failed to delete config file %s: %v", e.ConfigPath, err)