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
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
orHelm 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
.
export osm_namespace=osm-system # Replace osm-system with the namespace where OSM will be installed
export 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 namespace
kubectl create ns httpbin
# Add the namespace to the mesh
osm namespace add httpbin
# Deploy the application
kubectl 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 httpbin
NAME READY STATUS RESTARTS AGE
httpbin-74677b7df7-zzlm2 2/2 Running 0 11h
$ kubectl get svc -n httpbin
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
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.
kubectl apply -f - <<EOF
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: httpbin
namespace: httpbin
spec:
virtualhost:
fqdn: httpbin.org
routes:
- services:
- name: httpbin
port: 14001
---
kind: IngressBackend
apiVersion: policy.openservicemesh.io/v1alpha1
metadata:
name: httpbin
namespace: httpbin
spec:
backends:
- name: httpbin
port:
number: 14001 # targetPort of httpbin service
protocol: http
sources:
- kind: Service
namespace: "$osm_namespace"
name: osm-contour-envoy
EOF
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 OK
server: envoy
date: Fri, 06 Aug 2021 17:39:43 GMT
content-type: application/json
content-length: 314
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3
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:
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 - <<EOF
apiVersion: projectcontour.io/v1
kind: TLSCertificateDelegation
metadata:
name: ca-secret
namespace: "$osm_namespace"
spec:
delegations:
- secretName: osm-ca-bundle
targetNamespaces:
- httpbin
---
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: httpbin
namespace: httpbin
spec:
virtualhost:
fqdn: httpbin.org
routes:
- services:
- name: httpbin
port: 14001
validation:
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 service
subjectName: httpbin.httpbin.cluster.local
---
kind: IngressBackend
apiVersion: policy.openservicemesh.io/v1alpha1
metadata:
name: httpbin
namespace: httpbin
spec:
backends:
- name: httpbin
port:
number: 14001 # targetPort of httpbin service
protocol: https
tls:
skipClientCertValidation: false # mTLS (defaults to false)
sources:
- kind: Service
namespace: "$osm_namespace"
name: osm-contour-envoy
- kind: AuthenticatedPrincipal
name: "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 OK
server: envoy
date: Fri, 06 Aug 2021 17:39:43 GMT
content-type: application/json
content-length: 314
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3
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.
kubectl apply -f - <<EOF
kind: IngressBackend
apiVersion: policy.openservicemesh.io/v1alpha1
metadata:
name: httpbin
namespace: httpbin
spec:
backends:
- name: httpbin
port:
number: 14001 # targetPort of httpbin service
protocol: https
tls:
skipClientCertValidation: false # mTLS (defaults to false)
sources:
- kind: Service
namespace: "$osm_namespace"
name: osm-contour-envoy
- kind: AuthenticatedPrincipal
name: "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 Forbidden
content-length: 19
content-type: text/plain
date: Fri, 06 Aug 2021 18:40:45 GMT
server: envoy
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:
kubectl apply -f - <<EOF
kind: IngressBackend
apiVersion: policy.openservicemesh.io/v1alpha1
metadata:
name: httpbin
namespace: httpbin
spec:
backends:
- name: httpbin
port:
number: 14001 # targetPort of httpbin service
protocol: https
tls:
skipClientCertValidation: true
sources:
- kind: Service
namespace: "$osm_namespace"
name: osm-contour-envoy
- kind: AuthenticatedPrincipal
name: "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 OK
server: envoy
date: Fri, 06 Aug 2021 18:51:47 GMT
content-type: application/json
content-length: 314
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 4
vary: Accept-Encoding