Add secure ingress task (#1590)

This commit is contained in:
Vadim Eisenberg 2018-06-28 16:50:03 +03:00 committed by Martin Taillefer
parent 5a1b090fbc
commit 201cbd5928
5 changed files with 311 additions and 125 deletions

View File

@ -103,6 +103,7 @@ Kubernetes
L3-4
L4-L6
LabelDescription
LibreSSL
LoadBalancer
LoadBalancers
LoadBalancing.svg

View File

@ -53,11 +53,11 @@ Otherwise, ESP won't be able to access Google cloud service control.
EOF
```
1. Get the Ingress IP through [instructions](/docs/tasks/traffic-management/ingress/#verifying-the-gateway-for-http).
1. Get the Ingress IP and port by following the [instructions](/docs/tasks/traffic-management/ingress#determining-the-ingress-ip-and-ports).
You can verify accessing the Endpoints service through Ingress:
```command
$ curl --request POST --header "content-type:application/json" --data '{"message":"hello world"}' "http://${INGRESS_HOST}:80/echo?key=${ENDPOINTS_KEY}"i
$ curl --request POST --header "content-type:application/json" --data '{"message":"hello world"}' "http://${INGRESS_HOST}:${INGRESS_PORT}/echo?key=${ENDPOINTS_KEY}"i
```
## HTTPS Endpoints service using secured Ingress
@ -89,7 +89,7 @@ Adding `"--http_port=8081"` in the ESP deployment arguments and expose the HTTP
1. After this, you will find access to `EXTERNAL_IP` no longer works because istio proxy only accept secure mesh connections.
Accessing through Ingress works because Ingress does HTTP terminations.
1. To secure the access at Ingress, following the [instructions](/docs/tasks/traffic-management/ingress/#add-a-secure-port-https-to-our-gateway).
1. To secure the access at Ingress, follow the [instructions](/docs/tasks/traffic-management/secure-ingress/).
1. You can verify accessing the Endpoints service through secure Ingress:

View File

@ -37,11 +37,6 @@ This task describes how to configure Istio to expose a service outside of the se
$ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@)
```
* A private key and certificate can be created for testing using [OpenSSL](https://www.openssl.org/).
```command
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=httpbin.example.com"
```
* Determine the ingress IP and ports as described in the following subsection.
### Determining the ingress IP and ports
@ -120,9 +115,7 @@ but, unlike [Kubernetes Ingress Resources](https://kubernetes.io/docs/concepts/s
does not include any traffic routing configuration. Traffic routing for ingress traffic is instead configured
using Istio routing rules, exactly in the same was as for internal service requests.
In the following subsections we configure a `Gateway` on port 80 for unencrypted HTTP traffic first. Then we add a secure port 443 for HTTPS traffic.
### Configuring a gateway for HTTP
Let's see how you can configure a `Gateway` on port 80 for HTTP traffic.
1. Create an Istio `Gateway`
@ -184,8 +177,6 @@ In the following subsections we configure a `Gateway` on port 80 for unencrypted
but instead will simply default to round-robin routing. To apply these (or other rules) to internal calls,
we could add the special value `mesh` to the list of `gateways`.
### Verifying the gateway for HTTP
1. Access the _httpbin_ service using _curl_.
```command
@ -214,115 +205,6 @@ In the following subsections we configure a `Gateway` on port 80 for unencrypted
content-length: 0
```
### Add a secure port (HTTPS) to our gateway
In this subsection we add to our gateway the port 443 to handle the HTTPS traffic. We create a secret with a certificate and a private key. Then we replace the previous `Gateway` definition with a definition that contains a server on the port 443, in addition to the previously defined server on the port 80.
1. Create a Kubernetes `Secret` to hold the key/cert
Create the secret `istio-ingressgateway-certs` in namespace `istio-system` using `kubectl`. The Istio gateway
will automatically load the secret.
> The secret MUST be called `istio-ingressgateway-certs` in the `istio-system` namespace, or it will not
> be mounted and available to the Istio gateway.
```command
$ kubectl create -n istio-system secret tls istio-ingressgateway-certs --key /tmp/tls.key --cert /tmp/tls.crt
```
Note that by default all service accounts in the `istio-system` namespace can access this ingress key/cert,
which risks leaking the key/cert. You can change the Role-Based Access Control (RBAC) rules to protect them.
See (Link TBD) for details.
1. Add to the previous `Gateway` definition a server section for the 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.
```bash
cat <<EOF | istioctl replace -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "httpbin.example.com"
- 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"
EOF
```
### Verifying the gateway for HTTPS
1. Verify that our gateway still works for the port 80 and accepts unencrypted HTTP traffic as before. We do it by accessing the _httpbin_ service, port 80, as described in the [Verifying the gateway for HTTP](#verifying-the-gateway-for-http) subsection.
1. Access the _httpbin_ service with HTTPS by sending an https request using _curl_ to `SECURE_INGRESS_PORT`.
```command
$ curl -I -HHost:httpbin.example.com --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST -k https://httpbin.example.com:$SECURE_INGRESS_PORT/status/200
HTTP/2 200
server: envoy
date: Mon, 14 May 2018 13:54:53 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: 6
```
Note the `--resolve` flag that we use this time. Unlike HTTP requests, the `Host` header that we pass will be encrypted this time, so the ingress gateway will not be able to use it to match the request to our configuration. The `--resolve` flag instructs _curl_ to supply the [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) value "httpbin.example.com" when accessing the gateway IP over TLS. Here we also use _curl_'s `-k` option to instruct _curl_ not to check our certificate (since it is a fake certificate we created for testing the Gateway only, so _curl_ is not aware of it).
> It may take time for the new gateway definition to propagate and you may 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 again.
### Disable the HTTP port
If we want to only allow HTTPS traffic into our service mesh, we can remove the HTTP port from our gateway.
1. Redefine the `Gateway` without the HTTP port:
```bash
cat <<EOF | istioctl replace -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
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"
EOF
```
1. Access the HTTP port and verify that it is not accessible (an error is returned):
```command
$ curl -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/status/200
```
## Accessing ingress services using a browser
As you may have guessed, entering the httpbin service URL in a browser won't work because we don't have a way to tell the browser to pretend to be accessing "httpbin.example.com", like we did with _curl_. In a real world situation this wouldn't be a problem because the requested host would be properly configured and DNS resolvable, so we would simply be using its domain name in the URL (e.g., `https://httpbin.example.com/status/200`).
@ -376,7 +258,7 @@ Istio service mesh and make the traffic management and policy features of Istio
available for edge services.
In the preceding steps we created a service inside the Istio service mesh
and showed how to expose both HTTP and HTTPS endpoints of the service to
and showed how to expose an HTTP endpoint of the service to
external traffic.
## Cleanup
@ -386,7 +268,6 @@ Delete the `Gateway` configuration, the `VirtualService` and the secret, and shu
```command
$ istioctl delete gateway httpbin-gateway
$ istioctl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -n istio-system secret istio-ingressgateway-certs
$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@
```

View File

@ -0,0 +1,303 @@
---
title: Secure Ingress Traffic
description: Describes how to configure Istio to expose a service outside of the service mesh, over TLS or Mutual TLS.
weight: 31
keywords: [traffic-management,ingress]
---
> Note: This task uses the new [v1alpha3 traffic management API](/blog/2018/v1alpha3-routing/). The old API has been deprecated and will be removed in the next Istio release. If you need to use the old version, follow the docs [here](https://archive.istio.io/v0.7/docs/tasks/traffic-management/).
The [Control Ingress Traffic](/docs/tasks/traffic-management/ingress) task describes how to configure an ingress
gateway to enable TLS (non-mutual) traffic from outside the mesh into the mesh. This task extends that task to enable
Mutual TLS ingress traffic.
## Before you begin
1. Perform the steps in the [Before you begin](/docs/tasks/traffic-management/ingress#before-you-begin) and [Determining the ingress IP and ports](/docs/tasks/traffic-management/ingress#determining-the-ingress-ip-and-ports) sections of the
[Control Ingress Traffic](/docs/tasks/traffic-management/ingress) task. After performing those steps you should have Istio and _httpbin_ service deployed, and the environment variables `INGRESS_HOST` and `SECURE_INGRESS_PORT` set.
1. For macOS users, verify that you use _curl_ compiled with the [LibreSSL](http://www.libressl.org) library:
```command
$ 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.0
```
If a version of _LibreSSL_ is printed as in the output above, your _curl_ should work correctly with the
instructions in this task. Otherwise, try some other 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. We used [a script](https://github.com/nicholasjackson/mtls-go-example/blob/master/generate.sh)
from the https://github.com/nicholasjackson/mtls-go-example repository.
1. Clone the https://github.com/nicholasjackson/mtls-go-example repository:
```command
$ git clone https://github.com/nicholasjackson/mtls-go-example
```
1. Change directory to the cloned repository:
```command
$ cd mtls-go-example
```
1. Generate the certificates (use any password):
```command
$ generate.sh httpbin.example.com <password>
```
The command will generate four directories: `1_root`, `2_intermediate`, `3_application` and `4_client` with client
and server certificates you will use.
## Configure a TLS ingress gateway
In this subsection you configure an ingress gateway with the port 443 to handle HTTPS traffic. You create a secret
with a certificate and a private key. Then you create a `Gateway` definition that contains a `server` on the port 443.
1. Create a Kubernetes `Secret` to hold the server's certificate and private key. Use `kubectl` to create the secret
`istio-ingressgateway-certs` in namespace `istio-system` . The Istio gateway will load the secret automatically.
> The secret MUST be called `istio-ingressgateway-certs` in the `istio-system` namespace, or it will not
> be mounted and available to the Istio gateway.
```command
$ kubectl create -n istio-system secret tls istio-ingressgateway-certs --key 3_application/private/httpbin.example.com.key.pem --cert 3_application/certs/httpbin.example.com.cert.pem
secret "istio-ingressgateway-certs" created
```
Note that by default all the service accounts in the `istio-system` namespace can access this secret, so the private
key can be leaked. You can change the
[Role-Based Access Control (RBAC)](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) rules to protect
it.
1. Define a `Gateway` with a `server` section for the 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.
```bash
cat <<EOF | istioctl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
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"
EOF
```
1. Configure routes for traffic entering via the `Gateway`. Define the same `VirtualService` as in the [Control Ingress Traffic](/docs/tasks/traffic-management/ingress/#configuring-ingress-using-an-istio-gateway) task:
```bash
cat <<EOF | istioctl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "httpbin.example.com"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
EOF
```
1. Access the _httpbin_ service with HTTPS by sending an `https` request using _curl_ to `SECURE_INGRESS_PORT`.
The `--resolve` flag is used to instruct _curl_ to supply the
[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) value "httpbin.example.com" when accessing the gateway IP
over TLS. The `--cacert` option instructs _curl_ to use your generated certificate to verify the server.
By sending the request to the `/status/418` URL path, you get a nice visual clue that your _httpbin_ service was
indeed accessed. The _httpbin_ service will return the
[418 I'm a Teapot](https://tools.ietf.org/html/rfc7168#section-2.3.3) code.
```command
$ curl -v --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert 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 may take time for the gateway definition to propagate and you may 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 again.
Look for the _Server certificate_ section in the output of _curl_, note the line about matching the _common name_:
`common name: httpbin.example.com (matched)`. According to the line `SSL certificate verify ok` in 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](https://en.wikipedia.org/wiki/Mutual_authentication) 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](https://en.wikipedia.org/wiki/Mutual_authentication) between external clients and the gateway.
1. Create a Kubernetes `Secret` to hold the [CA](https://en.wikipedia.org/wiki/Certificate_authority) certificate that
the server will use to verify its clients. Create the secret `istio-ingressgateway-ca-certs` in namespace `istio-system`
using `kubectl`. The Istio gateway will automatically load the secret.
> The secret MUST be called `istio-ingressgateway-ca-certs` in the `istio-system` namespace, or it will not
> be mounted and available to the Istio gateway.
```command
$ kubectl create -n istio-system secret generic istio-ingressgateway-ca-certs --from-file=2_intermediate/certs/ca-chain.cert.pem
secret "istio-ingressgateway-ca-certs" created
```
1. Redefine your previous `Gateway` while changing the `tls` `mode` to `MUTUAL` and specifying `caCertificates`:
> 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 case `ca-chain.cert.pem`.
```bash
cat <<EOF | istioctl replace -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
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"
EOF
```
1. Access the _httpbin_ service by HTTPS as in the previous section:
```command
$ curl --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert 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 failure
```
> It may take time for the gateway definition to propagate and you may still get _418_. Wait for a minute and retry
the _curl_ call again.
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.
1. Resend the previous request by _curl_, this time passing as parameters your client certificate (the `--cert` option)
and your private key (the `--key` option):
```command
$ curl --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert 2_intermediate/certs/ca-chain.cert.pem --cert 4_client/certs/httpbin.example.com.cert.pem --key 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.
## Troubleshooting
1. Inspect the values of the `INGRESS_HOST` and `SECURE_INGRESS_PORT` environment variables. Make sure
they have valid values, according to the output of the following commands:
```command
$ kubectl get svc -n istio-system
$ echo INGRESS_HOST=$INGRESS_HOST, SECURE_INGRESS_PORT=$SECURE_INGRESS_PORT
```
1. Verify that the key and the certificate are successfully loaded in the `istio-ingressgateway` pod:
```command
$ 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-certs
```
`tls.crt` and `tls.key` should exist in the directory contents.
1. Check the log of `istio-ingressgateway` for error messages:
```command
$ kubectl logs -n istio-system -l istio=ingressgateway
```
1. For mutual TLS, verify that the CA certificate is loaded in the `istio-ingressgateway` pod:
```command
$ 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-certs
```
`ca-chain.cert.pem` should exist in the directory contents.
1. For macOS users, verify that you use _curl_ compiled with the [LibreSSL](http://www.libressl.org) library, as
described in the [Before you begin](#before-you-begin) section.
## Cleanup
1. Delete the `Gateway` configuration, the `VirtualService` and the secrets:
```command
$ istioctl delete gateway httpbin-gateway
$ istioctl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -n istio-system secret istio-ingressgateway-certs istio-ingressgateway-ca-certs
```
1. Shutdown the [httpbin](https://github.com/istio/istio/blob/{{<branch_name>}}/samples/httpbin) service:
```command
$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@
```

View File

@ -3,5 +3,6 @@ title: How to configure Istio Ingress to only accept TLS traffic?
weight: 130
---
By following the instructions on [Adding a secure port to a gateway](/docs/tasks/traffic-management/ingress/#add-a-secure-port-https-to-our-gateway) and on [Disabling an HTTP port in a gateway](/docs/tasks/traffic-management/ingress/#disable-the-http-port)
By following the instructions in the
[Secure Ingress Traffic](/docs/tasks/traffic-management/secure-ingress) task,
Istio Ingress can be secured to only accept TLS traffic.