EndpointSlice external endpoint service

Hi,

I was trying to reach an external endpoint with EndpointSlice and was wondering if I need to tune my GatewayParameters to make it work?

I’m aware that ExternalName services are not supported by the Gateway API.

This example was working fine with Envoy Gateway (GatewayClass), however:

---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: airlock-microgateway
spec:
  controllerName: microgateway.airlock.com/gatewayclass-controller
---
apiVersion: v1
kind: Namespace
metadata:
  name: ergon-external-service
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg-ergon-external-service
  namespace: ergon-external-service
spec:
  infrastructure:
    parametersRef:
      group: microgateway.airlock.com
      kind: GatewayParameters
      name: gatewayparameters
  gatewayClassName: airlock-microgateway
  listeners:
    - name: http
      hostname: www.ergon.ch
      protocol: HTTP
      port: 4447
---
apiVersion: microgateway.airlock.com/v1alpha1
kind: GatewayParameters
metadata:
  name: gatewayparameters
  namespace: ergon-external-service
spec:
  logging:
    level: info
  kubernetes:
    service:
      type: LoadBalancer
      externalTrafficPolicy: Cluster
    deployment:
      replicas: 1
      placement: {}
      engineContainer: {}
      automountServiceAccountToken: false
---
apiVersion: v1
kind: Service
metadata:
  name: verapp
  namespace: ergon-external-service
spec:
  ports:
    - port: 443
      protocol: TCP
      targetPort: 443
      name: https
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: verapp
  namespace: ergon-external-service
  labels:
    kubernetes.io/service-name: verapp
addressType: FQDN
ports:
- name: https
  protocol: TCP
  port: 443
endpoints:
- addresses:
  - www.ergon.ch
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: verapp
  namespace: ergon-external-service
spec:
  parentRefs:
    - name: eg-ergon-external-service
  hostnames:
    - www.ergon.ch
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: verapp
          port: 443
          weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /
---
apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
  name: verapp-ext-btls
  namespace: ergon-external-service
spec:
  targetRefs:
  - group: ''
    kind: Service
    name: verapp
    sectionName: https
  validation:
    wellKnownCACertificates: System
    hostname: www.ergon.ch

The request:

curl -I -H "Host: www.ergon.ch" --resolve "www.ergon.ch:4447:127.0.0.1" http://www.ergon.ch:4447
HTTP/1.1 503 Service Unavailable
content-length: 51
content-type: text/plain
x-frame-options: SAMEORIGIN
strict-transport-security: max-age=31536000
content-security-policy: default-src 'self'; img-src *
x-content-type-options: nosniff
referrer-policy: same-origin
permissions-policy: accelerometer=(self), ambient-light-sensor=(self), autoplay=(self), camera=(self), display-capture=(self), document-domain=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(self), midi=(self), payment=(self), usb=(self), xr-spatial-tracking=(self)
date: Wed, 18 Jun 2025 18:11:44 GMT

The logs show upstream_reset_before_response_started{remote_connection_failure|delayed_connect_error:_Connection_refused:

{"network":{"forwarded_ip":"10.244.0.1"},"@timestamp":"2025-06-18T18:11:45.326+0000","log":{"level":"info","logger":"access"},"destination":{"port":4447,"ip":"10.244.0.33"},"event":{"module":"envoy","end":"2025-06-18T18:11:45.329+0000","category":["web"],"outcome":"success","dataset":"envoy.access","start":"2025-06-18T18:11:45.326+0000","kind":"event","type":["access","error"],"duration":3285833},"source":{"ip":"10.244.0.1","port":40603},"ecs":{"version":"8.5"},"airlock":{"summary":{"flags":"UF","details":"upstream_reset_before_response_started{remote_connection_failure|delayed_connect_error:_Connection_refused}","action":"error"},"log_correlation":{"connection_id":13,"stream_id":"16011475674719698354"}},"http":{"response":{"bytes":591,"mime_type":"text/plain","status_code":503,"body":{"bytes":0}},"request":{"id":"e06c70f2-376c-9523-915a-fb6d85225750","bytes":187,"body":{"bytes":0},"method":"HEAD"},"version":"1.1"},"observer":{"product":"Airlock Microgateway","vendor":"Ergon Informatik AG","version":"4.6.0","type":"waap"},"url":{"domain":"www.ergon.ch","path":"/"},"user_agent":{"original":"curl/8.7.1"}}

Thank you for your input.

My software:

  • minikube with Kubernetes v1.33.1
  • MicroGateway 4.6.0

Best,
Andreas

Hi Andreas

Thank you for your detailed description. This helps a lot to understand your setup and what you try to achieve.

Protecting applications outside the K8s cluster is currently only possible with service type ExternalName. Could you try whether this works?

The Gateway API documentation states that this “SHOULD NOT”, but not “MUST NOT” be supported.

We are aware of this issue and think about a better solution for the future. In the meantime, it would be great if you could check if it works and provide us feedback on that.

Cheers
Stefan

1 Like

Hi @dieti

Many thanks for the fast response.

Indeed, I found it’s well supported using ExternalName.

curl -I -H "Host: www.ergon.ch" --resolve "www.ergon.ch:4447:127.0.0.1" http://www.ergon.ch:4447/error_path/404.php
HTTP/1.1 404 Not Found
cache-control: no-cache
pragma: no-cache
content-type: text/html
date: Thu, 19 Jun 2025 07:55:01 GMT
x-envoy-upstream-service-time: 31
x-frame-options: SAMEORIGIN
strict-transport-security: max-age=31536000
content-security-policy: default-src 'self'; img-src *
x-content-type-options: nosniff
referrer-policy: same-origin
permissions-policy: accelerometer=(self), ambient-light-sensor=(self), autoplay=(self), camera=(self), display-capture=(self), document-domain=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(self), midi=(self), payment=(self), usb=(self), xr-spatial-tracking=(self)
transfer-encoding: chunked

404 code is expected in this case.

The diff:

+++ b/manifests/external-service-ergon.yaml
@@ -53,27 +53,8 @@ metadata:
   name: verapp
   namespace: ergon-external-service
 spec:
-  ports:
-    - port: 443
-      protocol: TCP
-      targetPort: 443
-      name: https
----
-apiVersion: discovery.k8s.io/v1
-kind: EndpointSlice
-metadata:
-  name: verapp
-  namespace: ergon-external-service
-  labels:
-    kubernetes.io/service-name: verapp
-addressType: FQDN
-ports:
-- name: https
-  protocol: TCP
-  port: 443
-endpoints:
-- addresses:
-  - www.ergon.ch
+  type: ExternalName
+  externalName: www.ergon.ch
 ---
 apiVersion: gateway.networking.k8s.io/v1
 kind: HTTPRoute
@@ -113,7 +94,6 @@ spec:
   - group: ''
     kind: Service
     name: verapp
-    sectionName: https
   validation:
     wellKnownCACertificates: System
     hostname: www.ergon.ch

I needed to remove the sectionName in the targetRef of the BackendTLSPolicy, because this was tied to the implementation of my EndpointSlice.

For sake of completeness, here the logs of the gateway:

{"destination":{"ip":"10.244.0.33","port":4447},"@timestamp":"2025-06-19T07:55:01.454+0000","event":{"category":["web"],"outcome":"success","dataset":"envoy.access","type":["access","allowed"],"end":"2025-06-19T07:55:01.487+0000","kind":"event","duration":32467958,"module":"envoy","start":"2025-06-19T07:55:01.454+0000"},"source":{"port":3955,"ip":"10.244.0.1"},"user_agent":{"original":"curl/8.7.1"},"http":{"version":"1.1","request":{"body":{"bytes":0},"method":"HEAD","id":"ff67b418-c82f-9cb8-902a-d2dfb28165ba","bytes":205},"response":{"body":{"bytes":0},"status_code":404,"bytes":640,"mime_type":"text/html"}},"network":{"forwarded_ip":"10.244.0.1"},"url":{"domain":"www.ergon.ch","path":"/error_path/404.php"},"ecs":{"version":"8.5"},"log":{"logger":"access","level":"info"},"observer":{"vendor":"Ergon Informatik AG","version":"4.6.0","product":"Airlock Microgateway","type":"waap"},"airlock":{"upstream":{"destination":{"port":443,"ip":"87.239.214.154"},"http":{"version":"2"}},"summary":{"action":"allowed","details":"via_upstream","flags":"-"},"log_correlation":{"stream_id":"16821168556541186107","connection_id":7961}}}

I’ll continue exploring the DenyRules/ContentSecurityPolicy and usage of upstream Envoy filters via EnvoyHTTPFilter now.

Thanks and have a good day.

Hi Andreas

Thank you for your confirmation and updated description.
I am glad that we could assist you in this matter.
Please feel free to reach out to us if you have any further enquiries.

Cheers
Stefan