mirror of
https://github.com/tailscale/tailscale.git
synced 2026-04-04 06:36:01 -04:00
This commit implements a reconciler for the new `ProxyGroupPolicy` custom resource. When created, all `ProxyGroupPolicy` resources within the same namespace are merged into two `ValidatingAdmissionPolicy` resources, one for egress and one for ingress. These policies use CEL expressions to limit the usage of the "tailscale.com/proxy-group" annotation on `Service` and `Ingress` resources on create & update. Included here is also a new e2e test that ensures that resources that violate the policy return an error on creation, and that once the policy is changed to allow them they can be created. Closes: https://github.com/tailscale/corp/issues/36830 Signed-off-by: David Bond <davidsbond93@gmail.com>
176 lines
4.2 KiB
Go
176 lines
4.2 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"tailscale.com/types/ptr"
|
|
)
|
|
|
|
func applyPebbleResources(ctx context.Context, cl client.Client) error {
|
|
owner := client.FieldOwner("k8s-test")
|
|
|
|
if err := cl.Patch(ctx, pebbleDeployment(pebbleTag), client.Apply, owner); err != nil {
|
|
return fmt.Errorf("failed to apply pebble Deployment: %w", err)
|
|
}
|
|
if err := cl.Patch(ctx, pebbleService(), client.Apply, owner); err != nil {
|
|
return fmt.Errorf("failed to apply pebble Service: %w", err)
|
|
}
|
|
if err := cl.Patch(ctx, tailscaleNamespace(), client.Apply, owner); err != nil {
|
|
return fmt.Errorf("failed to apply tailscale Namespace: %w", err)
|
|
}
|
|
if err := cl.Patch(ctx, pebbleExternalNameService(), client.Apply, owner); err != nil {
|
|
return fmt.Errorf("failed to apply pebble ExternalName Service: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func pebbleDeployment(tag string) *appsv1.Deployment {
|
|
return &appsv1.Deployment{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Deployment",
|
|
APIVersion: "apps/v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pebble",
|
|
Namespace: ns,
|
|
},
|
|
Spec: appsv1.DeploymentSpec{
|
|
Replicas: ptr.To[int32](1),
|
|
Selector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"app": "pebble",
|
|
},
|
|
},
|
|
Template: corev1.PodTemplateSpec{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Labels: map[string]string{
|
|
"app": "pebble",
|
|
},
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "pebble",
|
|
Image: fmt.Sprintf("ghcr.io/letsencrypt/pebble:%s", tag),
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
Args: []string{
|
|
"-dnsserver=localhost:8053",
|
|
"-strict",
|
|
},
|
|
Ports: []corev1.ContainerPort{
|
|
{
|
|
Name: "acme",
|
|
ContainerPort: 14000,
|
|
},
|
|
{
|
|
Name: "pebble-api",
|
|
ContainerPort: 15000,
|
|
},
|
|
},
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "PEBBLE_VA_NOSLEEP",
|
|
Value: "1",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "challtestsrv",
|
|
Image: fmt.Sprintf("ghcr.io/letsencrypt/pebble-challtestsrv:%s", tag),
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
Args: []string{"-defaultIPv6="},
|
|
Ports: []corev1.ContainerPort{
|
|
{
|
|
Name: "mgmt-api",
|
|
ContainerPort: 8055,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func pebbleService() *corev1.Service {
|
|
return &corev1.Service{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Service",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pebble",
|
|
Namespace: ns,
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Type: corev1.ServiceTypeClusterIP,
|
|
Selector: map[string]string{
|
|
"app": "pebble",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "acme",
|
|
Port: 14000,
|
|
TargetPort: intstr.FromInt(14000),
|
|
},
|
|
{
|
|
Name: "pebble-api",
|
|
Port: 15000,
|
|
TargetPort: intstr.FromInt(15000),
|
|
},
|
|
{
|
|
Name: "mgmt-api",
|
|
Port: 8055,
|
|
TargetPort: intstr.FromInt(8055),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func tailscaleNamespace() *corev1.Namespace {
|
|
return &corev1.Namespace{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Namespace",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tailscale",
|
|
},
|
|
}
|
|
}
|
|
|
|
// pebbleExternalNameService ensures the operator in the tailscale namespace
|
|
// can reach pebble on a DNS name (pebble) that matches its TLS cert.
|
|
func pebbleExternalNameService() *corev1.Service {
|
|
return &corev1.Service{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Service",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pebble",
|
|
Namespace: "tailscale",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Type: corev1.ServiceTypeExternalName,
|
|
Selector: map[string]string{
|
|
"app": "pebble",
|
|
},
|
|
ExternalName: "pebble.default.svc.cluster.local",
|
|
},
|
|
}
|
|
}
|