Securing Gateways with HTTPS
The Control Ingress Traffic task describes how to configure an ingress gateway to expose an HTTP endpoint of a service to external traffic. This task extends that task to enable HTTPS access to the service using either simple or mutual TLS.
Before you begin
Perform the steps in the Before you begin and Determining the ingress IP and ports sections of the Control Ingress Traffic task. After performing those steps you should have Istio and the httpbin service deployed, and the environment variables
INGRESS_HOSTandSECURE_INGRESS_PORTset.For macOS users, verify that you use curl compiled with the LibreSSL library:
$ curl --version | grep LibreSSL curl 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0If a version of LibreSSL is printed as in the output above, your curl should work correctly with the instructions in this task. Otherwise, try another installation of curl, for example on a Linux machine.
Generate client and server certificates and keys
For this task you can use your favorite tool to generate certificates and keys. This example uses a script from the https://github.com/nicholasjackson/mtls-go-example repository.
Clone the https://github.com/nicholasjackson/mtls-go-example repository:
$ git clone https://github.com/nicholasjackson/mtls-go-exampleChange directory to the cloned repository:
$ pushd mtls-go-exampleGenerate the certificates for
httpbin.example.com. Use any password with the following command:$ ./generate.sh httpbin.example.com <password>When prompted, select
yfor all the questions. The command will generate four directories:1_root,2_intermediate,3_application, and4_clientcontaining the client and server certificates you use in the procedures below.Move the certificates into
httpbin.example.comdirectory:$ mkdir ~+1/httpbin.example.com && mv 1_root 2_intermediate 3_application 4_client ~+1/httpbin.example.comChange directory back:
$ popd
Configure a TLS ingress gateway
In this subsection you configure an ingress gateway with port 443 to handle HTTPS traffic. You first create a secret
with a certificate and a private key. Then you create a Gateway definition that contains a server on port 443.
Create a Kubernetes
Secretto hold the server's certificate and private key. Usekubectlto create the secretistio-ingressgateway-certsin namespaceistio-system. The Istio gateway will load the secret automatically.The secret must be called
istio-ingressgateway-certsin theistio-systemnamespace, or it will not be mounted and available to the Istio gateway.$ kubectl create -n istio-system secret tls istio-ingressgateway-certs --key httpbin.example.com/3_application/private/httpbin.example.com.key.pem --cert httpbin.example.com/3_application/certs/httpbin.example.com.cert.pem secret "istio-ingressgateway-certs" createdNote that by default all the service accounts in the
istio-systemnamespace can access this secret, so the private key can be leaked. You can change the Role-Based Access Control (RBAC) rules to protect it.Define a
Gatewaywith aserversection for port 443.The location of the certificate and the private key must be
/etc/istio/ingressgateway-certs, or the gateway will fail to load them.cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mygateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: SIMPLE serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key hosts: - "httpbin.example.com" EOFConfigure routes for traffic entering via the
Gateway. Define the sameVirtualServiceas in the Control Ingress Traffic task:cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - "httpbin.example.com" gateways: - mygateway http: - match: - uri: prefix: /status - uri: prefix: /delay route: - destination: port: number: 8000 host: httpbin EOFAccess the
httpbinservice with HTTPS by sending anhttpsrequest using curl toSECURE_INGRESS_PORT.The
--resolveflag instructs curl to supply the SNI valuehttpbin.example.comwhen accessing the gateway IP over TLS. The--cacertoption instructs curl to use your generated certificate to verify the server.By sending the request to the
/status/418URL path, you get a nice visual clue that yourhttpbinservice was indeed accessed. Thehttpbinservice will return the 418 I'm a Teapot code.$ curl -v -HHost:httpbin.example.com --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 ... Server certificate: subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=httpbin.example.com start date: Jun 24 18:45:18 2018 GMT expire date: Jul 4 18:45:18 2019 GMT common name: httpbin.example.com (matched) issuer: C=US; ST=Denial; O=Dis; CN=httpbin.example.com SSL certificate verify ok. ... HTTP/2 418 ... -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`It might take time for the gateway definition to propagate so you might get the following error:
Failed to connect to httpbin.example.com port <your secure port>: Connection refused. Wait for a minute and retry the curl call.Look for the Server certificate section in the curl output and note the line about matching the common name:
common name: httpbin.example.com (matched). According to the lineSSL certificate verify okin the output of curl, you can be sure that the server's certificate was verified successfully. Note the returned status of 418 and a nice drawing of a teapot.
If you need to support mutual TLS proceed to the next section.
Configure a mutual TLS ingress gateway
In this section you extend your gateway's definition from the previous section to support mutual TLS between external clients and the gateway.
Create a Kubernetes
Secretto hold the CA certificate that the server will use to verify its clients. Create the secretistio-ingressgateway-ca-certsin namespaceistio-systemusingkubectl. The Istio gateway will automatically load the secret.The secret must be called
istio-ingressgateway-ca-certsin theistio-systemnamespace, or it will not be mounted and available to the Istio gateway.$ kubectl create -n istio-system secret generic istio-ingressgateway-ca-certs --from-file=httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem secret "istio-ingressgateway-ca-certs" createdRedefine your previous
Gatewayto change thetlsmodetoMUTUALand specifyingcaCertificates:The location of the certificate must be
/etc/istio/ingressgateway-ca-certs, or the gateway will fail to load them. The file name of the certificate must be identical to the filename you create the secret from, in this caseca-chain.cert.pem.cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mygateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: MUTUAL serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key caCertificates: /etc/istio/ingressgateway-ca-certs/ca-chain.cert.pem hosts: - "httpbin.example.com" EOFAccess the
httpbinservice by HTTPS as in the previous section:$ curl -HHost:httpbin.example.com --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 curl: (35) error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failureIt might take time for the gateway definition to propagate so you might still get 418. Wait for a minute and retry the curl call.
This time you get an error since the server refuses to accept unauthenticated requests. You have to send a client certificate and pass curl your private key for signing the request.
Resend the previous request by curl, this time passing as parameters your client certificate (the
--certoption) and your private key (the--keyoption):$ curl -HHost:httpbin.example.com --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem --cert httpbin.example.com/4_client/certs/httpbin.example.com.cert.pem --key httpbin.example.com/4_client/private/httpbin.example.com.key.pem https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`This time the server performed client authentication successfully and you received the pretty teapot drawing again.
Configure a TLS ingress gateway for multiple hosts
In this section you will configure an ingress gateway for multiple hosts, httpbin.example.com and bookinfo.com. The ingress gateway will present the client the correct certificate according to the requested server.
Generate client and server certificates and keys for bookinfo.com
In this subsection, perform the same steps as in the Generate client and server certificates and keys subsection. I list them below for your convenience.
Change directory to the cloned repository:
$ pushd mtls-go-exampleGenerate the certificates for
bookinfo.com. Use any password with the following command:$ ./generate.sh bookinfo.com <password>When prompted, select
yfor all the questions.Move the certificates into
bookinfo.comdirectory:$ mkdir ~+1/bookinfo.com && mv 1_root 2_intermediate 3_application 4_client ~+1/bookinfo.comChange directory back:
$ popd
Redeploy istio-ingressgateway with the new certificates
Create a new secret to hold the certificate for
bookinfo.com$ kubectl create -n istio-system secret tls istio-ingressgateway-bookinfo-certs --key bookinfo.com/3_application/private/bookinfo.com.key.pem --cert bookinfo.com/3_application/certs/bookinfo.com.cert.pem secret "istio-ingressgateway-bookinfo-certs" createdGenerate the
istio-ingressgatewaydeployment with a volume to be mounted from the new secret. Use the same options you used for generating youristio.yaml:$ helm template install/kubernetes/helm/istio/ --name istio-ingressgateway --namespace istio-system -x charts/gateways/templates/deployment.yaml --set gateways.istio-egressgateway.enabled=false \ --set gateways.istio-ingressgateway.secretVolumes[0].name=ingressgateway-certs \ --set gateways.istio-ingressgateway.secretVolumes[0].secretName=istio-ingressgateway-certs \ --set gateways.istio-ingressgateway.secretVolumes[0].mountPath=/etc/istio/ingressgateway-certs \ --set gateways.istio-ingressgateway.secretVolumes[1].name=ingressgateway-ca-certs \ --set gateways.istio-ingressgateway.secretVolumes[1].secretName=istio-ingressgateway-ca-certs \ --set gateways.istio-ingressgateway.secretVolumes[1].mountPath=/etc/istio/ingressgateway-ca-certs \ --set gateways.istio-ingressgateway.secretVolumes[2].name=ingressgateway-bookinfo-certs \ --set gateways.istio-ingressgateway.secretVolumes[2].secretName=istio-ingressgateway-bookinfo-certs \ --set gateways.istio-ingressgateway.secretVolumes[2].mountPath=/etc/istio/ingressgateway-bookinfo-certs > \ $HOME/istio-ingressgateway.yamlRedeploy
istio-ingressgateway:$ kubectl apply -f $HOME/istio-ingressgateway.yaml deployment "istio-ingressgateway" configuredVerify that the key and the certificate are successfully loaded in the
istio-ingressgatewaypod:$ kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-bookinfo-certstls.crtandtls.keyshould exist in the directory contents.
Configure traffic for the bookinfo.com host
Deploy the Bookinfo sample application, without a gateway:
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yamlRedeploy the
Gatewaydefinition with a host forbookinfo.com:cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mygateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https-httpbin protocol: HTTPS tls: mode: SIMPLE serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key hosts: - "httpbin.example.com" - port: number: 443 name: https-bookinfo protocol: HTTPS tls: mode: SIMPLE serverCertificate: /etc/istio/ingressgateway-bookinfo-certs/tls.crt privateKey: /etc/istio/ingressgateway-bookinfo-certs/tls.key hosts: - "bookinfo.com" EOFConfigure the routes for
bookinfo.com. Define aVirtualServicesimilarly to the one insamples/bookinfo/networking/bookinfo-gateway.yaml:cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "bookinfo.com" gateways: - mygateway http: - match: - uri: exact: /productpage - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080 EOFSend a request to the Bookinfo
productpage:$ curl -o /dev/null -s -v -w "%{http_code}\n" --resolve bookinfo.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert bookinfo.com/2_intermediate/certs/ca-chain.cert.pem -HHost:bookinfo.com https://bookinfo.com:$SECURE_INGRESS_PORT/productpage ... Server certificate: subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=bookinfo.com start date: Aug 12 13:50:05 2018 GMT expire date: Aug 22 13:50:05 2019 GMT common name: bookinfo.com (matched) issuer: C=US; ST=Denial; O=Dis; CN=bookinfo.com SSL certificate verify ok. ... 200Verify that
httbin.example.comis accessible as previously. Send a request to it and see again the teapot you should already love:$ curl -v --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem -HHost:httpbin.example.com https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 ... -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`
Configure end-user authentication on ingress gateway
To support end-user authentication, the Istio ingress gateway sets up a JWT
authentication policy in the istio-ingressgateway file. The ingress gateway
rejects the unauthenticated requests and the request can't access the services
inside the mesh. This section shows how to use the authentication policy to
setup the end-user authentication for the Istio ingress gateway.
To verify the setup, run the following curl command and confirm a return value of 200:
$ curl -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/status/200Add the policy requiring the end-user JWT authentication for the
istio-ingressgatewayservice.cat <<EOF | kubectl apply -f - apiVersion: "authentication.istio.io/v1alpha1" kind: "Policy" metadata: name: "ingressgateway" namespace: istio-system spec: targets: - name: istio-ingressgateway origins: - jwt: issuer: "testing@secure.istio.io" jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.0/security/tools/jwt/samples/jwks.json" principalBinding: USE_ORIGIN EOFRun the following curl command:
$ curl -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/status/200Note the return value of 401. Istio returns this error code value because the server expects a JWT but the ingress gateway did not provide one:
$ HTTP/1.1 401 Unauthorized content-length: 29 content-type: text/plain date: Mon, 13 Aug 2018 22:33:32 GMT server: envoyRun the following curl command with the valid JWT:
$ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.0/security/tools/jwt/samples/demo.jwt -s) $ curl --header "Authorization: Bearer $TOKEN" -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/status/200Note the return value of 200. Istio returns a successful code because the request had the valid JWT attached:
$ HTTP/1.1 200 OK server: envoy date: Mon, 13 Aug 2018 22:37:10 GMT content-type: text/html; charset=utf-8 access-control-allow-origin: * access-control-allow-credentials: true content-length: 0 x-envoy-upstream-service-time: 10
Troubleshooting
Inspect the values of the
INGRESS_HOSTandSECURE_INGRESS_PORTenvironment variables. Make sure they have valid values, according to the output of the following commands:$ kubectl get svc -n istio-system $ echo INGRESS_HOST=$INGRESS_HOST, SECURE_INGRESS_PORT=$SECURE_INGRESS_PORTVerify that the key and the certificate are successfully loaded in the
istio-ingressgatewaypod:$ kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-certstls.crtandtls.keyshould exist in the directory contents.Verify that the Subject is correct in the certificate of the ingress gateway:
$ kubectl exec -i -n istio-system $(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].metadata.name}') -- cat /etc/istio/ingressgateway-certs/tls.crt | openssl x509 -text -noout | grep 'Subject:' Subject: C=US, ST=Denial, L=Springfield, O=Dis, CN=httpbin.example.comVerify that the proxy of the ingress gateway is aware of the certificates:
$ kubectl exec -ti $(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath={.items[0]..metadata.name}) -n istio-system -- curl 127.0.0.1:15000/certs { "ca_cert": "", "cert_chain": "Certificate Path: /etc/istio/ingressgateway-certs/tls.crt, Serial Number: 100212, Days until Expiration: 370" }Check the log of
istio-ingressgatewayfor error messages:$ kubectl logs -n istio-system -l istio=ingressgatewayIf the secret was created but the keys were not mounted, kill the ingress gateway pod and force it to reload certs:
$ kubectl delete pod -n istio-system -l istio=ingressgatewayFor macOS users, verify that you use curl compiled with the LibreSSL library, as described in the Before you begin section.
Troubleshooting for mutual TLS
In addition to the steps in the previous section, perform the following:
Verify that the CA certificate is loaded in the
istio-ingressgatewaypod:$ kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-ca-certsca-chain.cert.pemshould exist in the directory contents.Verify that the Subject is correct in the CA certificate of the ingress gateway:
$ kubectl exec -i -n istio-system $(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].metadata.name}') -- cat /etc/istio/ingressgateway-ca-certs/ca-chain.cert.pem | openssl x509 -text -noout | grep 'Subject:' Subject: C=US, ST=Denial, L=Springfield, O=Dis, CN=httpbin.example.comIf the secret was created but the keys were not mounted, kill the ingress gateway pod and force it to reload certs:
$ kubectl delete pod -n istio-system -l istio=ingressgateway
Cleanup
Delete the
Gatewayconfiguration, theVirtualService, and the secrets:$ kubectl delete gateway mygateway $ kubectl delete virtualservice httpbin $ kubectl delete --ignore-not-found=true -n istio-system secret istio-ingressgateway-certs istio-ingressgateway-ca-certs $ kubectl delete --ignore-not-found=true virtualservice bookinfoDelete the directories of the certificates and the repository used to generate them:
$ rm -rf httpbin.example.com bookinfo.com mtls-go-exampleRemove the file you used for redeployment of
istio-ingressgateway:$ rm -f $HOME/istio-ingressgateway.yamlShutdown the httpbin service:
$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@
See also
Deploy a custom ingress gateway using cert-manager
Describes how to deploy a custom ingress gateway using cert-manager manually.
Configuring Istio Ingress with AWS NLB
Describes how to configure Istio ingress with a network load balancer on AWS.
Describes how to configure Istio to expose a service outside of the service mesh.
Incremental Istio Part 1, Traffic Management
How to use Istio for traffic management without deploying sidecar proxies.
Introducing the Istio v1alpha3 routing API
Introduction, motivation and design principles for the Istio v1alpha3 routing API.
Traffic Mirroring with Istio for Testing in Production
An introduction to safer, lower-risk deployments and release to production.