package inject import ( "context" "strings" appmesh "github.com/aws/aws-app-mesh-controller-for-k8s/apis/appmesh/v1beta2" "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/references" "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/virtualgateway" "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/virtualnode" "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/webhook" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) var injectLogger = ctrl.Log.WithName("appmesh_inject") type PodMutator interface { mutate(*corev1.Pod) error } type SidecarInjector struct { config Config accountID string awsRegion string controllerVersion string k8sVersion string k8sClient client.Client referenceResolver references.Resolver vgMembershipDesignator virtualgateway.MembershipDesignator vnMembershipDesignator virtualnode.MembershipDesignator } func NewSidecarInjector(cfg Config, accountID string, awsRegion string, controllerVersion string, k8sVersion string, k8sClient client.Client, referenceResolver references.Resolver, vnMembershipDesignator virtualnode.MembershipDesignator, vgMembershipDesignator virtualgateway.MembershipDesignator) *SidecarInjector { return &SidecarInjector{ config: cfg, accountID: accountID, awsRegion: awsRegion, controllerVersion: controllerVersion, k8sVersion: k8sVersion, k8sClient: k8sClient, referenceResolver: referenceResolver, vgMembershipDesignator: vgMembershipDesignator, vnMembershipDesignator: vnMembershipDesignator, } } func (m *SidecarInjector) Inject(ctx context.Context, pod *corev1.Pod) error { injectMode, err := m.determineSidecarInjectMode(ctx, pod) if err != nil { return errors.Wrap(err, "failed to determine sidecarInject mode") } if injectMode == sidecarInjectModeDisabled { return nil } vn, err := m.vnMembershipDesignator.Designate(ctx, pod) if err != nil { return err } vg, err := m.vgMembershipDesignator.DesignateForPod(ctx, pod) if err != nil { return err } if vn != nil && vg != nil { return errors.Errorf("sidecarInject enabled for both virtualNode %s and virtualGateway %s on pod %s. Please use podSelector on one", vn.Name, vg.Name, pod.Name) } if (vn == nil || vn.Spec.MeshRef == nil) && (vg == nil || vg.Spec.MeshRef == nil) { if injectMode == sidecarInjectModeEnabled { return errors.New("sidecarInject enabled but no matching VirtualNode or VirtualGateway found") } return nil } var msRef *appmesh.MeshReference if vn != nil { msRef = vn.Spec.MeshRef } else if vg != nil { msRef = vg.Spec.MeshRef } else { return errors.New("No matching VirtualNode or VirtualGateway found to resolve Mesh reference") } ms, err := m.referenceResolver.ResolveMeshReference(ctx, *msRef) if err != nil { return err } return m.injectAppMeshPatches(ms, vn, vg, pod) } func (m *SidecarInjector) injectAppMeshPatches(ms *appmesh.Mesh, vn *appmesh.VirtualNode, vg *appmesh.VirtualGateway, pod *corev1.Pod) error { // List out all the mutators in sequence var mutators []PodMutator if vn != nil { mutators = []PodMutator{ newProxyMutator(proxyMutatorConfig{ egressIgnoredIPs: m.config.IgnoredIPs, initProxyMutatorConfig: initProxyMutatorConfig{ containerImage: m.config.InitImage, cpuRequests: m.config.SidecarCpuRequests, memoryRequests: m.config.SidecarMemoryRequests, cpuLimits: m.config.SidecarCpuLimits, memoryLimits: m.config.SidecarMemoryLimits, }, }, vn), newEnvoyMutator(envoyMutatorConfig{ accountID: m.accountID, awsRegion: m.awsRegion, preview: m.config.Preview, enableSDS: m.config.EnableSDS, sdsUdsPath: m.config.SdsUdsPath, logLevel: m.config.LogLevel, adminAccessPort: m.config.EnvoyAdminAcessPort, adminAccessLogFile: m.config.EnvoyAdminAccessLogFile, preStopDelay: m.config.PreStopDelay, readinessProbeInitialDelay: m.config.ReadinessProbeInitialDelay, readinessProbePeriod: m.config.ReadinessProbePeriod, sidecarImageRepository: m.config.SidecarImageRepository, sidecarImageTag: m.config.SidecarImageTag, sidecarCPURequests: m.config.SidecarCpuRequests, sidecarMemoryRequests: m.config.SidecarMemoryRequests, sidecarCPULimits: m.config.SidecarCpuLimits, sidecarMemoryLimits: m.config.SidecarMemoryLimits, enableXrayTracing: m.config.EnableXrayTracing, xrayDaemonPort: m.config.XrayDaemonPort, xraySamplingRate: m.config.XraySamplingRate, enableJaegerTracing: m.config.EnableJaegerTracing, jaegerPort: m.config.JaegerPort, jaegerAddress: m.config.JaegerAddress, enableDatadogTracing: m.config.EnableDatadogTracing, datadogTracerPort: m.config.DatadogPort, datadogTracerAddress: m.config.DatadogAddress, enableStatsTags: m.config.EnableStatsTags, enableStatsD: m.config.EnableStatsD, statsDPort: m.config.StatsDPort, statsDAddress: m.config.StatsDAddress, statsDSocketPath: m.config.StatsDSocketPath, waitUntilProxyReady: m.config.WaitUntilProxyReady, controllerVersion: m.controllerVersion, k8sVersion: m.k8sVersion, useDualStackEndpoint: m.config.DualStackEndpoint, enableAdminAccessIPv6: m.config.EnvoyAdminAccessEnableIPv6, postStartTimeout: m.config.PostStartTimeout, postStartInterval: m.config.PostStartInterval, useFipsEndpoint: m.config.FipsEndpoint, awsAccessKeyId: m.config.EnvoyAwsAccessKeyId, awsSecretAccessKey: m.config.EnvoyAwsSecretAccessKey, awsSessionToken: m.config.EnvoyAwsSessionToken, }, ms, vn), newXrayMutator(xrayMutatorConfig{ awsRegion: m.awsRegion, sidecarCPURequests: m.config.SidecarCpuRequests, sidecarMemoryRequests: m.config.SidecarMemoryRequests, sidecarCPULimits: m.config.SidecarCpuLimits, sidecarMemoryLimits: m.config.SidecarMemoryLimits, xRayImage: m.config.XRayImage, xRayDaemonPort: m.config.XrayDaemonPort, xRayLogLevel: m.config.XrayLogLevel, xRayConfigRoleArn: m.config.XrayConfigRoleArn, }, m.config.EnableXrayTracing), newCloudMapHealthyReadinessGate(vn), newIAMForServiceAccountsMutator(m.config.EnableIAMForServiceAccounts), newECRSecretMutator(m.config.EnableECRSecret), } } else if vg != nil { mutators = []PodMutator{newVirtualGatewayEnvoyConfig(virtualGatwayEnvoyConfig{ accountID: m.accountID, awsRegion: m.awsRegion, preview: m.config.Preview, enableSDS: m.config.EnableSDS, sdsUdsPath: m.config.SdsUdsPath, logLevel: m.config.LogLevel, adminAccessPort: m.config.EnvoyAdminAcessPort, adminAccessLogFile: m.config.EnvoyAdminAccessLogFile, sidecarImageRepository: m.config.SidecarImageRepository, sidecarImageTag: m.config.SidecarImageTag, readinessProbeInitialDelay: m.config.ReadinessProbeInitialDelay, readinessProbePeriod: m.config.ReadinessProbePeriod, enableXrayTracing: m.config.EnableXrayTracing, xrayDaemonPort: m.config.XrayDaemonPort, xraySamplingRate: m.config.XraySamplingRate, enableJaegerTracing: m.config.EnableJaegerTracing, jaegerPort: m.config.JaegerPort, jaegerAddress: m.config.JaegerAddress, enableDatadogTracing: m.config.EnableDatadogTracing, datadogTracerPort: m.config.DatadogPort, datadogTracerAddress: m.config.DatadogAddress, enableStatsTags: m.config.EnableStatsTags, enableStatsD: m.config.EnableStatsD, statsDPort: m.config.StatsDPort, statsDAddress: m.config.StatsDAddress, statsDSocketPath: m.config.StatsDSocketPath, controllerVersion: m.controllerVersion, k8sVersion: m.k8sVersion, useDualStackEndpoint: m.config.DualStackEndpoint, enableAdminAccessIPv6: m.config.EnvoyAdminAccessEnableIPv6, useFipsEndpoint: m.config.FipsEndpoint, awsAccessKeyId: m.config.EnvoyAwsAccessKeyId, awsSecretAccessKey: m.config.EnvoyAwsSecretAccessKey, awsSessionToken: m.config.EnvoyAwsSessionToken, }, ms, vg), newXrayMutator(xrayMutatorConfig{ awsRegion: m.awsRegion, sidecarCPURequests: m.config.SidecarCpuRequests, sidecarMemoryRequests: m.config.SidecarMemoryRequests, sidecarCPULimits: m.config.SidecarCpuLimits, sidecarMemoryLimits: m.config.SidecarMemoryLimits, xRayImage: m.config.XRayImage, xRayDaemonPort: m.config.XrayDaemonPort, xRayLogLevel: m.config.XrayLogLevel, xRayConfigRoleArn: m.config.XrayConfigRoleArn, }, m.config.EnableXrayTracing), } } for _, mutator := range mutators { err := mutator.mutate(pod) if err != nil { return err } } return nil } type sidecarInjectMode string const ( // when enabled, a virtualNode must be found for pod, otherwise, pod will be rejected. sidecarInjectModeEnabled = "enabled" // when disabled, pod injection will be skipped. sidecarInjectModeDisabled = "disabled" // when unspecified, if a virtualNode is found for pod, pod will be injected, otherwise, pod will be skipped. sidecarInjectModeUnspecified = "unspecified" ) func (m *SidecarInjector) determineSidecarInjectMode(ctx context.Context, pod *corev1.Pod) (sidecarInjectMode, error) { // The injector webhook uses the namespaceSelector to filter which requests // are intercepted. This makes sure all the requests sent to the injector have // sidecar injection label `appmesh.k8s.aws/sidecarInjectorWebhook` specified with valid values: enabled and disabled // appmesh.k8s.aws/sidecarInjectorWebhook: disabled // The sidecar injector will not inject the sidecar into pods by default. Add the `appmesh.k8s.aws/sidecarInjectorWebhook` annotation // with value `enabled` to the pod template spec to override the default and enable injection // appmesh.k8s.aws/sidecarInjectorWebhook: enabled // The sidecar injector will inject the sidecar into pods by default. Add the `appmesh.k8s.aws/sidecarInjectorWebhook` annotation // with value `disabled` to the pod template spec to override the default and disable injection. var namespaceDefaultInjectionMode string // see https://github.com/kubernetes/kubernetes/issues/88282 and https://github.com/kubernetes/kubernetes/issues/76680 req := webhook.ContextGetAdmissionRequest(ctx) objectNS := &corev1.Namespace{} if err := m.k8sClient.Get(ctx, types.NamespacedName{Name: req.Namespace}, objectNS); err != nil { return sidecarInjectModeUnspecified, err } if v, ok := objectNS.ObjectMeta.Labels[AppMeshSidecarInjectAnnotation]; ok { namespaceDefaultInjectionMode = v } sidecarInjectAnnotation := namespaceDefaultInjectionMode if v, ok := pod.ObjectMeta.Annotations[AppMeshSidecarInjectAnnotation]; ok { sidecarInjectAnnotation = v } switch strings.ToLower(sidecarInjectAnnotation) { case "enabled": return sidecarInjectModeEnabled, nil case "disabled": return sidecarInjectModeDisabled, nil default: return sidecarInjectModeUnspecified, nil } }