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.20.0 or greater.
- Have
kubectlavailable 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
osmorHelm 3CLI 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.
export osm_namespace=osm-system # Replace osm-system with the namespace where OSM will be installedexport osm_mesh_name=osm # Replace osm with the desired OSM mesh name
If using osm CLI:
osm install --set contour.enabled=true \--mesh-name "$osm_mesh_name" \--osm-namespace "$osm_namespace" \--set contour.configInline.tls.envoy-client-certificate.name=osm-contour-envoy-client-cert \--set contour.configInline.tls.envoy-client-certificate.namespace="$osm_namespace"
If using Helm:
helm install "$osm_mesh_name" osm --repo https://openservicemesh.github.io/osm \--set contour.enabled=true \--set contour.configInline.tls.envoy-client-certificate.name=osm-contour-envoy-client-cert \--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.
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:
export ingress_host="$(kubectl -n "$osm_namespace" get service osm-contour-envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"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.
# Create a namespacekubectl create ns httpbin# Add the namespace to the meshosm namespace add httpbin# Deploy the applicationkubectl apply -f https://raw.githubusercontent.com/openservicemesh/osm-docs/release-v1.1/manifests/samples/httpbin/httpbin.yaml -n httpbin
Confirm the httpbin service and pod is up and running:
$ kubectl get pods -n httpbinNAME READY STATUS RESTARTS AGEhttpbin-74677b7df7-zzlm2 2/2 Running 0 11h$ kubectl get svc -n httpbinNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEhttpbin 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.
kubectl apply -f - <<EOFapiVersion: projectcontour.io/v1kind: HTTPProxymetadata:name: httpbinnamespace: httpbinspec:virtualhost:fqdn: httpbin.orgroutes:- services:- name: httpbinport: 14001---kind: IngressBackendapiVersion: policy.openservicemesh.io/v1alpha1metadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpsources:- kind: Servicenamespace: "$osm_namespace"name: osm-contour-envoyEOF
Now, we expect external clients to be able to access the httpbin service for HTTP requests for the Host: header httpbin.org:
$ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"HTTP/1.1 200 OKserver: envoydate: Fri, 06 Aug 2021 17:39:43 GMTcontent-type: application/jsoncontent-length: 314access-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 3vary: 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:
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:
kubectl apply -f - <<EOFapiVersion: projectcontour.io/v1kind: TLSCertificateDelegationmetadata:name: ca-secretnamespace: "$osm_namespace"spec:delegations:- secretName: osm-ca-bundletargetNamespaces:- httpbin---apiVersion: projectcontour.io/v1kind: HTTPProxymetadata:name: httpbinnamespace: httpbinspec:virtualhost:fqdn: httpbin.orgroutes:- services:- name: httpbinport: 14001validation:caSecret: "$osm_namespace/osm-ca-bundle"# subjectName for a service is of the form <service-account>.<namespace>.cluster.local# where the service account and namespace is that of the pod backing the servicesubjectName: httpbin.httpbin.cluster.local---kind: IngressBackendapiVersion: policy.openservicemesh.io/v1alpha1metadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpstls:skipClientCertValidation: false # mTLS (defaults to false)sources:- kind: Servicenamespace: "$osm_namespace"name: osm-contour-envoy- kind: AuthenticatedPrincipalname: "osm-contour-envoy.$osm_namespace.cluster.local"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:
$ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"HTTP/1.1 200 OKserver: envoydate: Fri, 06 Aug 2021 17:39:43 GMTcontent-type: application/jsoncontent-length: 314access-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 3vary: 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.
kubectl apply -f - <<EOFkind: IngressBackendapiVersion: policy.openservicemesh.io/v1alpha1metadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpstls:skipClientCertValidation: false # mTLS (defaults to false)sources:- kind: Servicenamespace: "$osm_namespace"name: osm-contour-envoy- kind: AuthenticatedPrincipalname: "untrusted-client.cluster.local"EOF
Confirm the requests are rejected with an HTTP 403 Forbidden response:
$ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"HTTP/1.1 403 Forbiddencontent-length: 19content-type: text/plaindate: Fri, 06 Aug 2021 18:40:45 GMTserver: envoyx-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:
kubectl apply -f - <<EOFkind: IngressBackendapiVersion: policy.openservicemesh.io/v1alpha1metadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpstls:skipClientCertValidation: truesources:- kind: Servicenamespace: "$osm_namespace"name: osm-contour-envoy- kind: AuthenticatedPrincipalname: "untrusted-client.cluster.local"EOF
Confirm the requests succeed again since untrusted authenticated principals are allowed to connect to the backend:
$ curl -sI http://"$ingress_host":"$ingress_port"/get -H "Host: httpbin.org"HTTP/1.1 200 OKserver: envoydate: Fri, 06 Aug 2021 18:51:47 GMTcontent-type: application/jsoncontent-length: 314access-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 4vary: Accept-Encoding
