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
20 type Authorization struct {
21 Route string
22 Server string
23 ServerAuthorization string
24 AuthorizationPolicy string
25 }
26
27
28 var AuthorizationPolicyGVR = policyv1.SchemeGroupVersion.WithResource("authorizationpolicies")
29
30
31 var HTTPRouteGVR = policyv1.SchemeGroupVersion.WithResource("httproutes")
32
33
34 var SazGVR = serverauthorizationv1beta1.SchemeGroupVersion.WithResource("serverauthorizations")
35
36
37 var ServerGVR = serverv1beta2.SchemeGroupVersion.WithResource("servers")
38
39
40
41
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
169
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
193
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
236
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
263
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