diff --git a/cmd/atecontroller/internal/controllers/workerpool_apply.go b/cmd/atecontroller/internal/controllers/workerpool_apply.go index 1eef42264..f6d46654c 100644 --- a/cmd/atecontroller/internal/controllers/workerpool_apply.go +++ b/cmd/atecontroller/internal/controllers/workerpool_apply.go @@ -24,6 +24,13 @@ import ( atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1" ) +const ( + atunnelIdentityVolume = "atunnel-identity" + atunnelIdentityMountPath = "/run/podidentity.podcert.ate.dev" + atunnelEgressTrustVolume = "atunnel-egress-trust" + atunnelEgressTrustMountPath = "/run/servicedns.podcert.ate.dev" +) + // buildDeploymentApplyConfig constructs the SSA apply configuration for the // Deployment managed by a WorkerPool. Only fields owned by this controller // are declared here. @@ -33,7 +40,16 @@ func buildDeploymentApplyConfig(wp *atev1alpha1.WorkerPool) *appsv1ac.Deployment WithImage(wp.Spec.AteomImage). WithArgs( "--pod-uid=$(POD_UID)", + "--atunnel-listen-address=0.0.0.0:443", + "--atunnel-credential-bundle="+atunnelIdentityMountPath+"/credential-bundle.pem", + "--atunnel-trust-bundle="+atunnelIdentityMountPath+"/trust-bundle.pem", + "--atunnel-egress-listen-address=0.0.0.0:15001", + "--atunnel-egress-trust-bundle="+atunnelEgressTrustMountPath+"/trust-bundle.pem", ). + WithPorts(corev1ac.ContainerPort(). + WithName("https"). + WithContainerPort(443). + WithProtocol(corev1.ProtocolTCP)). WithSecurityContext(ateomSecurityContext(wp.Spec.SandboxClass)). WithEnv( corev1ac.EnvVar(). @@ -42,20 +58,61 @@ func buildDeploymentApplyConfig(wp *atev1alpha1.WorkerPool) *appsv1ac.Deployment WithFieldRef(corev1ac.ObjectFieldSelector(). WithFieldPath("metadata.uid"))), ). - WithVolumeMounts(corev1ac.VolumeMount(). - WithName("run-ateom"). - WithMountPath(ateompath.BasePath). - WithMountPropagation(corev1.MountPropagationHostToContainer)) + WithVolumeMounts( + corev1ac.VolumeMount(). + WithName("run-ateom"). + WithMountPath(ateompath.BasePath). + WithMountPropagation(corev1.MountPropagationHostToContainer), + corev1ac.VolumeMount(). + WithName(atunnelIdentityVolume). + WithMountPath(atunnelIdentityMountPath). + WithReadOnly(true), + corev1ac.VolumeMount(). + WithName(atunnelEgressTrustVolume). + WithMountPath(atunnelEgressTrustMountPath). + WithReadOnly(true), + ) podSpecAC := corev1ac.PodSpec(). WithSecurityContext(corev1ac.PodSecurityContext(). WithRunAsUser(0). WithRunAsGroup(0)). - WithVolumes(corev1ac.Volume(). - WithName("run-ateom"). - WithHostPath(corev1ac.HostPathVolumeSource(). - WithPath(ateompath.BasePath). - WithType(corev1.HostPathDirectoryOrCreate))) + WithVolumes( + corev1ac.Volume(). + WithName("run-ateom"). + WithHostPath(corev1ac.HostPathVolumeSource(). + WithPath(ateompath.BasePath). + WithType(corev1.HostPathDirectoryOrCreate)), + corev1ac.Volume(). + WithName(atunnelIdentityVolume). + WithProjected(corev1ac.ProjectedVolumeSource(). + WithSources( + corev1ac.VolumeProjection(). + WithPodCertificate(corev1ac.PodCertificateProjection(). + WithSignerName("podidentity.podcert.ate.dev/identity"). + WithKeyType("ECDSAP256"). + WithCredentialBundlePath("credential-bundle.pem")), + corev1ac.VolumeProjection(). + WithClusterTrustBundle(corev1ac.ClusterTrustBundleProjection(). + WithSignerName("podidentity.podcert.ate.dev/identity"). + WithLabelSelector(metav1ac.LabelSelector(). + WithMatchLabels(map[string]string{"podcert.ate.dev/canarying": "live"})). + WithPath("trust-bundle.pem")), + ), + ), + corev1ac.Volume(). + WithName(atunnelEgressTrustVolume). + WithProjected(corev1ac.ProjectedVolumeSource(). + WithSources( + corev1ac.VolumeProjection(). + WithClusterTrustBundle(corev1ac.ClusterTrustBundleProjection(). + WithSignerName("servicedns.podcert.ate.dev/identity"). + WithLabelSelector(metav1ac.LabelSelector(). + WithMatchLabels(map[string]string{"podcert.ate.dev/canarying": "live"})). + WithPath("trust-bundle.pem")), + ), + ), + ) applyWorkerPoolPodTemplate(podSpecAC, containerAC, wp.Spec.Template) maybeApplyMicroVMPodShape(podSpecAC, containerAC, wp.Spec.SandboxClass) diff --git a/cmd/atecontroller/internal/controllers/workerpool_apply_test.go b/cmd/atecontroller/internal/controllers/workerpool_apply_test.go index d54e277b2..4feb117a0 100644 --- a/cmd/atecontroller/internal/controllers/workerpool_apply_test.go +++ b/cmd/atecontroller/internal/controllers/workerpool_apply_test.go @@ -327,15 +327,57 @@ func expectedDeploymentApplyConfig(mutatePodSpec func(*corev1ac.PodSpecApplyConf WithSecurityContext(corev1ac.PodSecurityContext(). WithRunAsUser(0). WithRunAsGroup(0)). - WithVolumes(corev1ac.Volume(). - WithName("run-ateom"). - WithHostPath(corev1ac.HostPathVolumeSource(). - WithPath(ateompath.BasePath). - WithType(corev1.HostPathDirectoryOrCreate))). + WithVolumes( + corev1ac.Volume(). + WithName("run-ateom"). + WithHostPath(corev1ac.HostPathVolumeSource(). + WithPath(ateompath.BasePath). + WithType(corev1.HostPathDirectoryOrCreate)), + corev1ac.Volume(). + WithName(atunnelIdentityVolume). + WithProjected(corev1ac.ProjectedVolumeSource(). + WithSources( + corev1ac.VolumeProjection(). + WithPodCertificate(corev1ac.PodCertificateProjection(). + WithSignerName("podidentity.podcert.ate.dev/identity"). + WithKeyType("ECDSAP256"). + WithCredentialBundlePath("credential-bundle.pem")), + corev1ac.VolumeProjection(). + WithClusterTrustBundle(corev1ac.ClusterTrustBundleProjection(). + WithSignerName("podidentity.podcert.ate.dev/identity"). + WithLabelSelector(metav1ac.LabelSelector(). + WithMatchLabels(map[string]string{"podcert.ate.dev/canarying": "live"})). + WithPath("trust-bundle.pem")), + ), + ), + corev1ac.Volume(). + WithName(atunnelEgressTrustVolume). + WithProjected(corev1ac.ProjectedVolumeSource(). + WithSources( + corev1ac.VolumeProjection(). + WithClusterTrustBundle(corev1ac.ClusterTrustBundleProjection(). + WithSignerName("servicedns.podcert.ate.dev/identity"). + WithLabelSelector(metav1ac.LabelSelector(). + WithMatchLabels(map[string]string{"podcert.ate.dev/canarying": "live"})). + WithPath("trust-bundle.pem")), + ), + ), + ). WithContainers(corev1ac.Container(). WithName("ateom"). WithImage(wp.Spec.AteomImage). - WithArgs("--pod-uid=$(POD_UID)"). + WithArgs( + "--pod-uid=$(POD_UID)", + "--atunnel-listen-address=0.0.0.0:443", + "--atunnel-credential-bundle="+atunnelIdentityMountPath+"/credential-bundle.pem", + "--atunnel-trust-bundle="+atunnelIdentityMountPath+"/trust-bundle.pem", + "--atunnel-egress-listen-address=0.0.0.0:15001", + "--atunnel-egress-trust-bundle="+atunnelEgressTrustMountPath+"/trust-bundle.pem", + ). + WithPorts(corev1ac.ContainerPort(). + WithName("https"). + WithContainerPort(443). + WithProtocol(corev1.ProtocolTCP)). WithSecurityContext(corev1ac.SecurityContext(). WithRunAsUser(0). WithRunAsGroup(0). @@ -350,10 +392,20 @@ func expectedDeploymentApplyConfig(mutatePodSpec func(*corev1ac.PodSpecApplyConf WithValueFrom(corev1ac.EnvVarSource(). WithFieldRef(corev1ac.ObjectFieldSelector(). WithFieldPath("metadata.uid")))). - WithVolumeMounts(corev1ac.VolumeMount(). - WithName("run-ateom"). - WithMountPath(ateompath.BasePath). - WithMountPropagation(corev1.MountPropagationHostToContainer)). + WithVolumeMounts( + corev1ac.VolumeMount(). + WithName("run-ateom"). + WithMountPath(ateompath.BasePath). + WithMountPropagation(corev1.MountPropagationHostToContainer), + corev1ac.VolumeMount(). + WithName(atunnelIdentityVolume). + WithMountPath(atunnelIdentityMountPath). + WithReadOnly(true), + corev1ac.VolumeMount(). + WithName(atunnelEgressTrustVolume). + WithMountPath(atunnelEgressTrustMountPath). + WithReadOnly(true), + ). WithResources(corev1ac.ResourceRequirements())) podSpecAC.NodeSelector = map[string]string{} diff --git a/cmd/atecontroller/internal/controllers/workerpool_controller_test.go b/cmd/atecontroller/internal/controllers/workerpool_controller_test.go index c620988a9..a93167de7 100644 --- a/cmd/atecontroller/internal/controllers/workerpool_controller_test.go +++ b/cmd/atecontroller/internal/controllers/workerpool_controller_test.go @@ -138,8 +138,10 @@ func TestWorkerPoolCreatesDeployment(t *testing.T) { if len(dep.OwnerReferences) == 0 || dep.OwnerReferences[0].Name != wp.Name { return false, nil } - return len(dep.Spec.Template.Spec.Volumes) == 1 && - dep.Spec.Template.Spec.Volumes[0].Name == "run-ateom", nil + return len(dep.Spec.Template.Spec.Volumes) == 3 && + dep.Spec.Template.Spec.Volumes[0].Name == "run-ateom" && + dep.Spec.Template.Spec.Volumes[1].Name == atunnelIdentityVolume && + dep.Spec.Template.Spec.Volumes[2].Name == atunnelEgressTrustVolume, nil }) } diff --git a/cmd/atenet/README.md b/cmd/atenet/README.md index 9200e0f9c..13738e4df 100644 --- a/cmd/atenet/README.md +++ b/cmd/atenet/README.md @@ -9,8 +9,6 @@ This is built as a single binary for convenience in the prototyping. ## Cluster deployment -![atenet diagram](atenet-diagram.png) - ### router (Note: this deployment model combines Envoy dataplane with the router. This will @@ -22,6 +20,9 @@ likely be split in the future for better scalability.) * atenet router * Service will expose: * Envoy port 80 and 443. +* Upstream: Envoy's `ORIGINAL_DST` actor cluster dials the actor's in-worker + `atunnel` ingress server on the worker pod's port 443 over mTLS, using the + address `atenet router`'s ext_proc resolved into `x-ate-original-dst`. RBAC permissions: * read, list on ActorTemplate diff --git a/cmd/atenet/atenet-diagram.png b/cmd/atenet/atenet-diagram.png deleted file mode 100644 index c77c5baad..000000000 Binary files a/cmd/atenet/atenet-diagram.png and /dev/null differ diff --git a/cmd/atenet/internal/dns/README.md b/cmd/atenet/internal/dns/README.md index 90196b7ba..a134e829a 100644 --- a/cmd/atenet/internal/dns/README.md +++ b/cmd/atenet/internal/dns/README.md @@ -4,7 +4,7 @@ The DNS Controller orchestrates the configuration needed to setup the ATE routin We want to resolve requests for ..actors.resources.substrate.ate.dev to the router service address. -* Stub resolver mode: orchestrate running a CoreDNS instance with the actor name mapped to the router service address. +* Stub resolver mode: orchestrate running a CoreDNS instance with the actor name mapped to the atenet-router service address. Cluster resources: diff --git a/cmd/atenet/internal/router/cmd.go b/cmd/atenet/internal/router/cmd.go index 3e7a6957b..9be4d7572 100644 --- a/cmd/atenet/internal/router/cmd.go +++ b/cmd/atenet/internal/router/cmd.go @@ -54,6 +54,9 @@ func NewRouterCmd() *cobra.Command { cmd.Flags().DurationVar(&cfg.HealthInterval, "health-interval", 1*time.Second, "Interval for checking health of dependent services") cmd.Flags().IntVar(&cfg.HttpsPort, "port-https", 8443, "TCP port for HTTPS workload traffic entering through the Envoy Router") cmd.Flags().StringVar(&cfg.EnvoyCertPath, "envoy-cert-path", "", "Path to the Envoy certificate file.") + cmd.Flags().StringVar(&cfg.UpstreamClientCertPath, "upstream-client-cert-path", "/run/podidentity.podcert.ate.dev/credential-bundle.pem", "PEM credential bundle (cert+key) the router presents as the client cert when dialing the actor's atunnel ingress server over mTLS. Empty disables upstream mTLS (legacy plaintext pod-IP:80).") + cmd.Flags().StringVar(&cfg.UpstreamTrustPath, "upstream-trust-bundle-path", "/run/podidentity.podcert.ate.dev/trust-bundle.pem", "PEM trust bundle used to validate the actor's atunnel ingress server certificate.") + cmd.Flags().StringVar(&cfg.UpstreamSpiffePrefix, "upstream-spiffe-prefix", "spiffe://cluster.local/", "SPIFFE URI SAN prefix (trust domain) the actor's atunnel server cert must match. Empty falls back to default SAN check against the dialed pod IP (which SPIFFE-only certs never match).") cmd.Flags().StringVar(&cfg.OtlpCollectorAddress, "otlp-collector-address", "", "host:port of the OTLP gRPC collector that Envoy reports tracing spans to (empty disables Envoy tracing)") cmd.Flags().StringVar(&cfg.Auth.AteapiCAFile, "ateapi-ca-file", "", "PEM file with CAs trusted to verify the ateapi server cert. Required.") cmd.Flags().StringVar(&cfg.Auth.AteapiClientCertPath, "ateapi-client-cert", "", "Credential bundle presented as the client certificate when dialing ateapi. Required unless --ateapi-use-token-auth is set, ignored otherwise.") diff --git a/cmd/atenet/internal/router/config.go b/cmd/atenet/internal/router/config.go index 83893ea9b..e1aa33d82 100644 --- a/cmd/atenet/internal/router/config.go +++ b/cmd/atenet/internal/router/config.go @@ -48,8 +48,17 @@ type routerConfig struct { HealthInterval time.Duration HttpsPort int EnvoyCertPath string - LogLevel string - MetricsAddr string + // UpstreamClientCertPath is the router's podidentity credential bundle + // (cert+key) presented as the client cert when dialing the actor's atunnel + // ingress server over mTLS. UpstreamTrustPath is the CA bundle used to + // validate that server. Empty UpstreamClientCertPath disables upstream mTLS. + UpstreamClientCertPath string + UpstreamTrustPath string + // UpstreamSpiffePrefix validates the actor's atunnel server cert by its + // SPIFFE URI SAN prefix (trust domain) instead of the dialed pod IP. + UpstreamSpiffePrefix string + LogLevel string + MetricsAddr string // OtlpCollectorAddress is the host:port of the OTLP gRPC collector that // Envoy reports tracing spans to. Empty disables Envoy-side tracing. OtlpCollectorAddress string diff --git a/cmd/atenet/internal/router/extproc.go b/cmd/atenet/internal/router/extproc.go index ce7617415..14d4fbd1b 100644 --- a/cmd/atenet/internal/router/extproc.go +++ b/cmd/atenet/internal/router/extproc.go @@ -170,14 +170,22 @@ func (s *ExtProcServer) handleRequestHeaders( "actor %s routing failed", actorRef) } + // The actor is reached through the in-worker atunnel ingress server, which + // listens on :443 (mTLS) and forwards to the actor's :80. The worker no + // longer DNATs pod-IP:80 to the actor, so the router dials :443 and the + // ORIGINAL_DST cluster's upstream TLS context presents the router's + // podidentity client cert (see buildOriginalDstCluster and + // buildUpstreamTransportSocket). // TODO(bowei) -- handle more than port 80 on the actor. - targetAddr := net.JoinHostPort(workerIP, "80") + targetAddr := net.JoinHostPort(workerIP, "443") slog.InfoContext(ctx, "Route ok", slog.Any("actor", actorRef), slog.String("targetAddr", targetAddr)) - // Route by rewriting the :authority header. + // Route by telling the ORIGINAL_DST cluster which worker atunnel address to + // dial, without touching :authority — atunnel authorizes the actor by the + // original Host (actor DNS name). mutation := &extprocv3.HeaderMutation{} - addAuthorityMutation(targetAddr, mutation) + addOriginalDstMutation(targetAddr, mutation) return &extprocv3.HeadersResponse{ Response: &extprocv3.CommonResponse{ diff --git a/cmd/atenet/internal/router/extproc_out.go b/cmd/atenet/internal/router/extproc_out.go index 98d21d93e..6ca3fdf46 100644 --- a/cmd/atenet/internal/router/extproc_out.go +++ b/cmd/atenet/internal/router/extproc_out.go @@ -32,12 +32,22 @@ type reqError struct { func (e *reqError) Error() string { return e.msg } func (e *reqError) Unwrap() error { return e.cause } -func addAuthorityMutation(auth string, mut *extproc.HeaderMutation) { +// addOriginalDstMutation sets the header the ORIGINAL_DST cluster reads to pick +// the upstream address (the worker atunnel IP:443). Unlike an :authority +// rewrite it leaves the request Host intact, so atunnel still sees the actor +// DNS name and can authorize the active actor. +// +// Nothing strips this header from the incoming request, so overwrite rather +// than append: a client-supplied value must never influence the address Envoy +// dials. ext_proc mutations already default to replace, but the default is +// split across the deprecated append field and append_action — pin it. +func addOriginalDstMutation(dst string, mut *extproc.HeaderMutation) { mut.SetHeaders = append(mut.SetHeaders, &corev3.HeaderValueOption{ + AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, Header: &corev3.HeaderValue{ - Key: ":authority", - RawValue: []byte(auth), + Key: OriginalDstHeader, + RawValue: []byte(dst), }, }, ) diff --git a/cmd/atenet/internal/router/extproc_test.go b/cmd/atenet/internal/router/extproc_test.go index 873846353..f979d48ee 100644 --- a/cmd/atenet/internal/router/extproc_test.go +++ b/cmd/atenet/internal/router/extproc_test.go @@ -171,7 +171,7 @@ func TestExtProcHeadersEvaluation(t *testing.T) { }, }, expectErr: false, - expectedTarget: "10.0.0.52:80", + expectedTarget: "10.0.0.52:443", }, } @@ -235,7 +235,7 @@ func TestExtProcHeadersEvaluation(t *testing.T) { } headerOption := mutation.GetSetHeaders()[0] - if strings.ToLower(headerOption.Header.Key) != ":authority" { + if strings.ToLower(headerOption.Header.Key) != OriginalDstHeader { t.Errorf("invalid resulting dynamic parameter key: %s", headerOption.Header.Key) } diff --git a/cmd/atenet/internal/router/resumer_test.go b/cmd/atenet/internal/router/resumer_test.go index c112bf985..ed3bffef8 100644 --- a/cmd/atenet/internal/router/resumer_test.go +++ b/cmd/atenet/internal/router/resumer_test.go @@ -53,7 +53,6 @@ func TestActorResumer_ResumeActor(t *testing.T) { resumeCalled++ return &ateapipb.ResumeActorResponse{ Actor: &ateapipb.Actor{ - Metadata: &ateapipb.ResourceMetadata{Name: testActorName}, Status: ateapipb.Actor_STATUS_RUNNING, AteomPodIp: expectedIP, }, @@ -84,7 +83,6 @@ func TestActorResumer_ResumeActor(t *testing.T) { } return &ateapipb.ResumeActorResponse{ Actor: &ateapipb.Actor{ - Metadata: &ateapipb.ResourceMetadata{Name: testActorName}, Status: ateapipb.Actor_STATUS_RUNNING, AteomPodIp: expectedIP, }, @@ -131,7 +129,6 @@ func TestActorResumer_ResumeActor(t *testing.T) { time.Sleep(20 * time.Millisecond) return &ateapipb.ResumeActorResponse{ Actor: &ateapipb.Actor{ - Metadata: &ateapipb.ResourceMetadata{Name: testActorName}, Status: ateapipb.Actor_STATUS_RUNNING, AteomPodIp: expectedIP, }, diff --git a/cmd/atenet/internal/router/router.go b/cmd/atenet/internal/router/router.go index 61cfb0cf4..e8027912b 100644 --- a/cmd/atenet/internal/router/router.go +++ b/cmd/atenet/internal/router/router.go @@ -190,6 +190,7 @@ func (s *RouterServer) Run(ctx context.Context) error { } xdsSrv.SetTlsConfig(s.cfg.HttpsPort, s.cfg.EnvoyCertPath) + xdsSrv.SetUpstreamTls(s.cfg.UpstreamClientCertPath, s.cfg.UpstreamTrustPath, s.cfg.UpstreamSpiffePrefix) if s.extprocSrv == nil { routeDuration, err := newRouteDurationHistogram() if err != nil { diff --git a/cmd/atenet/internal/router/xds.go b/cmd/atenet/internal/router/xds.go index 5869805a8..dc94caf4a 100644 --- a/cmd/atenet/internal/router/xds.go +++ b/cmd/atenet/internal/router/xds.go @@ -38,13 +38,9 @@ import ( routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" tracev3 "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" streamaccesslogv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3" - dfpclusterv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3" - dfpcommonv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3" - dfpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3" extprocv3filter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3" routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - getaddrinfov3 "github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3" tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" httpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" clustergrpc "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3" @@ -52,6 +48,7 @@ import ( endpointgrpc "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3" listenergrpc "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3" routegrpc "github.com/envoyproxy/go-control-plane/envoy/service/route/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" "github.com/envoyproxy/go-control-plane/pkg/cache/types" cachev3 "github.com/envoyproxy/go-control-plane/pkg/cache/v3" @@ -66,12 +63,13 @@ const ( RouteName = "substrate_routes" ClusterName = "ate-cluster" OtlpClusterName = "otel_collector_cluster" - - // httpProtocolOptionsName is the well-known extension key Envoy looks for in - // a cluster's typed_extension_protocol_options. It must match the message's - // full proto type name exactly; a typo is silently ignored rather than - // rejected, so the options simply never take effect. - httpProtocolOptionsName = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + // OriginalDstClusterName routes actor traffic to the worker's atunnel + // ingress by the IP:port the ext_proc puts in OriginalDstHeader, while the + // request :authority stays the actor DNS name so atunnel can identify the + // active actor. + OriginalDstClusterName = "actor_original_dst" + // OriginalDstHeader carries the resolved worker atunnel address (IP:443). + OriginalDstHeader = "x-ate-original-dst" ) // XdsServer implements an aggregated discovery service server for dynamic Envoy router nodes. @@ -89,6 +87,19 @@ type XdsServer struct { httpsPort int certPath string + // Upstream (actor-facing) mTLS. When upstreamClientCertPath is set, the + // ORIGINAL_DST actor cluster dials the actor's in-worker atunnel ingress + // server over mTLS: it presents this podidentity credential bundle as the + // client cert and validates the atunnel server against upstreamTrustPath. + upstreamClientCertPath string + upstreamTrustPath string + // upstreamSpiffePrefix, when set, makes the upstream validator accept the + // atunnel server cert by matching its SPIFFE URI SAN against this prefix + // (trust-domain match) instead of the actor's ephemeral pod IP. The atunnel + // cert carries only a spiffe:// URI SAN, so without this Envoy's default + // SAN check against the dialed IP fails ("verify SAN list"). + upstreamSpiffePrefix string + otlpHost string otlpPort uint32 } @@ -122,6 +133,19 @@ func (x *XdsServer) SetTlsConfig(httpsPort int, certPath string) { x.certPath = certPath } +// SetUpstreamTls configures actor-facing mTLS on the ORIGINAL_DST actor +// cluster. clientCertPath is the router's podidentity credential bundle +// (cert+key concatenated) presented to the actor's atunnel ingress server; +// trustPath is the CA bundle used to validate that server. Empty clientCertPath +// leaves the upstream as plaintext. +func (x *XdsServer) SetUpstreamTls(clientCertPath, trustPath, spiffePrefix string) { + x.mu.Lock() + defer x.mu.Unlock() + x.upstreamClientCertPath = clientCertPath + x.upstreamTrustPath = trustPath + x.upstreamSpiffePrefix = spiffePrefix +} + // SetOtlpCollector enables Envoy-side tracing pointed at the OTLP gRPC // collector at host:port. addr empty disables tracing. port defaults to // 4317 if omitted. @@ -157,7 +181,7 @@ func (x *XdsServer) UpdateSnapshot() error { // Clusters clusters := []types.Resource{ x.buildCluster(), - x.buildDynamicForwardProxyCluster(), + x.buildOriginalDstCluster(), } if x.otlpHost != "" { clusters = append(clusters, x.buildOtlpCollectorCluster()) @@ -268,19 +292,7 @@ func (x *XdsServer) buildCluster() *clusterv3.Cluster { }, }, TypedExtensionProtocolOptions: map[string]*anypb.Any{ - httpProtocolOptionsName: h2Opts, - }, - } -} - -func buildDnsCacheConfig() *dfpcommonv3.DnsCacheConfig { - resolverConfigAny, _ := anypb.New(&getaddrinfov3.GetAddrInfoDnsResolverConfig{}) - return &dfpcommonv3.DnsCacheConfig{ - Name: "dynamic_forward_proxy_cache_config", - DnsLookupFamily: clusterv3.Cluster_V4_ONLY, - TypedDnsResolverConfig: &corev3.TypedExtensionConfig{ - Name: "envoy.network.dns_resolver.getaddrinfo", - TypedConfig: resolverConfigAny, + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": h2Opts, }, } } @@ -330,55 +342,109 @@ func (x *XdsServer) buildOtlpCollectorCluster() *clusterv3.Cluster { }, }, TypedExtensionProtocolOptions: map[string]*anypb.Any{ - httpProtocolOptionsName: h2Opts, + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": h2Opts, }, } } -func (x *XdsServer) buildDynamicForwardProxyCluster() *clusterv3.Cluster { - dfpClusterConfig := &dfpclusterv3.ClusterConfig{ - ClusterImplementationSpecifier: &dfpclusterv3.ClusterConfig_DnsCacheConfig{ - DnsCacheConfig: buildDnsCacheConfig(), - }, - // A DFP cluster rejects HttpProtocolOptions unless auto_sni and - // auto_san_validation are on or this is set. This cluster has no - // transport socket — plaintext to worker pod IPs, with an IP literal for - // the authority — so there is no certificate to validate against. - AllowInsecureClusterOptions: true, +// buildUpstreamTransportSocket returns the actor-facing mTLS transport socket +// for the ORIGINAL_DST actor cluster, or nil when upstream mTLS is not +// configured. The router presents its podidentity credential bundle as the +// client cert and validates the atunnel ingress server against the trust +// bundle. Validation is by the SPIFFE URI SAN prefix (see upstreamSpiffePrefix) +// rather than the dialed pod IP. +func (x *XdsServer) buildUpstreamTransportSocket() *corev3.TransportSocket { + if x.upstreamClientCertPath == "" { + return nil } - clusterConfigAny, _ := anypb.New(dfpClusterConfig) - - // One request per connection. Envoy pools by destination address, which - // assumes an address means one stable server. A worker pod's IP is stable - // but the actor sandbox behind port 80 is destroyed on every Suspend and a - // different actor takes the slot, so a pooled connection can belong to an - // actor that is already gone and the request 503s. - httpOpts, _ := anypb.New(&httpv3.HttpProtocolOptions{ - CommonHttpProtocolOptions: &corev3.HttpProtocolOptions{ - MaxRequestsPerConnection: wrapperspb.UInt32(1), + commonTls := &tlsv3.CommonTlsContext{ + TlsCertificates: []*tlsv3.TlsCertificate{ + { + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_Filename{Filename: x.upstreamClientCertPath}, + }, + PrivateKey: &corev3.DataSource{ + Specifier: &corev3.DataSource_Filename{Filename: x.upstreamClientCertPath}, + }, + }, }, - UpstreamProtocolOptions: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{ - ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{ - // HTTP/1.1 upstream, matching what actors serve on port 80. - ProtocolConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{}, + } + if x.upstreamTrustPath != "" { + validationCtx := &tlsv3.CertificateValidationContext{ + TrustedCa: &corev3.DataSource{ + Specifier: &corev3.DataSource_Filename{Filename: x.upstreamTrustPath}, }, + } + // Validate the atunnel server by its SPIFFE URI SAN (trust-domain + // prefix) rather than the dialed pod IP. Without this, Envoy checks the + // cert SAN against the ephemeral pod IP, which the SPIFFE-only cert + // never matches. + if x.upstreamSpiffePrefix != "" { + validationCtx.MatchTypedSubjectAltNames = []*tlsv3.SubjectAltNameMatcher{ + { + SanType: tlsv3.SubjectAltNameMatcher_URI, + Matcher: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Prefix{Prefix: x.upstreamSpiffePrefix}, + }, + }, + } + } + commonTls.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContext{ + ValidationContext: validationCtx, + } + } + + upstreamTls := &tlsv3.UpstreamTlsContext{CommonTlsContext: commonTls} + upstreamTlsAny, _ := anypb.New(upstreamTls) + return &corev3.TransportSocket{ + Name: "envoy.transport_sockets.tls", + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: upstreamTlsAny, }, - }) + } +} - return &clusterv3.Cluster{ - Name: "dynamic_forward_proxy_cluster", +// buildOriginalDstCluster dials the exact worker atunnel address supplied by +// the ext_proc in OriginalDstHeader. Unlike the dynamic_forward_proxy cluster, +// it does not derive the destination from :authority, so the request keeps the +// actor DNS name as its Host for atunnel to authorize. mTLS to atunnel is +// applied via the shared upstream transport socket (SPIFFE URI validation). +func (x *XdsServer) buildOriginalDstCluster() *clusterv3.Cluster { + cluster := &clusterv3.Cluster{ + Name: OriginalDstClusterName, + ConnectTimeout: durationpb.New(5 * time.Second), + ClusterDiscoveryType: &clusterv3.Cluster_Type{ + Type: clusterv3.Cluster_ORIGINAL_DST, + }, LbPolicy: clusterv3.Cluster_CLUSTER_PROVIDED, - ClusterDiscoveryType: &clusterv3.Cluster_ClusterType{ - ClusterType: &clusterv3.Cluster_CustomClusterType{ - Name: "envoy.clusters.dynamic_forward_proxy", - TypedConfig: clusterConfigAny, + LbConfig: &clusterv3.Cluster_OriginalDstLbConfig_{ + OriginalDstLbConfig: &clusterv3.Cluster_OriginalDstLbConfig{ + UseHttpHeader: true, + HttpHeaderName: OriginalDstHeader, }, }, - TypedExtensionProtocolOptions: map[string]*anypb.Any{ - httpProtocolOptionsName: httpOpts, - }, } + + if ts := x.buildUpstreamTransportSocket(); ts != nil { + cluster.TransportSocket = ts + // The atunnel ingress server terminates TLS and reverse-proxies to the + // actor over HTTP/1.1. + httpOpts, _ := anypb.New(&httpv3.HttpProtocolOptions{ + UpstreamProtocolOptions: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{ + HttpProtocolOptions: &corev3.Http1ProtocolOptions{}, + }, + }, + }, + }) + cluster.TypedExtensionProtocolOptions = map[string]*anypb.Any{ + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": httpOpts, + } + } + + return cluster } func (x *XdsServer) buildRoutes() *routev3.RouteConfiguration { @@ -398,7 +464,7 @@ func (x *XdsServer) buildRoutes() *routev3.RouteConfiguration { Action: &routev3.Route_Route{ Route: &routev3.RouteAction{ ClusterSpecifier: &routev3.RouteAction_Cluster{ - Cluster: "dynamic_forward_proxy_cluster", + Cluster: OriginalDstClusterName, }, Timeout: durationpb.New(10 * time.Second), }, @@ -435,12 +501,6 @@ func (x *XdsServer) buildHcm(statPrefix string) *anypb.Any { }, }) - dfpFilterConfig, _ := anypb.New(&dfpv3.FilterConfig{ - ImplementationSpecifier: &dfpv3.FilterConfig_DnsCacheConfig{ - DnsCacheConfig: buildDnsCacheConfig(), - }, - }) - routerAny, _ := anypb.New(&routerv3.Router{}) accessLogConfig, _ := anypb.New(&streamaccesslogv3.StdoutAccessLog{}) @@ -464,12 +524,6 @@ func (x *XdsServer) buildHcm(statPrefix string) *anypb.Any { TypedConfig: extProcConfig, }, }, - { - Name: "envoy.filters.http.dynamic_forward_proxy", - ConfigType: &hcmv3.HttpFilter_TypedConfig{ - TypedConfig: dfpFilterConfig, - }, - }, { Name: "envoy.filters.http.router", ConfigType: &hcmv3.HttpFilter_TypedConfig{ diff --git a/cmd/atenet/internal/router/xds_test.go b/cmd/atenet/internal/router/xds_test.go index 04f20a764..f666e0d51 100644 --- a/cmd/atenet/internal/router/xds_test.go +++ b/cmd/atenet/internal/router/xds_test.go @@ -24,8 +24,6 @@ import ( clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - dfpclusterv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3" - httpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" cachev3 "github.com/envoyproxy/go-control-plane/pkg/cache/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" ) @@ -78,12 +76,15 @@ func TestXdsServer_UpdateSnapshot(t *testing.T) { } } - if raw, exists := clustersMap["dynamic_forward_proxy_cluster"]; !exists { - t.Error("'dynamic_forward_proxy_cluster' is missing from clusters") + if raw, exists := clustersMap[OriginalDstClusterName]; !exists { + t.Errorf("'%s' is missing from clusters", OriginalDstClusterName) } else { c := raw.(*clusterv3.Cluster) - if c.GetName() != "dynamic_forward_proxy_cluster" { - t.Errorf("Expected 'dynamic_forward_proxy_cluster', got %s", c.GetName()) + if c.GetName() != OriginalDstClusterName { + t.Errorf("Expected '%s', got %s", OriginalDstClusterName, c.GetName()) + } + if c.GetType() != clusterv3.Cluster_ORIGINAL_DST { + t.Errorf("Expected ORIGINAL_DST cluster, got %s", c.GetType()) } } @@ -212,50 +213,3 @@ func TestXdsServer_Serve_Shutdown(t *testing.T) { t.Error("Timeout exceeded waiting for Serve to finish graceful closure") } } - -// TestDynamicForwardProxyCluster_EnvoyAcceptsHttpProtocolOptions guards a -// coupling that is invisible on the Go side but fatal at runtime. -// -// Envoy refuses a dynamic_forward_proxy cluster that carries -// HttpProtocolOptions unless the cluster config either turns on both auto_sni -// and auto_san_validation or sets allow_insecure_cluster_options. A snapshot -// that breaks the rule is a perfectly well-formed proto and passes -// Consistent(), so nothing here fails: the only symptom is Envoy NACKing every -// CDS push and the cluster silently missing from its config dump, which reads -// as "all actor traffic 503s" rather than as a config error. -func TestDynamicForwardProxyCluster_EnvoyAcceptsHttpProtocolOptions(t *testing.T) { - cluster := NewXdsServer(18000).buildDynamicForwardProxyCluster() - - var clusterConfig dfpclusterv3.ClusterConfig - if err := cluster.GetClusterType().GetTypedConfig().UnmarshalTo(&clusterConfig); err != nil { - t.Fatalf("Failed to unmarshal dynamic forward proxy cluster config: %v", err) - } - - if !clusterConfig.GetAllowInsecureClusterOptions() { - t.Errorf("Cluster carries %s but neither allows insecure cluster options nor "+ - "enables auto_sni and auto_san_validation; Envoy will reject every CDS push", - httpProtocolOptionsName) - } -} - -// TestDynamicForwardProxyCluster_DisablesConnectionReuse pins the fix for the -// 503 storm seen under actor churn. Worker pod IPs are stable while the actor -// sandbox behind them is destroyed on every Suspend, so a pooled connection -// outlives the actor that owned it. -func TestDynamicForwardProxyCluster_DisablesConnectionReuse(t *testing.T) { - cluster := NewXdsServer(18000).buildDynamicForwardProxyCluster() - - raw, ok := cluster.GetTypedExtensionProtocolOptions()[httpProtocolOptionsName] - if !ok { - t.Fatalf("Cluster is missing %s", httpProtocolOptionsName) - } - - var opts httpv3.HttpProtocolOptions - if err := raw.UnmarshalTo(&opts); err != nil { - t.Fatalf("Failed to unmarshal HttpProtocolOptions: %v", err) - } - - if got := opts.GetCommonHttpProtocolOptions().GetMaxRequestsPerConnection().GetValue(); got != 1 { - t.Errorf("Expected max_requests_per_connection 1, got %d", got) - } -} diff --git a/cmd/ateom-gvisor/main.go b/cmd/ateom-gvisor/main.go index 9c5c0289b..6e24ca203 100644 --- a/cmd/ateom-gvisor/main.go +++ b/cmd/ateom-gvisor/main.go @@ -22,6 +22,7 @@ import ( "fmt" "log/slog" "net" + "net/url" "os" "runtime" "sort" @@ -32,6 +33,7 @@ import ( "github.com/agent-substrate/substrate/internal/actorlog" "github.com/agent-substrate/substrate/internal/ateinterceptors" "github.com/agent-substrate/substrate/internal/ateompath" + "github.com/agent-substrate/substrate/internal/atunnel" "github.com/agent-substrate/substrate/internal/contextlogging" "github.com/agent-substrate/substrate/internal/imagecache" "github.com/agent-substrate/substrate/internal/proto/ateompb" @@ -56,6 +58,13 @@ import ( var ( podUID = pflag.String("pod-uid", "", "The UID of the current pod") + atunnelListenAddress = pflag.String("atunnel-listen-address", "0.0.0.0:443", "Address for actor ingress HTTPS") + atunnelCredentialBundle = pflag.String("atunnel-credential-bundle", "/run/podidentity.podcert.ate.dev/credential-bundle.pem", "PEM credential bundle for actor ingress HTTPS") + atunnelTrustBundle = pflag.String("atunnel-trust-bundle", "/run/podidentity.podcert.ate.dev/trust-bundle.pem", "PEM trust bundle for actor ingress clients") + atunnelClientIdentity = pflag.String("atunnel-client-identity", "spiffe://cluster.local/ns/ate-system/sa/atenet-router", "SPIFFE identity allowed to call actor ingress HTTPS") + atunnelEgressListenAddress = pflag.String("atunnel-egress-listen-address", "0.0.0.0:15001", "Address for transparently intercepted actor egress TCP") + atunnelEgressTrustBundle = pflag.String("atunnel-egress-trust-bundle", "/run/servicedns.podcert.ate.dev/trust-bundle.pem", "PEM trust bundle for the egress gateway") + showVersion = pflag.Bool("version", false, "Print version and exit.") reapLock sync.RWMutex @@ -70,6 +79,7 @@ const ( actorVethGateway = "169.254.17.1" actorVethIP = "169.254.17.2" actorNftTableName = "ateom_actor" + actorHTTPUpstream = "http://169.254.17.2:80" ) func main() { @@ -146,7 +156,16 @@ func do(ctx context.Context) error { } actorLogger := actorlog.NewActorLogger(syncedWriter, metadata.OnGCE()) - ateomService := NewService(interiorNetNS, actorLogger) + upstream, err := url.Parse(actorHTTPUpstream) + if err != nil { + return fmt.Errorf("while parsing atunnel upstream: %w", err) + } + atunnelServer, atunnelEgress, atunnelEgressPort, err := runAtunnel(ctx, upstream) + if err != nil { + return err + } + + ateomService := NewService(interiorNetNS, actorLogger, atunnelServer, atunnelEgress, atunnelEgressPort, *atunnelCredentialBundle, *atunnelEgressTrustBundle) svr := grpc.NewServer( grpc.StatsHandler(otelgrpc.NewServerHandler()), @@ -163,6 +182,50 @@ func do(ctx context.Context) error { return nil } +func runAtunnel(ctx context.Context, upstream *url.URL) (*atunnel.Server, *atunnel.Egress, uint16, error) { + atunnelServer, err := atunnel.NewServer(atunnel.Config{ + CredentialBundlePath: *atunnelCredentialBundle, + TrustBundlePath: *atunnelTrustBundle, + AllowedClientID: *atunnelClientIdentity, + Upstream: upstream, + }) + if err != nil { + return nil, nil, 0, fmt.Errorf("while configuring atunnel: %w", err) + } + atunnelListener, err := net.Listen("tcp", *atunnelListenAddress) + if err != nil { + return nil, nil, 0, fmt.Errorf("while opening atunnel listener: %w", err) + } + go func() { + if err := atunnelServer.Serve(ctx, atunnelListener); err != nil { + serverboot.Fatal(ctx, "Failed to serve actor ingress", err) + } + }() + slog.InfoContext(ctx, "atunnel serving", slog.String("address", *atunnelListenAddress)) + + atunnelEgress, err := atunnel.NewEgress(atunnel.TCPOriginalDestination) + if err != nil { + return nil, nil, 0, fmt.Errorf("while configuring atunnel egress: %w", err) + } + egressListener, err := net.Listen("tcp", *atunnelEgressListenAddress) + if err != nil { + return nil, nil, 0, fmt.Errorf("while opening atunnel egress listener: %w", err) + } + egressTCPAddr, ok := egressListener.Addr().(*net.TCPAddr) + if !ok || egressTCPAddr.Port < 1 || egressTCPAddr.Port > 65535 { + _ = egressListener.Close() + return nil, nil, 0, fmt.Errorf("atunnel egress listener has invalid address %q", egressListener.Addr()) + } + atunnelEgressPort := uint16(egressTCPAddr.Port) + go func() { + if err := atunnelEgress.Serve(ctx, egressListener); err != nil { + serverboot.Fatal(ctx, "Failed to serve actor egress", err) + } + }() + slog.InfoContext(ctx, "atunnel egress serving", slog.String("address", *atunnelEgressListenAddress)) + return atunnelServer, atunnelEgress, atunnelEgressPort, nil +} + // AteomService is a service for shepherding single microvm. type AteomService struct { ateompb.UnimplementedAteomServer @@ -173,22 +236,36 @@ type AteomService struct { interiorNetNS netns.NsHandle actorLogger *actorlog.ActorLogger + atunnel *atunnel.Server + atunnelEgress *atunnel.Egress + // atunnelEgressPort is zero when tunneled egress is disabled. Otherwise, + // actor TCP connections are transparently redirected to this local port. + atunnelEgressPort uint16 + atunnelCredentialBundle string + atunnelEgressTrustBundle string } var _ ateompb.AteomServer = (*AteomService)(nil) // NewService creates a new AteomService. -func NewService(interiorNetNS netns.NsHandle, actorLogger *actorlog.ActorLogger) *AteomService { - svc := &AteomService{ - interiorNetNS: interiorNetNS, - actorLogger: actorLogger, +func NewService(interiorNetNS netns.NsHandle, actorLogger *actorlog.ActorLogger, atunnelServer *atunnel.Server, atunnelEgress *atunnel.Egress, atunnelEgressPort uint16, credentialBundle, egressTrustBundle string) *AteomService { + return &AteomService{ + interiorNetNS: interiorNetNS, + actorLogger: actorLogger, + atunnel: atunnelServer, + atunnelEgress: atunnelEgress, + atunnelEgressPort: atunnelEgressPort, + atunnelCredentialBundle: credentialBundle, + atunnelEgressTrustBundle: egressTrustBundle, } - return svc } func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkloadRequest) (resp *ateompb.RunWorkloadResponse, retErr error) { s.lock.Lock() defer s.lock.Unlock() + if err := s.deactivateActorNetworking(ctx); err != nil { + return nil, err + } actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} s.actorLogger.EmitLifecycleLog("Actor starting", actorRef, req.GetActorUid(), req.GetActorTemplateNamespace(), req.GetActorTemplateName()) @@ -198,7 +275,7 @@ func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkload // * Correct runsc version is downloaded and placed on disk. // * All OCI bundles are set up, including for "pause" container. - if err := s.setupActorNetwork(ctx); err != nil { + if err := s.setupActorNetwork(ctx, req.GetEgressGatewayAddress() != ""); err != nil { return nil, fmt.Errorf("while setting up actor network: %w", err) } defer func() { @@ -259,6 +336,9 @@ func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkload if err := readyz.WaitAll(ctx, req.GetSpec().GetContainers(), actorVethIP); err != nil { return nil, fmt.Errorf("while waiting for container readyz: %w", err) } + if err := s.activateActorNetworking(req.GetAtespace(), req.GetActorName(), req.GetActorVersion(), req.GetEgressGatewayAddress()); err != nil { + return nil, err + } s.actorLogger.EmitLifecycleLog("Actor started", actorRef, req.GetActorUid(), req.GetActorTemplateNamespace(), req.GetActorTemplateName()) @@ -268,6 +348,9 @@ func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkload func (s *AteomService) CheckpointWorkload(ctx context.Context, req *ateompb.CheckpointWorkloadRequest) (*ateompb.CheckpointWorkloadResponse, error) { s.lock.Lock() defer s.lock.Unlock() + if err := s.deactivateActorNetworking(ctx); err != nil { + return nil, err + } actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} s.actorLogger.EmitLifecycleLog("Actor checkpointing", actorRef, req.GetActorUid(), req.GetActorTemplateNamespace(), req.GetActorTemplateName()) @@ -391,6 +474,9 @@ func (r *runsc) cleanupContainersAfterCheckpoint(ctx context.Context, containers func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.RestoreWorkloadRequest) (resp *ateompb.RestoreWorkloadResponse, retErr error) { s.lock.Lock() defer s.lock.Unlock() + if err := s.deactivateActorNetworking(ctx); err != nil { + return nil, err + } actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} s.actorLogger.EmitLifecycleLog("Actor restoring", actorRef, req.GetActorUid(), req.GetActorTemplateNamespace(), req.GetActorTemplateName()) @@ -401,7 +487,7 @@ func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.Restore // * All OCI bundles are set up, including for "pause" container. // * Checkpoint downloaded and placed on disk - if err := s.setupActorNetwork(ctx); err != nil { + if err := s.setupActorNetwork(ctx, req.GetEgressGatewayAddress() != ""); err != nil { return nil, fmt.Errorf("while setting up actor network: %w", err) } defer func() { @@ -485,13 +571,65 @@ func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.Restore if err := readyz.WaitAll(ctx, req.GetSpec().GetContainers(), actorVethIP); err != nil { return nil, fmt.Errorf("while waiting for container readyz: %w", err) } + if err := s.activateActorNetworking(req.GetAtespace(), req.GetActorName(), req.GetActorVersion(), req.GetEgressGatewayAddress()); err != nil { + return nil, err + } s.actorLogger.EmitLifecycleLog("Actor restored", actorRef, req.GetActorUid(), req.GetActorTemplateNamespace(), req.GetActorTemplateName()) return &ateompb.RestoreWorkloadResponse{}, nil } -func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { +func (s *AteomService) activateActorNetworking(atespace, actorName string, actorVersion int64, egressGatewayAddress string) error { + var egressClient atunnel.EgressDialer + if s.atunnelEgress != nil && egressGatewayAddress != "" { + serverName, _, err := net.SplitHostPort(egressGatewayAddress) + if err != nil { + return fmt.Errorf("invalid egress gateway address %q: %w", egressGatewayAddress, err) + } + egressClient, err = atunnel.NewClient(atunnel.ClientConfig{ + GatewayAddress: egressGatewayAddress, + ServerName: serverName, + CredentialBundlePath: s.atunnelCredentialBundle, + TrustBundlePath: s.atunnelEgressTrustBundle, + }) + if err != nil { + return fmt.Errorf("while configuring actor egress client: %w", err) + } + } + if s.atunnel != nil { + if err := s.atunnel.Activate(atespace, actorName); err != nil { + return fmt.Errorf("while activating actor ingress: %w", err) + } + } + if egressClient != nil { + if err := s.atunnelEgress.Activate(egressClient, atespace, actorName, actorVersion, ""); err != nil { + if s.atunnel != nil { + _ = s.atunnel.Deactivate(context.Background()) + } + return fmt.Errorf("while activating actor egress: %w", err) + } + } + return nil +} + +func (s *AteomService) deactivateActorNetworking(ctx context.Context) error { + // Stop admitting traffic and drain active streams before the Actor network + // is torn down. Attempt both directions even if one fails to deactivate. + var err error + if s.atunnel != nil { + err = errors.Join(err, s.atunnel.Deactivate(ctx)) + } + if s.atunnelEgress != nil { + err = errors.Join(err, s.atunnelEgress.Deactivate(ctx)) + } + if err != nil { + return fmt.Errorf("while deactivating actor networking: %w", err) + } + return nil +} + +func (s *AteomService) setupActorNetwork(ctx context.Context, redirectEgress bool) (retErr error) { // Build a fresh point-to-point network between the worker pod netns and the // gVisor interior netns. The worker side keeps the pod's real eth0, creates // ateom0 as the gateway, and moves only the veth peer into the actor netns. @@ -499,10 +637,8 @@ func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { // the worker-side veth address. This replaces the old behavior of moving the // Kubernetes-provided eth0 out of the worker pod. // - // The nftables rules installed here are a compatibility bridge for the - // current router assumptions: actor egress is masqueraded behind the worker - // pod IP, and inbound traffic to the worker pod's HTTP port is DNAT'd to the - // actor veth IP. + // nftables redirects actor TCP egress to atunnel when configured and + // masquerades traffic not handled by the TCP tunnel (notably DNS/UDP). // // Clean up stale state from a failed prior activation before creating the // next actor-side network. The worker currently runs one actor at a time. @@ -513,11 +649,6 @@ func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { } }() - podIP, err := podIPv4() - if err != nil { - return fmt.Errorf("while resolving pod IPv4 address: %w", err) - } - hostAddr, err := parseAddr(hostVethCIDR) if err != nil { return err @@ -559,7 +690,11 @@ func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { if err := enableIPv4Forwarding(); err != nil { return err } - if err := installActorNftablesRules(podIP); err != nil { + egressPort := uint16(0) + if redirectEgress { + egressPort = s.atunnelEgressPort + } + if err := installActorNftablesRules(egressPort); err != nil { return err } @@ -682,29 +817,6 @@ func (s *AteomService) cleanupActorNetworkOrExit(ctx context.Context, msg string } } -func podIPv4() (net.IP, error) { - // Resolve the worker pod IPv4 address from the pod namespace's real eth0. - // Because eth0 now stays in the pod namespace, this IP remains available for - // both normal worker connectivity and the temporary inbound DNAT rule. - eth0Link, err := netlink.LinkByName("eth0") - if err != nil { - return nil, fmt.Errorf("while getting pod eth0: %w", err) - } - addrs, err := netlink.AddrList(eth0Link, netlink.FAMILY_V4) - if err != nil { - return nil, fmt.Errorf("while listing pod eth0 addresses: %w", err) - } - for _, addr := range addrs { - if addr.IP == nil { - continue - } - if ip := addr.IP.To4(); ip != nil { - return ip, nil - } - } - return nil, fmt.Errorf("pod eth0 has no IPv4 address") -} - func parseAddr(cidr string) (*netlink.Addr, error) { addr, err := netlink.ParseAddr(cidr) if err != nil { @@ -862,26 +974,24 @@ func moveProcs(ctx context.Context, srcProcs, dstProcs string) error { return fmt.Errorf("%q did not drain after 100 iterations", srcProcs) } -func installActorNftablesRules(podIP net.IP) error { +func installActorNftablesRules(egressPort uint16) error { // Install a dedicated nftables table for the active actor. Keeping all // rules in an ateom-owned table makes cleanup simple and avoids mutating // Kubernetes or CNI-managed chains directly. // // TODO: Add IPv6 veth addressing, forwarding, and nftables rules once actor - // networking supports dual-stack pods. The current compatibility path is - // IPv4-only. + // networking supports dual-stack pods. The current actor network is IPv4-only. // - // The temporary compatibility rules do three things: + // The rules do three things: // - // * postrouting: masquerade actor egress from 169.254.17.2 behind the worker - // pod IP so replies route back to the pod. - // * prerouting: DNAT traffic sent to the worker pod IP on TCP/80 to the - // actor veth IP on TCP/80, preserving existing inbound behavior. + // * prerouting: redirect new actor TCP connections to atunnel's local + // listener. REDIRECT preserves SO_ORIGINAL_DST for the CONNECT authority. + // * postrouting: masquerade traffic not handled by the TCP tunnel, notably + // DNS over UDP, so hostname resolution continues to work. // * forward: accept forwarded packets between the actor veth and pod eth0. // - // This is not the final egress policy path. The later AgentGateway phase - // should replace the broad masquerade path with transparent TCP capture and - // default-deny rules. + // TODO: Restrict the compatibility masquerade to DNS traffic sent to the + // configured cluster resolver and drop all other non-tunneled actor egress. if err := removeActorNftablesRules(); err != nil { return err } @@ -900,32 +1010,9 @@ func installActorNftablesRules(podIP net.IP) error { Hooknum: nftables.ChainHookPrerouting, Priority: nftables.ChainPriorityNATDest, }) - // TODO: Support inbound UDP DNAT for actors that expose UDP protocols such - // as QUIC. - // TODO: Replace the hard-coded HTTP port with the actor's configured - // inbound ports, either by adding one rule per port or by matching a set. - preroutingExprs := append(ipDestinationEqual(podIP.String()), tcpDestinationPortEqual(80)...) - preroutingExprs = append(preroutingExprs, - &expr.Immediate{ - Register: 1, - Data: net.ParseIP(actorVethIP).To4(), - }, - &expr.Immediate{ - Register: 2, - Data: binaryutil.BigEndian.PutUint16(80), - }, - &expr.NAT{ - Type: expr.NATTypeDestNAT, - Family: unix.NFPROTO_IPV4, - RegAddrMin: 1, - RegProtoMin: 2, - }, - ) - c.AddRule(&nftables.Rule{ - Table: table, - Chain: prerouting, - Exprs: preroutingExprs, - }) + if redirectRule := actorEgressRedirectRule(table, prerouting, egressPort); redirectRule != nil { + c.AddRule(redirectRule) + } postrouting := c.AddChain(&nftables.Chain{ Name: "postrouting", @@ -989,10 +1076,6 @@ func ipSourceEqual(ip string) []expr.Any { return ipPayloadEqual(12, ip) } -func ipDestinationEqual(ip string) []expr.Any { - return ipPayloadEqual(16, ip) -} - func ipPayloadEqual(offset uint32, ip string) []expr.Any { return []expr.Any{ &expr.Payload{ @@ -1009,7 +1092,7 @@ func ipPayloadEqual(offset uint32, ip string) []expr.Any { } } -func tcpDestinationPortEqual(port uint16) []expr.Any { +func tcpProtocol() []expr.Any { return []expr.Any{ &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ @@ -1017,18 +1100,22 @@ func tcpDestinationPortEqual(port uint16) []expr.Any { Register: 1, Data: []byte{unix.IPPROTO_TCP}, }, - &expr.Payload{ - DestRegister: 1, - Base: expr.PayloadBaseTransportHeader, - Offset: 2, - Len: 2, - }, - &expr.Cmp{ - Op: expr.CmpOpEq, + } +} + +func actorEgressRedirectRule(table *nftables.Table, chain *nftables.Chain, port uint16) *nftables.Rule { + if port == 0 { + return nil + } + exprs := append(ipSourceEqual(actorVethIP), tcpProtocol()...) + exprs = append(exprs, + &expr.Immediate{ Register: 1, Data: binaryutil.BigEndian.PutUint16(port), }, - } + &expr.Redir{RegisterProtoMin: 1}, + ) + return &nftables.Rule{Table: table, Chain: chain, Exprs: exprs} } func createNetNSWithoutSwitching(ctx context.Context, name string) (netns.NsHandle, error) { diff --git a/cmd/ateom-microvm/checkpoint.go b/cmd/ateom-microvm/checkpoint.go index 66b7fb247..6580f943f 100644 --- a/cmd/ateom-microvm/checkpoint.go +++ b/cmd/ateom-microvm/checkpoint.go @@ -59,6 +59,9 @@ import ( func (s *AteomService) CheckpointWorkload(ctx context.Context, req *ateompb.CheckpointWorkloadRequest) (*ateompb.CheckpointWorkloadResponse, error) { s.lock.Lock() defer s.lock.Unlock() + if err := s.deactivateActorNetworking(ctx); err != nil { + return nil, err + } actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} actorUID := req.GetActorUid() diff --git a/cmd/ateom-microvm/main.go b/cmd/ateom-microvm/main.go index 1dcce1cb1..5a0a2f3df 100644 --- a/cmd/ateom-microvm/main.go +++ b/cmd/ateom-microvm/main.go @@ -24,10 +24,12 @@ package main import ( "context" + "errors" "flag" "fmt" "log/slog" "net" + "net/url" "os" "strings" "sync" @@ -37,6 +39,7 @@ import ( "github.com/agent-substrate/substrate/internal/actorlog" "github.com/agent-substrate/substrate/internal/ateinterceptors" "github.com/agent-substrate/substrate/internal/ateompath" + "github.com/agent-substrate/substrate/internal/atunnel" "github.com/agent-substrate/substrate/internal/proto/ateompb" "github.com/agent-substrate/substrate/internal/serverboot" "github.com/agent-substrate/substrate/internal/version" @@ -54,6 +57,13 @@ var ( kataConfig = flag.String("kata-config", "", "Path to a kata configuration.toml (passed to the shim as KATA_CONF_FILE). Empty uses kata's default. atelet generates one pointing at runtime-fetched assets.") kataDebug = flag.Bool("kata-debug", false, "Verbose kata-agent debugging: raise the guest agent log level and forward the guest console (incl. agent logs) into the pod logs.") showVersion = flag.Bool("version", false, "Print version and exit.") + + atunnelListenAddress = flag.String("atunnel-listen-address", "0.0.0.0:443", "Address for actor ingress HTTPS") + atunnelCredentialBundle = flag.String("atunnel-credential-bundle", "/run/podidentity.podcert.ate.dev/credential-bundle.pem", "PEM credential bundle for actor ingress HTTPS") + atunnelTrustBundle = flag.String("atunnel-trust-bundle", "/run/podidentity.podcert.ate.dev/trust-bundle.pem", "PEM trust bundle for actor ingress clients") + atunnelClientIdentity = flag.String("atunnel-client-identity", "spiffe://cluster.local/ns/ate-system/sa/atenet-router", "SPIFFE identity allowed to call actor ingress HTTPS") + atunnelEgressListenAddress = flag.String("atunnel-egress-listen-address", "0.0.0.0:15001", "Address for transparently intercepted actor egress TCP") + atunnelEgressTrustBundle = flag.String("atunnel-egress-trust-bundle", "/run/servicedns.podcert.ate.dev/trust-bundle.pem", "PEM trust bundle for the egress gateway") ) func main() { @@ -138,12 +148,55 @@ func do(ctx context.Context) error { // logWriter with the runtime logger so the two streams to os.Stdout are // serialized through one SyncedWriter and never interleave-corrupt lines. actorLogger := actorlog.NewActorLogger(logWriter, metadata.OnGCE()) + upstream, err := url.Parse("http://169.254.17.2:80") + if err != nil { + return fmt.Errorf("while parsing atunnel upstream: %w", err) + } + atunnelServer, err := atunnel.NewServer(atunnel.Config{ + CredentialBundlePath: *atunnelCredentialBundle, + TrustBundlePath: *atunnelTrustBundle, + AllowedClientID: *atunnelClientIdentity, + Upstream: upstream, + }) + if err != nil { + return fmt.Errorf("while configuring atunnel: %w", err) + } + atunnelListener, err := net.Listen("tcp", *atunnelListenAddress) + if err != nil { + return fmt.Errorf("while opening atunnel listener: %w", err) + } + go func() { + if err := atunnelServer.Serve(ctx, atunnelListener); err != nil { + serverboot.Fatal(ctx, "Failed to serve actor ingress", err) + } + }() + slog.InfoContext(ctx, "atunnel serving", slog.String("address", *atunnelListenAddress)) + atunnelEgress, err := atunnel.NewEgress(atunnel.TCPOriginalDestination) + if err != nil { + return fmt.Errorf("while configuring atunnel egress: %w", err) + } + egressListener, err := net.Listen("tcp", *atunnelEgressListenAddress) + if err != nil { + return fmt.Errorf("while opening atunnel egress listener: %w", err) + } + egressTCPAddr, ok := egressListener.Addr().(*net.TCPAddr) + if !ok || egressTCPAddr.Port < 1 || egressTCPAddr.Port > 65535 { + _ = egressListener.Close() + return fmt.Errorf("atunnel egress listener has invalid address %q", egressListener.Addr()) + } + atunnelEgressPort := uint16(egressTCPAddr.Port) + go func() { + if err := atunnelEgress.Serve(ctx, egressListener); err != nil { + serverboot.Fatal(ctx, "Failed to serve actor egress", err) + } + }() + slog.InfoContext(ctx, "atunnel egress serving", slog.String("address", *atunnelEgressListenAddress)) svr := grpc.NewServer( grpc.StatsHandler(otelgrpc.NewServerHandler()), grpc.UnaryInterceptor(ateinterceptors.InternalServerUnaryInterceptor), ) - ateompb.RegisterAteomServer(svr, NewService(*podUID, *chBinary, *kataConfig, *kataDebug, interiorNetNS, actorLogger)) + ateompb.RegisterAteomServer(svr, NewService(*podUID, *chBinary, *kataConfig, *kataDebug, interiorNetNS, actorLogger, atunnelServer, atunnelEgress, atunnelEgressPort, *atunnelCredentialBundle, *atunnelEgressTrustBundle)) reflection.Register(svr) slog.InfoContext(ctx, "ateom-microvm serving", slog.String("socket", sockPath)) @@ -201,7 +254,14 @@ type AteomService struct { // actorLogger forwards the actor container's stdout/stderr to the worker pod's // stdout as ate.dev/*-labeled JSON and emits actor lifecycle events (parity // with ateom-gvisor). - actorLogger *actorlog.ActorLogger + actorLogger *actorlog.ActorLogger + atunnel *atunnel.Server + atunnelEgress *atunnel.Egress + // atunnelEgressPort is zero when tunneled egress is disabled. Otherwise, + // actor TCP connections are transparently redirected to this local port. + atunnelEgressPort uint16 + atunnelCredentialBundle string + atunnelEgressTrustBundle string // running maps actor UID -> the live micro-VM, kept so CheckpointWorkload can // pause+snapshot+teardown the same sandbox (and RestoreWorkload can track the @@ -212,14 +272,68 @@ type AteomService struct { var _ ateompb.AteomServer = (*AteomService)(nil) // NewService creates a new AteomService. -func NewService(podUID, chBinary, kataConfig string, kataDebug bool, interiorNetNS netns.NsHandle, actorLogger *actorlog.ActorLogger) *AteomService { +func NewService(podUID, chBinary, kataConfig string, kataDebug bool, interiorNetNS netns.NsHandle, actorLogger *actorlog.ActorLogger, atunnelServer *atunnel.Server, atunnelEgress *atunnel.Egress, atunnelEgressPort uint16, credentialBundle, egressTrustBundle string) *AteomService { return &AteomService{ - podUID: podUID, - chBinary: chBinary, - kataConfig: kataConfig, - kataDebug: kataDebug, - interiorNetNS: interiorNetNS, - actorLogger: actorLogger, - running: map[string]*runningActor{}, + podUID: podUID, + chBinary: chBinary, + kataConfig: kataConfig, + kataDebug: kataDebug, + interiorNetNS: interiorNetNS, + actorLogger: actorLogger, + atunnel: atunnelServer, + atunnelEgress: atunnelEgress, + atunnelEgressPort: atunnelEgressPort, + atunnelCredentialBundle: credentialBundle, + atunnelEgressTrustBundle: egressTrustBundle, + running: map[string]*runningActor{}, } } + +func (s *AteomService) activateActorNetworking(atespace, actorName string, actorVersion int64, egressGatewayAddress string) error { + var egressClient atunnel.EgressDialer + if s.atunnelEgress != nil && egressGatewayAddress != "" { + serverName, _, err := net.SplitHostPort(egressGatewayAddress) + if err != nil { + return fmt.Errorf("invalid egress gateway address %q: %w", egressGatewayAddress, err) + } + egressClient, err = atunnel.NewClient(atunnel.ClientConfig{ + GatewayAddress: egressGatewayAddress, + ServerName: serverName, + CredentialBundlePath: s.atunnelCredentialBundle, + TrustBundlePath: s.atunnelEgressTrustBundle, + }) + if err != nil { + return fmt.Errorf("while configuring actor egress client: %w", err) + } + } + if s.atunnel != nil { + if err := s.atunnel.Activate(atespace, actorName); err != nil { + return fmt.Errorf("while activating actor ingress: %w", err) + } + } + if egressClient != nil { + if err := s.atunnelEgress.Activate(egressClient, atespace, actorName, actorVersion, ""); err != nil { + if s.atunnel != nil { + _ = s.atunnel.Deactivate(context.Background()) + } + return fmt.Errorf("while activating actor egress: %w", err) + } + } + return nil +} + +func (s *AteomService) deactivateActorNetworking(ctx context.Context) error { + // Stop admitting traffic and drain active streams before the Actor network + // is torn down. Attempt both directions even if one fails to deactivate. + var err error + if s.atunnel != nil { + err = errors.Join(err, s.atunnel.Deactivate(ctx)) + } + if s.atunnelEgress != nil { + err = errors.Join(err, s.atunnelEgress.Deactivate(ctx)) + } + if err != nil { + return fmt.Errorf("while deactivating actor networking: %w", err) + } + return nil +} diff --git a/cmd/ateom-microvm/net.go b/cmd/ateom-microvm/net.go index cc047d4f2..60643df8a 100644 --- a/cmd/ateom-microvm/net.go +++ b/cmd/ateom-microvm/net.go @@ -20,8 +20,8 @@ package main // point-to-point veth pair per activation, with the worker side (ateom0, // 169.254.17.1/30) staying in the pod netns next to the pod's real eth0, and // the peer moved into the interior netns, renamed eth0, and given the stable -// actor address 169.254.17.2/30. nftables rules in the pod netns masquerade -// actor egress behind the pod IP and DNAT inbound pod-IP:80 to the actor. +// actor address 169.254.17.2/30. nftables rules in the pod netns redirect actor +// TCP egress to atunnel and masquerade DNS/UDP. // // kata consumes the interior netns exactly like a CNI-provisioned container // netns: its tcfilter network model builds a tap cross-connected to eth0 (the @@ -120,7 +120,7 @@ func mustParseIP(s string) net.IP { // pod netns and the kata interior netns (see the package comment). Idempotent // via cleanup-before-setup; also sweeps stale kata taps out of the interior // netns so the sandbox always builds on a clean slate. -func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { +func (s *AteomService) setupActorNetwork(ctx context.Context, redirectEgress bool) (retErr error) { s.cleanupActorNetworkOrExit(ctx, "Failed to clean up stale actor network before setup") defer func() { if retErr != nil { @@ -128,11 +128,6 @@ func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { } }() - podIP, err := podIPv4() - if err != nil { - return fmt.Errorf("while resolving pod IPv4 address: %w", err) - } - // Sweep leftover links (kata taps from torn-down runs, restore taps) from // the persistent interior netns before the new veth peer arrives. if err := netNSDo(ctx, s.interiorNetNS, func(ctx context.Context) error { @@ -190,7 +185,11 @@ func (s *AteomService) setupActorNetwork(ctx context.Context) (retErr error) { if err := enableIPv4Forwarding(); err != nil { return err } - if err := installActorNftablesRules(podIP); err != nil { + egressPort := uint16(0) + if redirectEgress { + egressPort = s.atunnelEgressPort + } + if err := installActorNftablesRules(egressPort); err != nil { return err } @@ -287,28 +286,6 @@ func (s *AteomService) cleanupActorNetworkOrExit(ctx context.Context, msg string } } -// podIPv4 resolves the worker pod IPv4 address from the pod namespace's real -// eth0 (which stays in the pod namespace in the veth model). -func podIPv4() (net.IP, error) { - eth0Link, err := netlink.LinkByName("eth0") - if err != nil { - return nil, fmt.Errorf("while getting pod eth0: %w", err) - } - addrs, err := netlink.AddrList(eth0Link, netlink.FAMILY_V4) - if err != nil { - return nil, fmt.Errorf("while listing pod eth0 addresses: %w", err) - } - for _, addr := range addrs { - if addr.IP == nil { - continue - } - if ip := addr.IP.To4(); ip != nil { - return ip, nil - } - } - return nil, fmt.Errorf("pod eth0 has no IPv4 address") -} - func parseAddr(cidr string) (*netlink.Addr, error) { addr, err := netlink.ParseAddr(cidr) if err != nil { @@ -333,12 +310,16 @@ func enableIPv4Forwarding() error { return nil } -func installActorNftablesRules(podIP net.IP) error { +func installActorNftablesRules(egressPort uint16) error { // Dedicated ateom-owned IPv4 table (cheap cleanup, no CNI chain mutation): - // * postrouting: masquerade actor egress (169.254.17.2) behind the pod IP. - // * prerouting: DNAT pod-IP:80/tcp to the actor veth IP. + // * prerouting: redirect actor TCP to atunnel, preserving SO_ORIGINAL_DST. + // * postrouting: masquerade traffic not handled by the TCP tunnel, notably + // DNS over UDP. // * forward: accept forwarded packets between the actor veth and pod eth0. - // Mirrors cmd/ateom-gvisor (same compatibility-bridge caveats and TODOs). + // Mirrors cmd/ateom-gvisor. + // + // TODO: Restrict the compatibility masquerade to DNS traffic sent to the + // configured cluster resolver and drop all other non-tunneled actor egress. if err := removeActorNftablesRules(); err != nil { return err } @@ -357,28 +338,9 @@ func installActorNftablesRules(podIP net.IP) error { Hooknum: nftables.ChainHookPrerouting, Priority: nftables.ChainPriorityNATDest, }) - preroutingExprs := append(ipDestinationEqual(podIP.String()), tcpDestinationPortEqual(80)...) - preroutingExprs = append(preroutingExprs, - &expr.Immediate{ - Register: 1, - Data: net.ParseIP(actorVethIP).To4(), - }, - &expr.Immediate{ - Register: 2, - Data: binaryutil.BigEndian.PutUint16(80), - }, - &expr.NAT{ - Type: expr.NATTypeDestNAT, - Family: unix.NFPROTO_IPV4, - RegAddrMin: 1, - RegProtoMin: 2, - }, - ) - c.AddRule(&nftables.Rule{ - Table: table, - Chain: prerouting, - Exprs: preroutingExprs, - }) + if redirectRule := actorEgressRedirectRule(table, prerouting, egressPort); redirectRule != nil { + c.AddRule(redirectRule) + } postrouting := c.AddChain(&nftables.Chain{ Name: "postrouting", @@ -439,10 +401,6 @@ func ipSourceEqual(ip string) []expr.Any { return ipPayloadEqual(12, ip) } -func ipDestinationEqual(ip string) []expr.Any { - return ipPayloadEqual(16, ip) -} - func ipPayloadEqual(offset uint32, ip string) []expr.Any { return []expr.Any{ &expr.Payload{ @@ -459,7 +417,7 @@ func ipPayloadEqual(offset uint32, ip string) []expr.Any { } } -func tcpDestinationPortEqual(port uint16) []expr.Any { +func tcpProtocol() []expr.Any { return []expr.Any{ &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ @@ -467,18 +425,22 @@ func tcpDestinationPortEqual(port uint16) []expr.Any { Register: 1, Data: []byte{unix.IPPROTO_TCP}, }, - &expr.Payload{ - DestRegister: 1, - Base: expr.PayloadBaseTransportHeader, - Offset: 2, - Len: 2, - }, - &expr.Cmp{ - Op: expr.CmpOpEq, + } +} + +func actorEgressRedirectRule(table *nftables.Table, chain *nftables.Chain, port uint16) *nftables.Rule { + if port == 0 { + return nil + } + exprs := append(ipSourceEqual(actorVethIP), tcpProtocol()...) + exprs = append(exprs, + &expr.Immediate{ Register: 1, Data: binaryutil.BigEndian.PutUint16(port), }, - } + &expr.Redir{RegisterProtoMin: 1}, + ) + return &nftables.Rule{Table: table, Chain: chain, Exprs: exprs} } // createNetNSWithoutSwitching creates a named netns and returns its handle, diff --git a/cmd/ateom-microvm/restore.go b/cmd/ateom-microvm/restore.go index e24967fb2..bf19b18ba 100644 --- a/cmd/ateom-microvm/restore.go +++ b/cmd/ateom-microvm/restore.go @@ -52,6 +52,10 @@ func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.Restore s.lock.Lock() defer s.lock.Unlock() + if err := s.deactivateActorNetworking(ctx); err != nil { + return nil, err + } + p := actorBootParams{ actorRef: resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()}, actorUID: req.GetActorUid(), @@ -59,6 +63,9 @@ func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.Restore templateName: req.GetActorTemplateName(), containers: req.GetSpec().GetContainers(), assetPaths: req.GetRuntimeAssetPaths(), + + actorVersion: req.GetActorVersion(), + egressGatewayAddress: req.GetEgressGatewayAddress(), } restoreDir := ateompath.RestoreStateDir(p.actorUID) durableDir := ateompath.DurableDirVolumeMountsDir(p.actorUID) @@ -181,7 +188,7 @@ func (s *AteomService) restoreFullScope(ctx context.Context, p actorBootParams, // Networking: rebuild the per-activation veth + tap; the snapshot's virtio-net // is fd-backed, so CH needs fresh tap FDs (net_fds) on restore. - if err := s.setupActorNetwork(ctx); err != nil { + if err := s.setupActorNetwork(ctx, p.egressGatewayAddress != ""); err != nil { return fmt.Errorf("while setting up actor network: %w", err) } defer func() { @@ -275,6 +282,9 @@ func (s *AteomService) restoreFullScope(ctx context.Context, p actorBootParams, } } + if err := s.activateActorNetworking(atespace, name, p.actorVersion, p.egressGatewayAddress); err != nil { + return err + } s.running[actorUID] = ra slog.InfoContext(ctx, "Actor restored (overlay rootfs)", slog.String("id", actorUID), slog.Duration("total", time.Since(tStart))) diff --git a/cmd/ateom-microvm/run.go b/cmd/ateom-microvm/run.go index 01629d372..f6eeb6231 100644 --- a/cmd/ateom-microvm/run.go +++ b/cmd/ateom-microvm/run.go @@ -197,6 +197,9 @@ func writeGuestResolvConf(rootfs string) error { func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkloadRequest) (*ateompb.RunWorkloadResponse, error) { s.lock.Lock() defer s.lock.Unlock() + if err := s.deactivateActorNetworking(ctx); err != nil { + return nil, err + } p := actorBootParams{ actorRef: resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()}, @@ -205,6 +208,9 @@ func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkload templateName: req.GetActorTemplateName(), containers: req.GetSpec().GetContainers(), assetPaths: req.GetRuntimeAssetPaths(), + + actorVersion: req.GetActorVersion(), + egressGatewayAddress: req.GetEgressGatewayAddress(), } s.actorLogger.EmitLifecycleLog("Actor starting", p.actorRef, p.actorUID, p.templateNS, p.templateName) @@ -226,6 +232,12 @@ type actorBootParams struct { templateName string containers []*ateompb.Container assetPaths map[string]string + // actorVersion is the Actor resource version ate-api observed when it + // assigned this worker; atunnel asserts it to the egress gateway. + actorVersion int64 + // egressGatewayAddress is empty unless an egress gateway is configured, in + // which case actor TCP egress is redirected to atunnel's local listener. + egressGatewayAddress string } // coldBootActor boots the actor's micro-VM from scratch and starts its @@ -258,7 +270,7 @@ func (s *AteomService) coldBootActor(ctx context.Context, p actorBootParams) (re // Networking (host side): per-activation veth into the interior netns. The // tap + TC mirror is built below (after the VM exists) so its FDs are fresh. - if err := s.setupActorNetwork(ctx); err != nil { + if err := s.setupActorNetwork(ctx, p.egressGatewayAddress != ""); err != nil { return fmt.Errorf("while setting up actor network: %w", err) } defer func() { @@ -415,6 +427,9 @@ func (s *AteomService) coldBootActor(ctx context.Context, p actorBootParams) (re } ra := &runningActor{chCmd: chCmd, vfsdCmd: vfsdCmd, durableVfsdCmd: durableVfsdCmd, apiSocket: apiSocket, baseID: actorUID, logAgent: ac} + if err := s.activateActorNetworking(atespace, name, p.actorVersion, p.egressGatewayAddress); err != nil { + return err + } s.running[actorUID] = ra // Forward each container's stdout/stderr into the pod logs. The overlay workload's diff --git a/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner.go b/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner.go index 815d38b6d..3ea8c6688 100644 --- a/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner.go +++ b/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner.go @@ -30,6 +30,7 @@ import ( "github.com/agent-substrate/substrate/internal/localca" "github.com/agent-substrate/substrate/internal/substratex509" certsv1beta1 "k8s.io/api/certificates/v1beta1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/utils/clock" @@ -47,9 +48,18 @@ const ( ateletServiceAccount = "atelet" ) -func extKeyUsages(namespace, serviceAccount string) []x509.ExtKeyUsage { +// workerPoolLabel marks pods created by atecontroller for a WorkerPool. Worker +// pods host the atunnel ingress server, presenting this cert as a TLS server +// cert to atenet-router, so they need the serverAuth EKU too. They run as the +// actor namespace's default ServiceAccount, so the label is what distinguishes +// them rather than their identity. +const workerPoolLabel = "ate.dev/worker-pool" + +func extKeyUsages(pod *corev1.Pod, namespace, serviceAccount string) []x509.ExtKeyUsage { usages := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} - if namespace == ateletNamespace && serviceAccount == ateletServiceAccount { + _, isWorker := pod.ObjectMeta.Labels[workerPoolLabel] + isAtelet := namespace == ateletNamespace && serviceAccount == ateletServiceAccount + if isAtelet || isWorker { usages = append(usages, x509.ExtKeyUsageServerAuth) } return usages @@ -146,7 +156,7 @@ func (h *Impl) MakeCert(ctx context.Context, pcr *certsv1beta1.PodCertificateReq NotAfter: notAfter, URIs: []*url.URL{spiffeURI}, KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: extKeyUsages(pcr.ObjectMeta.Namespace, pcr.Spec.ServiceAccountName), + ExtKeyUsage: extKeyUsages(pod, pcr.ObjectMeta.Namespace, pcr.Spec.ServiceAccountName), // Link the leaf to its issuing CA by key id so verifiers can disambiguate // a multi-CA trust bundle (e.g. valkey trusts both the servicedns and // podidentity CAs). diff --git a/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner_test.go b/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner_test.go index 6afd1d50c..f3bd18d1f 100644 --- a/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner_test.go +++ b/cmd/podcertcontroller/internal/podidentitysigner/podidentitysigner_test.go @@ -97,6 +97,7 @@ func TestMakeCert(t *testing.T) { namespace string podName string serviceAccount string + podLabels map[string]string maxExpirationSeconds int32 wantLifetime time.Duration wantURI string @@ -122,6 +123,28 @@ func TestMakeCert(t *testing.T) { NodeUID: "node-uid-1", }, }, + { + // Worker pods host the atunnel ingress server, so they serve TLS + // despite running as the actor namespace's default ServiceAccount. + name: "worker pod also serves", + namespace: "ate-demo-counter", + podName: "counter-abcde", + serviceAccount: "default", + podLabels: map[string]string{"ate.dev/worker-pool": "counter"}, + maxExpirationSeconds: 86400, + wantLifetime: 24 * time.Hour, + wantURI: "spiffe://cluster.local/ns/ate-demo-counter/sa/default", + wantEKUs: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + wantIdentity: &substratex509.PodIdentity{ + Namespace: "ate-demo-counter", + ServiceAccountName: "default", + ServiceAccountUID: "sa-uid-1", + PodName: "counter-abcde", + PodUID: "pod-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + }, + }, { name: "ordinary workload is client-only", namespace: "default", @@ -197,6 +220,7 @@ func TestMakeCert(t *testing.T) { } pod, pcr := makePodAndPCR(tc.namespace, tc.podName, tc.serviceAccount, tc.maxExpirationSeconds) + pod.ObjectMeta.Labels = tc.podLabels pcr.Spec.StubPKCS10Request = stubCSR(t, subjectPriv) kc := fake.NewSimpleClientset(pod, pcr) diff --git a/docs/architecture.md b/docs/architecture.md index 9a2a512cb..86d2364bc 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -340,13 +340,22 @@ A `WorkerPool` selects a **sandbox class** (`spec.sandboxClass`), and each class * **micro-VM** (`ateom-microvm`): Runs the workload inside a [Kata Containers](https://katacontainers.io/) guest on the [Cloud Hypervisor](https://www.cloudhypervisor.org/) VMM. Suspend and resume capture a memory-only VM snapshot and restore it on-demand using `userfaultfd` memory demand-paging, with container rootfs writes captured in guest RAM via a `tmpfs` overlay. `DurableDir` volumes are host-backed instead, served over a second (writable) virtio-fs share and shipped in snapshots as a tar, so a `Data`-scope snapshot can capture them without any guest memory. -### Networking Stack (`atenet` + Envoy) +### Networking Stack (`atenet` DNS + `atunnel`) Handles session-aware routing and automatic re-animation. * **Uniform DNS Mesh**: Substrate provides a location-transparent actor discovery scheme via a global DNS suffix (`..actors.resources.substrate.ate.dev`). - * **Routing**: The `atenet` router (powered by Envoy and an External Processing server) intercepts traffic destined for the mesh. It extracts the actor name from the `Host` header, queries the Control Plane to determine the actor's current location, and triggers a `ResumeActor` workflow if the session is currently suspended. + * **Ingress Routing**: `atenet-router` runs Envoy with an `ext_proc` external + processor and accepts HTTP traffic for the Actor DNS suffix. The ext_proc + extracts the Actor name and Atespace from the `Host` header and calls the + Control Plane to resume the Actor and resolve its current worker assignment. + + * **Worker Tunnel**: After resolving the assignment, `atenet-router` opens an + authenticated TLS tunnel to the worker's `atunnel` listener on port 443. + `atunnel`, hosted by `ateom`, forwards the request to the active Actor over + its private veth interface. Worker pod port 80 is not a direct Actor ingress + path. * **Latency**: The data plane is optimized for sub-100ms activation by bypassing Kubernetes' eventual consistency and performing atomic physical assignments. @@ -360,26 +369,29 @@ suspended (UML sequence diagram): sequenceDiagram actor Client participant DNS as atenet DNS - participant Router as atenet router + participant Gateway as atenet-router participant API as ate-api-server participant Atelet as atelet - participant Ateom as ateom + participant Tunnel as ateom / atunnel + participant Actor participant Store as snapshot storage Client->>DNS: resolve actor DNS name - DNS-->>Client: router address - Client->>Router: HTTP request (Host = actor) - Router->>API: ResumeActor(actorName) + DNS-->>Client: ingress gateway address + Client->>Gateway: HTTP request (Host = actor) + Gateway->>API: ResumeActor(atespace, actor name) API->>Atelet: Restore Store-->>Atelet: download snapshot - Atelet->>Ateom: RestoreWorkload - Note over Ateom: runsc restore - Ateom-->>Atelet: ready + Atelet->>Tunnel: RestoreWorkload + Note over Tunnel: restore sandbox and activate Actor network + Tunnel-->>Atelet: ready Atelet-->>API: worker pod IP - API-->>Router: worker pod IP - Router->>Ateom: proxy request to worker pod - Ateom-->>Router: response - Router-->>Client: response + API-->>Gateway: worker assignment + Gateway->>Tunnel: mTLS tunnel to worker port 443 + Tunnel->>Actor: forward request to Actor port + Actor-->>Tunnel: response + Tunnel-->>Gateway: response + Gateway-->>Client: response Note over API,Store: later: an explicit SuspendActor checkpoints back to storage and frees the worker ``` @@ -423,8 +435,9 @@ Triggered by an inbound request at the Gateway or an explicit API call. 4. **Status**: Status transitions to `STATUS_RUNNING`. The actor now has an active Worker IP. - 5. **Response**: The Control Plane returns the worker IP to the Gateway, - which forwards the original request. + 5. **Response**: The Control Plane returns the worker assignment to the + Gateway. The Gateway opens an authenticated tunnel to `atunnel` on that + worker, which forwards the original request to the active Actor. ### Phase 3: Hibernation (`SuspendActor`) diff --git a/docs/threat-model.md b/docs/threat-model.md index 7e3a0d1ac..e40b08393 100644 --- a/docs/threat-model.md +++ b/docs/threat-model.md @@ -41,9 +41,9 @@ Substrate is an early, fast moving product. It is full of debate and subject to * **ateom:** Per-worker Pod sidecar, running inside the worker Pod. Ateom sets up "interior" sandboxes in the worker Pod and manages sandbox lifecycle, including image pulls. It currently uses gvisor but Substrate will support multiple microvm solutions. * **Worker:** Preprovisioned Pods that actors get scheduled to. * **Actor:** The core compute primitive, gets scheduled to/from worker via Run for cold start and Resume for snapshot resume. -* **Actor IP:** Actor networking is based on Pod networking. Each actor gets the IP of the worker it's currently scheduled to. The ateom has the opportunity to set up additional rules when it sets up interior sandboxes. -* **Actor DNS:** Each Actor gets a DNS name like `..actors.resources.substrate.ate.dev`. Substrate runs a custom CoreDNS instance that returns atenet-router's IP address for any A record query matching the actor DNS name pattern. Substrate also includes a built-in controller that both keeps Substrate's CoreDNS configuration up to date with the router's Service IP and updates kube-dns with a stub domain for `actors.resources.substrate.ate.dev` that points to the IP of Substrate's CoreDNS Service. The latter enables traditional Kubernetes Pods to resolve Substrate Actor DNS names. -* **atenet-router:** Substrate runs an Envoy proxy to handle ingress to actors. When a client sends a request to an actor's DNS name, it is resolved to atenet-router's Envoy sidecar. Envoy then forwards headers to atenet-router (ext\_proc filter), which extracts the actor name, automatically resumes the actor if it is in a suspended state, queries the Substrate API for the current IP of the actor, and tells Envoy to rewrite the host header to the actor IP before forwarding the request to the actor. atenet-router includes a local xDS server that configures the Envoy sidecar with this behavior. +* **Actor Network:** `ateom` creates a private point-to-point veth network for the active Actor inside a worker Pod. The Actor is not served directly from the worker Pod's port 80; ingress enters through `atunnel`'s authenticated listener on port 443. +* **Actor DNS:** Each Actor gets a DNS name like `..actors.resources.substrate.ate.dev`. Substrate runs a custom CoreDNS instance that returns the `atenet-router` Service IP for A record queries matching the Actor DNS pattern. A controller keeps this target current and configures kube-dns with a stub domain for `actors.resources.substrate.ate.dev`, enabling traditional Kubernetes Pods to resolve Actor names. +* **atenet-router:** Substrate runs Envoy with an `ext_proc` external processor to handle Actor ingress. The ext_proc extracts the Actor name and Atespace from the HTTP `Host` header, calls the Substrate API to resume the Actor and obtain its current worker assignment, and selects that worker as a dynamic backend. The router then connects with mTLS to `atunnel` on worker port 443; `atunnel` validates the router identity and forwards traffic only to the Actor currently assigned to that worker. * **Object Storage:** Used to store actor snapshots. * **Filesystem support:** Container local filesystem is saved in snapshots, future integrations likely to include networked storage. * **Substrate Database:** Currently Valkey (Redis-compatible API). The choice of backend database/interface is under active debate. @@ -73,13 +73,13 @@ While it's probably unlikely that Substrate, which is expected to be an internal | GitHub Issue | Priority | Threat | Mitigating Invariants | Suggested Concrete Mitigations | Notes | | :---- | :---- | :---- | :---- | :---- | :---- | -| | Critical | Access to the internal network allows arbitrary actions to be performed on ate-apiserver, atelet, substrate backend database, etc. | All system components must have basic mutual authentication and authorization, and communicate over TLS. All clients (including end users and actors) must be authenticated and authorized. Unauthenticated traffic must be rejected. Where possible, use firewalls to block access from ranges that have no business communicating with Substrate components. | mTLS or other secure channel (e.g. UDS) between networked system components (ate-apiserver, atelet, ateom, etc) each atelet has a unique identity cryptographically tied to the node identity ate-router should check client permissions before resuming actors or forwarding traffic to actors. The only component authorized to connect directly to the backend database should be ate-apiserver. Use Kubernetes NetworkPolicy to block access to the Substrate API and other core components by default. | | +| | Critical | Access to the internal network allows arbitrary actions to be performed on ate-apiserver, atelet, substrate backend database, etc. | All system components must have basic mutual authentication and authorization, and communicate over TLS. All clients (including end users and actors) must be authenticated and authorized. Unauthenticated traffic must be rejected. Where possible, use firewalls to block access from ranges that have no business communicating with Substrate components. | mTLS or other secure channel (e.g. UDS) between networked system components (ate-apiserver, atelet, ateom, etc); each atelet has a unique identity cryptographically tied to the node identity. The ingress gateway should check client permissions before resuming actors or forwarding traffic to actors. The only component authorized to connect directly to the backend database should be ate-apiserver. Use Kubernetes NetworkPolicy to block access to the Substrate API and other core components by default. | | | | High | Privilege escalation via access to sensitive labels. | If Substrate offers its own resource labeling mechanism, it must also offer a way to authorize label updates on a per-label basis. | Substrate authorization system requires explicit authorization to update metadata, separate from updating resource body. Substrate authorization system supports per-label authorization rules. | Plenty of attacks in K8s were possible because labels had semantic meaning, but the permission model could implicitly granted access to modify labels, even if it was inappropriate. For example, /status subresource allows label updates. Substrate shouldn't repeat this mistake. | -| | High | Attacker gains control of Substrate API server, router, or other ingress/egress proxy. | Isolate the control plane from the data plane, and isolate data plane ingress from sandboxes. | Don't co-locate ate-apiserver or other control-plane components on the same machines as the untrusted sandboxes. This also supports control-plane / data-plane isolation from a reliability and performance perspective. Consider running any gateway/router that enables direct interaction with sandboxes on a separate VM from the sandboxes. Consider using a zero-trust architecture where traffic is encrypted and authenticated end-to-end and routed via a mesh. | While the sandbox is trusted as an isolation layer, the residual risk is typically not worth the marginal cost savings of colocating, and isolation is recommend to avoid noisy neighbor problems and other reliability issues that stem from mixing the control-plane and data-plane, especially for large scale deployments of Substrate. | +| | High | Attacker gains control of the Substrate API server or an ingress/egress gateway. | Isolate the control plane from the data plane, and isolate data plane ingress from sandboxes. | Don't co-locate ate-apiserver or other control-plane components on the same machines as the untrusted sandboxes. This also supports control-plane / data-plane isolation from a reliability and performance perspective. Consider running any gateway that enables direct interaction with sandboxes on a separate VM from the sandboxes. Consider using a zero-trust architecture where traffic is encrypted and authenticated end-to-end and routed via a mesh. | While the sandbox is trusted as an isolation layer, the residual risk is typically not worth the marginal cost savings of colocating, and isolation is recommend to avoid noisy neighbor problems or other reliability issues that stem from mixing the control-plane and data-plane, especially for large scale deployments of Substrate. | | | High | Attacker who can create ActorTemplates specifies malicious runtime. | Ensure available runtime can only be configured by administrators. | Consider a mechanism like RuntimeClass to decouple configuration of available runtimes from consumption of available runtimes. | | | | High | Attacker who can create ActorTemplates can read or write any storage buckets atelet has access to. | Ensure that bucket access is least-privilege. | Use credentials derived from actor identity to read snapshots. Configure permissions to prevent atelet or nodes from having access to sensitive buckets. Don't support arbitrary URLs in APIs, instead design a standard approach for accessing the correct resource given the ActorTemplate name and compute the mapping internally, subject to scheduling-aware authorization checks. | For example: Attacker creates an ActorTemplate with the runsc URL or golden snapshot URL pointing to an arbitrary bucket in the same project/resource scope as the cluster. If atelet has project-wide access to buckets, this could cause the state to be pulled into the worker pod or malicious actor. Similarly, an attacker could set the snapshots URL to point to an internal infrastructure bucket, causing data to be written to that bucket. | -| | Medium | DoS attack against API, router, or available cluster resources. | Minimize exposure and ensure APIs and proxies implement appropriately scoped quotas and rate limiting. | Don't co-locate ate-apiserver or other control-plane components on the same machines as the untrusted sandboxes. Don't expose ate-apiserver directly to the internet. Consider an identity-aware WAF (likely up to providers) if external access is required. Use network policy to block direct interaction between untrusted sandboxes and the ate-apiserver. Implement quotas and rate limiting in the API and in proxies. This includes quotas for the number of actors a user can allocate. Use a zero-trust architecture that prevents identity spoofing or intentional misrouting to get around limits. | Notably, the router checks ate-apiserver for the actor IP on each request. A flood of traffic to actors could potentially result in high read load on ate-apiserver. Caching could be considered, but cache invalidation during actor rescheduling would be important to avoid misrouting traffic. | -| | Medium | Internal network traffic is intercepted or spoofed | Encrypt all traffic by default | Use mTLS between all system components, and between the router and actors. | It may be desirable to rely on cloud providers to transparently encrypt traffic between VMs on their internal network. Worth discussing what makes the most sense. | +| | Medium | DoS attack against API, gateway, or available cluster resources. | Minimize exposure and ensure APIs and proxies implement appropriately scoped quotas and rate limiting. | Don't co-locate ate-apiserver or other control-plane components on the same machines as the untrusted sandboxes. Don't expose ate-apiserver directly to the internet. Consider an identity-aware WAF (likely up to providers) if external access is required. Use network policy to block direct interaction between untrusted sandboxes and the ate-apiserver. Implement quotas and rate limiting in the API and in proxies. This includes quotas for the number of actors a user can allocate. Use a zero-trust architecture that prevents identity spoofing or intentional misrouting to get around limits. | The gateway calls ate-apiserver to resolve Actor assignments and uses a short-lived cache. A flood of Actor traffic could still create high API load; cache invalidation during Actor rescheduling must avoid stale routing. | +| | Medium | Internal network traffic is intercepted or spoofed | Encrypt all traffic by default | Use mTLS between all system components, including the gateway-to-worker `atunnel` connection. | It may be desirable to rely on cloud providers to transparently encrypt traffic between VMs on their internal network. Worth discussing what makes the most sense. | ## Misconfiguration Risks @@ -88,7 +88,7 @@ While it's probably unlikely that Substrate, which is expected to be an internal | | High | Improper handling of Secrets | Ensure there is an official, secure, recommended way to pass secret data, like API access tokens, to actors. | Support env and filesystem plumbing for Kubernetes Secrets, to provide an official path that avoids secret material being plumbed via nonspecific fields that are difficult to audit. Ensure secrets are encrypted in transit and ideally stored in memory. If exposed via the filesystem, do so via in-memory tmpfs. | If we don't support this, users will inevitably put secrets in plaintext. | | | Medium | Complexity configuring permissions for frameworks on top of Substrate may lead to unintentional privilege escalation. | It must be clear to users what the downstream effects of auth config in substrate are. | If it's not intuitive, it must be documented in a user guide. | AI framework has to set up permissions to access ATE, and to access K8s, and potentially for actors (based on ATE identity and K8s identity) to access the framework. We need to make this easy. Think about past K8s issues like escalate/bind risk. Substrate resource model is spread across ate-apiserver and K8s, increasing complexity and chance for error. | | | Medium | Flat namespace of actors encourages broad permission grants or complex graph-oriented policy. | Support a grouping mechanism that can be used in policy controls. | Add namespaces to Substrate, similar to Kubernetes. | | -| | Medium | DNS misconfiguration | Access to DNS configuration should be limited to authoritative controllers. Routing should use stable configurations and query the API for the current IP before routing each request. | Don't co-locate controllers with access to sensitive system state on the same nodes as actors. Limit permissions to update DNS configuration. Actively query Substrate API to ensure IPs are as up-to-date as possible. Potentially use mTLS based on actor DNS name between ate-router and actors. | As noted above, a flood of requests could create high read load on ate-apiserver. Caching could be considered, but cache invalidation during actor rescheduling would be important to avoid misrouting traffic. Establishing a backend mTLS tunnel between ate-router and each actor based on a serving cert signed for the actor's DNS name could be another approach to avoiding misrouting. | +| | Medium | DNS misconfiguration | Access to DNS configuration should be limited to authoritative controllers. Routing should use stable configurations and query the API for the current worker assignment before forwarding requests. | Don't co-locate controllers with access to sensitive system state on the same nodes as actors. Limit permissions to update DNS configuration. Use the Substrate API and bounded caching to keep assignments current. Authenticate gateway-to-worker traffic with mTLS. | A flood of requests could create high read load on ate-apiserver. Cache invalidation during Actor rescheduling is important to avoid stale routing. The gateway-to-`atunnel` mTLS connection prevents the removed direct worker-port path from bypassing worker assignment checks. | ## Attacks from Actors diff --git a/internal/atunnel/client.go b/internal/atunnel/client.go new file mode 100644 index 000000000..13692208c --- /dev/null +++ b/internal/atunnel/client.go @@ -0,0 +1,213 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package atunnel + +import ( + "bufio" + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + "strconv" + "strings" + + "github.com/agent-substrate/substrate/internal/resources" +) + +const ( + // ActorAtespaceHeader identifies the atespace whose actor opened an egress + // tunnel. The egress gateway must authenticate this metadata before using it + // for policy decisions. + ActorAtespaceHeader = "X-Ate-Atespace" + // ActorNameHeader identifies the actor that opened an egress tunnel. + ActorNameHeader = "X-Ate-Actor" + // ActorVersionHeader is the Actor resource version observed when the worker + // was assigned. Gateways use it as a lower bound on cached Actor metadata. + ActorVersionHeader = "X-Ate-Actor-Version" +) + +// ClientConfig configures an egress CONNECT client. +type ClientConfig struct { + GatewayAddress string + ServerName string + CredentialBundlePath string + TrustBundlePath string + + // DialContext is injectable for tests. When nil, a net.Dialer is used. + DialContext func(context.Context, string, string) (net.Conn, error) +} + +// EgressMetadata is attached to an egress CONNECT request. BearerToken is +// optional until actor JWT issuance is wired into ateom. +type EgressMetadata struct { + Atespace string + ActorName string + ActorVersion int64 + BearerToken string +} + +// Client opens actor egress streams through an mTLS-authenticated gateway. +type Client struct { + gatewayAddress string + tlsConfig *tls.Config + dialContext func(context.Context, string, string) (net.Conn, error) +} + +var _ EgressDialer = (*Client)(nil) + +// NewClient creates an egress CONNECT client and validates its TLS material. +func NewClient(cfg ClientConfig) (*Client, error) { + if _, _, err := net.SplitHostPort(cfg.GatewayAddress); err != nil { + return nil, fmt.Errorf("atunnel: invalid egress gateway address %q: %w", cfg.GatewayAddress, err) + } + if cfg.ServerName == "" { + return nil, fmt.Errorf("atunnel: egress gateway server name is required") + } + if cfg.CredentialBundlePath == "" { + return nil, fmt.Errorf("atunnel: credential bundle path is required") + } + if cfg.TrustBundlePath == "" { + return nil, fmt.Errorf("atunnel: trust bundle path is required") + } + if _, err := loadCredentialBundle(cfg.CredentialBundlePath); err != nil { + return nil, err + } + trustPEM, err := os.ReadFile(cfg.TrustBundlePath) + if err != nil { + return nil, fmt.Errorf("atunnel: reading trust bundle: %w", err) + } + rootCAs := x509.NewCertPool() + if !rootCAs.AppendCertsFromPEM(trustPEM) { + return nil, fmt.Errorf("atunnel: trust bundle %q contains no certificates", cfg.TrustBundlePath) + } + + dialContext := cfg.DialContext + if dialContext == nil { + dialContext = (&net.Dialer{}).DialContext + } + credentialBundlePath := cfg.CredentialBundlePath + return &Client{ + gatewayAddress: cfg.GatewayAddress, + dialContext: dialContext, + tlsConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + RootCAs: rootCAs, + ServerName: cfg.ServerName, + GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + return loadCredentialBundle(credentialBundlePath) + }, + }, + }, nil +} + +// DialContext opens a CONNECT tunnel to destination. destination becomes the +// request authority, so it must include an explicit port. +func (c *Client) DialContext(ctx context.Context, destination string, metadata EgressMetadata) (net.Conn, error) { + if err := validateDestination(destination); err != nil { + return nil, err + } + if !resources.IsValidResourceName(metadata.Atespace) || !resources.IsValidResourceName(metadata.ActorName) { + return nil, fmt.Errorf("atunnel: invalid actor identity %q/%q", metadata.Atespace, metadata.ActorName) + } + if metadata.ActorVersion < 1 { + return nil, fmt.Errorf("atunnel: actor version must be positive") + } + + rawConn, err := c.dialContext(ctx, "tcp", c.gatewayAddress) + if err != nil { + return nil, fmt.Errorf("atunnel: connecting to egress gateway: %w", err) + } + tlsConn := tls.Client(rawConn, c.tlsConfig.Clone()) + if err := tlsConn.HandshakeContext(ctx); err != nil { + _ = rawConn.Close() + return nil, fmt.Errorf("atunnel: egress gateway TLS handshake: %w", err) + } + + req := &http.Request{ + Method: http.MethodConnect, + URL: &url.URL{Host: destination}, + Host: destination, + Header: http.Header{ + ActorAtespaceHeader: []string{metadata.Atespace}, + ActorNameHeader: []string{metadata.ActorName}, + ActorVersionHeader: []string{strconv.FormatInt(metadata.ActorVersion, 10)}, + }, + } + if metadata.BearerToken != "" { + req.Header.Set("Authorization", "Bearer "+metadata.BearerToken) + } + if err := req.Write(tlsConn); err != nil { + _ = tlsConn.Close() + return nil, fmt.Errorf("atunnel: writing CONNECT request: %w", err) + } + + reader := bufio.NewReader(tlsConn) + resp, err := http.ReadResponse(reader, req) + if err != nil { + _ = tlsConn.Close() + return nil, fmt.Errorf("atunnel: reading CONNECT response: %w", err) + } + if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { + body, _ := io.ReadAll(io.LimitReader(resp.Body, 4<<10)) + _ = resp.Body.Close() + _ = tlsConn.Close() + message := strings.TrimSpace(string(body)) + if message == "" { + message = http.StatusText(resp.StatusCode) + } + return nil, fmt.Errorf("atunnel: egress gateway rejected CONNECT with %s: %s", resp.Status, message) + } + + return &bufferedConn{Conn: tlsConn, reader: reader}, nil +} + +func validateDestination(destination string) error { + host, port, err := net.SplitHostPort(destination) + if err != nil { + return fmt.Errorf("atunnel: invalid egress destination %q: %w", destination, err) + } + if host == "" { + return fmt.Errorf("atunnel: invalid egress destination %q: host is empty", destination) + } + if net.ParseIP(host) == nil { + return fmt.Errorf("atunnel: invalid egress destination %q: host must be an IP address", destination) + } + portNumber, err := strconv.Atoi(port) + if err != nil || portNumber < 1 || portNumber > 65535 { + return fmt.Errorf("atunnel: invalid egress destination %q: port must be between 1 and 65535", destination) + } + return nil +} + +type bufferedConn struct { + net.Conn + reader *bufio.Reader +} + +func (c *bufferedConn) Read(p []byte) (int, error) { + return c.reader.Read(p) +} + +func (c *bufferedConn) CloseWrite() error { + if conn, ok := c.Conn.(interface{ CloseWrite() error }); ok { + return conn.CloseWrite() + } + return nil +} diff --git a/internal/atunnel/client_test.go b/internal/atunnel/client_test.go new file mode 100644 index 000000000..f52629f19 --- /dev/null +++ b/internal/atunnel/client_test.go @@ -0,0 +1,251 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package atunnel + +import ( + "bufio" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "io" + "math/big" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +func TestClientDialContext(t *testing.T) { + ca := newTestCA(t) + client := newTestClient(t, ca) + request := make(chan *http.Request, 1) + gatewayAddress := serveTestConnectGateway(t, ca, func(conn net.Conn, req *http.Request) { + request <- req + if _, err := io.WriteString(conn, "HTTP/1.1 200 Connection Established\r\n\r\nhello"); err != nil { + t.Errorf("writing CONNECT response: %v", err) + return + } + payload := make([]byte, len("ping")) + if _, err := io.ReadFull(conn, payload); err != nil { + t.Errorf("reading tunneled payload: %v", err) + return + } + if string(payload) != "ping" { + t.Errorf("tunneled payload = %q, want ping", payload) + } + }) + client.gatewayAddress = gatewayAddress + + conn, err := client.DialContext(context.Background(), "192.0.2.10:443", EgressMetadata{ + Atespace: "team-a", + ActorName: "actor-1", + ActorVersion: 7, + BearerToken: "actor-token", + }) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + gotRequest := <-request + if gotRequest.Method != http.MethodConnect { + t.Errorf("method = %q, want CONNECT", gotRequest.Method) + } + if gotRequest.Host != "192.0.2.10:443" { + t.Errorf("authority = %q, want 192.0.2.10:443", gotRequest.Host) + } + if got := gotRequest.Header.Get(ActorAtespaceHeader); got != "team-a" { + t.Errorf("%s = %q, want team-a", ActorAtespaceHeader, got) + } + if got := gotRequest.Header.Get(ActorNameHeader); got != "actor-1" { + t.Errorf("%s = %q, want actor-1", ActorNameHeader, got) + } + if got := gotRequest.Header.Get(ActorVersionHeader); got != "7" { + t.Errorf("%s = %q, want 7", ActorVersionHeader, got) + } + if got := gotRequest.Header.Get("Authorization"); got != "Bearer actor-token" { + t.Errorf("Authorization = %q, want Bearer actor-token", got) + } + + buffered := make([]byte, len("hello")) + if _, err := io.ReadFull(conn, buffered); err != nil { + t.Fatal(err) + } + if string(buffered) != "hello" { + t.Errorf("buffered payload = %q, want hello", buffered) + } + if _, err := io.WriteString(conn, "ping"); err != nil { + t.Fatal(err) + } +} + +func TestClientDialContextRejected(t *testing.T) { + ca := newTestCA(t) + client := newTestClient(t, ca) + client.gatewayAddress = serveTestConnectGateway(t, ca, func(conn net.Conn, _ *http.Request) { + body := "denied by policy" + _, _ = fmt.Fprintf(conn, "HTTP/1.1 403 Forbidden\r\nContent-Length: %d\r\n\r\n%s", len(body), body) + }) + + _, err := client.DialContext(context.Background(), "192.0.2.10:443", EgressMetadata{ + Atespace: "team-a", + ActorName: "actor-1", + ActorVersion: 7, + }) + if err == nil || !strings.Contains(err.Error(), "denied by policy") { + t.Fatalf("DialContext error = %v, want policy rejection", err) + } +} + +func TestClientDialContextValidatesInput(t *testing.T) { + ca := newTestCA(t) + client := newTestClient(t, ca) + tests := []struct { + name string + destination string + metadata EgressMetadata + }{ + { + name: "destination has no port", + destination: "192.0.2.10", + metadata: EgressMetadata{Atespace: "team-a", ActorName: "actor-1", ActorVersion: 7}, + }, + { + name: "destination is a hostname", + destination: "example.com:443", + metadata: EgressMetadata{Atespace: "team-a", ActorName: "actor-1", ActorVersion: 7}, + }, + { + name: "invalid atespace", + destination: "192.0.2.10:443", + metadata: EgressMetadata{Atespace: "TEAM A", ActorName: "actor-1", ActorVersion: 7}, + }, + { + name: "invalid actor", + destination: "192.0.2.10:443", + metadata: EgressMetadata{Atespace: "team-a", ActorName: "actor/1", ActorVersion: 7}, + }, + { + name: "invalid actor version", + destination: "192.0.2.10:443", + metadata: EgressMetadata{Atespace: "team-a", ActorName: "actor-1"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := client.DialContext(context.Background(), tt.destination, tt.metadata); err == nil { + t.Fatal("DialContext unexpectedly succeeded") + } + }) + } +} + +func newTestClient(t *testing.T, ca *testCA) *Client { + t.Helper() + dir := t.TempDir() + bundlePath := filepath.Join(dir, "client.pem") + trustPath := filepath.Join(dir, "trust.pem") + writeCredentialBundle(t, bundlePath, ca.issue(t, + "spiffe://cluster.local/ns/ate-demo/sa/ateom", + []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + )) + if err := os.WriteFile(trustPath, ca.certPEM, 0o600); err != nil { + t.Fatal(err) + } + client, err := NewClient(ClientConfig{ + GatewayAddress: "127.0.0.1:1", + ServerName: "egress.test", + CredentialBundlePath: bundlePath, + TrustBundlePath: trustPath, + }) + if err != nil { + t.Fatal(err) + } + return client +} + +func serveTestConnectGateway(t *testing.T, ca *testCA, handle func(net.Conn, *http.Request)) string { + t.Helper() + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + serverCert := issueDNSCertificate(t, ca, "egress.test") + clientCAs := x509.NewCertPool() + clientCAs.AppendCertsFromPEM(ca.certPEM) + tlsListener := tls.NewListener(listener, &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{serverCert}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: clientCAs, + }) + t.Cleanup(func() { _ = tlsListener.Close() }) + + go func() { + conn, err := tlsListener.Accept() + if err != nil { + return + } + defer conn.Close() + req, err := http.ReadRequest(bufio.NewReader(conn)) + if err != nil { + t.Errorf("reading CONNECT request: %v", err) + return + } + handle(conn, req) + }() + return listener.Addr().String() +} + +func issueDNSCertificate(t *testing.T, ca *testCA, dnsName string) tls.Certificate { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + now := time.Now() + template := &x509.Certificate{ + SerialNumber: big.NewInt(now.UnixNano()), + NotBefore: now.Add(-time.Minute), + NotAfter: now.Add(time.Hour), + DNSNames: []string{dnsName}, + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + der, err := x509.CreateCertificate(rand.Reader, template, ca.cert, &key.PublicKey, ca.key) + if err != nil { + t.Fatal(err) + } + keyDER, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + t.Fatal(err) + } + cert, err := tls.X509KeyPair( + pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), + pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER}), + ) + if err != nil { + t.Fatal(err) + } + return cert +} diff --git a/internal/atunnel/egress.go b/internal/atunnel/egress.go new file mode 100644 index 000000000..5b1d63b08 --- /dev/null +++ b/internal/atunnel/egress.go @@ -0,0 +1,207 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package atunnel + +import ( + "context" + "errors" + "fmt" + "io" + "log/slog" + "net" + "sync" + + "github.com/agent-substrate/substrate/internal/resources" +) + +// EgressDialer opens an authenticated tunnel to an original destination. +type EgressDialer interface { + DialContext(context.Context, string, EgressMetadata) (net.Conn, error) +} + +// OriginalDestination returns the address that a transparently intercepted +// connection originally targeted. +type OriginalDestination func(net.Conn) (string, error) + +// Egress proxies actor TCP connections through an egress CONNECT dialer. It is +// long-lived across actor activations, but only carries traffic while an actor +// is assigned to its worker. +type Egress struct { + originalDestination OriginalDestination + + mu sync.Mutex + active *egressActivation +} + +type egressActivation struct { + metadata EgressMetadata + dialer EgressDialer + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup +} + +// NewEgress creates an activation-aware egress proxy. +func NewEgress(originalDestination OriginalDestination) (*Egress, error) { + if originalDestination == nil { + return nil, fmt.Errorf("atunnel: original destination resolver is required") + } + return &Egress{ + originalDestination: originalDestination, + }, nil +} + +// Serve accepts intercepted actor connections until ctx is canceled or the +// listener fails. +func (e *Egress) Serve(ctx context.Context, listener net.Listener) error { + done := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + _ = listener.Close() + case <-done: + } + }() + defer close(done) + + for { + conn, err := listener.Accept() + if err != nil { + if ctx.Err() != nil || errors.Is(err, net.ErrClosed) { + return nil + } + return fmt.Errorf("atunnel: accepting actor egress connection: %w", err) + } + e.handle(conn) + } +} + +// Activate allows egress for one actor. There can be only one active actor per +// worker. bearerToken may be empty until actor JWT issuance is available. +func (e *Egress) Activate(dialer EgressDialer, atespace, actorName string, actorVersion int64, bearerToken string) error { + if dialer == nil { + return fmt.Errorf("atunnel: egress dialer is required") + } + if !resources.IsValidResourceName(atespace) || !resources.IsValidResourceName(actorName) { + return fmt.Errorf("atunnel: invalid actor identity %q/%q", atespace, actorName) + } + if actorVersion < 1 { + return fmt.Errorf("atunnel: actor version must be positive") + } + e.mu.Lock() + defer e.mu.Unlock() + if e.active != nil { + return fmt.Errorf("atunnel: actor %s/%s already has active egress", e.active.metadata.Atespace, e.active.metadata.ActorName) + } + ctx, cancel := context.WithCancel(context.Background()) + e.active = &egressActivation{ + metadata: EgressMetadata{ + Atespace: atespace, + ActorName: actorName, + ActorVersion: actorVersion, + BearerToken: bearerToken, + }, + dialer: dialer, + ctx: ctx, + cancel: cancel, + } + return nil +} + +// Deactivate rejects new egress, closes active streams, and waits for their +// forwarding goroutines to exit. +func (e *Egress) Deactivate(ctx context.Context) error { + e.mu.Lock() + active := e.active + e.active = nil + if active != nil { + active.cancel() + } + e.mu.Unlock() + if active == nil { + return nil + } + + done := make(chan struct{}) + go func() { + active.wg.Wait() + close(done) + }() + select { + case <-done: + return nil + case <-ctx.Done(): + return fmt.Errorf("atunnel: waiting for active egress streams to stop: %w", ctx.Err()) + } +} + +func (e *Egress) handle(downstream net.Conn) { + e.mu.Lock() + active := e.active + if active == nil { + e.mu.Unlock() + _ = downstream.Close() + return + } + active.wg.Add(1) + e.mu.Unlock() + + go func() { + defer active.wg.Done() + defer downstream.Close() + + destination, err := e.originalDestination(downstream) + if err != nil { + slog.WarnContext(active.ctx, "atunnel failed to resolve original egress destination", slog.Any("err", err)) + return + } + upstream, err := active.dialer.DialContext(active.ctx, destination, active.metadata) + if err != nil { + slog.WarnContext(active.ctx, "atunnel failed to open egress tunnel", slog.String("destination", destination), slog.Any("err", err)) + return + } + defer upstream.Close() + + stop := context.AfterFunc(active.ctx, func() { + _ = downstream.Close() + _ = upstream.Close() + }) + defer stop() + + copyBothWays(downstream, upstream) + }() +} + +func copyBothWays(a, b net.Conn) { + done := make(chan struct{}, 2) + go func() { + _, _ = io.Copy(a, b) + closeWrite(a) + done <- struct{}{} + }() + go func() { + _, _ = io.Copy(b, a) + closeWrite(b) + done <- struct{}{} + }() + <-done + <-done +} + +func closeWrite(conn net.Conn) { + if conn, ok := conn.(interface{ CloseWrite() error }); ok { + _ = conn.CloseWrite() + } +} diff --git a/internal/atunnel/egress_test.go b/internal/atunnel/egress_test.go new file mode 100644 index 000000000..17828fbd5 --- /dev/null +++ b/internal/atunnel/egress_test.go @@ -0,0 +1,114 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package atunnel + +import ( + "context" + "io" + "net" + "testing" + "time" +) + +func TestEgressForwardsActiveActor(t *testing.T) { + dialed := make(chan egressDial, 1) + upstreamProxy, upstreamGateway := net.Pipe() + t.Cleanup(func() { + _ = upstreamProxy.Close() + _ = upstreamGateway.Close() + }) + dialer := egressDialerFunc(func(_ context.Context, destination string, metadata EgressMetadata) (net.Conn, error) { + dialed <- egressDial{destination: destination, metadata: metadata} + return upstreamProxy, nil + }) + egress, err := NewEgress(func(net.Conn) (string, error) { + return "192.0.2.10:443", nil + }) + if err != nil { + t.Fatal(err) + } + if err := egress.Activate(dialer, "team-a", "actor-1", 7, "actor-token"); err != nil { + t.Fatal(err) + } + + downstreamActor, downstreamProxy := net.Pipe() + t.Cleanup(func() { + _ = downstreamActor.Close() + _ = downstreamProxy.Close() + }) + egress.handle(downstreamProxy) + + gotDial := <-dialed + if gotDial.destination != "192.0.2.10:443" { + t.Errorf("destination = %q, want 192.0.2.10:443", gotDial.destination) + } + if gotDial.metadata != (EgressMetadata{Atespace: "team-a", ActorName: "actor-1", ActorVersion: 7, BearerToken: "actor-token"}) { + t.Errorf("metadata = %+v", gotDial.metadata) + } + + actorPayload := []byte("from actor") + go func() { _, _ = downstreamActor.Write(actorPayload) }() + gotAtGateway := make([]byte, len(actorPayload)) + if _, err := io.ReadFull(upstreamGateway, gotAtGateway); err != nil { + t.Fatal(err) + } + if string(gotAtGateway) != string(actorPayload) { + t.Errorf("gateway payload = %q, want %q", gotAtGateway, actorPayload) + } + + gatewayPayload := []byte("from gateway") + go func() { _, _ = upstreamGateway.Write(gatewayPayload) }() + gotAtActor := make([]byte, len(gatewayPayload)) + if _, err := io.ReadFull(downstreamActor, gotAtActor); err != nil { + t.Fatal(err) + } + if string(gotAtActor) != string(gatewayPayload) { + t.Errorf("actor payload = %q, want %q", gotAtActor, gatewayPayload) + } + + if err := egress.Deactivate(context.Background()); err != nil { + t.Fatal(err) + } +} + +func TestEgressRejectsInactiveConnection(t *testing.T) { + egress, err := NewEgress(func(net.Conn) (string, error) { + t.Fatal("inactive egress resolved destination") + return "", nil + }) + if err != nil { + t.Fatal(err) + } + actor, proxy := net.Pipe() + defer actor.Close() + if err := actor.SetReadDeadline(time.Now().Add(time.Second)); err != nil { + t.Fatal(err) + } + egress.handle(proxy) + if _, err := actor.Read(make([]byte, 1)); err == nil { + t.Fatal("inactive connection remained open") + } +} + +type egressDial struct { + destination string + metadata EgressMetadata +} + +type egressDialerFunc func(context.Context, string, EgressMetadata) (net.Conn, error) + +func (f egressDialerFunc) DialContext(ctx context.Context, destination string, metadata EgressMetadata) (net.Conn, error) { + return f(ctx, destination, metadata) +} diff --git a/internal/atunnel/original_dst_linux.go b/internal/atunnel/original_dst_linux.go new file mode 100644 index 000000000..07dd0f934 --- /dev/null +++ b/internal/atunnel/original_dst_linux.go @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux + +package atunnel + +import ( + "encoding/binary" + "fmt" + "net" + "strconv" + "unsafe" + + "golang.org/x/sys/unix" +) + +// TCPOriginalDestination reads the IPv4 destination preserved by a Linux +// REDIRECT rule. Actor networking is currently IPv4-only. +// TODO(liorlieberman) add the IPv6 IP6T_SO_ORIGINAL_DST variant +// when actor veth setup gains dual-stack support. +func TCPOriginalDestination(conn net.Conn) (string, error) { + tcpConn, ok := conn.(*net.TCPConn) + if !ok { + return "", fmt.Errorf("atunnel: original destination requires a TCP connection, got %T", conn) + } + rawConn, err := tcpConn.SyscallConn() + if err != nil { + return "", fmt.Errorf("atunnel: acquiring TCP syscall connection: %w", err) + } + + var addr unix.RawSockaddrInet4 + var sockoptErr error + if err := rawConn.Control(func(fd uintptr) { + size := uint32(unsafe.Sizeof(addr)) + _, _, errno := unix.Syscall6( + unix.SYS_GETSOCKOPT, + fd, + unix.SOL_IP, + unix.SO_ORIGINAL_DST, + uintptr(unsafe.Pointer(&addr)), + uintptr(unsafe.Pointer(&size)), + 0, + ) + if errno != 0 { + sockoptErr = errno + } + }); err != nil { + return "", fmt.Errorf("atunnel: accessing TCP socket: %w", err) + } + if sockoptErr != nil { + return "", fmt.Errorf("atunnel: reading original TCP destination: %w", sockoptErr) + } + + portBytes := (*[2]byte)(unsafe.Pointer(&addr.Port)) + port := binary.BigEndian.Uint16(portBytes[:]) + if port == 0 { + return "", fmt.Errorf("atunnel: original TCP destination has port zero") + } + return net.JoinHostPort(net.IP(addr.Addr[:]).String(), strconv.Itoa(int(port))), nil +} diff --git a/internal/atunnel/server.go b/internal/atunnel/server.go new file mode 100644 index 000000000..e117a2df6 --- /dev/null +++ b/internal/atunnel/server.go @@ -0,0 +1,284 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package atunnel carries actor ingress and egress through an ateom worker pod. +package atunnel + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "log/slog" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/agent-substrate/substrate/internal/resources" +) + +const ( + // StaleAssignmentHeader distinguishes an atunnel routing rejection from a + // 421 returned by the actor application itself. + StaleAssignmentHeader = "X-Ate-Assignment-Stale" +) + +// Config configures an ingress Server. +type Config struct { + CredentialBundlePath string + TrustBundlePath string + AllowedClientID string + Upstream *url.URL +} + +// Server is an activation-aware HTTPS reverse proxy. It is long-lived across +// actor activations, but only routes requests for the actor currently assigned +// to its worker. +type Server struct { + credentialBundlePath string + tlsConfig *tls.Config + proxy *httputil.ReverseProxy + + mu sync.Mutex + active *activation +} + +type activation struct { + ref resources.ActorRef + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup +} + +// NewServer creates a Server and validates its TLS material. +func NewServer(cfg Config) (*Server, error) { + if cfg.CredentialBundlePath == "" { + return nil, fmt.Errorf("atunnel: credential bundle path is required") + } + if cfg.TrustBundlePath == "" { + return nil, fmt.Errorf("atunnel: trust bundle path is required") + } + if cfg.AllowedClientID == "" { + return nil, fmt.Errorf("atunnel: allowed client identity is required") + } + if cfg.Upstream == nil || cfg.Upstream.Scheme == "" || cfg.Upstream.Host == "" { + return nil, fmt.Errorf("atunnel: upstream URL is required") + } + + // Load once at startup so a malformed or missing projection fails the pod + // promptly. GetCertificate reloads the bundle for every new TLS connection, + // allowing kubelet's projected certificate rotation to take effect. + if _, err := loadCredentialBundle(cfg.CredentialBundlePath); err != nil { + return nil, err + } + trustPEM, err := os.ReadFile(cfg.TrustBundlePath) + if err != nil { + return nil, fmt.Errorf("atunnel: reading trust bundle: %w", err) + } + clientCAs := x509.NewCertPool() + if !clientCAs.AppendCertsFromPEM(trustPEM) { + return nil, fmt.Errorf("atunnel: trust bundle %q contains no certificates", cfg.TrustBundlePath) + } + + proxy := httputil.NewSingleHostReverseProxy(cfg.Upstream) + transport := http.DefaultTransport.(*http.Transport).Clone() + proxy.Transport = transport + proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { + slog.WarnContext(r.Context(), "atunnel upstream request failed", slog.Any("err", err)) + http.Error(w, "bad gateway", http.StatusBadGateway) + } + + s := &Server{ + credentialBundlePath: cfg.CredentialBundlePath, + proxy: proxy, + } + s.tlsConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) { + return loadCredentialBundle(s.credentialBundlePath) + }, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: clientCAs, + VerifyConnection: func(cs tls.ConnectionState) error { + if len(cs.PeerCertificates) == 0 { + return fmt.Errorf("atunnel: client certificate is required") + } + for _, uri := range cs.PeerCertificates[0].URIs { + if uri.String() == cfg.AllowedClientID { + return nil + } + } + return fmt.Errorf("atunnel: client is not %q", cfg.AllowedClientID) + }, + } + return s, nil +} + +func loadCredentialBundle(path string) (*tls.Certificate, error) { + pemBytes, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("atunnel: reading credential bundle: %w", err) + } + cert, err := tls.X509KeyPair(pemBytes, pemBytes) + if err != nil { + return nil, fmt.Errorf("atunnel: parsing credential bundle: %w", err) + } + return &cert, nil +} + +// Serve serves HTTPS on lis until ctx is canceled or the server fails. +func (s *Server) Serve(ctx context.Context, lis net.Listener) error { + httpServer := &http.Server{ + Handler: s, + TLSConfig: s.tlsConfig, + ReadHeaderTimeout: 10 * time.Second, + } + done := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + _ = httpServer.Close() + case <-done: + } + }() + err := httpServer.ServeTLS(lis, "", "") + close(done) + if errors.Is(err, http.ErrServerClosed) && ctx.Err() != nil { + return nil + } + return err +} + +// Activate allows requests for actorName in atespace. There can be only one +// active actor per worker. +func (s *Server) Activate(atespace, actorName string) error { + if !resources.IsValidResourceName(atespace) || !resources.IsValidResourceName(actorName) { + return fmt.Errorf("atunnel: invalid actor identity %q/%q", atespace, actorName) + } + s.mu.Lock() + defer s.mu.Unlock() + if s.active != nil { + return fmt.Errorf("atunnel: actor %s is already active", s.active.ref) + } + ctx, cancel := context.WithCancel(context.Background()) + s.active = &activation{ + ref: resources.ActorRef{Atespace: atespace, Name: actorName}, + ctx: ctx, + cancel: cancel, + } + return nil +} + +// Deactivate rejects new requests, cancels requests for the active actor, and +// waits for their handlers to exit before returning. +func (s *Server) Deactivate(ctx context.Context) error { + s.mu.Lock() + active := s.active + s.active = nil + if active != nil { + active.cancel() + } + s.mu.Unlock() + if active == nil { + return nil + } + + done := make(chan struct{}) + go func() { + active.wg.Wait() + close(done) + }() + select { + case <-done: + s.closeIdleUpstreamConnections() + return nil + case <-ctx.Done(): + s.closeIdleUpstreamConnections() + return fmt.Errorf("atunnel: waiting for active requests to stop: %w", ctx.Err()) + } +} + +func (s *Server) closeIdleUpstreamConnections() { + if transport, ok := s.proxy.Transport.(interface{ CloseIdleConnections() }); ok { + transport.CloseIdleConnections() + } +} + +// ServeHTTP validates the actor hostname on every request before proxying it. +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + host, err := requestHostname(r.Host) + if err != nil { + s.reject(w) + return + } + ref, err := resources.ParseActorDNSName(host) + if err != nil { + s.reject(w) + return + } + + s.mu.Lock() + active := s.active + if active == nil || active.ref != ref { + s.mu.Unlock() + s.reject(w) + return + } + active.wg.Add(1) + s.mu.Unlock() + defer active.wg.Done() + + requestCtx, cancel := context.WithCancel(r.Context()) + stop := context.AfterFunc(active.ctx, cancel) + defer func() { + stop() + cancel() + }() + + // ReverseProxy changes the URL destination but intentionally retains Host, + // allowing the actor application to observe its stable mesh hostname. + s.proxy.ServeHTTP(w, r.WithContext(requestCtx)) +} + +func (s *Server) reject(w http.ResponseWriter) { + w.Header().Set(StaleAssignmentHeader, "true") + http.Error(w, "misdirected request", http.StatusMisdirectedRequest) +} + +func requestHostname(hostport string) (string, error) { + if hostport == "" { + return "", fmt.Errorf("empty host") + } + host := hostport + if strings.Contains(hostport, ":") { + var port string + var err error + host, port, err = net.SplitHostPort(hostport) + if err != nil { + return "", fmt.Errorf("invalid host %q: %w", hostport, err) + } + portNumber, err := strconv.Atoi(port) + if err != nil || portNumber < 1 || portNumber > 65535 { + return "", fmt.Errorf("invalid port in host %q", hostport) + } + } + return strings.ToLower(host), nil +} diff --git a/internal/atunnel/server_test.go b/internal/atunnel/server_test.go new file mode 100644 index 000000000..1b75a836a --- /dev/null +++ b/internal/atunnel/server_test.go @@ -0,0 +1,424 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package atunnel + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "testing" + "time" +) + +func TestServeHTTP(t *testing.T) { + upstreamHost := make(chan string, 3) + upstreamURL, err := url.Parse("http://actor.internal:80") + if err != nil { + t.Fatal(err) + } + + s := newTestServer(t, upstreamURL) + s.proxy.Transport = roundTripFunc(func(r *http.Request) (*http.Response, error) { + upstreamHost <- r.Host + return &http.Response{ + StatusCode: http.StatusNoContent, + Header: make(http.Header), + Body: http.NoBody, + }, nil + }) + if err := s.Activate("team-a", "actor-1"); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + host string + wantStatus int + }{ + {"active actor", "actor-1.team-a.actors.resources.substrate.ate.dev", http.StatusNoContent}, + {"active actor with port", "actor-1.team-a.actors.resources.substrate.ate.dev:443", http.StatusNoContent}, + {"DNS case insensitive", "ACTOR-1.TEAM-A.ACTORS.RESOURCES.SUBSTRATE.ATE.DEV", http.StatusNoContent}, + {"wrong actor", "actor-2.team-a.actors.resources.substrate.ate.dev", http.StatusMisdirectedRequest}, + {"wrong atespace", "actor-1.team-b.actors.resources.substrate.ate.dev", http.StatusMisdirectedRequest}, + {"suffix confusion", "actor-1.team-a.actors.resources.substrate.ate.dev.example.com", http.StatusMisdirectedRequest}, + {"malformed port", "actor-1.team-a.actors.resources.substrate.ate.dev:nope", http.StatusMisdirectedRequest}, + {"empty", "", http.StatusMisdirectedRequest}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "https://worker/hello", nil) + req.Host = tt.host + rec := httptest.NewRecorder() + s.ServeHTTP(rec, req) + if rec.Code != tt.wantStatus { + t.Fatalf("status = %d, want %d", rec.Code, tt.wantStatus) + } + if tt.wantStatus == http.StatusMisdirectedRequest && rec.Header().Get(StaleAssignmentHeader) != "true" { + t.Errorf("missing %s response header", StaleAssignmentHeader) + } + }) + } + + for range 3 { + if got := <-upstreamHost; got != "actor-1.team-a.actors.resources.substrate.ate.dev" && got != "actor-1.team-a.actors.resources.substrate.ate.dev:443" && got != "ACTOR-1.TEAM-A.ACTORS.RESOURCES.SUBSTRATE.ATE.DEV" { + t.Errorf("upstream Host = %q", got) + } + } +} + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { + return f(r) +} + +type idleClosingRoundTripper struct { + closed bool +} + +func (t *idleClosingRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusNoContent, + Header: make(http.Header), + Body: http.NoBody, + }, nil +} + +func (t *idleClosingRoundTripper) CloseIdleConnections() { + t.closed = true +} + +func TestDeactivateClosesIdleUpstreamConnections(t *testing.T) { + upstream, err := url.Parse("http://actor.internal:80") + if err != nil { + t.Fatal(err) + } + s := newTestServer(t, upstream) + transport := &idleClosingRoundTripper{} + s.proxy.Transport = transport + if err := s.Activate("team-a", "actor-1"); err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest(http.MethodGet, "https://worker/", nil) + req.Host = "actor-1.team-a.actors.resources.substrate.ate.dev" + rec := httptest.NewRecorder() + s.ServeHTTP(rec, req) + if rec.Code != http.StatusNoContent { + t.Fatalf("status = %d, want %d", rec.Code, http.StatusNoContent) + } + if err := s.Deactivate(context.Background()); err != nil { + t.Fatal(err) + } + if !transport.closed { + t.Fatal("Deactivate did not close idle upstream connections") + } +} + +func TestInactive(t *testing.T) { + upstream, err := url.Parse("http://127.0.0.1:1") + if err != nil { + t.Fatal(err) + } + s := newTestServer(t, upstream) + req := httptest.NewRequest(http.MethodGet, "https://worker/", nil) + req.Host = "actor-1.team-a.actors.resources.substrate.ate.dev" + + for _, phase := range []string{"before activation", "after deactivation"} { + t.Run(phase, func(t *testing.T) { + rec := httptest.NewRecorder() + s.ServeHTTP(rec, req) + if rec.Code != http.StatusMisdirectedRequest { + t.Fatalf("status = %d, want %d", rec.Code, http.StatusMisdirectedRequest) + } + }) + if phase == "before activation" { + if err := s.Activate("team-a", "actor-1"); err != nil { + t.Fatal(err) + } + if err := s.Deactivate(context.Background()); err != nil { + t.Fatal(err) + } + } + } +} + +func TestMutualTLSClientIdentity(t *testing.T) { + dir := t.TempDir() + ca := newTestCA(t) + serverCert := ca.issue(t, "", []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}) + bundlePath := filepath.Join(dir, "server.pem") + trustPath := filepath.Join(dir, "trust.pem") + writeCredentialBundle(t, bundlePath, serverCert) + if err := os.WriteFile(trustPath, ca.certPEM, 0o600); err != nil { + t.Fatal(err) + } + upstream, err := url.Parse("http://actor.internal:80") + if err != nil { + t.Fatal(err) + } + s, err := NewServer(Config{ + CredentialBundlePath: bundlePath, + TrustBundlePath: trustPath, + AllowedClientID: "spiffe://cluster.local/ns/ate-system/sa/atenet-router", + Upstream: upstream, + }) + if err != nil { + t.Fatal(err) + } + + untrustedCA := newTestCA(t) + tests := []struct { + name string + cert tls.Certificate + wantErr bool + }{ + { + name: "allowed identity", + cert: ca.issue(t, "spiffe://cluster.local/ns/ate-system/sa/atenet-router", []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), + }, + { + name: "wrong identity", + cert: ca.issue(t, "spiffe://cluster.local/ns/ate-system/sa/not-the-gateway", []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), + wantErr: true, + }, + { + name: "untrusted identity", + cert: untrustedCA.issue(t, "spiffe://cluster.local/ns/ate-system/sa/atenet-router", []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + serverErr, clientErr := tlsHandshake(s.tlsConfig, &tls.Config{ + MinVersion: tls.VersionTLS12, + InsecureSkipVerify: true, // Test only the server's client authentication here. + Certificates: []tls.Certificate{tt.cert}, + }) + gotErr := serverErr != nil || clientErr != nil + if gotErr != tt.wantErr { + t.Fatalf("server error = %v, client error = %v, want error %v", serverErr, clientErr, tt.wantErr) + } + }) + } +} + +func TestDeactivateCancelsInflightRequest(t *testing.T) { + upstream, err := url.Parse("http://actor.internal:80") + if err != nil { + t.Fatal(err) + } + s := newTestServer(t, upstream) + if err := s.Activate("team-a", "actor-1"); err != nil { + t.Fatal(err) + } + started := make(chan struct{}) + s.proxy.Transport = roundTripFunc(func(r *http.Request) (*http.Response, error) { + close(started) + <-r.Context().Done() + return nil, r.Context().Err() + }) + + done := make(chan struct{}) + go func() { + defer close(done) + req := httptest.NewRequest(http.MethodGet, "https://worker/", nil) + req.Host = "actor-1.team-a.actors.resources.substrate.ate.dev" + s.ServeHTTP(httptest.NewRecorder(), req) + }() + <-started + if err := s.Deactivate(context.Background()); err != nil { + t.Fatal(err) + } + <-done +} + +func newTestServer(t *testing.T, upstream *url.URL) *Server { + t.Helper() + dir := t.TempDir() + bundle, trust := makeCertFiles(t, dir) + s, err := NewServer(Config{ + CredentialBundlePath: bundle, + TrustBundlePath: trust, + AllowedClientID: "spiffe://cluster.local/ns/ate-system/sa/atenet-router", + Upstream: upstream, + }) + if err != nil { + t.Fatal(err) + } + return s +} + +func makeCertFiles(t *testing.T, dir string) (bundlePath, trustPath string) { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + now := time.Now() + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "test"}, + NotBefore: now.Add(-time.Minute), + NotAfter: now.Add(time.Hour), + IsCA: true, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + t.Fatal(err) + } + keyDER, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + t.Fatal(err) + } + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER}) + bundlePath = filepath.Join(dir, "bundle.pem") + trustPath = filepath.Join(dir, "trust.pem") + if err := os.WriteFile(bundlePath, append(certPEM, keyPEM...), 0o600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(trustPath, certPEM, 0o600); err != nil { + t.Fatal(err) + } + return bundlePath, trustPath +} + +type testCA struct { + cert *x509.Certificate + key *ecdsa.PrivateKey + certPEM []byte +} + +func newTestCA(t *testing.T) *testCA { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + now := time.Now() + template := &x509.Certificate{ + SerialNumber: big.NewInt(now.UnixNano()), + Subject: pkix.Name{CommonName: "test CA"}, + NotBefore: now.Add(-time.Minute), + NotAfter: now.Add(time.Hour), + IsCA: true, + KeyUsage: x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + t.Fatal(err) + } + cert, err := x509.ParseCertificate(der) + if err != nil { + t.Fatal(err) + } + return &testCA{ + cert: cert, + key: key, + certPEM: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), + } +} + +func (ca *testCA) issue(t *testing.T, spiffeID string, usages []x509.ExtKeyUsage) tls.Certificate { + t.Helper() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + now := time.Now() + template := &x509.Certificate{ + SerialNumber: big.NewInt(now.UnixNano()), + NotBefore: now.Add(-time.Minute), + NotAfter: now.Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: usages, + } + if spiffeID != "" { + uri, err := url.Parse(spiffeID) + if err != nil { + t.Fatal(err) + } + template.URIs = []*url.URL{uri} + } + der, err := x509.CreateCertificate(rand.Reader, template, ca.cert, &key.PublicKey, ca.key) + if err != nil { + t.Fatal(err) + } + keyDER, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + t.Fatal(err) + } + cert, err := tls.X509KeyPair( + append(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), ca.certPEM...), + pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER}), + ) + if err != nil { + t.Fatal(err) + } + return cert +} + +func writeCredentialBundle(t *testing.T, path string, cert tls.Certificate) { + t.Helper() + key, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + t.Fatalf("private key has type %T", cert.PrivateKey) + } + keyDER, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + t.Fatal(err) + } + var bundle []byte + for _, der := range cert.Certificate { + bundle = append(bundle, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})...) + } + bundle = append(bundle, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})...) + if err := os.WriteFile(path, bundle, 0o600); err != nil { + t.Fatal(err) + } +} + +func tlsHandshake(serverConfig, clientConfig *tls.Config) (serverErr, clientErr error) { + serverConn, clientConn := net.Pipe() + serverTLS := tls.Server(serverConn, serverConfig) + clientTLS := tls.Client(clientConn, clientConfig) + done := make(chan error, 1) + go func() { + err := serverTLS.Handshake() + _ = serverConn.Close() + done <- err + }() + clientErr = clientTLS.Handshake() + _ = clientConn.Close() + serverErr = <-done + return serverErr, clientErr +} diff --git a/internal/e2e/suites/networking/networking_test.go b/internal/e2e/suites/networking/networking_test.go new file mode 100644 index 000000000..5f3c16866 --- /dev/null +++ b/internal/e2e/suites/networking/networking_test.go @@ -0,0 +1,121 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package networking + +import ( + "context" + "fmt" + "io" + "net/http" + "testing" + "time" + + "github.com/agent-substrate/substrate/internal/e2e" + "github.com/agent-substrate/substrate/internal/resources" + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" +) + +const networkingAtespace = "networking-e2e" + +func TestActorDirectAccess(t *testing.T) { + ctx := context.Background() + actorName, actor := createAndResumeActor(t, ctx, "direct") + router := mustRouterClient(t, ctx) + defer router.Close() + + t.Run("direct", func(t *testing.T) { + assertDirectActorAccess(t, ctx, e2e.GetClients(), actor) + }) + t.Run("via ingress", func(t *testing.T) { + actorRef := resources.ActorRef{Atespace: networkingAtespace, Name: actorName} + response, err := router.Get(ctx, actorRef, "/readyz") + if err != nil { + t.Fatalf("GET Actor through ingress: %v", err) + } + defer response.Body.Close() + body, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("reading ingress response body (HTTP %d): %v", response.StatusCode, err) + } + if response.StatusCode != http.StatusOK { + t.Fatalf("Actor access through ingress returned HTTP %d, want 200; body: %s", response.StatusCode, body) + } + t.Logf("Actor access through ingress succeeded; body: %s", body) + }) +} + +func createAndResumeActor(t *testing.T, ctx context.Context, prefix string) (string, *ateapipb.Actor) { + t.Helper() + clients := e2e.GetClients() + actorName := fmt.Sprintf("%s-%d", prefix, time.Now().UnixNano()) + actorRef := &ateapipb.ObjectRef{Atespace: networkingAtespace, Name: actorName} + + t.Logf("creating actor %s/%s", networkingAtespace, actorName) + _, _ = clients.SubstrateAPI.CreateAtespace(ctx, &ateapipb.CreateAtespaceRequest{ + Atespace: &ateapipb.Atespace{Metadata: &ateapipb.ResourceMetadata{Name: networkingAtespace}}, + }) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: networkingAtespace, Name: actorName}, + ActorTemplateNamespace: "ate-demo-counter", + ActorTemplateName: "counter", + }}); err != nil { + t.Fatalf("CreateActor: %v (deploy the fixture with --deploy-demo-counter)", err) + } + t.Cleanup(func() { + _, _ = clients.SubstrateAPI.SuspendActor(context.Background(), &ateapipb.SuspendActorRequest{Actor: actorRef}) + _, _ = clients.SubstrateAPI.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{Actor: actorRef}) + }) + + resumeResponse, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: actorRef}) + if err != nil { + t.Fatalf("ResumeActor: %v", err) + } + t.Logf("resumed actor %s/%s", networkingAtespace, actorName) + return actorName, resumeResponse.GetActor() +} + +func mustRouterClient(t *testing.T, ctx context.Context) *e2e.RouterClient { + t.Helper() + router, err := e2e.NewRouterClient(ctx) + if err != nil { + t.Fatalf("NewRouterClient: %v", err) + } + return router +} + +func assertDirectActorAccess(t *testing.T, ctx context.Context, clients *e2e.Clients, actor *ateapipb.Actor) { + t.Helper() + if actor.GetAteomPodNamespace() == "" || actor.GetAteomPodName() == "" { + t.Fatalf("resumed Actor has no worker pod assignment: %+v", actor) + } + + // The Kubernetes pod proxy performs this request from inside the cluster to + // the assigned worker's port 80. It bypasses atenet-router and therefore + // verifies that the old direct path remains unavailable without relying on + // the test runner having a route to the pod CIDR. + result := clients.K8s.CoreV1().RESTClient().Get(). + Namespace(actor.GetAteomPodNamespace()). + Resource("pods"). + Name(actor.GetAteomPodName() + ":80"). + SubResource("proxy"). + Suffix("readyz"). + Do(ctx) + body, err := result.Raw() + + if err == nil { + t.Fatalf("direct Actor access through %s/%s:80 unexpectedly succeeded; body: %s", actor.GetAteomPodNamespace(), actor.GetAteomPodName(), body) + } + t.Logf("direct Actor access through %s/%s:80 was blocked as expected: %v", actor.GetAteomPodNamespace(), actor.GetAteomPodName(), err) +} diff --git a/internal/e2e/suites/networking/testmain_test.go b/internal/e2e/suites/networking/testmain_test.go new file mode 100644 index 000000000..2d31b9c9f --- /dev/null +++ b/internal/e2e/suites/networking/testmain_test.go @@ -0,0 +1,24 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package networking + +import ( + "os" + "testing" + + "github.com/agent-substrate/substrate/internal/e2e" +) + +func TestMain(m *testing.M) { os.Exit(e2e.RunTestMain(m)) } diff --git a/internal/proto/ateompb/ateom.pb.go b/internal/proto/ateompb/ateom.pb.go index 12c4fa1a7..b91d4beef 100644 --- a/internal/proto/ateompb/ateom.pb.go +++ b/internal/proto/ateompb/ateom.pb.go @@ -91,21 +91,26 @@ func (SnapshotScope) EnumDescriptor() ([]byte, []int) { } type RunWorkloadRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` - ActorName string `protobuf:"bytes,2,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` - ActorUid string `protobuf:"bytes,3,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` - ActorTemplateNamespace string `protobuf:"bytes,4,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` - ActorTemplateName string `protobuf:"bytes,5,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` - RunscPath string `protobuf:"bytes,6,opt,name=runsc_path,json=runscPath,proto3" json:"runsc_path,omitempty"` - Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` + ActorName string `protobuf:"bytes,2,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` + ActorUid string `protobuf:"bytes,3,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` + // Actor resource version observed by ate-api when assigning this worker. + ActorVersion int64 `protobuf:"varint,9,opt,name=actor_version,json=actorVersion,proto3" json:"actor_version,omitempty"` + ActorTemplateNamespace string `protobuf:"bytes,4,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` + ActorTemplateName string `protobuf:"bytes,5,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` + RunscPath string `protobuf:"bytes,6,opt,name=runsc_path,json=runscPath,proto3" json:"runsc_path,omitempty"` + Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` // runtime_asset_paths maps a runtime asset name (e.g. "cloud-hypervisor", // "virtiofsd", "kata-kernel", "kata-image", "kata-config") // to the local on-disk path atelet fetched it to (content-addressed, like // runsc_path). Empty for the gVisor runtime, which uses runsc_path. RuntimeAssetPaths map[string]string `protobuf:"bytes,8,rep,name=runtime_asset_paths,json=runtimeAssetPaths,proto3" json:"runtime_asset_paths,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Remote egress gateway selected for this activation. When absent, actor + // traffic uses direct egress instead of being redirected through atunnel. + EgressGatewayAddress *string `protobuf:"bytes,10,opt,name=egress_gateway_address,json=egressGatewayAddress,proto3,oneof" json:"egress_gateway_address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RunWorkloadRequest) Reset() { @@ -159,6 +164,13 @@ func (x *RunWorkloadRequest) GetActorUid() string { return "" } +func (x *RunWorkloadRequest) GetActorVersion() int64 { + if x != nil { + return x.ActorVersion + } + return 0 +} + func (x *RunWorkloadRequest) GetActorTemplateNamespace() string { if x != nil { return x.ActorTemplateNamespace @@ -194,6 +206,13 @@ func (x *RunWorkloadRequest) GetRuntimeAssetPaths() map[string]string { return nil } +func (x *RunWorkloadRequest) GetEgressGatewayAddress() string { + if x != nil && x.EgressGatewayAddress != nil { + return *x.EgressGatewayAddress + } + return "" +} + // WorkloadSpec parallels Pod, but with far fewer configurable fields. type WorkloadSpec struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -611,23 +630,28 @@ func (x *CheckpointWorkloadResponse) GetSnapshotFiles() []string { } type RestoreWorkloadRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` - ActorName string `protobuf:"bytes,2,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` - ActorUid string `protobuf:"bytes,3,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` - ActorTemplateNamespace string `protobuf:"bytes,4,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` - ActorTemplateName string `protobuf:"bytes,5,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` - RunscPath string `protobuf:"bytes,6,opt,name=runsc_path,json=runscPath,proto3" json:"runsc_path,omitempty"` - Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` + ActorName string `protobuf:"bytes,2,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` + ActorUid string `protobuf:"bytes,3,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` + // Actor resource version observed by ate-api when assigning this worker. + ActorVersion int64 `protobuf:"varint,11,opt,name=actor_version,json=actorVersion,proto3" json:"actor_version,omitempty"` + ActorTemplateNamespace string `protobuf:"bytes,4,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` + ActorTemplateName string `protobuf:"bytes,5,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` + RunscPath string `protobuf:"bytes,6,opt,name=runsc_path,json=runscPath,proto3" json:"runsc_path,omitempty"` + Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` // The object storage URI prefix of the snapshot to restore. SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` // runtime_asset_paths maps a runtime asset name to the local on-disk path // atelet fetched it to (see RunWorkloadRequest). Empty for gVisor. RuntimeAssetPaths map[string]string `protobuf:"bytes,9,rep,name=runtime_asset_paths,json=runtimeAssetPaths,proto3" json:"runtime_asset_paths,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // What content to restore from the snapshot. - Scope SnapshotScope `protobuf:"varint,10,opt,name=scope,proto3,enum=ateom.SnapshotScope" json:"scope,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + Scope SnapshotScope `protobuf:"varint,10,opt,name=scope,proto3,enum=ateom.SnapshotScope" json:"scope,omitempty"` + // Remote egress gateway selected for this activation. When absent, actor + // traffic uses direct egress instead of being redirected through atunnel. + EgressGatewayAddress *string `protobuf:"bytes,12,opt,name=egress_gateway_address,json=egressGatewayAddress,proto3,oneof" json:"egress_gateway_address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RestoreWorkloadRequest) Reset() { @@ -681,6 +705,13 @@ func (x *RestoreWorkloadRequest) GetActorUid() string { return "" } +func (x *RestoreWorkloadRequest) GetActorVersion() int64 { + if x != nil { + return x.ActorVersion + } + return 0 +} + func (x *RestoreWorkloadRequest) GetActorTemplateNamespace() string { if x != nil { return x.ActorTemplateNamespace @@ -730,6 +761,13 @@ func (x *RestoreWorkloadRequest) GetScope() SnapshotScope { return SnapshotScope_SNAPSHOT_SCOPE_UNSPECIFIED } +func (x *RestoreWorkloadRequest) GetEgressGatewayAddress() string { + if x != nil && x.EgressGatewayAddress != nil { + return *x.EgressGatewayAddress + } + return "" +} + type RestoreWorkloadResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -770,21 +808,25 @@ var File_ateom_proto protoreflect.FileDescriptor const file_ateom_proto_rawDesc = "" + "\n" + - "\vateom.proto\x12\x05ateom\"\xc6\x03\n" + + "\vateom.proto\x12\x05ateom\"\xc1\x04\n" + "\x12RunWorkloadRequest\x12\x1a\n" + "\batespace\x18\x01 \x01(\tR\batespace\x12\x1d\n" + "\n" + "actor_name\x18\x02 \x01(\tR\tactorName\x12\x1b\n" + - "\tactor_uid\x18\x03 \x01(\tR\bactorUid\x128\n" + + "\tactor_uid\x18\x03 \x01(\tR\bactorUid\x12#\n" + + "\ractor_version\x18\t \x01(\x03R\factorVersion\x128\n" + "\x18actor_template_namespace\x18\x04 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x05 \x01(\tR\x11actorTemplateName\x12\x1d\n" + "\n" + "runsc_path\x18\x06 \x01(\tR\trunscPath\x12'\n" + "\x04spec\x18\a \x01(\v2\x13.ateom.WorkloadSpecR\x04spec\x12`\n" + - "\x13runtime_asset_paths\x18\b \x03(\v20.ateom.RunWorkloadRequest.RuntimeAssetPathsEntryR\x11runtimeAssetPaths\x1aD\n" + + "\x13runtime_asset_paths\x18\b \x03(\v20.ateom.RunWorkloadRequest.RuntimeAssetPathsEntryR\x11runtimeAssetPaths\x129\n" + + "\x16egress_gateway_address\x18\n" + + " \x01(\tH\x00R\x14egressGatewayAddress\x88\x01\x01\x1aD\n" + "\x16RuntimeAssetPathsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"@\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x19\n" + + "\x17_egress_gateway_address\"@\n" + "\fWorkloadSpec\x120\n" + "\n" + "containers\x18\x01 \x03(\v2\x10.ateom.ContainerR\n" + @@ -817,12 +859,13 @@ const file_ateom_proto_rawDesc = "" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"C\n" + "\x1aCheckpointWorkloadResponse\x12%\n" + - "\x0esnapshot_files\x18\x01 \x03(\tR\rsnapshotFiles\"\xaa\x04\n" + + "\x0esnapshot_files\x18\x01 \x03(\tR\rsnapshotFiles\"\xa5\x05\n" + "\x16RestoreWorkloadRequest\x12\x1a\n" + "\batespace\x18\x01 \x01(\tR\batespace\x12\x1d\n" + "\n" + "actor_name\x18\x02 \x01(\tR\tactorName\x12\x1b\n" + - "\tactor_uid\x18\x03 \x01(\tR\bactorUid\x128\n" + + "\tactor_uid\x18\x03 \x01(\tR\bactorUid\x12#\n" + + "\ractor_version\x18\v \x01(\x03R\factorVersion\x128\n" + "\x18actor_template_namespace\x18\x04 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x05 \x01(\tR\x11actorTemplateName\x12\x1d\n" + "\n" + @@ -831,10 +874,12 @@ const file_ateom_proto_rawDesc = "" + "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\x12d\n" + "\x13runtime_asset_paths\x18\t \x03(\v24.ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntryR\x11runtimeAssetPaths\x12*\n" + "\x05scope\x18\n" + - " \x01(\x0e2\x14.ateom.SnapshotScopeR\x05scope\x1aD\n" + + " \x01(\x0e2\x14.ateom.SnapshotScopeR\x05scope\x129\n" + + "\x16egress_gateway_address\x18\f \x01(\tH\x00R\x14egressGatewayAddress\x88\x01\x01\x1aD\n" + "\x16RuntimeAssetPathsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x19\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x19\n" + + "\x17_egress_gateway_address\"\x19\n" + "\x17RestoreWorkloadResponse*a\n" + "\rSnapshotScope\x12\x1e\n" + "\x1aSNAPSHOT_SCOPE_UNSPECIFIED\x10\x00\x12\x17\n" + @@ -905,6 +950,8 @@ func file_ateom_proto_init() { if File_ateom_proto != nil { return } + file_ateom_proto_msgTypes[0].OneofWrappers = []any{} + file_ateom_proto_msgTypes[8].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/internal/proto/ateompb/ateom.proto b/internal/proto/ateompb/ateom.proto index a282d81cb..89ca97c27 100644 --- a/internal/proto/ateompb/ateom.proto +++ b/internal/proto/ateompb/ateom.proto @@ -51,6 +51,8 @@ message RunWorkloadRequest { string atespace = 1; string actor_name = 2; string actor_uid = 3; + // Actor resource version observed by ate-api when assigning this worker. + int64 actor_version = 9; string actor_template_namespace = 4; string actor_template_name = 5; @@ -64,6 +66,10 @@ message RunWorkloadRequest { // to the local on-disk path atelet fetched it to (content-addressed, like // runsc_path). Empty for the gVisor runtime, which uses runsc_path. map runtime_asset_paths = 8; + + // Remote egress gateway selected for this activation. When absent, actor + // traffic uses direct egress instead of being redirected through atunnel. + optional string egress_gateway_address = 10; } // WorkloadSpec parallels Pod, but with far fewer configurable fields. @@ -147,6 +153,8 @@ message RestoreWorkloadRequest { string atespace = 1; string actor_name = 2; string actor_uid = 3; + // Actor resource version observed by ate-api when assigning this worker. + int64 actor_version = 11; string actor_template_namespace = 4; string actor_template_name = 5; @@ -164,6 +172,10 @@ message RestoreWorkloadRequest { // What content to restore from the snapshot. SnapshotScope scope = 10; + + // Remote egress gateway selected for this activation. When absent, actor + // traffic uses direct egress instead of being redirected through atunnel. + optional string egress_gateway_address = 12; } message RestoreWorkloadResponse { diff --git a/manifests/ate-install/atenet-router.yaml b/manifests/ate-install/atenet-router.yaml index 8fe146561..d49c7f96d 100644 --- a/manifests/ate-install/atenet-router.yaml +++ b/manifests/ate-install/atenet-router.yaml @@ -193,6 +193,15 @@ spec: mountPath: /etc/envoy - name: "servicedns" mountPath: "/run/servicedns.podcert.ate.dev" + # podidentity credential + trust bundles for actor-facing (upstream) + # mTLS. Envoy's ORIGINAL_DST actor cluster presents this credential + # bundle as its client cert when dialing the actor's atunnel ingress + # server on :443, and validates that server against the trust bundle. + # The issued SPIFFE identity is + # spiffe://cluster.local/ns/ate-system/sa/atenet-router, which atunnel's + # --atunnel-client-identity allows. + - name: "podidentity" + mountPath: "/run/podidentity.podcert.ate.dev" volumes: - name: envoy-config configMap: @@ -211,6 +220,12 @@ spec: signerName: podidentity.podcert.ate.dev/identity keyType: ECDSAP256 credentialBundlePath: credential-bundle.pem + - clusterTrustBundle: + signerName: podidentity.podcert.ate.dev/identity + labelSelector: + matchLabels: + podcert.ate.dev/canarying: live + path: trust-bundle.pem - name: "servicedns-ca" projected: sources: diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config.pb.go deleted file mode 100644 index fdc417226..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config.pb.go +++ /dev/null @@ -1,131 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.10 -// protoc v6.33.2 -// source: envoy/config/common/key_value/v3/config.proto - -package key_valuev3 - -import ( - _ "github.com/cncf/xds/go/udpa/annotations" - _ "github.com/cncf/xds/go/xds/annotations/v3" - v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - _ "github.com/envoyproxy/protoc-gen-validate/validate" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// This shared configuration for Envoy key value stores. -type KeyValueStoreConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // [#extension-category: envoy.common.key_value] - Config *v3.TypedExtensionConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *KeyValueStoreConfig) Reset() { - *x = KeyValueStoreConfig{} - mi := &file_envoy_config_common_key_value_v3_config_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *KeyValueStoreConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*KeyValueStoreConfig) ProtoMessage() {} - -func (x *KeyValueStoreConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_config_common_key_value_v3_config_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use KeyValueStoreConfig.ProtoReflect.Descriptor instead. -func (*KeyValueStoreConfig) Descriptor() ([]byte, []int) { - return file_envoy_config_common_key_value_v3_config_proto_rawDescGZIP(), []int{0} -} - -func (x *KeyValueStoreConfig) GetConfig() *v3.TypedExtensionConfig { - if x != nil { - return x.Config - } - return nil -} - -var File_envoy_config_common_key_value_v3_config_proto protoreflect.FileDescriptor - -const file_envoy_config_common_key_value_v3_config_proto_rawDesc = "" + - "\n" + - "-envoy/config/common/key_value/v3/config.proto\x12 envoy.config.common.key_value.v3\x1a$envoy/config/core/v3/extension.proto\x1a\x1fxds/annotations/v3/status.proto\x1a\x1dudpa/annotations/status.proto\x1a\x17validate/validate.proto\"m\n" + - "\x13KeyValueStoreConfig\x12L\n" + - "\x06config\x18\x01 \x01(\v2*.envoy.config.core.v3.TypedExtensionConfigB\b\xfaB\x05\x8a\x01\x02\x10\x01R\x06config:\b\xd2Ƥ\xe1\x06\x02\b\x01B\x9c\x01\xba\x80\xc8\xd1\x06\x02\x10\x02\n" + - ".io.envoyproxy.envoy.config.common.key_value.v3B\vConfigProtoP\x01ZSgithub.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3;key_valuev3b\x06proto3" - -var ( - file_envoy_config_common_key_value_v3_config_proto_rawDescOnce sync.Once - file_envoy_config_common_key_value_v3_config_proto_rawDescData []byte -) - -func file_envoy_config_common_key_value_v3_config_proto_rawDescGZIP() []byte { - file_envoy_config_common_key_value_v3_config_proto_rawDescOnce.Do(func() { - file_envoy_config_common_key_value_v3_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_envoy_config_common_key_value_v3_config_proto_rawDesc), len(file_envoy_config_common_key_value_v3_config_proto_rawDesc))) - }) - return file_envoy_config_common_key_value_v3_config_proto_rawDescData -} - -var file_envoy_config_common_key_value_v3_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_envoy_config_common_key_value_v3_config_proto_goTypes = []any{ - (*KeyValueStoreConfig)(nil), // 0: envoy.config.common.key_value.v3.KeyValueStoreConfig - (*v3.TypedExtensionConfig)(nil), // 1: envoy.config.core.v3.TypedExtensionConfig -} -var file_envoy_config_common_key_value_v3_config_proto_depIdxs = []int32{ - 1, // 0: envoy.config.common.key_value.v3.KeyValueStoreConfig.config:type_name -> envoy.config.core.v3.TypedExtensionConfig - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_envoy_config_common_key_value_v3_config_proto_init() } -func file_envoy_config_common_key_value_v3_config_proto_init() { - if File_envoy_config_common_key_value_v3_config_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_envoy_config_common_key_value_v3_config_proto_rawDesc), len(file_envoy_config_common_key_value_v3_config_proto_rawDesc)), - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_envoy_config_common_key_value_v3_config_proto_goTypes, - DependencyIndexes: file_envoy_config_common_key_value_v3_config_proto_depIdxs, - MessageInfos: file_envoy_config_common_key_value_v3_config_proto_msgTypes, - }.Build() - File_envoy_config_common_key_value_v3_config_proto = out.File - file_envoy_config_common_key_value_v3_config_proto_goTypes = nil - file_envoy_config_common_key_value_v3_config_proto_depIdxs = nil -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config.pb.validate.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config.pb.validate.go deleted file mode 100644 index 7243a25c4..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config.pb.validate.go +++ /dev/null @@ -1,179 +0,0 @@ -//go:build !disable_pgv -// Code generated by protoc-gen-validate. DO NOT EDIT. -// source: envoy/config/common/key_value/v3/config.proto - -package key_valuev3 - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/mail" - "net/url" - "regexp" - "sort" - "strings" - "time" - "unicode/utf8" - - "google.golang.org/protobuf/types/known/anypb" -) - -// ensure the imports are used -var ( - _ = bytes.MinRead - _ = errors.New("") - _ = fmt.Print - _ = utf8.UTFMax - _ = (*regexp.Regexp)(nil) - _ = (*strings.Reader)(nil) - _ = net.IPv4len - _ = time.Duration(0) - _ = (*url.URL)(nil) - _ = (*mail.Address)(nil) - _ = anypb.Any{} - _ = sort.Sort -) - -// Validate checks the field values on KeyValueStoreConfig with the rules -// defined in the proto definition for this message. If any rules are -// violated, the first error encountered is returned, or nil if there are no violations. -func (m *KeyValueStoreConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on KeyValueStoreConfig with the rules -// defined in the proto definition for this message. If any rules are -// violated, the result is a list of violation errors wrapped in -// KeyValueStoreConfigMultiError, or nil if none found. -func (m *KeyValueStoreConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *KeyValueStoreConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - if m.GetConfig() == nil { - err := KeyValueStoreConfigValidationError{ - field: "Config", - reason: "value is required", - } - if !all { - return err - } - errors = append(errors, err) - } - - if all { - switch v := interface{}(m.GetConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, KeyValueStoreConfigValidationError{ - field: "Config", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, KeyValueStoreConfigValidationError{ - field: "Config", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return KeyValueStoreConfigValidationError{ - field: "Config", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if len(errors) > 0 { - return KeyValueStoreConfigMultiError(errors) - } - - return nil -} - -// KeyValueStoreConfigMultiError is an error wrapping multiple validation -// errors returned by KeyValueStoreConfig.ValidateAll() if the designated -// constraints aren't met. -type KeyValueStoreConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m KeyValueStoreConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m KeyValueStoreConfigMultiError) AllErrors() []error { return m } - -// KeyValueStoreConfigValidationError is the validation error returned by -// KeyValueStoreConfig.Validate if the designated constraints aren't met. -type KeyValueStoreConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e KeyValueStoreConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e KeyValueStoreConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e KeyValueStoreConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e KeyValueStoreConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e KeyValueStoreConfigValidationError) ErrorName() string { - return "KeyValueStoreConfigValidationError" -} - -// Error satisfies the builtin error interface -func (e KeyValueStoreConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sKeyValueStoreConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = KeyValueStoreConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = KeyValueStoreConfigValidationError{} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config_vtproto.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config_vtproto.pb.go deleted file mode 100644 index 0d7bf3742..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3/config_vtproto.pb.go +++ /dev/null @@ -1,95 +0,0 @@ -//go:build vtprotobuf -// +build vtprotobuf - -// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// source: envoy/config/common/key_value/v3/config.proto - -package key_valuev3 - -import ( - protohelpers "github.com/planetscale/vtprotobuf/protohelpers" - proto "google.golang.org/protobuf/proto" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -func (m *KeyValueStoreConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *KeyValueStoreConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *KeyValueStoreConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.Config != nil { - if vtmsg, ok := interface{}(m.Config).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.Config) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *KeyValueStoreConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Config != nil { - if size, ok := interface{}(m.Config).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.Config) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - n += len(m.unknownFields) - return n -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.pb.go deleted file mode 100644 index f21ba01c2..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.pb.go +++ /dev/null @@ -1,327 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.10 -// protoc v6.33.2 -// source: envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto - -package dynamic_forward_proxyv3 - -import ( - _ "github.com/cncf/xds/go/udpa/annotations" - v31 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - v32 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3" - _ "github.com/envoyproxy/protoc-gen-validate/validate" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Configuration for the dynamic forward proxy cluster. See the :ref:`architecture overview -// ` for more information. -// [#extension: envoy.clusters.dynamic_forward_proxy] -type ClusterConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to ClusterImplementationSpecifier: - // - // *ClusterConfig_DnsCacheConfig - // *ClusterConfig_SubClustersConfig - ClusterImplementationSpecifier isClusterConfig_ClusterImplementationSpecifier `protobuf_oneof:"cluster_implementation_specifier"` - // If true allow the cluster configuration to disable the auto_sni and auto_san_validation options - // in the :ref:`cluster's upstream_http_protocol_options - // ` - AllowInsecureClusterOptions bool `protobuf:"varint,2,opt,name=allow_insecure_cluster_options,json=allowInsecureClusterOptions,proto3" json:"allow_insecure_cluster_options,omitempty"` - // If true allow HTTP/2 and HTTP/3 connections to be reused for requests to different - // origins than the connection was initially created for. This will only happen when the - // resolved address for the new connection matches the peer address of the connection and - // the TLS certificate is also valid for the new hostname. For example, if a connection - // has previously been established to foo.example.com at IP 1.2.3.4 with a certificate - // that is valid for “*.example.com“, then this connection could be used for requests to - // bar.example.com if that also resolved to 1.2.3.4. - // - // .. note:: - // - // By design, this feature will maximize reuse of connections. This means that instead - // opening a new connection when an existing connection reaches the maximum number of - // concurrent streams, requests will instead be sent to the existing connection. - // - // .. note:: - // - // The coalesced connections might be to upstreams that would not be otherwise - // selected by Envoy. See the section `Connection Reuse in RFC 7540 - // `_ - AllowCoalescedConnections bool `protobuf:"varint,3,opt,name=allow_coalesced_connections,json=allowCoalescedConnections,proto3" json:"allow_coalesced_connections,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ClusterConfig) Reset() { - *x = ClusterConfig{} - mi := &file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ClusterConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClusterConfig) ProtoMessage() {} - -func (x *ClusterConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClusterConfig.ProtoReflect.Descriptor instead. -func (*ClusterConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescGZIP(), []int{0} -} - -func (x *ClusterConfig) GetClusterImplementationSpecifier() isClusterConfig_ClusterImplementationSpecifier { - if x != nil { - return x.ClusterImplementationSpecifier - } - return nil -} - -func (x *ClusterConfig) GetDnsCacheConfig() *v3.DnsCacheConfig { - if x != nil { - if x, ok := x.ClusterImplementationSpecifier.(*ClusterConfig_DnsCacheConfig); ok { - return x.DnsCacheConfig - } - } - return nil -} - -func (x *ClusterConfig) GetSubClustersConfig() *SubClustersConfig { - if x != nil { - if x, ok := x.ClusterImplementationSpecifier.(*ClusterConfig_SubClustersConfig); ok { - return x.SubClustersConfig - } - } - return nil -} - -func (x *ClusterConfig) GetAllowInsecureClusterOptions() bool { - if x != nil { - return x.AllowInsecureClusterOptions - } - return false -} - -func (x *ClusterConfig) GetAllowCoalescedConnections() bool { - if x != nil { - return x.AllowCoalescedConnections - } - return false -} - -type isClusterConfig_ClusterImplementationSpecifier interface { - isClusterConfig_ClusterImplementationSpecifier() -} - -type ClusterConfig_DnsCacheConfig struct { - // The DNS cache configuration that the cluster will attach to. Note this configuration must - // match that of associated :ref:`dynamic forward proxy HTTP filter configuration - // `. - DnsCacheConfig *v3.DnsCacheConfig `protobuf:"bytes,1,opt,name=dns_cache_config,json=dnsCacheConfig,proto3,oneof"` -} - -type ClusterConfig_SubClustersConfig struct { - // Configuration for sub clusters, when this configuration is enabled, - // Envoy will create an independent sub cluster dynamically for each host:port. - // Most of the configuration of a sub cluster is inherited from the current cluster, - // i.e. health_checks, dns_resolvers and etc. - // And the load_assignment will be set to the only one endpoint, host:port. - // - // Compared to the dns_cache_config, it has the following advantages: - // - // 1. sub clusters will be created with the STRICT_DNS DiscoveryType, - // so that Envoy will use all of the IPs resolved from the host. - // - // 2. each sub cluster is full featured cluster, with lb_policy and health check and etc enabled. - SubClustersConfig *SubClustersConfig `protobuf:"bytes,4,opt,name=sub_clusters_config,json=subClustersConfig,proto3,oneof"` -} - -func (*ClusterConfig_DnsCacheConfig) isClusterConfig_ClusterImplementationSpecifier() {} - -func (*ClusterConfig_SubClustersConfig) isClusterConfig_ClusterImplementationSpecifier() {} - -// Configuration for sub clusters. Hard code STRICT_DNS cluster type now. -type SubClustersConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The :ref:`load balancer type ` to use - // when picking a host in a sub cluster. Note that CLUSTER_PROVIDED is not allowed here. - LbPolicy v31.Cluster_LbPolicy `protobuf:"varint,1,opt,name=lb_policy,json=lbPolicy,proto3,enum=envoy.config.cluster.v3.Cluster_LbPolicy" json:"lb_policy,omitempty"` - // The maximum number of sub clusters that the DFP cluster will hold. If not specified defaults to 1024. - MaxSubClusters *wrapperspb.UInt32Value `protobuf:"bytes,2,opt,name=max_sub_clusters,json=maxSubClusters,proto3" json:"max_sub_clusters,omitempty"` - // The TTL for sub clusters that are unused. Sub clusters that have not been used in the configured time - // interval will be purged. If not specified defaults to 5m. - SubClusterTtl *durationpb.Duration `protobuf:"bytes,3,opt,name=sub_cluster_ttl,json=subClusterTtl,proto3" json:"sub_cluster_ttl,omitempty"` - // Sub clusters that should be created & warmed upon creation. This might provide a - // performance improvement, in the form of cache hits, for sub clusters that are going to be - // warmed during steady state and are known at config load time. - PreresolveClusters []*v32.SocketAddress `protobuf:"bytes,4,rep,name=preresolve_clusters,json=preresolveClusters,proto3" json:"preresolve_clusters,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubClustersConfig) Reset() { - *x = SubClustersConfig{} - mi := &file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubClustersConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubClustersConfig) ProtoMessage() {} - -func (x *SubClustersConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubClustersConfig.ProtoReflect.Descriptor instead. -func (*SubClustersConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescGZIP(), []int{1} -} - -func (x *SubClustersConfig) GetLbPolicy() v31.Cluster_LbPolicy { - if x != nil { - return x.LbPolicy - } - return v31.Cluster_LbPolicy(0) -} - -func (x *SubClustersConfig) GetMaxSubClusters() *wrapperspb.UInt32Value { - if x != nil { - return x.MaxSubClusters - } - return nil -} - -func (x *SubClustersConfig) GetSubClusterTtl() *durationpb.Duration { - if x != nil { - return x.SubClusterTtl - } - return nil -} - -func (x *SubClustersConfig) GetPreresolveClusters() []*v32.SocketAddress { - if x != nil { - return x.PreresolveClusters - } - return nil -} - -var File_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto protoreflect.FileDescriptor - -const file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDesc = "" + - "\n" + - "@envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto\x122envoy.extensions.clusters.dynamic_forward_proxy.v3\x1a%envoy/config/cluster/v3/cluster.proto\x1a\"envoy/config/core/v3/address.proto\x1a@envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x1dudpa/annotations/status.proto\x1a!udpa/annotations/versioning.proto\x1a\x17validate/validate.proto\"\xe8\x03\n" + - "\rClusterConfig\x12l\n" + - "\x10dns_cache_config\x18\x01 \x01(\v2@.envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfigH\x00R\x0ednsCacheConfig\x12w\n" + - "\x13sub_clusters_config\x18\x04 \x01(\v2E.envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfigH\x00R\x11subClustersConfig\x12C\n" + - "\x1eallow_insecure_cluster_options\x18\x02 \x01(\bR\x1ballowInsecureClusterOptions\x12>\n" + - "\x1ballow_coalesced_connections\x18\x03 \x01(\bR\x19allowCoalescedConnections:G\x9aň\x1eB\n" + - "@envoy.config.cluster.dynamic_forward_proxy.v2alpha.ClusterConfigB\"\n" + - " cluster_implementation_specifier\"\xd9\x02\n" + - "\x11SubClustersConfig\x12P\n" + - "\tlb_policy\x18\x01 \x01(\x0e2).envoy.config.cluster.v3.Cluster.LbPolicyB\b\xfaB\x05\x82\x01\x02\x10\x01R\blbPolicy\x12O\n" + - "\x10max_sub_clusters\x18\x02 \x01(\v2\x1c.google.protobuf.UInt32ValueB\a\xfaB\x04*\x02 \x00R\x0emaxSubClusters\x12K\n" + - "\x0fsub_cluster_ttl\x18\x03 \x01(\v2\x19.google.protobuf.DurationB\b\xfaB\x05\xaa\x01\x02*\x00R\rsubClusterTtl\x12T\n" + - "\x13preresolve_clusters\x18\x04 \x03(\v2#.envoy.config.core.v3.SocketAddressR\x12preresolveClustersB\xcd\x01\xba\x80\xc8\xd1\x06\x02\x10\x02\n" + - "@io.envoyproxy.envoy.extensions.clusters.dynamic_forward_proxy.v3B\fClusterProtoP\x01Zqgithub.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3;dynamic_forward_proxyv3b\x06proto3" - -var ( - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescOnce sync.Once - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescData []byte -) - -func file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescGZIP() []byte { - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescOnce.Do(func() { - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDesc), len(file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDesc))) - }) - return file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDescData -} - -var file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_goTypes = []any{ - (*ClusterConfig)(nil), // 0: envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig - (*SubClustersConfig)(nil), // 1: envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfig - (*v3.DnsCacheConfig)(nil), // 2: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig - (v31.Cluster_LbPolicy)(0), // 3: envoy.config.cluster.v3.Cluster.LbPolicy - (*wrapperspb.UInt32Value)(nil), // 4: google.protobuf.UInt32Value - (*durationpb.Duration)(nil), // 5: google.protobuf.Duration - (*v32.SocketAddress)(nil), // 6: envoy.config.core.v3.SocketAddress -} -var file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_depIdxs = []int32{ - 2, // 0: envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig.dns_cache_config:type_name -> envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig - 1, // 1: envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig.sub_clusters_config:type_name -> envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfig - 3, // 2: envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfig.lb_policy:type_name -> envoy.config.cluster.v3.Cluster.LbPolicy - 4, // 3: envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfig.max_sub_clusters:type_name -> google.protobuf.UInt32Value - 5, // 4: envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfig.sub_cluster_ttl:type_name -> google.protobuf.Duration - 6, // 5: envoy.extensions.clusters.dynamic_forward_proxy.v3.SubClustersConfig.preresolve_clusters:type_name -> envoy.config.core.v3.SocketAddress - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_init() } -func file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_init() { - if File_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto != nil { - return - } - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes[0].OneofWrappers = []any{ - (*ClusterConfig_DnsCacheConfig)(nil), - (*ClusterConfig_SubClustersConfig)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDesc), len(file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_rawDesc)), - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_goTypes, - DependencyIndexes: file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_depIdxs, - MessageInfos: file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_msgTypes, - }.Build() - File_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto = out.File - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_goTypes = nil - file_envoy_extensions_clusters_dynamic_forward_proxy_v3_cluster_proto_depIdxs = nil -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.pb.validate.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.pb.validate.go deleted file mode 100644 index 26884ce91..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.pb.validate.go +++ /dev/null @@ -1,424 +0,0 @@ -//go:build !disable_pgv -// Code generated by protoc-gen-validate. DO NOT EDIT. -// source: envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto - -package dynamic_forward_proxyv3 - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/mail" - "net/url" - "regexp" - "sort" - "strings" - "time" - "unicode/utf8" - - "google.golang.org/protobuf/types/known/anypb" - - v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" -) - -// ensure the imports are used -var ( - _ = bytes.MinRead - _ = errors.New("") - _ = fmt.Print - _ = utf8.UTFMax - _ = (*regexp.Regexp)(nil) - _ = (*strings.Reader)(nil) - _ = net.IPv4len - _ = time.Duration(0) - _ = (*url.URL)(nil) - _ = (*mail.Address)(nil) - _ = anypb.Any{} - _ = sort.Sort - - _ = v3.Cluster_LbPolicy(0) -) - -// Validate checks the field values on ClusterConfig with the rules defined in -// the proto definition for this message. If any rules are violated, the first -// error encountered is returned, or nil if there are no violations. -func (m *ClusterConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on ClusterConfig with the rules defined -// in the proto definition for this message. If any rules are violated, the -// result is a list of violation errors wrapped in ClusterConfigMultiError, or -// nil if none found. -func (m *ClusterConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *ClusterConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - // no validation rules for AllowInsecureClusterOptions - - // no validation rules for AllowCoalescedConnections - - switch v := m.ClusterImplementationSpecifier.(type) { - case *ClusterConfig_DnsCacheConfig: - if v == nil { - err := ClusterConfigValidationError{ - field: "ClusterImplementationSpecifier", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - - if all { - switch v := interface{}(m.GetDnsCacheConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, ClusterConfigValidationError{ - field: "DnsCacheConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, ClusterConfigValidationError{ - field: "DnsCacheConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetDnsCacheConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return ClusterConfigValidationError{ - field: "DnsCacheConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - case *ClusterConfig_SubClustersConfig: - if v == nil { - err := ClusterConfigValidationError{ - field: "ClusterImplementationSpecifier", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - - if all { - switch v := interface{}(m.GetSubClustersConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, ClusterConfigValidationError{ - field: "SubClustersConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, ClusterConfigValidationError{ - field: "SubClustersConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetSubClustersConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return ClusterConfigValidationError{ - field: "SubClustersConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - default: - _ = v // ensures v is used - } - - if len(errors) > 0 { - return ClusterConfigMultiError(errors) - } - - return nil -} - -// ClusterConfigMultiError is an error wrapping multiple validation errors -// returned by ClusterConfig.ValidateAll() if the designated constraints -// aren't met. -type ClusterConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m ClusterConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m ClusterConfigMultiError) AllErrors() []error { return m } - -// ClusterConfigValidationError is the validation error returned by -// ClusterConfig.Validate if the designated constraints aren't met. -type ClusterConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e ClusterConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e ClusterConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e ClusterConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e ClusterConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e ClusterConfigValidationError) ErrorName() string { return "ClusterConfigValidationError" } - -// Error satisfies the builtin error interface -func (e ClusterConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sClusterConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = ClusterConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = ClusterConfigValidationError{} - -// Validate checks the field values on SubClustersConfig with the rules defined -// in the proto definition for this message. If any rules are violated, the -// first error encountered is returned, or nil if there are no violations. -func (m *SubClustersConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on SubClustersConfig with the rules -// defined in the proto definition for this message. If any rules are -// violated, the result is a list of violation errors wrapped in -// SubClustersConfigMultiError, or nil if none found. -func (m *SubClustersConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *SubClustersConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - if _, ok := v3.Cluster_LbPolicy_name[int32(m.GetLbPolicy())]; !ok { - err := SubClustersConfigValidationError{ - field: "LbPolicy", - reason: "value must be one of the defined enum values", - } - if !all { - return err - } - errors = append(errors, err) - } - - if wrapper := m.GetMaxSubClusters(); wrapper != nil { - - if wrapper.GetValue() <= 0 { - err := SubClustersConfigValidationError{ - field: "MaxSubClusters", - reason: "value must be greater than 0", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - - if d := m.GetSubClusterTtl(); d != nil { - dur, err := d.AsDuration(), d.CheckValid() - if err != nil { - err = SubClustersConfigValidationError{ - field: "SubClusterTtl", - reason: "value is not a valid duration", - cause: err, - } - if !all { - return err - } - errors = append(errors, err) - } else { - - gt := time.Duration(0*time.Second + 0*time.Nanosecond) - - if dur <= gt { - err := SubClustersConfigValidationError{ - field: "SubClusterTtl", - reason: "value must be greater than 0s", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - } - - for idx, item := range m.GetPreresolveClusters() { - _, _ = idx, item - - if all { - switch v := interface{}(item).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, SubClustersConfigValidationError{ - field: fmt.Sprintf("PreresolveClusters[%v]", idx), - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, SubClustersConfigValidationError{ - field: fmt.Sprintf("PreresolveClusters[%v]", idx), - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return SubClustersConfigValidationError{ - field: fmt.Sprintf("PreresolveClusters[%v]", idx), - reason: "embedded message failed validation", - cause: err, - } - } - } - - } - - if len(errors) > 0 { - return SubClustersConfigMultiError(errors) - } - - return nil -} - -// SubClustersConfigMultiError is an error wrapping multiple validation errors -// returned by SubClustersConfig.ValidateAll() if the designated constraints -// aren't met. -type SubClustersConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m SubClustersConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m SubClustersConfigMultiError) AllErrors() []error { return m } - -// SubClustersConfigValidationError is the validation error returned by -// SubClustersConfig.Validate if the designated constraints aren't met. -type SubClustersConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e SubClustersConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e SubClustersConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e SubClustersConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e SubClustersConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e SubClustersConfigValidationError) ErrorName() string { - return "SubClustersConfigValidationError" -} - -// Error satisfies the builtin error interface -func (e SubClustersConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sSubClustersConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = SubClustersConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = SubClustersConfigValidationError{} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster_vtproto.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster_vtproto.pb.go deleted file mode 100644 index ac609ad10..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster_vtproto.pb.go +++ /dev/null @@ -1,315 +0,0 @@ -//go:build vtprotobuf -// +build vtprotobuf - -// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// source: envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto - -package dynamic_forward_proxyv3 - -import ( - protohelpers "github.com/planetscale/vtprotobuf/protohelpers" - durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" - wrapperspb "github.com/planetscale/vtprotobuf/types/known/wrapperspb" - proto "google.golang.org/protobuf/proto" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -func (m *ClusterConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ClusterConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *ClusterConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if msg, ok := m.ClusterImplementationSpecifier.(*ClusterConfig_SubClustersConfig); ok { - size, err := msg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - } - if m.AllowCoalescedConnections { - i-- - if m.AllowCoalescedConnections { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x18 - } - if m.AllowInsecureClusterOptions { - i-- - if m.AllowInsecureClusterOptions { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x10 - } - if msg, ok := m.ClusterImplementationSpecifier.(*ClusterConfig_DnsCacheConfig); ok { - size, err := msg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - } - return len(dAtA) - i, nil -} - -func (m *ClusterConfig_DnsCacheConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *ClusterConfig_DnsCacheConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - i := len(dAtA) - if m.DnsCacheConfig != nil { - if vtmsg, ok := interface{}(m.DnsCacheConfig).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.DnsCacheConfig) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0xa - } else { - i = protohelpers.EncodeVarint(dAtA, i, 0) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} -func (m *ClusterConfig_SubClustersConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *ClusterConfig_SubClustersConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - i := len(dAtA) - if m.SubClustersConfig != nil { - size, err := m.SubClustersConfig.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x22 - } else { - i = protohelpers.EncodeVarint(dAtA, i, 0) - i-- - dAtA[i] = 0x22 - } - return len(dAtA) - i, nil -} -func (m *SubClustersConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *SubClustersConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *SubClustersConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if len(m.PreresolveClusters) > 0 { - for iNdEx := len(m.PreresolveClusters) - 1; iNdEx >= 0; iNdEx-- { - if vtmsg, ok := interface{}(m.PreresolveClusters[iNdEx]).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.PreresolveClusters[iNdEx]) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0x22 - } - } - if m.SubClusterTtl != nil { - size, err := (*durationpb.Duration)(m.SubClusterTtl).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x1a - } - if m.MaxSubClusters != nil { - size, err := (*wrapperspb.UInt32Value)(m.MaxSubClusters).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x12 - } - if m.LbPolicy != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.LbPolicy)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *ClusterConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if vtmsg, ok := m.ClusterImplementationSpecifier.(interface{ SizeVT() int }); ok { - n += vtmsg.SizeVT() - } - if m.AllowInsecureClusterOptions { - n += 2 - } - if m.AllowCoalescedConnections { - n += 2 - } - n += len(m.unknownFields) - return n -} - -func (m *ClusterConfig_DnsCacheConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.DnsCacheConfig != nil { - if size, ok := interface{}(m.DnsCacheConfig).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.DnsCacheConfig) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } else { - n += 2 - } - return n -} -func (m *ClusterConfig_SubClustersConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.SubClustersConfig != nil { - l = m.SubClustersConfig.SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } else { - n += 2 - } - return n -} -func (m *SubClustersConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.LbPolicy != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.LbPolicy)) - } - if m.MaxSubClusters != nil { - l = (*wrapperspb.UInt32Value)(m.MaxSubClusters).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.SubClusterTtl != nil { - l = (*durationpb.Duration)(m.SubClusterTtl).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if len(m.PreresolveClusters) > 0 { - for _, e := range m.PreresolveClusters { - if size, ok := interface{}(e).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(e) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - } - n += len(m.unknownFields) - return n -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.go deleted file mode 100644 index 884066627..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.go +++ /dev/null @@ -1,417 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.10 -// protoc v6.33.2 -// source: envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto - -package dynamic_forward_proxyv3 - -import ( - _ "github.com/cncf/xds/go/udpa/annotations" - _ "github.com/envoyproxy/go-control-plane/envoy/annotations" - v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - v32 "github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3" - v31 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - _ "github.com/envoyproxy/protoc-gen-validate/validate" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Configuration of circuit breakers for resolver. -type DnsCacheCircuitBreakers struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The maximum number of pending requests that Envoy will allow to the - // resolver. If not specified, the default is 1024. - MaxPendingRequests *wrapperspb.UInt32Value `protobuf:"bytes,1,opt,name=max_pending_requests,json=maxPendingRequests,proto3" json:"max_pending_requests,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DnsCacheCircuitBreakers) Reset() { - *x = DnsCacheCircuitBreakers{} - mi := &file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DnsCacheCircuitBreakers) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DnsCacheCircuitBreakers) ProtoMessage() {} - -func (x *DnsCacheCircuitBreakers) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DnsCacheCircuitBreakers.ProtoReflect.Descriptor instead. -func (*DnsCacheCircuitBreakers) Descriptor() ([]byte, []int) { - return file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescGZIP(), []int{0} -} - -func (x *DnsCacheCircuitBreakers) GetMaxPendingRequests() *wrapperspb.UInt32Value { - if x != nil { - return x.MaxPendingRequests - } - return nil -} - -// Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview -// ` for more information. -// [#next-free-field: 16] -type DnsCacheConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The name of the cache. Multiple named caches allow independent dynamic forward proxy - // configurations to operate within a single Envoy process using different configurations. All - // configurations with the same name *must* otherwise have the same settings when referenced - // from different configuration components. Configuration will fail to load if this is not - // the case. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The DNS lookup family to use during resolution. - // - // [#comment:TODO(mattklein123): Figure out how to support IPv4/IPv6 "happy eyeballs" mode. The - // way this might work is a new lookup family which returns both IPv4 and IPv6 addresses, and - // then configures a host to have a primary and fall back address. With this, we could very - // likely build a "happy eyeballs" connection pool which would race the primary / fall back - // address and return the one that wins. This same method could potentially also be used for - // QUIC to TCP fall back.] - DnsLookupFamily v3.Cluster_DnsLookupFamily `protobuf:"varint,2,opt,name=dns_lookup_family,json=dnsLookupFamily,proto3,enum=envoy.config.cluster.v3.Cluster_DnsLookupFamily" json:"dns_lookup_family,omitempty"` - // The DNS refresh rate for unresolved DNS hosts. If not specified defaults to 60s. - // - // The refresh rate is rounded to the closest millisecond, and must be at least 1ms. - // - // Once a host has been resolved, the refresh rate will be the DNS TTL, capped - // at a minimum of “dns_min_refresh_rate“. - DnsRefreshRate *durationpb.Duration `protobuf:"bytes,3,opt,name=dns_refresh_rate,json=dnsRefreshRate,proto3" json:"dns_refresh_rate,omitempty"` - // The minimum rate that DNS resolution will occur. Per “dns_refresh_rate“, once a host is - // resolved, the DNS TTL will be used, with a minimum set by “dns_min_refresh_rate“. - // “dns_min_refresh_rate“ defaults to 5s and must also be >= 1s. - DnsMinRefreshRate *durationpb.Duration `protobuf:"bytes,14,opt,name=dns_min_refresh_rate,json=dnsMinRefreshRate,proto3" json:"dns_min_refresh_rate,omitempty"` - // The TTL for hosts that are unused. Hosts that have not been used in the configured time - // interval will be purged. If not specified defaults to 5m. - // - // .. note: - // - // The TTL is only checked at the time of DNS refresh, as specified by ``dns_refresh_rate``. This - // means that if the configured TTL is shorter than the refresh rate the host may not be removed - // immediately. - // - // .. note: - // - // The TTL has no relation to DNS TTL and is only used to control Envoy's resource usage. - HostTtl *durationpb.Duration `protobuf:"bytes,4,opt,name=host_ttl,json=hostTtl,proto3" json:"host_ttl,omitempty"` - // The maximum number of hosts that the cache will hold. If not specified defaults to 1024. - // - // .. note: - // - // The implementation is approximate and enforced independently on each worker thread, thus - // it is possible for the maximum hosts in the cache to go slightly above the configured - // value depending on timing. This is similar to how other circuit breakers work. - MaxHosts *wrapperspb.UInt32Value `protobuf:"bytes,5,opt,name=max_hosts,json=maxHosts,proto3" json:"max_hosts,omitempty"` - // Disable the DNS refresh on failure. If this field is set to true, it will ignore the - // :ref:`typed_dns_resolver_config `. - // If not specified, it defaults to false. By enabling this feature, the failed hosts will now be treated as a cache miss, - // allowing the failed hosts to be resolved on demand. - DisableDnsRefreshOnFailure bool `protobuf:"varint,15,opt,name=disable_dns_refresh_on_failure,json=disableDnsRefreshOnFailure,proto3" json:"disable_dns_refresh_on_failure,omitempty"` - // If the DNS failure refresh rate is specified, - // this is used as the cache's DNS refresh rate when DNS requests are failing. If this setting is - // not specified, the failure refresh rate defaults to the dns_refresh_rate. - DnsFailureRefreshRate *v3.Cluster_RefreshRate `protobuf:"bytes,6,opt,name=dns_failure_refresh_rate,json=dnsFailureRefreshRate,proto3" json:"dns_failure_refresh_rate,omitempty"` - // The config of circuit breakers for resolver. It provides a configurable threshold. - // Envoy will use dns cache circuit breakers with default settings even if this value is not set. - DnsCacheCircuitBreaker *DnsCacheCircuitBreakers `protobuf:"bytes,7,opt,name=dns_cache_circuit_breaker,json=dnsCacheCircuitBreaker,proto3" json:"dns_cache_circuit_breaker,omitempty"` - // Always use TCP queries instead of UDP queries for DNS lookups. - // This field is deprecated in favor of “dns_resolution_config“ - // which aggregates all of the DNS resolver configuration in a single message. - // - // Deprecated: Marked as deprecated in envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto. - UseTcpForDnsLookups bool `protobuf:"varint,8,opt,name=use_tcp_for_dns_lookups,json=useTcpForDnsLookups,proto3" json:"use_tcp_for_dns_lookups,omitempty"` - // DNS resolution configuration which includes the underlying dns resolver addresses and options. - // This field is deprecated in favor of - // :ref:`typed_dns_resolver_config `. - // - // Deprecated: Marked as deprecated in envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto. - DnsResolutionConfig *v31.DnsResolutionConfig `protobuf:"bytes,9,opt,name=dns_resolution_config,json=dnsResolutionConfig,proto3" json:"dns_resolution_config,omitempty"` - // DNS resolver type configuration extension. This extension can be used to configure c-ares, apple, - // or any other DNS resolver types and the related parameters. - // For example, an object of - // :ref:`CaresDnsResolverConfig ` - // can be packed into this “typed_dns_resolver_config“. This configuration replaces the - // :ref:`dns_resolution_config ` - // configuration. - // During the transition period when both “dns_resolution_config“ and “typed_dns_resolver_config“ exists, - // when “typed_dns_resolver_config“ is in place, Envoy will use it and ignore “dns_resolution_config“. - // When “typed_dns_resolver_config“ is missing, the default behavior is in place. - // [#extension-category: envoy.network.dns_resolver] - TypedDnsResolverConfig *v31.TypedExtensionConfig `protobuf:"bytes,12,opt,name=typed_dns_resolver_config,json=typedDnsResolverConfig,proto3" json:"typed_dns_resolver_config,omitempty"` - // Hostnames that should be preresolved into the cache upon creation. This might provide a - // performance improvement, in the form of cache hits, for hostnames that are going to be - // resolved during steady state and are known at config load time. - PreresolveHostnames []*v31.SocketAddress `protobuf:"bytes,10,rep,name=preresolve_hostnames,json=preresolveHostnames,proto3" json:"preresolve_hostnames,omitempty"` - // The timeout used for DNS queries. This timeout is independent of any timeout and retry policy - // used by the underlying DNS implementation (e.g., c-areas and Apple DNS) which are opaque. - // Setting this timeout will ensure that queries succeed or fail within the specified time frame - // and are then retried using the standard refresh rates. Setting it to 0 will disable the Envoy DNS - // query timeout and use the underlying DNS implementation timeout. Defaults to 5s if not set. - DnsQueryTimeout *durationpb.Duration `protobuf:"bytes,11,opt,name=dns_query_timeout,json=dnsQueryTimeout,proto3" json:"dns_query_timeout,omitempty"` - // Configuration to flush the DNS cache to long term storage. - KeyValueConfig *v32.KeyValueStoreConfig `protobuf:"bytes,13,opt,name=key_value_config,json=keyValueConfig,proto3" json:"key_value_config,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DnsCacheConfig) Reset() { - *x = DnsCacheConfig{} - mi := &file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DnsCacheConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DnsCacheConfig) ProtoMessage() {} - -func (x *DnsCacheConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DnsCacheConfig.ProtoReflect.Descriptor instead. -func (*DnsCacheConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescGZIP(), []int{1} -} - -func (x *DnsCacheConfig) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DnsCacheConfig) GetDnsLookupFamily() v3.Cluster_DnsLookupFamily { - if x != nil { - return x.DnsLookupFamily - } - return v3.Cluster_DnsLookupFamily(0) -} - -func (x *DnsCacheConfig) GetDnsRefreshRate() *durationpb.Duration { - if x != nil { - return x.DnsRefreshRate - } - return nil -} - -func (x *DnsCacheConfig) GetDnsMinRefreshRate() *durationpb.Duration { - if x != nil { - return x.DnsMinRefreshRate - } - return nil -} - -func (x *DnsCacheConfig) GetHostTtl() *durationpb.Duration { - if x != nil { - return x.HostTtl - } - return nil -} - -func (x *DnsCacheConfig) GetMaxHosts() *wrapperspb.UInt32Value { - if x != nil { - return x.MaxHosts - } - return nil -} - -func (x *DnsCacheConfig) GetDisableDnsRefreshOnFailure() bool { - if x != nil { - return x.DisableDnsRefreshOnFailure - } - return false -} - -func (x *DnsCacheConfig) GetDnsFailureRefreshRate() *v3.Cluster_RefreshRate { - if x != nil { - return x.DnsFailureRefreshRate - } - return nil -} - -func (x *DnsCacheConfig) GetDnsCacheCircuitBreaker() *DnsCacheCircuitBreakers { - if x != nil { - return x.DnsCacheCircuitBreaker - } - return nil -} - -// Deprecated: Marked as deprecated in envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto. -func (x *DnsCacheConfig) GetUseTcpForDnsLookups() bool { - if x != nil { - return x.UseTcpForDnsLookups - } - return false -} - -// Deprecated: Marked as deprecated in envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto. -func (x *DnsCacheConfig) GetDnsResolutionConfig() *v31.DnsResolutionConfig { - if x != nil { - return x.DnsResolutionConfig - } - return nil -} - -func (x *DnsCacheConfig) GetTypedDnsResolverConfig() *v31.TypedExtensionConfig { - if x != nil { - return x.TypedDnsResolverConfig - } - return nil -} - -func (x *DnsCacheConfig) GetPreresolveHostnames() []*v31.SocketAddress { - if x != nil { - return x.PreresolveHostnames - } - return nil -} - -func (x *DnsCacheConfig) GetDnsQueryTimeout() *durationpb.Duration { - if x != nil { - return x.DnsQueryTimeout - } - return nil -} - -func (x *DnsCacheConfig) GetKeyValueConfig() *v32.KeyValueStoreConfig { - if x != nil { - return x.KeyValueConfig - } - return nil -} - -var File_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto protoreflect.FileDescriptor - -const file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDesc = "" + - "\n" + - "@envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto\x120envoy.extensions.common.dynamic_forward_proxy.v3\x1a%envoy/config/cluster/v3/cluster.proto\x1a-envoy/config/common/key_value/v3/config.proto\x1a\"envoy/config/core/v3/address.proto\x1a$envoy/config/core/v3/extension.proto\x1a#envoy/config/core/v3/resolver.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a#envoy/annotations/deprecation.proto\x1a\x1dudpa/annotations/status.proto\x1a!udpa/annotations/versioning.proto\x1a\x17validate/validate.proto\"i\n" + - "\x17DnsCacheCircuitBreakers\x12N\n" + - "\x14max_pending_requests\x18\x01 \x01(\v2\x1c.google.protobuf.UInt32ValueR\x12maxPendingRequests\"\xdf\n" + - "\n" + - "\x0eDnsCacheConfig\x12\x1b\n" + - "\x04name\x18\x01 \x01(\tB\a\xfaB\x04r\x02\x10\x01R\x04name\x12f\n" + - "\x11dns_lookup_family\x18\x02 \x01(\x0e20.envoy.config.cluster.v3.Cluster.DnsLookupFamilyB\b\xfaB\x05\x82\x01\x02\x10\x01R\x0fdnsLookupFamily\x12Q\n" + - "\x10dns_refresh_rate\x18\x03 \x01(\v2\x19.google.protobuf.DurationB\f\xfaB\t\xaa\x01\x062\x04\x10\xc0\x84=R\x0ednsRefreshRate\x12V\n" + - "\x14dns_min_refresh_rate\x18\x0e \x01(\v2\x19.google.protobuf.DurationB\n" + - "\xfaB\a\xaa\x01\x042\x02\b\x01R\x11dnsMinRefreshRate\x12>\n" + - "\bhost_ttl\x18\x04 \x01(\v2\x19.google.protobuf.DurationB\b\xfaB\x05\xaa\x01\x02*\x00R\ahostTtl\x12B\n" + - "\tmax_hosts\x18\x05 \x01(\v2\x1c.google.protobuf.UInt32ValueB\a\xfaB\x04*\x02 \x00R\bmaxHosts\x12B\n" + - "\x1edisable_dns_refresh_on_failure\x18\x0f \x01(\bR\x1adisableDnsRefreshOnFailure\x12e\n" + - "\x18dns_failure_refresh_rate\x18\x06 \x01(\v2,.envoy.config.cluster.v3.Cluster.RefreshRateR\x15dnsFailureRefreshRate\x12\x84\x01\n" + - "\x19dns_cache_circuit_breaker\x18\a \x01(\v2I.envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheCircuitBreakersR\x16dnsCacheCircuitBreaker\x12A\n" + - "\x17use_tcp_for_dns_lookups\x18\b \x01(\bB\v\x92dž\xd8\x04\x033.0\x18\x01R\x13useTcpForDnsLookups\x12j\n" + - "\x15dns_resolution_config\x18\t \x01(\v2).envoy.config.core.v3.DnsResolutionConfigB\v\x92dž\xd8\x04\x033.0\x18\x01R\x13dnsResolutionConfig\x12e\n" + - "\x19typed_dns_resolver_config\x18\f \x01(\v2*.envoy.config.core.v3.TypedExtensionConfigR\x16typedDnsResolverConfig\x12V\n" + - "\x14preresolve_hostnames\x18\n" + - " \x03(\v2#.envoy.config.core.v3.SocketAddressR\x13preresolveHostnames\x12O\n" + - "\x11dns_query_timeout\x18\v \x01(\v2\x19.google.protobuf.DurationB\b\xfaB\x05\xaa\x01\x022\x00R\x0fdnsQueryTimeout\x12_\n" + - "\x10key_value_config\x18\r \x01(\v25.envoy.config.common.key_value.v3.KeyValueStoreConfigR\x0ekeyValueConfig:G\x9aň\x1eB\n" + - "@envoy.config.common.dynamic_forward_proxy.v2alpha.DnsCacheConfigB\xca\x01\xba\x80\xc8\xd1\x06\x02\x10\x02\n" + - ">io.envoyproxy.envoy.extensions.common.dynamic_forward_proxy.v3B\rDnsCacheProtoP\x01Zogithub.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3;dynamic_forward_proxyv3b\x06proto3" - -var ( - file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescOnce sync.Once - file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescData []byte -) - -func file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescGZIP() []byte { - file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescOnce.Do(func() { - file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDesc), len(file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDesc))) - }) - return file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDescData -} - -var file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_goTypes = []any{ - (*DnsCacheCircuitBreakers)(nil), // 0: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheCircuitBreakers - (*DnsCacheConfig)(nil), // 1: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig - (*wrapperspb.UInt32Value)(nil), // 2: google.protobuf.UInt32Value - (v3.Cluster_DnsLookupFamily)(0), // 3: envoy.config.cluster.v3.Cluster.DnsLookupFamily - (*durationpb.Duration)(nil), // 4: google.protobuf.Duration - (*v3.Cluster_RefreshRate)(nil), // 5: envoy.config.cluster.v3.Cluster.RefreshRate - (*v31.DnsResolutionConfig)(nil), // 6: envoy.config.core.v3.DnsResolutionConfig - (*v31.TypedExtensionConfig)(nil), // 7: envoy.config.core.v3.TypedExtensionConfig - (*v31.SocketAddress)(nil), // 8: envoy.config.core.v3.SocketAddress - (*v32.KeyValueStoreConfig)(nil), // 9: envoy.config.common.key_value.v3.KeyValueStoreConfig -} -var file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_depIdxs = []int32{ - 2, // 0: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheCircuitBreakers.max_pending_requests:type_name -> google.protobuf.UInt32Value - 3, // 1: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_lookup_family:type_name -> envoy.config.cluster.v3.Cluster.DnsLookupFamily - 4, // 2: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_refresh_rate:type_name -> google.protobuf.Duration - 4, // 3: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_min_refresh_rate:type_name -> google.protobuf.Duration - 4, // 4: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.host_ttl:type_name -> google.protobuf.Duration - 2, // 5: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.max_hosts:type_name -> google.protobuf.UInt32Value - 5, // 6: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_failure_refresh_rate:type_name -> envoy.config.cluster.v3.Cluster.RefreshRate - 0, // 7: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_cache_circuit_breaker:type_name -> envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheCircuitBreakers - 6, // 8: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_resolution_config:type_name -> envoy.config.core.v3.DnsResolutionConfig - 7, // 9: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.typed_dns_resolver_config:type_name -> envoy.config.core.v3.TypedExtensionConfig - 8, // 10: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.preresolve_hostnames:type_name -> envoy.config.core.v3.SocketAddress - 4, // 11: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.dns_query_timeout:type_name -> google.protobuf.Duration - 9, // 12: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.key_value_config:type_name -> envoy.config.common.key_value.v3.KeyValueStoreConfig - 13, // [13:13] is the sub-list for method output_type - 13, // [13:13] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name -} - -func init() { file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_init() } -func file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_init() { - if File_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDesc), len(file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_rawDesc)), - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_goTypes, - DependencyIndexes: file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_depIdxs, - MessageInfos: file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_msgTypes, - }.Build() - File_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto = out.File - file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_goTypes = nil - file_envoy_extensions_common_dynamic_forward_proxy_v3_dns_cache_proto_depIdxs = nil -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.validate.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.validate.go deleted file mode 100644 index d0bb5fdba..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.validate.go +++ /dev/null @@ -1,612 +0,0 @@ -//go:build !disable_pgv -// Code generated by protoc-gen-validate. DO NOT EDIT. -// source: envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto - -package dynamic_forward_proxyv3 - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/mail" - "net/url" - "regexp" - "sort" - "strings" - "time" - "unicode/utf8" - - "google.golang.org/protobuf/types/known/anypb" - - v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" -) - -// ensure the imports are used -var ( - _ = bytes.MinRead - _ = errors.New("") - _ = fmt.Print - _ = utf8.UTFMax - _ = (*regexp.Regexp)(nil) - _ = (*strings.Reader)(nil) - _ = net.IPv4len - _ = time.Duration(0) - _ = (*url.URL)(nil) - _ = (*mail.Address)(nil) - _ = anypb.Any{} - _ = sort.Sort - - _ = v3.Cluster_DnsLookupFamily(0) -) - -// Validate checks the field values on DnsCacheCircuitBreakers with the rules -// defined in the proto definition for this message. If any rules are -// violated, the first error encountered is returned, or nil if there are no violations. -func (m *DnsCacheCircuitBreakers) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on DnsCacheCircuitBreakers with the -// rules defined in the proto definition for this message. If any rules are -// violated, the result is a list of violation errors wrapped in -// DnsCacheCircuitBreakersMultiError, or nil if none found. -func (m *DnsCacheCircuitBreakers) ValidateAll() error { - return m.validate(true) -} - -func (m *DnsCacheCircuitBreakers) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - if all { - switch v := interface{}(m.GetMaxPendingRequests()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheCircuitBreakersValidationError{ - field: "MaxPendingRequests", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheCircuitBreakersValidationError{ - field: "MaxPendingRequests", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetMaxPendingRequests()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheCircuitBreakersValidationError{ - field: "MaxPendingRequests", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if len(errors) > 0 { - return DnsCacheCircuitBreakersMultiError(errors) - } - - return nil -} - -// DnsCacheCircuitBreakersMultiError is an error wrapping multiple validation -// errors returned by DnsCacheCircuitBreakers.ValidateAll() if the designated -// constraints aren't met. -type DnsCacheCircuitBreakersMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m DnsCacheCircuitBreakersMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m DnsCacheCircuitBreakersMultiError) AllErrors() []error { return m } - -// DnsCacheCircuitBreakersValidationError is the validation error returned by -// DnsCacheCircuitBreakers.Validate if the designated constraints aren't met. -type DnsCacheCircuitBreakersValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e DnsCacheCircuitBreakersValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e DnsCacheCircuitBreakersValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e DnsCacheCircuitBreakersValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e DnsCacheCircuitBreakersValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e DnsCacheCircuitBreakersValidationError) ErrorName() string { - return "DnsCacheCircuitBreakersValidationError" -} - -// Error satisfies the builtin error interface -func (e DnsCacheCircuitBreakersValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sDnsCacheCircuitBreakers.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = DnsCacheCircuitBreakersValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = DnsCacheCircuitBreakersValidationError{} - -// Validate checks the field values on DnsCacheConfig with the rules defined in -// the proto definition for this message. If any rules are violated, the first -// error encountered is returned, or nil if there are no violations. -func (m *DnsCacheConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on DnsCacheConfig with the rules defined -// in the proto definition for this message. If any rules are violated, the -// result is a list of violation errors wrapped in DnsCacheConfigMultiError, -// or nil if none found. -func (m *DnsCacheConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *DnsCacheConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - if utf8.RuneCountInString(m.GetName()) < 1 { - err := DnsCacheConfigValidationError{ - field: "Name", - reason: "value length must be at least 1 runes", - } - if !all { - return err - } - errors = append(errors, err) - } - - if _, ok := v3.Cluster_DnsLookupFamily_name[int32(m.GetDnsLookupFamily())]; !ok { - err := DnsCacheConfigValidationError{ - field: "DnsLookupFamily", - reason: "value must be one of the defined enum values", - } - if !all { - return err - } - errors = append(errors, err) - } - - if d := m.GetDnsRefreshRate(); d != nil { - dur, err := d.AsDuration(), d.CheckValid() - if err != nil { - err = DnsCacheConfigValidationError{ - field: "DnsRefreshRate", - reason: "value is not a valid duration", - cause: err, - } - if !all { - return err - } - errors = append(errors, err) - } else { - - gte := time.Duration(0*time.Second + 1000000*time.Nanosecond) - - if dur < gte { - err := DnsCacheConfigValidationError{ - field: "DnsRefreshRate", - reason: "value must be greater than or equal to 1ms", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - } - - if d := m.GetDnsMinRefreshRate(); d != nil { - dur, err := d.AsDuration(), d.CheckValid() - if err != nil { - err = DnsCacheConfigValidationError{ - field: "DnsMinRefreshRate", - reason: "value is not a valid duration", - cause: err, - } - if !all { - return err - } - errors = append(errors, err) - } else { - - gte := time.Duration(1*time.Second + 0*time.Nanosecond) - - if dur < gte { - err := DnsCacheConfigValidationError{ - field: "DnsMinRefreshRate", - reason: "value must be greater than or equal to 1s", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - } - - if d := m.GetHostTtl(); d != nil { - dur, err := d.AsDuration(), d.CheckValid() - if err != nil { - err = DnsCacheConfigValidationError{ - field: "HostTtl", - reason: "value is not a valid duration", - cause: err, - } - if !all { - return err - } - errors = append(errors, err) - } else { - - gt := time.Duration(0*time.Second + 0*time.Nanosecond) - - if dur <= gt { - err := DnsCacheConfigValidationError{ - field: "HostTtl", - reason: "value must be greater than 0s", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - } - - if wrapper := m.GetMaxHosts(); wrapper != nil { - - if wrapper.GetValue() <= 0 { - err := DnsCacheConfigValidationError{ - field: "MaxHosts", - reason: "value must be greater than 0", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - - // no validation rules for DisableDnsRefreshOnFailure - - if all { - switch v := interface{}(m.GetDnsFailureRefreshRate()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "DnsFailureRefreshRate", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "DnsFailureRefreshRate", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetDnsFailureRefreshRate()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheConfigValidationError{ - field: "DnsFailureRefreshRate", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if all { - switch v := interface{}(m.GetDnsCacheCircuitBreaker()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "DnsCacheCircuitBreaker", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "DnsCacheCircuitBreaker", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetDnsCacheCircuitBreaker()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheConfigValidationError{ - field: "DnsCacheCircuitBreaker", - reason: "embedded message failed validation", - cause: err, - } - } - } - - // no validation rules for UseTcpForDnsLookups - - if all { - switch v := interface{}(m.GetDnsResolutionConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "DnsResolutionConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "DnsResolutionConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetDnsResolutionConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheConfigValidationError{ - field: "DnsResolutionConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if all { - switch v := interface{}(m.GetTypedDnsResolverConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "TypedDnsResolverConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "TypedDnsResolverConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetTypedDnsResolverConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheConfigValidationError{ - field: "TypedDnsResolverConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - for idx, item := range m.GetPreresolveHostnames() { - _, _ = idx, item - - if all { - switch v := interface{}(item).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: fmt.Sprintf("PreresolveHostnames[%v]", idx), - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: fmt.Sprintf("PreresolveHostnames[%v]", idx), - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheConfigValidationError{ - field: fmt.Sprintf("PreresolveHostnames[%v]", idx), - reason: "embedded message failed validation", - cause: err, - } - } - } - - } - - if d := m.GetDnsQueryTimeout(); d != nil { - dur, err := d.AsDuration(), d.CheckValid() - if err != nil { - err = DnsCacheConfigValidationError{ - field: "DnsQueryTimeout", - reason: "value is not a valid duration", - cause: err, - } - if !all { - return err - } - errors = append(errors, err) - } else { - - gte := time.Duration(0*time.Second + 0*time.Nanosecond) - - if dur < gte { - err := DnsCacheConfigValidationError{ - field: "DnsQueryTimeout", - reason: "value must be greater than or equal to 0s", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - } - - if all { - switch v := interface{}(m.GetKeyValueConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "KeyValueConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, DnsCacheConfigValidationError{ - field: "KeyValueConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetKeyValueConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return DnsCacheConfigValidationError{ - field: "KeyValueConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if len(errors) > 0 { - return DnsCacheConfigMultiError(errors) - } - - return nil -} - -// DnsCacheConfigMultiError is an error wrapping multiple validation errors -// returned by DnsCacheConfig.ValidateAll() if the designated constraints -// aren't met. -type DnsCacheConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m DnsCacheConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m DnsCacheConfigMultiError) AllErrors() []error { return m } - -// DnsCacheConfigValidationError is the validation error returned by -// DnsCacheConfig.Validate if the designated constraints aren't met. -type DnsCacheConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e DnsCacheConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e DnsCacheConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e DnsCacheConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e DnsCacheConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e DnsCacheConfigValidationError) ErrorName() string { return "DnsCacheConfigValidationError" } - -// Error satisfies the builtin error interface -func (e DnsCacheConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sDnsCacheConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = DnsCacheConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = DnsCacheConfigValidationError{} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache_vtproto.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache_vtproto.pb.go deleted file mode 100644 index a4be6cdc2..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache_vtproto.pb.go +++ /dev/null @@ -1,415 +0,0 @@ -//go:build vtprotobuf -// +build vtprotobuf - -// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// source: envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto - -package dynamic_forward_proxyv3 - -import ( - protohelpers "github.com/planetscale/vtprotobuf/protohelpers" - durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" - wrapperspb "github.com/planetscale/vtprotobuf/types/known/wrapperspb" - proto "google.golang.org/protobuf/proto" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -func (m *DnsCacheCircuitBreakers) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *DnsCacheCircuitBreakers) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *DnsCacheCircuitBreakers) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.MaxPendingRequests != nil { - size, err := (*wrapperspb.UInt32Value)(m.MaxPendingRequests).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *DnsCacheConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *DnsCacheConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *DnsCacheConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.DisableDnsRefreshOnFailure { - i-- - if m.DisableDnsRefreshOnFailure { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x78 - } - if m.DnsMinRefreshRate != nil { - size, err := (*durationpb.Duration)(m.DnsMinRefreshRate).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x72 - } - if m.KeyValueConfig != nil { - if vtmsg, ok := interface{}(m.KeyValueConfig).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.KeyValueConfig) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0x6a - } - if m.TypedDnsResolverConfig != nil { - if vtmsg, ok := interface{}(m.TypedDnsResolverConfig).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.TypedDnsResolverConfig) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0x62 - } - if m.DnsQueryTimeout != nil { - size, err := (*durationpb.Duration)(m.DnsQueryTimeout).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x5a - } - if len(m.PreresolveHostnames) > 0 { - for iNdEx := len(m.PreresolveHostnames) - 1; iNdEx >= 0; iNdEx-- { - if vtmsg, ok := interface{}(m.PreresolveHostnames[iNdEx]).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.PreresolveHostnames[iNdEx]) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0x52 - } - } - if m.DnsResolutionConfig != nil { - if vtmsg, ok := interface{}(m.DnsResolutionConfig).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.DnsResolutionConfig) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0x4a - } - if m.UseTcpForDnsLookups { - i-- - if m.UseTcpForDnsLookups { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x40 - } - if m.DnsCacheCircuitBreaker != nil { - size, err := m.DnsCacheCircuitBreaker.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x3a - } - if m.DnsFailureRefreshRate != nil { - if vtmsg, ok := interface{}(m.DnsFailureRefreshRate).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.DnsFailureRefreshRate) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0x32 - } - if m.MaxHosts != nil { - size, err := (*wrapperspb.UInt32Value)(m.MaxHosts).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x2a - } - if m.HostTtl != nil { - size, err := (*durationpb.Duration)(m.HostTtl).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x22 - } - if m.DnsRefreshRate != nil { - size, err := (*durationpb.Duration)(m.DnsRefreshRate).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x1a - } - if m.DnsLookupFamily != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DnsLookupFamily)) - i-- - dAtA[i] = 0x10 - } - if len(m.Name) > 0 { - i -= len(m.Name) - copy(dAtA[i:], m.Name) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *DnsCacheCircuitBreakers) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.MaxPendingRequests != nil { - l = (*wrapperspb.UInt32Value)(m.MaxPendingRequests).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - n += len(m.unknownFields) - return n -} - -func (m *DnsCacheConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.DnsLookupFamily != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.DnsLookupFamily)) - } - if m.DnsRefreshRate != nil { - l = (*durationpb.Duration)(m.DnsRefreshRate).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.HostTtl != nil { - l = (*durationpb.Duration)(m.HostTtl).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.MaxHosts != nil { - l = (*wrapperspb.UInt32Value)(m.MaxHosts).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.DnsFailureRefreshRate != nil { - if size, ok := interface{}(m.DnsFailureRefreshRate).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.DnsFailureRefreshRate) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.DnsCacheCircuitBreaker != nil { - l = m.DnsCacheCircuitBreaker.SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.UseTcpForDnsLookups { - n += 2 - } - if m.DnsResolutionConfig != nil { - if size, ok := interface{}(m.DnsResolutionConfig).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.DnsResolutionConfig) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if len(m.PreresolveHostnames) > 0 { - for _, e := range m.PreresolveHostnames { - if size, ok := interface{}(e).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(e) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - } - if m.DnsQueryTimeout != nil { - l = (*durationpb.Duration)(m.DnsQueryTimeout).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.TypedDnsResolverConfig != nil { - if size, ok := interface{}(m.TypedDnsResolverConfig).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.TypedDnsResolverConfig) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.KeyValueConfig != nil { - if size, ok := interface{}(m.KeyValueConfig).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.KeyValueConfig) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.DnsMinRefreshRate != nil { - l = (*durationpb.Duration)(m.DnsMinRefreshRate).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.DisableDnsRefreshOnFailure { - n += 2 - } - n += len(m.unknownFields) - return n -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.go deleted file mode 100644 index b244f30b4..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.go +++ /dev/null @@ -1,383 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.10 -// protoc v6.33.2 -// source: envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto - -package dynamic_forward_proxyv3 - -import ( - _ "github.com/cncf/xds/go/udpa/annotations" - v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3" - _ "github.com/envoyproxy/protoc-gen-validate/validate" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Configuration for the dynamic forward proxy HTTP filter. See the :ref:`architecture overview -// ` for more information. -// [#extension: envoy.filters.http.dynamic_forward_proxy] -type FilterConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to ImplementationSpecifier: - // - // *FilterConfig_DnsCacheConfig - // *FilterConfig_SubClusterConfig - ImplementationSpecifier isFilterConfig_ImplementationSpecifier `protobuf_oneof:"implementation_specifier"` - // When this flag is set, the filter will add the resolved upstream address in the filter - // state. The state should be saved with key - // “envoy.stream.upstream_address“ (See - // :repo:`upstream_address.h`). - SaveUpstreamAddress bool `protobuf:"varint,2,opt,name=save_upstream_address,json=saveUpstreamAddress,proto3" json:"save_upstream_address,omitempty"` - // When this flag is set, the filter will check for the “envoy.upstream.dynamic_host“ - // and/or “envoy.upstream.dynamic_port“ filter state values before using the HTTP - // Host header for DNS resolution. This provides consistency with the - // :ref:`SNI dynamic forward proxy ` and - // :ref:`UDP dynamic forward proxy ` - // filters behavior when enabled. - // - // If the flag is not set (default), the filter will use the HTTP Host header - // for DNS resolution, maintaining backward compatibility. - AllowDynamicHostFromFilterState bool `protobuf:"varint,4,opt,name=allow_dynamic_host_from_filter_state,json=allowDynamicHostFromFilterState,proto3" json:"allow_dynamic_host_from_filter_state,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FilterConfig) Reset() { - *x = FilterConfig{} - mi := &file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FilterConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FilterConfig) ProtoMessage() {} - -func (x *FilterConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FilterConfig.ProtoReflect.Descriptor instead. -func (*FilterConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescGZIP(), []int{0} -} - -func (x *FilterConfig) GetImplementationSpecifier() isFilterConfig_ImplementationSpecifier { - if x != nil { - return x.ImplementationSpecifier - } - return nil -} - -func (x *FilterConfig) GetDnsCacheConfig() *v3.DnsCacheConfig { - if x != nil { - if x, ok := x.ImplementationSpecifier.(*FilterConfig_DnsCacheConfig); ok { - return x.DnsCacheConfig - } - } - return nil -} - -func (x *FilterConfig) GetSubClusterConfig() *SubClusterConfig { - if x != nil { - if x, ok := x.ImplementationSpecifier.(*FilterConfig_SubClusterConfig); ok { - return x.SubClusterConfig - } - } - return nil -} - -func (x *FilterConfig) GetSaveUpstreamAddress() bool { - if x != nil { - return x.SaveUpstreamAddress - } - return false -} - -func (x *FilterConfig) GetAllowDynamicHostFromFilterState() bool { - if x != nil { - return x.AllowDynamicHostFromFilterState - } - return false -} - -type isFilterConfig_ImplementationSpecifier interface { - isFilterConfig_ImplementationSpecifier() -} - -type FilterConfig_DnsCacheConfig struct { - // The DNS cache configuration that the filter will attach to. Note this configuration must - // match that of associated :ref:`dynamic forward proxy cluster configuration - // `. - DnsCacheConfig *v3.DnsCacheConfig `protobuf:"bytes,1,opt,name=dns_cache_config,json=dnsCacheConfig,proto3,oneof"` -} - -type FilterConfig_SubClusterConfig struct { - // The configuration that the filter will use, when the related dynamic forward proxy cluster enabled - // sub clusters. - SubClusterConfig *SubClusterConfig `protobuf:"bytes,3,opt,name=sub_cluster_config,json=subClusterConfig,proto3,oneof"` -} - -func (*FilterConfig_DnsCacheConfig) isFilterConfig_ImplementationSpecifier() {} - -func (*FilterConfig_SubClusterConfig) isFilterConfig_ImplementationSpecifier() {} - -// Per route Configuration for the dynamic forward proxy HTTP filter. -type PerRouteConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to HostRewriteSpecifier: - // - // *PerRouteConfig_HostRewriteLiteral - // *PerRouteConfig_HostRewriteHeader - HostRewriteSpecifier isPerRouteConfig_HostRewriteSpecifier `protobuf_oneof:"host_rewrite_specifier"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PerRouteConfig) Reset() { - *x = PerRouteConfig{} - mi := &file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PerRouteConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PerRouteConfig) ProtoMessage() {} - -func (x *PerRouteConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PerRouteConfig.ProtoReflect.Descriptor instead. -func (*PerRouteConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescGZIP(), []int{1} -} - -func (x *PerRouteConfig) GetHostRewriteSpecifier() isPerRouteConfig_HostRewriteSpecifier { - if x != nil { - return x.HostRewriteSpecifier - } - return nil -} - -func (x *PerRouteConfig) GetHostRewriteLiteral() string { - if x != nil { - if x, ok := x.HostRewriteSpecifier.(*PerRouteConfig_HostRewriteLiteral); ok { - return x.HostRewriteLiteral - } - } - return "" -} - -func (x *PerRouteConfig) GetHostRewriteHeader() string { - if x != nil { - if x, ok := x.HostRewriteSpecifier.(*PerRouteConfig_HostRewriteHeader); ok { - return x.HostRewriteHeader - } - } - return "" -} - -type isPerRouteConfig_HostRewriteSpecifier interface { - isPerRouteConfig_HostRewriteSpecifier() -} - -type PerRouteConfig_HostRewriteLiteral struct { - // Indicates that before DNS lookup, the host header will be swapped with - // this value. If not set or empty, the original host header value - // will be used and no rewrite will happen. - // - // .. note:: - // - // This rewrite affects both DNS lookup and host header forwarding. However, this option shouldn't be used with - // :ref:`HCM host rewrite header ` given that - // the value set here would be used for DNS lookups whereas the value set in the HCM would be used for host - // header forwarding which might not be the desired outcome. - HostRewriteLiteral string `protobuf:"bytes,1,opt,name=host_rewrite_literal,json=hostRewriteLiteral,proto3,oneof"` -} - -type PerRouteConfig_HostRewriteHeader struct { - // Indicates that before DNS lookup, the host header will be swapped with - // the value of this header. If not set or empty, the original host header - // value will be used and no rewrite will happen. - // - // .. note:: - // - // This rewrite affects both DNS lookup and host header forwarding. However, this option shouldn't be used with - // :ref:`HCM host rewrite header ` given that - // the value set here would be used for DNS lookups whereas the value set in the HCM would be used for host - // header forwarding which might not be the desired outcome. - // - // .. note:: - // - // If the header appears multiple times only the first value is used. - HostRewriteHeader string `protobuf:"bytes,2,opt,name=host_rewrite_header,json=hostRewriteHeader,proto3,oneof"` -} - -func (*PerRouteConfig_HostRewriteLiteral) isPerRouteConfig_HostRewriteSpecifier() {} - -func (*PerRouteConfig_HostRewriteHeader) isPerRouteConfig_HostRewriteSpecifier() {} - -type SubClusterConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The timeout used for sub cluster initialization. Defaults to **5s** if not set. - ClusterInitTimeout *durationpb.Duration `protobuf:"bytes,3,opt,name=cluster_init_timeout,json=clusterInitTimeout,proto3" json:"cluster_init_timeout,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubClusterConfig) Reset() { - *x = SubClusterConfig{} - mi := &file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubClusterConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubClusterConfig) ProtoMessage() {} - -func (x *SubClusterConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubClusterConfig.ProtoReflect.Descriptor instead. -func (*SubClusterConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescGZIP(), []int{2} -} - -func (x *SubClusterConfig) GetClusterInitTimeout() *durationpb.Duration { - if x != nil { - return x.ClusterInitTimeout - } - return nil -} - -var File_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto protoreflect.FileDescriptor - -const file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDesc = "" + - "\n" + - "Renvoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto\x126envoy.extensions.filters.http.dynamic_forward_proxy.v3\x1a@envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1dudpa/annotations/status.proto\x1a!udpa/annotations/versioning.proto\x1a\x17validate/validate.proto\"\xe1\x03\n" + - "\fFilterConfig\x12l\n" + - "\x10dns_cache_config\x18\x01 \x01(\v2@.envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfigH\x00R\x0ednsCacheConfig\x12x\n" + - "\x12sub_cluster_config\x18\x03 \x01(\v2H.envoy.extensions.filters.http.dynamic_forward_proxy.v3.SubClusterConfigH\x00R\x10subClusterConfig\x122\n" + - "\x15save_upstream_address\x18\x02 \x01(\bR\x13saveUpstreamAddress\x12M\n" + - "$allow_dynamic_host_from_filter_state\x18\x04 \x01(\bR\x1fallowDynamicHostFromFilterState:J\x9aň\x1eE\n" + - "Cenvoy.config.filter.http.dynamic_forward_proxy.v2alpha.FilterConfigB\x1a\n" + - "\x18implementation_specifier\"\xde\x01\n" + - "\x0ePerRouteConfig\x122\n" + - "\x14host_rewrite_literal\x18\x01 \x01(\tH\x00R\x12hostRewriteLiteral\x120\n" + - "\x13host_rewrite_header\x18\x02 \x01(\tH\x00R\x11hostRewriteHeader:L\x9aň\x1eG\n" + - "Eenvoy.config.filter.http.dynamic_forward_proxy.v2alpha.PerRouteConfigB\x18\n" + - "\x16host_rewrite_specifier\"i\n" + - "\x10SubClusterConfig\x12U\n" + - "\x14cluster_init_timeout\x18\x03 \x01(\v2\x19.google.protobuf.DurationB\b\xfaB\x05\xaa\x01\x02*\x00R\x12clusterInitTimeoutB\xe1\x01\xba\x80\xc8\xd1\x06\x02\x10\x02\n" + - "Dio.envoyproxy.envoy.extensions.filters.http.dynamic_forward_proxy.v3B\x18DynamicForwardProxyProtoP\x01Zugithub.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3;dynamic_forward_proxyv3b\x06proto3" - -var ( - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescOnce sync.Once - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescData []byte -) - -func file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescGZIP() []byte { - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescOnce.Do(func() { - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDesc), len(file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDesc))) - }) - return file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDescData -} - -var file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_goTypes = []any{ - (*FilterConfig)(nil), // 0: envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig - (*PerRouteConfig)(nil), // 1: envoy.extensions.filters.http.dynamic_forward_proxy.v3.PerRouteConfig - (*SubClusterConfig)(nil), // 2: envoy.extensions.filters.http.dynamic_forward_proxy.v3.SubClusterConfig - (*v3.DnsCacheConfig)(nil), // 3: envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig - (*durationpb.Duration)(nil), // 4: google.protobuf.Duration -} -var file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_depIdxs = []int32{ - 3, // 0: envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig.dns_cache_config:type_name -> envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig - 2, // 1: envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig.sub_cluster_config:type_name -> envoy.extensions.filters.http.dynamic_forward_proxy.v3.SubClusterConfig - 4, // 2: envoy.extensions.filters.http.dynamic_forward_proxy.v3.SubClusterConfig.cluster_init_timeout:type_name -> google.protobuf.Duration - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name -} - -func init() { - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_init() -} -func file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_init() { - if File_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto != nil { - return - } - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[0].OneofWrappers = []any{ - (*FilterConfig_DnsCacheConfig)(nil), - (*FilterConfig_SubClusterConfig)(nil), - } - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes[1].OneofWrappers = []any{ - (*PerRouteConfig_HostRewriteLiteral)(nil), - (*PerRouteConfig_HostRewriteHeader)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDesc), len(file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_rawDesc)), - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_goTypes, - DependencyIndexes: file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_depIdxs, - MessageInfos: file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_msgTypes, - }.Build() - File_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto = out.File - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_goTypes = nil - file_envoy_extensions_filters_http_dynamic_forward_proxy_v3_dynamic_forward_proxy_proto_depIdxs = nil -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.validate.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.validate.go deleted file mode 100644 index fa53c4272..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.validate.go +++ /dev/null @@ -1,486 +0,0 @@ -//go:build !disable_pgv -// Code generated by protoc-gen-validate. DO NOT EDIT. -// source: envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto - -package dynamic_forward_proxyv3 - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/mail" - "net/url" - "regexp" - "sort" - "strings" - "time" - "unicode/utf8" - - "google.golang.org/protobuf/types/known/anypb" -) - -// ensure the imports are used -var ( - _ = bytes.MinRead - _ = errors.New("") - _ = fmt.Print - _ = utf8.UTFMax - _ = (*regexp.Regexp)(nil) - _ = (*strings.Reader)(nil) - _ = net.IPv4len - _ = time.Duration(0) - _ = (*url.URL)(nil) - _ = (*mail.Address)(nil) - _ = anypb.Any{} - _ = sort.Sort -) - -// Validate checks the field values on FilterConfig with the rules defined in -// the proto definition for this message. If any rules are violated, the first -// error encountered is returned, or nil if there are no violations. -func (m *FilterConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on FilterConfig with the rules defined -// in the proto definition for this message. If any rules are violated, the -// result is a list of violation errors wrapped in FilterConfigMultiError, or -// nil if none found. -func (m *FilterConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *FilterConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - // no validation rules for SaveUpstreamAddress - - // no validation rules for AllowDynamicHostFromFilterState - - switch v := m.ImplementationSpecifier.(type) { - case *FilterConfig_DnsCacheConfig: - if v == nil { - err := FilterConfigValidationError{ - field: "ImplementationSpecifier", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - - if all { - switch v := interface{}(m.GetDnsCacheConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, FilterConfigValidationError{ - field: "DnsCacheConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, FilterConfigValidationError{ - field: "DnsCacheConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetDnsCacheConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return FilterConfigValidationError{ - field: "DnsCacheConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - case *FilterConfig_SubClusterConfig: - if v == nil { - err := FilterConfigValidationError{ - field: "ImplementationSpecifier", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - - if all { - switch v := interface{}(m.GetSubClusterConfig()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, FilterConfigValidationError{ - field: "SubClusterConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, FilterConfigValidationError{ - field: "SubClusterConfig", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetSubClusterConfig()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return FilterConfigValidationError{ - field: "SubClusterConfig", - reason: "embedded message failed validation", - cause: err, - } - } - } - - default: - _ = v // ensures v is used - } - - if len(errors) > 0 { - return FilterConfigMultiError(errors) - } - - return nil -} - -// FilterConfigMultiError is an error wrapping multiple validation errors -// returned by FilterConfig.ValidateAll() if the designated constraints aren't met. -type FilterConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m FilterConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m FilterConfigMultiError) AllErrors() []error { return m } - -// FilterConfigValidationError is the validation error returned by -// FilterConfig.Validate if the designated constraints aren't met. -type FilterConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e FilterConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e FilterConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e FilterConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e FilterConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e FilterConfigValidationError) ErrorName() string { return "FilterConfigValidationError" } - -// Error satisfies the builtin error interface -func (e FilterConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sFilterConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = FilterConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = FilterConfigValidationError{} - -// Validate checks the field values on PerRouteConfig with the rules defined in -// the proto definition for this message. If any rules are violated, the first -// error encountered is returned, or nil if there are no violations. -func (m *PerRouteConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on PerRouteConfig with the rules defined -// in the proto definition for this message. If any rules are violated, the -// result is a list of violation errors wrapped in PerRouteConfigMultiError, -// or nil if none found. -func (m *PerRouteConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *PerRouteConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - switch v := m.HostRewriteSpecifier.(type) { - case *PerRouteConfig_HostRewriteLiteral: - if v == nil { - err := PerRouteConfigValidationError{ - field: "HostRewriteSpecifier", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - // no validation rules for HostRewriteLiteral - case *PerRouteConfig_HostRewriteHeader: - if v == nil { - err := PerRouteConfigValidationError{ - field: "HostRewriteSpecifier", - reason: "oneof value cannot be a typed-nil", - } - if !all { - return err - } - errors = append(errors, err) - } - // no validation rules for HostRewriteHeader - default: - _ = v // ensures v is used - } - - if len(errors) > 0 { - return PerRouteConfigMultiError(errors) - } - - return nil -} - -// PerRouteConfigMultiError is an error wrapping multiple validation errors -// returned by PerRouteConfig.ValidateAll() if the designated constraints -// aren't met. -type PerRouteConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m PerRouteConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m PerRouteConfigMultiError) AllErrors() []error { return m } - -// PerRouteConfigValidationError is the validation error returned by -// PerRouteConfig.Validate if the designated constraints aren't met. -type PerRouteConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e PerRouteConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e PerRouteConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e PerRouteConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e PerRouteConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e PerRouteConfigValidationError) ErrorName() string { return "PerRouteConfigValidationError" } - -// Error satisfies the builtin error interface -func (e PerRouteConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sPerRouteConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = PerRouteConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = PerRouteConfigValidationError{} - -// Validate checks the field values on SubClusterConfig with the rules defined -// in the proto definition for this message. If any rules are violated, the -// first error encountered is returned, or nil if there are no violations. -func (m *SubClusterConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on SubClusterConfig with the rules -// defined in the proto definition for this message. If any rules are -// violated, the result is a list of violation errors wrapped in -// SubClusterConfigMultiError, or nil if none found. -func (m *SubClusterConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *SubClusterConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - if d := m.GetClusterInitTimeout(); d != nil { - dur, err := d.AsDuration(), d.CheckValid() - if err != nil { - err = SubClusterConfigValidationError{ - field: "ClusterInitTimeout", - reason: "value is not a valid duration", - cause: err, - } - if !all { - return err - } - errors = append(errors, err) - } else { - - gt := time.Duration(0*time.Second + 0*time.Nanosecond) - - if dur <= gt { - err := SubClusterConfigValidationError{ - field: "ClusterInitTimeout", - reason: "value must be greater than 0s", - } - if !all { - return err - } - errors = append(errors, err) - } - - } - } - - if len(errors) > 0 { - return SubClusterConfigMultiError(errors) - } - - return nil -} - -// SubClusterConfigMultiError is an error wrapping multiple validation errors -// returned by SubClusterConfig.ValidateAll() if the designated constraints -// aren't met. -type SubClusterConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m SubClusterConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m SubClusterConfigMultiError) AllErrors() []error { return m } - -// SubClusterConfigValidationError is the validation error returned by -// SubClusterConfig.Validate if the designated constraints aren't met. -type SubClusterConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e SubClusterConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e SubClusterConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e SubClusterConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e SubClusterConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e SubClusterConfigValidationError) ErrorName() string { return "SubClusterConfigValidationError" } - -// Error satisfies the builtin error interface -func (e SubClusterConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sSubClusterConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = SubClusterConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = SubClusterConfigValidationError{} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy_vtproto.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy_vtproto.pb.go deleted file mode 100644 index b81060977..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy_vtproto.pb.go +++ /dev/null @@ -1,364 +0,0 @@ -//go:build vtprotobuf -// +build vtprotobuf - -// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// source: envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto - -package dynamic_forward_proxyv3 - -import ( - protohelpers "github.com/planetscale/vtprotobuf/protohelpers" - durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" - proto "google.golang.org/protobuf/proto" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -func (m *FilterConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *FilterConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *FilterConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.AllowDynamicHostFromFilterState { - i-- - if m.AllowDynamicHostFromFilterState { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x20 - } - if msg, ok := m.ImplementationSpecifier.(*FilterConfig_SubClusterConfig); ok { - size, err := msg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - } - if m.SaveUpstreamAddress { - i-- - if m.SaveUpstreamAddress { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x10 - } - if msg, ok := m.ImplementationSpecifier.(*FilterConfig_DnsCacheConfig); ok { - size, err := msg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - } - return len(dAtA) - i, nil -} - -func (m *FilterConfig_DnsCacheConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *FilterConfig_DnsCacheConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - i := len(dAtA) - if m.DnsCacheConfig != nil { - if vtmsg, ok := interface{}(m.DnsCacheConfig).(interface { - MarshalToSizedBufferVTStrict([]byte) (int, error) - }); ok { - size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - } else { - encoded, err := proto.Marshal(m.DnsCacheConfig) - if err != nil { - return 0, err - } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) - } - i-- - dAtA[i] = 0xa - } else { - i = protohelpers.EncodeVarint(dAtA, i, 0) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} -func (m *FilterConfig_SubClusterConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *FilterConfig_SubClusterConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - i := len(dAtA) - if m.SubClusterConfig != nil { - size, err := m.SubClusterConfig.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x1a - } else { - i = protohelpers.EncodeVarint(dAtA, i, 0) - i-- - dAtA[i] = 0x1a - } - return len(dAtA) - i, nil -} -func (m *PerRouteConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PerRouteConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *PerRouteConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if msg, ok := m.HostRewriteSpecifier.(*PerRouteConfig_HostRewriteHeader); ok { - size, err := msg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - } - if msg, ok := m.HostRewriteSpecifier.(*PerRouteConfig_HostRewriteLiteral); ok { - size, err := msg.MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - } - return len(dAtA) - i, nil -} - -func (m *PerRouteConfig_HostRewriteLiteral) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *PerRouteConfig_HostRewriteLiteral) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - i := len(dAtA) - i -= len(m.HostRewriteLiteral) - copy(dAtA[i:], m.HostRewriteLiteral) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HostRewriteLiteral))) - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} -func (m *PerRouteConfig_HostRewriteHeader) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *PerRouteConfig_HostRewriteHeader) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - i := len(dAtA) - i -= len(m.HostRewriteHeader) - copy(dAtA[i:], m.HostRewriteHeader) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HostRewriteHeader))) - i-- - dAtA[i] = 0x12 - return len(dAtA) - i, nil -} -func (m *SubClusterConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *SubClusterConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *SubClusterConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.ClusterInitTimeout != nil { - size, err := (*durationpb.Duration)(m.ClusterInitTimeout).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x1a - } - return len(dAtA) - i, nil -} - -func (m *FilterConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if vtmsg, ok := m.ImplementationSpecifier.(interface{ SizeVT() int }); ok { - n += vtmsg.SizeVT() - } - if m.SaveUpstreamAddress { - n += 2 - } - if m.AllowDynamicHostFromFilterState { - n += 2 - } - n += len(m.unknownFields) - return n -} - -func (m *FilterConfig_DnsCacheConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.DnsCacheConfig != nil { - if size, ok := interface{}(m.DnsCacheConfig).(interface { - SizeVT() int - }); ok { - l = size.SizeVT() - } else { - l = proto.Size(m.DnsCacheConfig) - } - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } else { - n += 2 - } - return n -} -func (m *FilterConfig_SubClusterConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.SubClusterConfig != nil { - l = m.SubClusterConfig.SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } else { - n += 2 - } - return n -} -func (m *PerRouteConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if vtmsg, ok := m.HostRewriteSpecifier.(interface{ SizeVT() int }); ok { - n += vtmsg.SizeVT() - } - n += len(m.unknownFields) - return n -} - -func (m *PerRouteConfig_HostRewriteLiteral) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.HostRewriteLiteral) - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - return n -} -func (m *PerRouteConfig_HostRewriteHeader) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.HostRewriteHeader) - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - return n -} -func (m *SubClusterConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.ClusterInitTimeout != nil { - l = (*durationpb.Duration)(m.ClusterInitTimeout).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - n += len(m.unknownFields) - return n -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.go deleted file mode 100644 index 0b097d0da..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.go +++ /dev/null @@ -1,150 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.10 -// protoc v6.33.2 -// source: envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.proto - -package getaddrinfov3 - -import ( - _ "github.com/cncf/xds/go/udpa/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Configuration for getaddrinfo DNS resolver. This resolver will use the system's getaddrinfo() -// function to resolve hosts. -// -// .. attention:: -// -// Resolutions currently use a hard coded TTL of 60s because the getaddrinfo() API does not -// provide the actual TTL. Configuration for this can be added in the future if needed. -type GetAddrInfoDnsResolverConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Specifies the number of retries before the resolver gives up. If not specified, the resolver will - // retry indefinitely until it succeeds or the DNS query times out. - NumRetries *wrapperspb.UInt32Value `protobuf:"bytes,1,opt,name=num_retries,json=numRetries,proto3" json:"num_retries,omitempty"` - // Specifies the number of threads used to resolve pending DNS queries. If not specified, one thread is used. - NumResolverThreads *wrapperspb.UInt32Value `protobuf:"bytes,2,opt,name=num_resolver_threads,json=numResolverThreads,proto3" json:"num_resolver_threads,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetAddrInfoDnsResolverConfig) Reset() { - *x = GetAddrInfoDnsResolverConfig{} - mi := &file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetAddrInfoDnsResolverConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetAddrInfoDnsResolverConfig) ProtoMessage() {} - -func (x *GetAddrInfoDnsResolverConfig) ProtoReflect() protoreflect.Message { - mi := &file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetAddrInfoDnsResolverConfig.ProtoReflect.Descriptor instead. -func (*GetAddrInfoDnsResolverConfig) Descriptor() ([]byte, []int) { - return file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescGZIP(), []int{0} -} - -func (x *GetAddrInfoDnsResolverConfig) GetNumRetries() *wrapperspb.UInt32Value { - if x != nil { - return x.NumRetries - } - return nil -} - -func (x *GetAddrInfoDnsResolverConfig) GetNumResolverThreads() *wrapperspb.UInt32Value { - if x != nil { - return x.NumResolverThreads - } - return nil -} - -var File_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto protoreflect.FileDescriptor - -const file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDesc = "" + - "\n" + - "Senvoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.proto\x124envoy.extensions.network.dns_resolver.getaddrinfo.v3\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x1dudpa/annotations/status.proto\"\xad\x01\n" + - "\x1cGetAddrInfoDnsResolverConfig\x12=\n" + - "\vnum_retries\x18\x01 \x01(\v2\x1c.google.protobuf.UInt32ValueR\n" + - "numRetries\x12N\n" + - "\x14num_resolver_threads\x18\x02 \x01(\v2\x1c.google.protobuf.UInt32ValueR\x12numResolverThreadsB\xd6\x01\xba\x80\xc8\xd1\x06\x02\x10\x02\n" + - "Bio.envoyproxy.envoy.extensions.network.dns_resolver.getaddrinfo.v3B\x1bGetaddrinfoDnsResolverProtoP\x01Zigithub.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3;getaddrinfov3b\x06proto3" - -var ( - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescOnce sync.Once - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescData []byte -) - -func file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescGZIP() []byte { - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescOnce.Do(func() { - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDesc), len(file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDesc))) - }) - return file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDescData -} - -var file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_goTypes = []any{ - (*GetAddrInfoDnsResolverConfig)(nil), // 0: envoy.extensions.network.dns_resolver.getaddrinfo.v3.GetAddrInfoDnsResolverConfig - (*wrapperspb.UInt32Value)(nil), // 1: google.protobuf.UInt32Value -} -var file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_depIdxs = []int32{ - 1, // 0: envoy.extensions.network.dns_resolver.getaddrinfo.v3.GetAddrInfoDnsResolverConfig.num_retries:type_name -> google.protobuf.UInt32Value - 1, // 1: envoy.extensions.network.dns_resolver.getaddrinfo.v3.GetAddrInfoDnsResolverConfig.num_resolver_threads:type_name -> google.protobuf.UInt32Value - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_init() -} -func file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_init() { - if File_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDesc), len(file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_rawDesc)), - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_goTypes, - DependencyIndexes: file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_depIdxs, - MessageInfos: file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_msgTypes, - }.Build() - File_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto = out.File - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_goTypes = nil - file_envoy_extensions_network_dns_resolver_getaddrinfo_v3_getaddrinfo_dns_resolver_proto_depIdxs = nil -} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.validate.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.validate.go deleted file mode 100644 index 769f7d878..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.validate.go +++ /dev/null @@ -1,198 +0,0 @@ -//go:build !disable_pgv -// Code generated by protoc-gen-validate. DO NOT EDIT. -// source: envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.proto - -package getaddrinfov3 - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/mail" - "net/url" - "regexp" - "sort" - "strings" - "time" - "unicode/utf8" - - "google.golang.org/protobuf/types/known/anypb" -) - -// ensure the imports are used -var ( - _ = bytes.MinRead - _ = errors.New("") - _ = fmt.Print - _ = utf8.UTFMax - _ = (*regexp.Regexp)(nil) - _ = (*strings.Reader)(nil) - _ = net.IPv4len - _ = time.Duration(0) - _ = (*url.URL)(nil) - _ = (*mail.Address)(nil) - _ = anypb.Any{} - _ = sort.Sort -) - -// Validate checks the field values on GetAddrInfoDnsResolverConfig with the -// rules defined in the proto definition for this message. If any rules are -// violated, the first error encountered is returned, or nil if there are no violations. -func (m *GetAddrInfoDnsResolverConfig) Validate() error { - return m.validate(false) -} - -// ValidateAll checks the field values on GetAddrInfoDnsResolverConfig with the -// rules defined in the proto definition for this message. If any rules are -// violated, the result is a list of violation errors wrapped in -// GetAddrInfoDnsResolverConfigMultiError, or nil if none found. -func (m *GetAddrInfoDnsResolverConfig) ValidateAll() error { - return m.validate(true) -} - -func (m *GetAddrInfoDnsResolverConfig) validate(all bool) error { - if m == nil { - return nil - } - - var errors []error - - if all { - switch v := interface{}(m.GetNumRetries()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, GetAddrInfoDnsResolverConfigValidationError{ - field: "NumRetries", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, GetAddrInfoDnsResolverConfigValidationError{ - field: "NumRetries", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetNumRetries()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return GetAddrInfoDnsResolverConfigValidationError{ - field: "NumRetries", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if all { - switch v := interface{}(m.GetNumResolverThreads()).(type) { - case interface{ ValidateAll() error }: - if err := v.ValidateAll(); err != nil { - errors = append(errors, GetAddrInfoDnsResolverConfigValidationError{ - field: "NumResolverThreads", - reason: "embedded message failed validation", - cause: err, - }) - } - case interface{ Validate() error }: - if err := v.Validate(); err != nil { - errors = append(errors, GetAddrInfoDnsResolverConfigValidationError{ - field: "NumResolverThreads", - reason: "embedded message failed validation", - cause: err, - }) - } - } - } else if v, ok := interface{}(m.GetNumResolverThreads()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return GetAddrInfoDnsResolverConfigValidationError{ - field: "NumResolverThreads", - reason: "embedded message failed validation", - cause: err, - } - } - } - - if len(errors) > 0 { - return GetAddrInfoDnsResolverConfigMultiError(errors) - } - - return nil -} - -// GetAddrInfoDnsResolverConfigMultiError is an error wrapping multiple -// validation errors returned by GetAddrInfoDnsResolverConfig.ValidateAll() if -// the designated constraints aren't met. -type GetAddrInfoDnsResolverConfigMultiError []error - -// Error returns a concatenation of all the error messages it wraps. -func (m GetAddrInfoDnsResolverConfigMultiError) Error() string { - msgs := make([]string, 0, len(m)) - for _, err := range m { - msgs = append(msgs, err.Error()) - } - return strings.Join(msgs, "; ") -} - -// AllErrors returns a list of validation violation errors. -func (m GetAddrInfoDnsResolverConfigMultiError) AllErrors() []error { return m } - -// GetAddrInfoDnsResolverConfigValidationError is the validation error returned -// by GetAddrInfoDnsResolverConfig.Validate if the designated constraints -// aren't met. -type GetAddrInfoDnsResolverConfigValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e GetAddrInfoDnsResolverConfigValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e GetAddrInfoDnsResolverConfigValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e GetAddrInfoDnsResolverConfigValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e GetAddrInfoDnsResolverConfigValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e GetAddrInfoDnsResolverConfigValidationError) ErrorName() string { - return "GetAddrInfoDnsResolverConfigValidationError" -} - -// Error satisfies the builtin error interface -func (e GetAddrInfoDnsResolverConfigValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sGetAddrInfoDnsResolverConfig.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = GetAddrInfoDnsResolverConfigValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = GetAddrInfoDnsResolverConfigValidationError{} diff --git a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver_vtproto.pb.go b/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver_vtproto.pb.go deleted file mode 100644 index 5c11edf1e..000000000 --- a/vendor/github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver_vtproto.pb.go +++ /dev/null @@ -1,91 +0,0 @@ -//go:build vtprotobuf -// +build vtprotobuf - -// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// source: envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.proto - -package getaddrinfov3 - -import ( - protohelpers "github.com/planetscale/vtprotobuf/protohelpers" - wrapperspb "github.com/planetscale/vtprotobuf/types/known/wrapperspb" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -func (m *GetAddrInfoDnsResolverConfig) MarshalVTStrict() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetAddrInfoDnsResolverConfig) MarshalToVTStrict(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVTStrict(dAtA[:size]) -} - -func (m *GetAddrInfoDnsResolverConfig) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.NumResolverThreads != nil { - size, err := (*wrapperspb.UInt32Value)(m.NumResolverThreads).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x12 - } - if m.NumRetries != nil { - size, err := (*wrapperspb.UInt32Value)(m.NumRetries).MarshalToSizedBufferVTStrict(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *GetAddrInfoDnsResolverConfig) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.NumRetries != nil { - l = (*wrapperspb.UInt32Value)(m.NumRetries).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - if m.NumResolverThreads != nil { - l = (*wrapperspb.UInt32Value)(m.NumResolverThreads).SizeVT() - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) - } - n += len(m.unknownFields) - return n -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0454f7a91..f4bfd1002 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -293,7 +293,6 @@ github.com/envoyproxy/go-control-plane/envoy/annotations github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3 github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3 github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3 -github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3 github.com/envoyproxy/go-control-plane/envoy/config/common/matcher/v3 github.com/envoyproxy/go-control-plane/envoy/config/common/mutation_rules/v3 github.com/envoyproxy/go-control-plane/envoy/config/core/v3 @@ -308,10 +307,7 @@ github.com/envoyproxy/go-control-plane/envoy/config/trace/v3 github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3 -github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3 -github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3 -github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3 @@ -324,7 +320,6 @@ github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/pick_first/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/wrr_locality/v3 -github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/getaddrinfo/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/rbac/audit_loggers/stream/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3 github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3