istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/authz/authz.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 authz
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  
    23  	"github.com/spf13/cobra"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  
    26  	"istio.io/istio/istioctl/pkg/cli"
    27  	"istio.io/istio/istioctl/pkg/completion"
    28  	"istio.io/istio/istioctl/pkg/util"
    29  	"istio.io/istio/istioctl/pkg/util/configdump"
    30  	"istio.io/istio/pkg/kube"
    31  	"istio.io/istio/pkg/log"
    32  )
    33  
    34  var configDumpFile string
    35  
    36  func checkCmd(ctx cli.Context) *cobra.Command {
    37  	cmd := &cobra.Command{
    38  		Use:   "check [<type>/]<name>[.<namespace>]",
    39  		Short: "Check AuthorizationPolicy applied in the pod.",
    40  		Long: `Check prints the AuthorizationPolicy applied to a pod by directly checking
    41  the Envoy configuration of the pod. The command is especially useful for inspecting
    42  the policy propagation from Istiod to Envoy and the final AuthorizationPolicy list merged
    43  from multiple sources (mesh-level, namespace-level and workload-level).
    44  
    45  The command also supports reading from a standalone config dump file with flag -f.`,
    46  		Example: `  # Check AuthorizationPolicy applied to pod httpbin-88ddbcfdd-nt5jb:
    47    istioctl x authz check httpbin-88ddbcfdd-nt5jb
    48  
    49    # Check AuthorizationPolicy applied to one pod under a deployment
    50    istioctl x authz check deployment/productpage-v1
    51  
    52    # Check AuthorizationPolicy from Envoy config dump file:
    53    istioctl x authz check -f httpbin_config_dump.json`,
    54  		Args: func(cmd *cobra.Command, args []string) error {
    55  			if len(args) > 1 {
    56  				cmd.Println(cmd.UsageString())
    57  				return fmt.Errorf("check requires only <pod-name>[.<pod-namespace>]")
    58  			}
    59  			return nil
    60  		},
    61  		RunE: func(cmd *cobra.Command, args []string) error {
    62  			kubeClient, err := ctx.CLIClient()
    63  			if err != nil {
    64  				return fmt.Errorf("failed to create k8s client: %w", err)
    65  			}
    66  			var configDump *configdump.Wrapper
    67  			if configDumpFile != "" {
    68  				configDump, err = getConfigDumpFromFile(configDumpFile)
    69  				if err != nil {
    70  					return fmt.Errorf("failed to get config dump from file %s: %s", configDumpFile, err)
    71  				}
    72  			} else if len(args) == 1 {
    73  				podName, podNamespace, err := ctx.InferPodInfoFromTypedResource(args[0], ctx.Namespace())
    74  				if err != nil {
    75  					return err
    76  				}
    77  				configDump, err = getConfigDumpFromPod(kubeClient, podName, podNamespace)
    78  				if err != nil {
    79  					return fmt.Errorf("failed to get config dump from pod %s in %s", podName, podNamespace)
    80  				}
    81  			} else {
    82  				return fmt.Errorf("expecting pod name or config dump, found: %d", len(args))
    83  			}
    84  
    85  			analyzer, err := NewAnalyzer(configDump)
    86  			if err != nil {
    87  				return err
    88  			}
    89  			analyzer.Print(cmd.OutOrStdout())
    90  			return nil
    91  		},
    92  		ValidArgsFunction: completion.ValidPodsNameArgs(ctx),
    93  	}
    94  	cmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "",
    95  		"The json file with Envoy config dump to be checked")
    96  	return cmd
    97  }
    98  
    99  func getConfigDumpFromFile(filename string) (*configdump.Wrapper, error) {
   100  	file, err := os.Open(filename)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	defer func() {
   105  		if err := file.Close(); err != nil {
   106  			log.Errorf("failed to close %s: %s", filename, err)
   107  		}
   108  	}()
   109  	data, err := io.ReadAll(file)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	envoyConfig := &configdump.Wrapper{}
   115  	if err := envoyConfig.UnmarshalJSON(data); err != nil {
   116  		return nil, fmt.Errorf("failed to unmarshal proxy config: %s", err)
   117  	}
   118  	return envoyConfig, nil
   119  }
   120  
   121  func getConfigDumpFromPod(kubeClient kube.CLIClient, podName, podNamespace string) (*configdump.Wrapper, error) {
   122  	pods, err := kubeClient.GetIstioPods(context.TODO(), podNamespace, metav1.ListOptions{
   123  		FieldSelector: "metadata.name=" + podName,
   124  	})
   125  	if err != nil {
   126  		return nil, fmt.Errorf("failed to get pod: %s", err)
   127  	}
   128  	if len(pods) != 1 {
   129  		return nil, fmt.Errorf("expecting only 1 pod for %s.%s, found: %d", podName, podNamespace, len(pods))
   130  	}
   131  
   132  	data, err := kubeClient.EnvoyDo(context.TODO(), podName, podNamespace, "GET", "config_dump")
   133  	if err != nil {
   134  		return nil, fmt.Errorf("failed to get proxy config for %s.%s: %s", podName, podNamespace, err)
   135  	}
   136  	envoyConfig := &configdump.Wrapper{}
   137  	if err := envoyConfig.UnmarshalJSON(data); err != nil {
   138  		return nil, fmt.Errorf("failed to unmarshal proxy config: %s", err)
   139  	}
   140  	return envoyConfig, nil
   141  }
   142  
   143  // AuthZ groups commands used for inspecting and interacting the authorization policy.
   144  // Note: this is still under active development and is not ready for real use.
   145  func AuthZ(ctx cli.Context) *cobra.Command {
   146  	cmd := &cobra.Command{
   147  		Use:   "authz",
   148  		Short: "Inspect Istio AuthorizationPolicy",
   149  	}
   150  
   151  	cmd.AddCommand(checkCmd(ctx))
   152  	cmd.Long += "\n\n" + util.ExperimentalMsg
   153  	return cmd
   154  }