...

Source file src/github.com/linkerd/linkerd2/pkg/k8s/policy.go

Documentation: github.com/linkerd/linkerd2/pkg/k8s

     1  package k8s
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"k8s.io/apimachinery/pkg/labels"
    10  
    11  	policyv1 "github.com/linkerd/linkerd2/controller/gen/apis/policy/v1alpha1"
    12  	serverv1beta2 "github.com/linkerd/linkerd2/controller/gen/apis/server/v1beta2"
    13  	serverauthorizationv1beta1 "github.com/linkerd/linkerd2/controller/gen/apis/serverauthorization/v1beta1"
    14  	corev1 "k8s.io/api/core/v1"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/client-go/kubernetes"
    17  )
    18  
    19  // Authorization holds the names of the resources involved in an authorization.
    20  type Authorization struct {
    21  	Route               string
    22  	Server              string
    23  	ServerAuthorization string
    24  	AuthorizationPolicy string
    25  }
    26  
    27  // AuthorizationPolicyGVR is the GroupVersionResource for the AuthorizationPolicy resource.
    28  var AuthorizationPolicyGVR = policyv1.SchemeGroupVersion.WithResource("authorizationpolicies")
    29  
    30  // HTTPRouteGVR is the GroupVersionResource for the HTTPRoute resource.
    31  var HTTPRouteGVR = policyv1.SchemeGroupVersion.WithResource("httproutes")
    32  
    33  // SazGVR is the GroupVersionResource for the ServerAuthorization resource.
    34  var SazGVR = serverauthorizationv1beta1.SchemeGroupVersion.WithResource("serverauthorizations")
    35  
    36  // ServerGVR is the GroupVersionResource for the Server resource.
    37  var ServerGVR = serverv1beta2.SchemeGroupVersion.WithResource("servers")
    38  
    39  // AuthorizationsForResource returns a list of ServerAuthorizations and
    40  // AuthorizationPolicies which apply to any Server or HttpRoute which select
    41  // pods belonging to the given resource.
    42  func AuthorizationsForResource(ctx context.Context, k8sAPI *KubernetesAPI, namespace string, resource string) ([]Authorization, error) {
    43  	pods, err := getPodsForResourceOrKind(ctx, k8sAPI, namespace, resource, "")
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	results := make([]Authorization, 0)
    49  
    50  	sazs, err := k8sAPI.L5dCrdClient.ServerauthorizationV1beta1().ServerAuthorizations(namespace).List(ctx, metav1.ListOptions{})
    51  	if err != nil {
    52  		fmt.Fprintf(os.Stderr, "Failed to get serverauthorization resources: %s\n", err)
    53  		os.Exit(1)
    54  	}
    55  
    56  	for _, saz := range sazs.Items {
    57  		var servers []serverv1beta2.Server
    58  
    59  		if saz.Spec.Server.Name != "" {
    60  			server, err := k8sAPI.L5dCrdClient.ServerV1beta2().Servers(saz.GetNamespace()).Get(ctx, saz.Spec.Server.Name, metav1.GetOptions{})
    61  			if err != nil {
    62  				fmt.Fprintf(os.Stderr, "ServerAuthorization/%s targets Server/%s but we failed to get it: %s\n", saz.Name, saz.Spec.Server.Name, err)
    63  				continue
    64  			}
    65  			servers = []serverv1beta2.Server{*server}
    66  		} else if saz.Spec.Server.Selector != nil {
    67  			selector, err := metav1.LabelSelectorAsSelector(saz.Spec.Server.Selector)
    68  			if err != nil {
    69  				fmt.Fprintf(os.Stderr, "Failed to parse Server selector for ServerAuthorization/%s: %s\n", saz.Name, err)
    70  				continue
    71  			}
    72  			serverList, err := k8sAPI.L5dCrdClient.ServerV1beta2().Servers(saz.GetNamespace()).List(ctx, metav1.ListOptions{LabelSelector: selector.String()})
    73  			if err != nil {
    74  				fmt.Fprintf(os.Stderr, "Failed to get Servers for ServerAuthorization/%s: %s\n", saz.Name, err)
    75  				continue
    76  			}
    77  			servers = serverList.Items
    78  		}
    79  
    80  		for _, server := range servers {
    81  			if serverIncludesPod(server, pods) {
    82  				results = append(results, Authorization{
    83  					Route:               "",
    84  					Server:              server.GetName(),
    85  					ServerAuthorization: saz.GetName(),
    86  					AuthorizationPolicy: "",
    87  				})
    88  			}
    89  		}
    90  	}
    91  
    92  	policies, err := k8sAPI.L5dCrdClient.PolicyV1alpha1().AuthorizationPolicies(namespace).List(ctx, metav1.ListOptions{})
    93  	if err != nil {
    94  		fmt.Fprintf(os.Stderr, "Failed to get AuthorizationPolicy resources: %s\n", err)
    95  		os.Exit(1)
    96  	}
    97  
    98  	allServersInNamespace := map[string]*serverv1beta2.ServerList{}
    99  
   100  	for _, p := range policies.Items {
   101  		target := p.Spec.TargetRef
   102  		if target.Kind == NamespaceKind && target.Group == K8sCoreAPIGroup {
   103  			serverList, ok := allServersInNamespace[p.Namespace]
   104  			if !ok {
   105  				serverList, err = k8sAPI.L5dCrdClient.ServerV1beta2().Servers(p.Namespace).List(ctx, metav1.ListOptions{})
   106  				if err != nil {
   107  					fmt.Fprintf(os.Stderr, "Failed to get Servers for Namespace/%s: %s\n", p.Namespace, err)
   108  					continue
   109  				}
   110  
   111  				allServersInNamespace[p.Namespace] = serverList
   112  			}
   113  
   114  			for _, server := range serverList.Items {
   115  				if serverIncludesPod(server, pods) {
   116  					results = append(results, Authorization{
   117  						Route:               "",
   118  						Server:              server.GetName(),
   119  						ServerAuthorization: "",
   120  						AuthorizationPolicy: p.GetName(),
   121  					})
   122  				}
   123  			}
   124  		} else if target.Kind == ServerKind && target.Group == PolicyAPIGroup {
   125  			server, err := k8sAPI.L5dCrdClient.ServerV1beta2().Servers(p.Namespace).Get(ctx, string(target.Name), metav1.GetOptions{})
   126  			if err != nil {
   127  				fmt.Fprintf(os.Stderr, "AuthorizationPolicy/%s targets Server/%s but we failed to get it: %s\n", p.Name, target.Name, err)
   128  				continue
   129  			}
   130  			if serverIncludesPod(*server, pods) {
   131  				results = append(results, Authorization{
   132  					Route:               "",
   133  					Server:              server.GetName(),
   134  					ServerAuthorization: "",
   135  					AuthorizationPolicy: p.GetName(),
   136  				})
   137  			}
   138  		} else if target.Kind == HTTPRouteKind && target.Group == PolicyAPIGroup {
   139  			route, err := k8sAPI.L5dCrdClient.PolicyV1alpha1().HTTPRoutes(p.Namespace).Get(ctx, string(target.Name), metav1.GetOptions{})
   140  			if err != nil {
   141  				fmt.Fprintf(os.Stderr, "AuthorizationPolicy/%s targets HTTPRoute/%s but we failed to get it: %s\n", p.Name, target.Name, err)
   142  				continue
   143  			}
   144  			for _, parent := range route.Spec.ParentRefs {
   145  				if parent.Kind != nil && *parent.Kind == ServerKind &&
   146  					parent.Group != nil && *parent.Group == PolicyAPIGroup {
   147  					server, err := k8sAPI.L5dCrdClient.ServerV1beta2().Servers(p.Namespace).Get(ctx, string(parent.Name), metav1.GetOptions{})
   148  					if err != nil {
   149  						fmt.Fprintf(os.Stderr, "HTTPRoute/%s belongs to Server/%s but we failed to get it: %s\n", target.Name, parent.Name, err)
   150  						continue
   151  					}
   152  					if serverIncludesPod(*server, pods) {
   153  						results = append(results, Authorization{
   154  							Route:               route.GetName(),
   155  							Server:              server.GetName(),
   156  							ServerAuthorization: "",
   157  							AuthorizationPolicy: p.GetName(),
   158  						})
   159  					}
   160  				}
   161  			}
   162  		}
   163  	}
   164  
   165  	return results, nil
   166  }
   167  
   168  // ServersForResource returns a list of Server names of Servers which select pods
   169  // belonging to the given resource.
   170  func ServersForResource(ctx context.Context, k8sAPI *KubernetesAPI, namespace string, resource string, labelSelector string) ([]string, error) {
   171  	pods, err := getPodsForResourceOrKind(ctx, k8sAPI, namespace, resource, labelSelector)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	results := make([]string, 0)
   177  
   178  	servers, err := k8sAPI.L5dCrdClient.ServerV1beta2().Servers(namespace).List(ctx, metav1.ListOptions{})
   179  	if err != nil {
   180  		fmt.Fprintf(os.Stderr, "Failed to get serverauthorization resources: %s\n", err)
   181  		os.Exit(1)
   182  	}
   183  
   184  	for _, server := range servers.Items {
   185  		if serverIncludesPod(server, pods) {
   186  			results = append(results, server.GetName())
   187  		}
   188  	}
   189  	return results, nil
   190  }
   191  
   192  // ServerAuthorizationsForServer returns a list of ServerAuthorization names of
   193  // ServerAuthorizations which select the given Server.
   194  func ServerAuthorizationsForServer(ctx context.Context, k8sAPI *KubernetesAPI, namespace string, server string) ([]string, error) {
   195  	results := make([]string, 0)
   196  
   197  	sazs, err := k8sAPI.L5dCrdClient.ServerauthorizationV1beta1().ServerAuthorizations(namespace).List(ctx, metav1.ListOptions{})
   198  	if err != nil {
   199  		fmt.Fprintf(os.Stderr, "Failed to get serverauthorization resources: %s\n", err)
   200  		os.Exit(1)
   201  	}
   202  
   203  	for _, saz := range sazs.Items {
   204  		if saz.Spec.Server.Name != "" {
   205  			s, err := k8sAPI.DynamicClient.Resource(ServerGVR).Namespace(saz.GetNamespace()).Get(ctx, saz.Spec.Server.Name, metav1.GetOptions{})
   206  			if err != nil {
   207  				fmt.Fprintf(os.Stderr, "Failed to get server %s: %s\n", saz.Spec.Server.Name, err)
   208  				os.Exit(1)
   209  			}
   210  			if s.GetName() == server {
   211  				results = append(results, saz.GetName())
   212  			}
   213  		} else if saz.Spec.Server.Selector != nil {
   214  			selector, err := metav1.LabelSelectorAsSelector(saz.Spec.Server.Selector)
   215  			if err != nil {
   216  				fmt.Fprintf(os.Stderr, "Failed to get servers: %s\n", err)
   217  				os.Exit(1)
   218  			}
   219  			serverList, err := k8sAPI.L5dCrdClient.ServerV1beta2().Servers(saz.GetNamespace()).List(ctx, metav1.ListOptions{LabelSelector: selector.String()})
   220  			if err != nil {
   221  				fmt.Fprintf(os.Stderr, "Failed to get servers: %s\n", err)
   222  				os.Exit(1)
   223  			}
   224  			for _, s := range serverList.Items {
   225  				if s.GetName() == server {
   226  					results = append(results, saz.GetName())
   227  					break
   228  				}
   229  			}
   230  		}
   231  	}
   232  	return results, nil
   233  }
   234  
   235  // serverIncludesPod returns true the given server selects any of the given pods
   236  // and that pod uses the server's port.
   237  func serverIncludesPod(server serverv1beta2.Server, pods []corev1.Pod) bool {
   238  	if server.Spec.PodSelector == nil {
   239  		return false
   240  	}
   241  
   242  	selector, err := metav1.LabelSelectorAsSelector(server.Spec.PodSelector)
   243  	if err != nil {
   244  		fmt.Fprintf(os.Stderr, "Failed to parse PodSelector of Server/%s: %s\n", server.Name, err)
   245  		return false
   246  	}
   247  
   248  	for _, pod := range pods {
   249  		if selector.Matches(labels.Set(pod.Labels)) {
   250  			for _, container := range pod.Spec.Containers {
   251  				for _, p := range container.Ports {
   252  					if server.Spec.Port.IntVal == p.ContainerPort || server.Spec.Port.StrVal == p.Name {
   253  						return true
   254  					}
   255  				}
   256  			}
   257  		}
   258  	}
   259  	return false
   260  }
   261  
   262  // getPodsForResourceOrKind is similar to getPodsForResource, but also supports
   263  // querying for all resources of a given kind (i.e. when resource name is unspecified).
   264  func getPodsForResourceOrKind(ctx context.Context, k8sAPI kubernetes.Interface, namespace string, resource string, labelSelector string) ([]corev1.Pod, error) {
   265  
   266  	elems := strings.Split(resource, "/")
   267  	if len(elems) > 2 {
   268  		return nil, fmt.Errorf("invalid resource: %s", resource)
   269  	}
   270  	if len(elems) == 2 {
   271  		pods, err := GetPodsFor(ctx, k8sAPI, namespace, resource)
   272  		if err != nil {
   273  			fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   274  			os.Exit(1)
   275  		}
   276  		return pods, nil
   277  	}
   278  	pods := []corev1.Pod{}
   279  
   280  	typ, err := CanonicalResourceNameFromFriendlyName(elems[0])
   281  	if err != nil {
   282  		return nil, fmt.Errorf("invalid resource: %s", resource)
   283  	}
   284  
   285  	selector := metav1.ListOptions{
   286  		LabelSelector: labelSelector,
   287  	}
   288  
   289  	switch typ {
   290  	case Pod:
   291  		ps, err := k8sAPI.CoreV1().Pods(namespace).List(ctx, selector)
   292  		if err != nil {
   293  			fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   294  			os.Exit(1)
   295  		}
   296  		pods = append(pods, ps.Items...)
   297  
   298  	case CronJob:
   299  		jobs, err := k8sAPI.BatchV1().CronJobs(namespace).List(ctx, selector)
   300  		if err != nil {
   301  			fmt.Fprintf(os.Stderr, "failed to get cronjobs: %s", err)
   302  			os.Exit(1)
   303  		}
   304  		for _, job := range jobs.Items {
   305  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", CronJob, job.Name))
   306  			if err != nil {
   307  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   308  				os.Exit(1)
   309  			}
   310  			pods = append(pods, ps...)
   311  		}
   312  
   313  	case DaemonSet:
   314  		dss, err := k8sAPI.AppsV1().DaemonSets(namespace).List(ctx, selector)
   315  		if err != nil {
   316  			fmt.Fprintf(os.Stderr, "failed to get demonsets: %s", err)
   317  			os.Exit(1)
   318  		}
   319  		for _, ds := range dss.Items {
   320  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", DaemonSet, ds.Name))
   321  			if err != nil {
   322  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   323  				os.Exit(1)
   324  			}
   325  			pods = append(pods, ps...)
   326  		}
   327  
   328  	case Deployment:
   329  		deploys, err := k8sAPI.AppsV1().Deployments(namespace).List(ctx, selector)
   330  		if err != nil {
   331  			fmt.Fprintf(os.Stderr, "failed to get deployments: %s", err)
   332  			os.Exit(1)
   333  		}
   334  		for _, deploy := range deploys.Items {
   335  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", Deployment, deploy.Name))
   336  			if err != nil {
   337  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   338  				os.Exit(1)
   339  			}
   340  			pods = append(pods, ps...)
   341  		}
   342  
   343  	case Job:
   344  		jobs, err := k8sAPI.BatchV1().Jobs(namespace).List(ctx, selector)
   345  		if err != nil {
   346  			fmt.Fprintf(os.Stderr, "failed to get jobs: %s", err)
   347  			os.Exit(1)
   348  		}
   349  		for _, job := range jobs.Items {
   350  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", Job, job.Name))
   351  			if err != nil {
   352  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   353  				os.Exit(1)
   354  			}
   355  			pods = append(pods, ps...)
   356  		}
   357  
   358  	case ReplicaSet:
   359  		rss, err := k8sAPI.AppsV1().ReplicaSets(namespace).List(ctx, selector)
   360  		if err != nil {
   361  			fmt.Fprintf(os.Stderr, "failed to get replicasets: %s", err)
   362  			os.Exit(1)
   363  		}
   364  		for _, rs := range rss.Items {
   365  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", ReplicaSet, rs.Name))
   366  			if err != nil {
   367  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   368  				os.Exit(1)
   369  			}
   370  			pods = append(pods, ps...)
   371  		}
   372  
   373  	case ReplicationController:
   374  		rcs, err := k8sAPI.CoreV1().ReplicationControllers(namespace).List(ctx, selector)
   375  		if err != nil {
   376  			fmt.Fprintf(os.Stderr, "failed to get replicationcontrollers: %s", err)
   377  			os.Exit(1)
   378  		}
   379  		for _, rc := range rcs.Items {
   380  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", ReplicationController, rc.Name))
   381  			if err != nil {
   382  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   383  				os.Exit(1)
   384  			}
   385  			pods = append(pods, ps...)
   386  		}
   387  
   388  	case StatefulSet:
   389  		sss, err := k8sAPI.AppsV1().StatefulSets(namespace).List(ctx, selector)
   390  		if err != nil {
   391  			fmt.Fprintf(os.Stderr, "failed to get statefulsets: %s", err)
   392  			os.Exit(1)
   393  		}
   394  		for _, ss := range sss.Items {
   395  			ps, err := GetPodsFor(ctx, k8sAPI, namespace, fmt.Sprintf("%s/%s", StatefulSet, ss.Name))
   396  			if err != nil {
   397  				fmt.Fprintf(os.Stderr, "failed to get pods: %s", err)
   398  				os.Exit(1)
   399  			}
   400  			pods = append(pods, ps...)
   401  		}
   402  
   403  	default:
   404  		return nil, fmt.Errorf("unsupported resource type: %s", typ)
   405  	}
   406  	return pods, nil
   407  }
   408  

View as plain text