Skip to content

Commit

Permalink
Merge tag '1.19.5' into tetratefips-release-1.19
Browse files Browse the repository at this point in the history
Istio release 1.19.5
  • Loading branch information
github-actions committed Dec 13, 2023
2 parents ff46c4e + a61a751 commit edf4777
Show file tree
Hide file tree
Showing 61 changed files with 1,257 additions and 793 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "istio build-tools",
"image": "gcr.io/istio-testing/build-tools:release-1.19-ae4f4b509789426724c404fba8b228a1b487fd0a",
"image": "gcr.io/istio-testing/build-tools:release-1.19-ef344298e65eeb2d9e2d07b87eb4e715c2def613",
"privileged": true,
"remoteEnv": {
"USE_GKE_GCLOUD_AUTH_PLUGIN": "True",
Expand Down
2 changes: 1 addition & 1 deletion Makefile.core.mk
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ endif
export VERSION

# Base version of Istio image to use
BASE_VERSION ?= 1.19-2023-11-06T19-02-47
BASE_VERSION ?= 1.19-2023-12-07T19-03-18
ISTIO_BASE_REGISTRY ?= gcr.io/istio-release

export GO111MODULE ?= on
Expand Down
4 changes: 4 additions & 0 deletions cni/pkg/ambient/podutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func AnnotateEnrolledPod(client kubernetes.Interface, pod *corev1.Pod) error {
types.MergePatchType,
annotationPatch,
metav1.PatchOptions{},
// Both "pods" and "pods/status" can mutate the metadata. However, pods/status is lower privilege, so we use that instead.
"status",
)
return err
}
Expand All @@ -94,6 +96,8 @@ func AnnotateUnenrollPod(client kubernetes.Interface, pod *corev1.Pod) error {
types.MergePatchType,
annotationRemovePatch,
metav1.PatchOptions{},
// Both "pods" and "pods/status" can mutate the metadata. However, pods/status is lower privilege, so we use that instead.
"status",
)
if errors.IsNotFound(err) {
return nil
Expand Down
1 change: 1 addition & 0 deletions cni/pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ func constructConfig() (*config.Config, error) {

repairCfg := config.RepairConfig{
Enabled: viper.GetBool(constants.RepairEnabled),
RepairPods: viper.GetBool(constants.RepairRepairPods),
DeletePods: viper.GetBool(constants.RepairDeletePods),
LabelPods: viper.GetBool(constants.RepairLabelPods),
LabelKey: viper.GetString(constants.RepairLabelKey),
Expand Down
3 changes: 3 additions & 0 deletions cni/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ type RepairConfig struct {
LabelKey string
LabelValue string

// Whether to fix race condition by repairing them
RepairPods bool

// Whether to fix race condition by delete broken pods
DeletePods bool

Expand Down
1 change: 1 addition & 0 deletions cni/pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (
// Repair
RepairEnabled = "repair-enabled"
RepairDeletePods = "repair-delete-pods"
RepairRepairPods = "repair-repair-pods"
RepairLabelPods = "repair-label-pods"
RepairLabelKey = "repair-broken-pod-label-key"
RepairLabelValue = "repair-broken-pod-label-value"
Expand Down
57 changes: 25 additions & 32 deletions cni/pkg/plugin/iptables_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ import (
"fmt"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/spf13/viper"

"istio.io/istio/pkg/log"
"istio.io/istio/tools/istio-iptables/pkg/cmd"
"istio.io/istio/tools/istio-iptables/pkg/constants"
"istio.io/istio/tools/istio-iptables/pkg/config"
"istio.io/istio/tools/istio-iptables/pkg/dependencies"
)

Expand All @@ -34,25 +33,27 @@ var getNs = ns.GetNS
// Program defines a method which programs iptables based on the parameters
// provided in Redirect.
func (ipt *iptables) Program(podName, netns string, rdrct *Redirect) error {
viper.Set(constants.CNIMode, true)
viper.Set(constants.NetworkNamespace, netns)
viper.Set(constants.EnvoyPort, rdrct.targetPort)
viper.Set(constants.ProxyUID, rdrct.noRedirectUID)
viper.Set(constants.ProxyGID, rdrct.noRedirectGID)
viper.Set(constants.InboundInterceptionMode, rdrct.redirectMode)
viper.Set(constants.ServiceCidr, rdrct.includeIPCidrs)
viper.Set(constants.LocalExcludePorts, rdrct.excludeInboundPorts)
viper.Set(constants.InboundPorts, rdrct.includeInboundPorts)
viper.Set(constants.ExcludeInterfaces, rdrct.excludeInterfaces)
viper.Set(constants.LocalOutboundPortsExclude, rdrct.excludeOutboundPorts)
viper.Set(constants.OutboundPorts, rdrct.includeOutboundPorts)
viper.Set(constants.ServiceExcludeCidr, rdrct.excludeIPCidrs)
viper.Set(constants.KubeVirtInterfaces, rdrct.kubevirtInterfaces)
viper.Set(constants.DryRun, dependencies.DryRunFilePath.Get() != "")
viper.Set(constants.RedirectDNS, rdrct.dnsRedirect)
viper.Set(constants.CaptureAllDNS, rdrct.dnsRedirect)
viper.Set(constants.DropInvalid, rdrct.invalidDrop)
viper.Set(constants.DualStack, rdrct.dualStack)
cfg := config.DefaultConfig()
cfg.CNIMode = true
cfg.NetworkNamespace = netns
cfg.ProxyPort = rdrct.targetPort
cfg.ProxyUID = rdrct.noRedirectUID
cfg.ProxyGID = rdrct.noRedirectGID
cfg.InboundInterceptionMode = rdrct.redirectMode
cfg.OutboundIPRangesInclude = rdrct.includeIPCidrs
cfg.InboundPortsExclude = rdrct.excludeInboundPorts
cfg.InboundPortsInclude = rdrct.includeInboundPorts
cfg.ExcludeInterfaces = rdrct.excludeInterfaces
cfg.OutboundPortsExclude = rdrct.excludeOutboundPorts
cfg.OutboundPortsInclude = rdrct.includeOutboundPorts
cfg.OutboundIPRangesExclude = rdrct.excludeIPCidrs
cfg.KubeVirtInterfaces = rdrct.kubevirtInterfaces
cfg.DryRun = dependencies.DryRunFilePath.Get() != ""
cfg.RedirectDNS = rdrct.dnsRedirect
cfg.CaptureAllDNS = rdrct.dnsRedirect
cfg.DropInvalid = rdrct.invalidDrop
cfg.DualStack = rdrct.dualStack
cfg.FillConfigFromEnvironment()

netNs, err := getNs(netns)
if err != nil {
Expand All @@ -61,17 +62,9 @@ func (ipt *iptables) Program(podName, netns string, rdrct *Redirect) error {
}
defer netNs.Close()

if err = netNs.Do(func(_ ns.NetNS) error {
iptablesCmd := cmd.GetCommand()
return netNs.Do(func(_ ns.NetNS) error {
log.Infof("============= Start iptables configuration for %v =============", podName)
defer log.Infof("============= End iptables configuration for %v =============", podName)
if err := iptablesCmd.Execute(); err != nil {
return err
}
return nil
}); err != nil {
return err
}

return nil
return cmd.ProgramIptables(cfg)
})
}
11 changes: 8 additions & 3 deletions cni/pkg/plugin/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ func getK8sPodInfo(client *kubernetes.Clientset, podName, podNamespace string) (
return nil, err
}

pi := ExtractPodInfo(pod)
log.Debugf("Pod %v/%v info: \n%+v", podNamespace, podName, pi)

return pi, nil
}

func ExtractPodInfo(pod *v1.Pod) *PodInfo {
pi := &PodInfo{
Containers: sets.New[string](),
Labels: pod.Labels,
Expand All @@ -91,9 +98,7 @@ func getK8sPodInfo(client *kubernetes.Clientset, podName, podNamespace string) (
}
}
}
log.Debugf("Pod %v/%v info: \n%+v", podNamespace, podName, pi)

return pi, nil
return pi
}

// containers fetches all containers in the pod.
Expand Down
2 changes: 1 addition & 1 deletion cni/pkg/plugin/plugin_dryrun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func TestIPTablesRuleGeneration(t *testing.T) {
input: &PodInfo{
Containers: sets.New("test", "istio-proxy", "istio-validate"),
Annotations: map[string]string{annotation.SidecarStatus.Name: "true"},
ProxyEnvironments: map[string]string{cmd.InvalidDropByIptables.Name: "true"},
ProxyEnvironments: map[string]string{cmd.InvalidDropByIptables: "true"},
},
golden: filepath.Join(env.IstioSrc, "cni/pkg/plugin/testdata/invalid-drop.txt.golden"),
},
Expand Down
2 changes: 1 addition & 1 deletion cni/pkg/plugin/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func NewRedirect(pi *PodInfo) (*Redirect, error) {
log.Warnf("cannot parse dual stack environment variable %v", valErr)
}
}
if v, found := pi.ProxyEnvironments[cmd.InvalidDropByIptables.Name]; found {
if v, found := pi.ProxyEnvironments[cmd.InvalidDropByIptables]; found {
// parse and set the bool value of invalidDrop
redir.invalidDrop, valErr = strconv.ParseBool(v)
if valErr != nil {
Expand Down
1 change: 1 addition & 0 deletions cni/pkg/repair/monitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
var (
typeLabel = monitoring.CreateLabel("type")
deleteType = "delete"
repairType = "repair"
labelType = "label"

resultLabel = monitoring.CreateLabel("result")
Expand Down
150 changes: 150 additions & 0 deletions cni/pkg/repair/netns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package repair

import (
"fmt"
"math"
"net"
"strconv"

netns "github.com/containernetworking/plugins/pkg/ns"
"github.com/prometheus/procfs"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
corev1 "k8s.io/api/core/v1"

"istio.io/istio/pkg/log"
)

func getPidNamespace(pid int) string {
return "/host/proc/" + strconv.Itoa(pid) + "/ns/net"
}

func runInHost[T any](f func() (T, error)) (T, error) {
var res T
ns, err := netns.GetNS(getPidNamespace(1))
if err != nil {
return res, fmt.Errorf("failed to get host network: %v", err)
}
err = ns.Do(func(ns netns.NetNS) error {
var err error
res, err = f()
return err
})
if err != nil {
return res, fmt.Errorf("in host network: %v", err)
}
return res, nil
}

func findNetworkIDByIP(ip string) (int, error) {
link, err := getLinkWithDestinationOf(ip)
if err != nil {
return 0, fmt.Errorf("find link for %v: %v", ip, err)
}
return link.Attrs().NetNsID, nil
}

func getLinkWithDestinationOf(ip string) (netlink.Link, error) {
routes, err := netlink.RouteListFiltered(
netlink.FAMILY_V4,
&netlink.Route{Dst: &net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(32, 32)}},
netlink.RT_FILTER_DST)
if err != nil {
return nil, err
}

if len(routes) == 0 {
return nil, fmt.Errorf("no routes found for %s", ip)
}

linkIndex := routes[0].LinkIndex
return netlink.LinkByIndex(linkIndex)
}

// getPodNetNs finds the network namespace for a given pod. There is not a great way to do this. Network namespaces live
// under the procfs, /proc/<pid>/ns/net. In majority of cases, this is not used directly, but is rather bind mounted to
// /var/run/netns/<name>. However, this pattern is not ubiquitous. Some platforms bind mount to other places. As we run
// in a pod, we cannot just access any arbitrary file they happen to bind mount in, as we don't know ahead of time where
// it might be.
//
// Instead, we rely directly on the procfs.
// This rules out two possible methods:
// * use crictl to inspect the pod; this returns the bind-mounted network namespace file.
// * /var/lib/cni/results shows the outputs of CNI plugins; this containers the bind-mounted network namespace file.
//
// Instead, we traverse the procfs. Comments on this method are inline.
func getPodNetNs(pod *corev1.Pod) (string, error) {
// First, find the network namespace id by looking the interface with the given Pod IP.
// This could break on some platforms if they do not have an interface-per-pod.
wantID, err := findNetworkIDByIP(pod.Status.PodIP)
if err != nil {
return "", fmt.Errorf("network id: %v", err)
}
fs, err := procfs.NewFS("/host/proc")
if err != nil {
return "", fmt.Errorf("read procfs: %v", err)
}
procs, err := fs.AllProcs()
if err != nil {
return "", fmt.Errorf("read procs: %v", err)
}
oldest := uint64(math.MaxUint64)
best := ""
// We will iterate over all processes. Our goal is to find a process with the same network ID as we found above.
// There should be 1 or 2 processes that match: the pause container should always be there, and the istio-validation *might*.
// We want the pause container, as the istio-validation one may exit before we are done.
// We do this by detecting the longest running process. We could look at `cmdline`, but is likely more reliable to weird platforms.
for _, p := range procs {
ns := getPidNamespace(p.PID)
fd, err := unix.Open(ns, unix.O_RDONLY, 0)
if err != nil {
// Not uncommon, many processes are transient and we have a TOCTOU here.
// No problem, must not be the one we are after.
log.Debugf("failed to open pid %v: %v", p.PID, err)
continue
}
id, err := netlink.GetNetNsIdByFd(fd)
_ = unix.Close(fd)
if err != nil {
log.Debugf("failed to get netns for pid %v: %v", p.PID, err)
continue
}

if id != wantID {
// Not the network we want, skip
continue
}
s, err := p.Stat()
if err != nil {
// Unexpected... we will use it, but only if we find nothing without errors
log.Warnf("failed to read proc %v stats: %v", p.PID, err)
if best == "" {
best = ns
}
continue
}
// Get the oldest one.
if s.Starttime < oldest {
oldest = s.Starttime
best = ns
}
}
if best == "" {
return "", fmt.Errorf("failed to find network namespace")
}
return best, nil
}
Loading

0 comments on commit edf4777

Please sign in to comment.