This guide will demonstrate how to configure HTTP and HTTPS ingress to a service part of an OSM managed service mesh when using Kubernetes Nginx Ingress Controller.
Prerequisites
- Kubernetes cluster running Kubernetes v1.20.0 or greater.
- Have
kubectlavailable to interact with the API server. - Have OSM version >= v0.10.0 installed.
- Have Kubernetes Nginx Ingress Controller installed. Refer to the deployment guide to install it.
Demo
First, note the details regarding OSM and Nginx installations:
osm_namespace=osm-system # Replace osm-system with the namespace where OSM is installedosm_mesh_name=osm # replace osm with the mesh name (use `osm mesh list` command)nginx_ingress_namespace=<nginx-namespace> # replace <nginx-namespace> with the namespace where Nginx is installednginx_ingress_service=<nginx-ingress-controller-service> # replace <nginx-ingress-controller-service> with the name of the nginx ingress controller servicenginx_ingress_host="$(kubectl -n "$nginx_ingress_namespace" get service "$nginx_ingress_service" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"nginx_ingress_port="$(kubectl -n "$nginx_ingress_namespace" get service "$nginx_ingress_service" -o jsonpath='{.spec.ports[?(@.name=="http")].port}')"
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 Nginx Ingress Controller service can route traffic to the service backend. To be able to discover the endpoints of this service, we need OSM controller to monitor the corresponding namespace. However, Nginx must NOT be injected with an Envoy sidecar to function properly.
osm namespace add "$nginx_ingress_namespace" --mesh-name "$osm_mesh_name" --disable-sidecar-injection
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 Ingress and IngressBackend configurations necessary to allow external clients to access the httpbin service on port 14001 in the httpbin namespace. The connection from the Nginx’s ingress service to the httpbin backend pod will be unencrypted since we aren’t using TLS.
kubectl apply -f - <<EOFapiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: httpbinnamespace: httpbinspec:ingressClassName: nginxrules:- http:paths:- path: /pathType: Prefixbackend:service:name: httpbinport:number: 14001---kind: IngressBackendapiVersion: policy.openservicemesh.io/v1alpha1metadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpsources:- kind: Servicenamespace: "$nginx_ingress_namespace"name: "$nginx_ingress_service"EOF
Now, we expect external clients to be able to access the httpbin service for HTTP requests:
$ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/getHTTP/1.1 200 OKDate: Wed, 18 Aug 2021 18:12:35 GMTContent-Type: application/jsonContent-Length: 366Connection: keep-aliveaccess-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 2
HTTPS Ingress (mTLS and TLS)
To proxy connections to HTTPS backends, we will configure the Ingress and IngressBackend configurations to use https as the backend protocol, and have OSM issue a certificate that Nginx will use as the client certificate to proxy HTTPS connections to TLS backends. The client certificate and CA certificate will be stored in a Kubernetes secret that Nginx will use to authenticate service mesh backends.
To issue a client certificate for the Nginx ingress service, update the osm-mesh-config MeshConfig resource.
kubectl edit meshconfig osm-mesh-config -n "$osm_namespace"
Add the ingressGateway field under spec.certificate:
certificate:ingressGateway:secret:name: osm-nginx-client-certnamespace: <osm-namespace> # replace <osm-namespace> with the namespace where OSM is installedsubjectAltNames:- ingress-nginx.ingress-nginx.cluster.localvalidityDuration: 24h
Note: The Subject Alternative Name (SAN) is of the form
<service-account>.<namespace>.cluster.local, where the service account and namespace correspond to the Ngnix service.
Next, we need to create an Ingress and IngressBackend configuration to use TLS proxying to the backend service, while enabling proxying to the backend over mTLS. For this to work, 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 provisioned a client certificate for the Nginx ingress service with the Subject ALternative Name (SAN) ingress-nginx.ingress-nginx.cluster.local, so the IngressBackend configuration needs to reference the same SAN for mTLS authentication between the Nginx ingress service and the httpbin backend.
Apply the configurations:
kubectl apply -f - <<EOFapiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: httpbinnamespace: httpbinannotations:nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"# proxy_ssl_name for a service is of the form <service-account>.<namespace>.cluster.localnginx.ingress.kubernetes.io/configuration-snippet: |proxy_ssl_name "httpbin.httpbin.cluster.local";nginx.ingress.kubernetes.io/proxy-ssl-secret: "osm-system/osm-nginx-client-cert"nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"spec:ingressClassName: nginxrules:- http:paths:- path: /pathType: Prefixbackend:service:name: httpbinport:number: 14001---apiVersion: policy.openservicemesh.io/v1alpha1kind: IngressBackendmetadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpstls:skipClientCertValidation: falsesources:- kind: Servicename: "$nginx_ingress_service"namespace: "$nginx_ingress_namespace"- kind: AuthenticatedPrincipalname: ingress-nginx.ingress-nginx.cluster.localEOF
Now, we expect external clients to be able to access the httpbin service for requests with HTTPS proxying over mTLS between the ingress gateway and service backend:
$ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/getHTTP/1.1 200 OKDate: Wed, 18 Aug 2021 18:12:35 GMTContent-Type: application/jsonContent-Length: 366Connection: keep-aliveaccess-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 2
To verify that unauthorized clients are not allowed to access the backend, update the sources specified in the IngressBackend configuration. Let’s update the principal to something other than the SAN encoded in the Nginx client’s certificate.
kubectl apply -f - <<EOFapiVersion: policy.openservicemesh.io/v1alpha1kind: IngressBackendmetadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpstls:skipClientCertValidation: falsesources:- kind: Servicename: "$nginx_ingress_service"namespace: "$nginx_ingress_namespace"- kind: AuthenticatedPrincipalname: untrusted-client.cluster.local # untrustedEOF
Confirm the requests are rejected with an HTTP 403 Forbidden response:
$ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/getHTTP/1.1 403 ForbiddenDate: Wed, 18 Aug 2021 18:36:09 GMTContent-Type: text/plainContent-Length: 19Connection: keep-alive
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 - <<EOFapiVersion: policy.openservicemesh.io/v1alpha1kind: IngressBackendmetadata:name: httpbinnamespace: httpbinspec:backends:- name: httpbinport:number: 14001 # targetPort of httpbin serviceprotocol: httpstls:skipClientCertValidation: truesources:- kind: Servicename: "$nginx_ingress_service"namespace: "$nginx_ingress_namespace"- kind: AuthenticatedPrincipalname: untrusted-client.cluster.local # untrustedEOF
Confirm the requests succeed again since untrusted authenticated principals are allowed to connect to the backend:
$ curl -sI http://"$nginx_ingress_host":"$nginx_ingress_port"/getHTTP/1.1 200 OKDate: Wed, 18 Aug 2021 18:36:49 GMTContent-Type: application/jsonContent-Length: 364Connection: keep-aliveaccess-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 2
