OSM provides the option to use Contour ingress controller and Envoy based edge proxy to route external traffic to service mesh backends. This guide will demonstrate how to configure HTTP and HTTPS ingress to a service part of an OSM managed service mesh.

Prerequisites

  • Kubernetes cluster running Kubernetes v1.19.0 or greater.
  • Have kubectl available to interact with the API server.
  • No existing installation of OSM. Any existing installation must first be uninstalled prior to proceeding with this demo.
  • Have osm or Helm 3 CLI available for installing OSM and Contour.
  • OSM version >= v0.10.0.

Demo

First, we will install OSM and Contour as in the osm-system namespace and name the mesh name as osm.

  1. export osm_namespace=osm-system # Replace osm-system with the namespace where OSM will be installed
  2. export osm_mesh_name=osm # Replace osm with the desired OSM mesh name

If using osm CLI:

  1. osm install --set contour.enabled=true \
  2. --mesh-name "$osm_mesh_name" \
  3. --osm-namespace "$osm_namespace" \
  4. --set contour.configInline.tls.envoy-client-certificate.name=osm-contour-envoy-client-cert \
  5. --set contour.configInline.tls.envoy-client-certificate.namespace="$osm_namespace"

If using Helm:

  1. helm install "$osm_mesh_name" osm --repo https://openservicemesh.github.io/osm \
  2. --set contour.enabled=true \
  3. --set contour.configInline.tls.envoy-client-certificate.name=osm-contour-envoy-client-cert \
  4. --set contour.configInline.tls.envoy-client-certificate.namespace="$osm_namespace"

To restrict ingress traffic on backends to authorized clients, we will set up the IngressBackend configuration such that only ingress traffic from the endpoints of the osm-contour-envoy service can route traffic to the service backend. To be able to discover the endpoints of osm-contour-envoy service, we need OSM controller to monitor the corresponding namespace. However, Contour must NOT be injected with an Envoy sidecar to function properly.

  1. kubectl label namespace "$osm_namespace" openservicemesh.io/monitored-by="$osm_mesh_name"

Save the ingress gateway’s external IP address and port which we will later use to test access to the backend application:

  1. export ingress_host="$(kubectl -n "$osm_namespace" get service osm-contour-envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
  2. export ingress_port="$(kubectl -n "$osm_namespace" get service osm-contour-envoy -o jsonpath='{.spec.ports[?(@.name=="http")].port}')"

Next, we will deploy the sample httpbin service.

  1. # Create a namespace
  2. kubectl create ns httpbin
  3. # Add the namespace to the mesh
  4. osm namespace add httpbin
  5. # Deploy the application
  6. kubectl apply -f https://raw.githubusercontent.com/openservicemesh/osm-docs/release-v1.0/manifests/samples/httpbin/httpbin.yaml -n httpbin

Confirm the httpbin service and pod is up and running:

  1. $ kubectl get pods -n httpbin
  2. NAME READY STATUS RESTARTS AGE
  3. httpbin-74677b7df7-zzlm2 2/2 Running 0 11h
  4. $ kubectl get svc -n httpbin
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. httpbin ClusterIP 10.0.22.196 <none> 14001/TCP 11h

HTTP Ingress

Next, we will create the HTTPProxy and IngressBackend configurations necessary to allow external clients to access the httpbin service on port 14001 in the httpbin namespace. The connection from the Contour’s ingress gateway to the httpbin backend pod will be unencrypted since we aren’t using TLS.

  1. kubectl apply -f - <<EOF
  2. apiVersion: projectcontour.io/v1
  3. kind: HTTPProxy
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. spec:
  8. virtualhost:
  9. fqdn: httpbin.org
  10. routes:
  11. - services:
  12. - name: httpbin
  13. port: 14001
  14. ---
  15. kind: IngressBackend
  16. apiVersion: policy.openservicemesh.io/v1alpha1
  17. metadata:
  18. name: httpbin
  19. namespace: httpbin
  20. spec:
  21. backends:
  22. - name: httpbin
  23. port:
  24. number: 14001 # targetPort of httpbin service
  25. protocol: http
  26. sources:
  27. - kind: Service
  28. namespace: "$osm_namespace"
  29. name: osm-contour-envoy
  30. EOF

Now, we expect external clients to be able to access the httpbin service for HTTP requests for the Host: header httpbin.org:

  1. $ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"
  2. HTTP/1.1 200 OK
  3. server: envoy
  4. date: Fri, 06 Aug 2021 17:39:43 GMT
  5. content-type: application/json
  6. content-length: 314
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. x-envoy-upstream-service-time: 3
  10. vary: Accept-Encoding

HTTPS Ingress (mTLS and TLS)

To proxy connections to TLS backends using HTTPS, the backend service must be annotated with the port as follows:

  1. kubectl annotate service httpbin -n httpbin projectcontour.io/upstream-protocol.tls='14001' --overwrite

Next, we need to create an HTTPProxy configuration to use TLS proxying to the backend service, and providing a CA certificate to validate the server certificate presented by the backend service. For this to work, we need to first delegate to Contour the permission to read OSM’s CA certificate secret from the OSM’s namespace when referenced in the HTTPProxy configuration in the httpbin namespace. Refer to the Upstream TLS section to learn more about upstream certificate validation and when certificate delegation is necessary. In addition, we must create an IngressBackend resource that specifies HTTPS ingress traffic directed to the httpbin service must only accept traffic from a trusted client, osm-contour-envoy in the ingress edge proxy we deployed. OSM automatically provisioned a client certificate for the osm-contour-envoy ingress gateway with the Subject Alternative Name (SAN) osm-contour-envoy.$osm_namespace.cluster.local during install, so the IngressBackend configuration needs to reference the same SAN for mTLS authentication between the osm-contour-envoy edge and the httpbin backend.

Note: <osm-namespace> refers to the namespace where the osm control plane is installed.

Apply the configurations:

  1. kubectl apply -f - <<EOF
  2. apiVersion: projectcontour.io/v1
  3. kind: TLSCertificateDelegation
  4. metadata:
  5. name: ca-secret
  6. namespace: "$osm_namespace"
  7. spec:
  8. delegations:
  9. - secretName: osm-ca-bundle
  10. targetNamespaces:
  11. - httpbin
  12. ---
  13. apiVersion: projectcontour.io/v1
  14. kind: HTTPProxy
  15. metadata:
  16. name: httpbin
  17. namespace: httpbin
  18. spec:
  19. virtualhost:
  20. fqdn: httpbin.org
  21. routes:
  22. - services:
  23. - name: httpbin
  24. port: 14001
  25. validation:
  26. caSecret: "$osm_namespace/osm-ca-bundle"
  27. # subjectName for a service is of the form <service-account>.<namespace>.cluster.local
  28. # where the service account and namespace is that of the pod backing the service
  29. subjectName: httpbin.httpbin.cluster.local
  30. ---
  31. kind: IngressBackend
  32. apiVersion: policy.openservicemesh.io/v1alpha1
  33. metadata:
  34. name: httpbin
  35. namespace: httpbin
  36. spec:
  37. backends:
  38. - name: httpbin
  39. port:
  40. number: 14001 # targetPort of httpbin service
  41. protocol: https
  42. tls:
  43. skipClientCertValidation: false # mTLS (defaults to false)
  44. sources:
  45. - kind: Service
  46. namespace: "$osm_namespace"
  47. name: osm-contour-envoy
  48. - kind: AuthenticatedPrincipal
  49. name: "osm-contour-envoy.$osm_namespace.cluster.local"
  50. EOF

Now, we expect external clients to be able to access the httpbin service for HTTP requests for the Host: header httpbin.org with HTTPS proxying over mTLS between the ingress gateway and service backend:

  1. $ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"
  2. HTTP/1.1 200 OK
  3. server: envoy
  4. date: Fri, 06 Aug 2021 17:39:43 GMT
  5. content-type: application/json
  6. content-length: 314
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. x-envoy-upstream-service-time: 3
  10. vary: Accept-Encoding

To verify that unauthorized clients are not allowed to access the backend, we can update the sources specified in the IngressBackend configuration. Let’s update the principal to something other than the SAN encoded in the ingress gateway’s certificate.

  1. kubectl apply -f - <<EOF
  2. kind: IngressBackend
  3. apiVersion: policy.openservicemesh.io/v1alpha1
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. spec:
  8. backends:
  9. - name: httpbin
  10. port:
  11. number: 14001 # targetPort of httpbin service
  12. protocol: https
  13. tls:
  14. skipClientCertValidation: false # mTLS (defaults to false)
  15. sources:
  16. - kind: Service
  17. namespace: "$osm_namespace"
  18. name: osm-contour-envoy
  19. - kind: AuthenticatedPrincipal
  20. name: "untrusted-client.cluster.local"
  21. EOF

Confirm the requests are rejected with an HTTP 403 Forbidden response:

  1. $ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"
  2. HTTP/1.1 403 Forbidden
  3. content-length: 19
  4. content-type: text/plain
  5. date: Fri, 06 Aug 2021 18:40:45 GMT
  6. server: envoy
  7. x-envoy-upstream-service-time: 8

Next, we demonstrate support for disabling client certificate validation on the service backend if necessary, by updating our IngressBackend configuration to set skipClientCertValidation: true, while still using an untrusted client:

  1. kubectl apply -f - <<EOF
  2. kind: IngressBackend
  3. apiVersion: policy.openservicemesh.io/v1alpha1
  4. metadata:
  5. name: httpbin
  6. namespace: httpbin
  7. spec:
  8. backends:
  9. - name: httpbin
  10. port:
  11. number: 14001 # targetPort of httpbin service
  12. protocol: https
  13. tls:
  14. skipClientCertValidation: true
  15. sources:
  16. - kind: Service
  17. namespace: "$osm_namespace"
  18. name: osm-contour-envoy
  19. - kind: AuthenticatedPrincipal
  20. name: "untrusted-client.cluster.local"
  21. EOF

Confirm the requests succeed again since untrusted authenticated principals are allowed to connect to the backend:

  1. $ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"
  2. HTTP/1.1 200 OK
  3. server: envoy
  4. date: Fri, 06 Aug 2021 18:51:47 GMT
  5. content-type: application/json
  6. content-length: 314
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. x-envoy-upstream-service-time: 4
  10. vary: Accept-Encoding